Skip to content

Commit 5d685da

Browse files
anthonatorIgor Kapkov
authored and
Igor Kapkov
committed
Add support for DateTime and NaiveDateTime (elixirs#87)
* Added DateTime support * Standardized Faker.Date with Faker.DateTime * Updated CHANGELOG * Updated USAGE.md * Don't test against now() for DateTime.t * Fixed test for Faker.DateTime.forward/1 * Removed private now() function * Cleaned up Faker.DateTime.between\2 implementation; fixed tests * Added Faker.DateTime.between(Date.T, Date.t) :: DateTime.t implementation * Fixed comments * Added NaiveDateTime support for Faker.DateTime.between/2 * Fixed comment for NaiveDateTime version of Faker.DateTime.between/2 * Removed unused variable * Added NaiveDateTime support * Updated CHANGELOG.md and USAGE.md * Use NaiveDateTime in USAGE * Added copy and paste examples for Faker.DateTime.between/2 and Faker.NaiveDateTime.between/2
1 parent 8d8dd3e commit 5d685da

8 files changed

+322
-10
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Change log itself follows [Keep a CHANGELOG](http://keepachangelog.com) format.
1515
* `Faker.Code.iban` [@tobyhinloopen][]
1616
* `Faker.Beer` [@orieken][]
1717
* `Faker.Date` [@tobyhinloopen][]
18+
* `Faker.Date.between` [@anthonator][]
19+
* `Faker.DateTime` [@anthonator][]
20+
* `Faker.NaiveDateTime` [@anthonator][]
1821
* `Faker.Nato` [@petehamilton][]
1922
* `Faker.Pokemon` [@orieken][]
2023
* `Faker.App.semver` [@wojtekmach][]

USAGE.md

+40
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- [Faker.Commerce](#fakercommerce)
88
- [Faker.Company](#fakercompany)
99
- [Faker.Date](#fakerdate)
10+
- [Faker.DateTime](#fakerdatetime)
11+
- [Faker.NaiveDateTime](#fakernaivedatetime)
1012
- [Faker.File](#fakerfile)
1113
- [Faker.Internet](#fakerinternet)
1214
- [Faker.Internet.UserAgent](#fakerinternetuseragent)
@@ -177,6 +179,44 @@ Faker.Date.date_of_birth(10..19) #=> ~D[2004-05-15]
177179
Faker.Date.forward(4) #=> ~D[2016-12-25]
178180

179181
Faker.Date.backward(4) #=> ~D[2016-12-20]
182+
183+
Faker.Date.between(~D[2016-12-20], ~D[2016-12-25]) #=> ~D[2016-12-23]
184+
```
185+
186+
### Faker.DateTime
187+
188+
```elixir
189+
Faker.DateTime.forward(4)
190+
#=> %DateTime{calendar: Calendar.ISO, day: 25, hour: 6,
191+
#=> microsecond: {922180, 6}, minute: 2, month: 12, second: 17,
192+
#=> std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0, year: 2016,
193+
#=> zone_abbr: "UTC"}
194+
195+
Faker.DateTime.backward(4)
196+
#=> %DateTime{calendar: Calendar.ISO, day: 20, hour: 6,
197+
#=> microsecond: {922180, 6}, minute: 2, month: 12, second: 17,
198+
#=> std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0, year: 2016,
199+
#=> zone_abbr: "UTC"}
200+
201+
from = DateTime.utc_now()
202+
to = DateTime.utc_now() |> DateTime.to_unix
203+
to = to + 86400 * 10 |> DateTime.from_unix!
204+
205+
Faker.DateTime.between(from, to)
206+
#=> %DateTime{calendar: Calendar.ISO, day: 23, hour: 6,
207+
#=> microsecond: {922180, 6}, minute: 2, month: 12, second: 17,
208+
#=> std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0, year: 2016,
209+
#=> zone_abbr: "UTC"}
210+
```
211+
212+
### Faker.NaiveDateTime
213+
214+
```elixir
215+
Faker.NaiveDateTime.forward(4) #=> ~N[2016-12-25 06:02:17.922180]
216+
217+
Faker.NaiveDateTime.backward(4) #=> ~N[2016-12-20 06:02:17.922180]
218+
219+
Faker.NaiveDateTime.between(~N[2016-12-20 00:00:00], ~N[2016-12-25 00:00:00]) #=> ~N[2016-12-23 06:02:17.922180]
180220
```
181221

182222
### Faker.File

lib/faker/date.ex

+10-10
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ if Version.match?(System.version(), ">= 1.3.0") do
44
Functions for generating dates
55
"""
66

7-
@seconds_per_day 86400
8-
97
@doc """
108
Returns a random date of birth for a person with an age specified by a number or range
119
@@ -47,15 +45,17 @@ if Version.match?(System.version(), ">= 1.3.0") do
4745
"""
4846
@spec forward(integer) :: Date.t
4947
def forward(days) do
50-
unix_now =
51-
DateTime.utc_now()
52-
|> DateTime.to_unix()
53-
54-
sign = if days < 0, do: -1, else: 1
48+
Faker.DateTime.forward(days)
49+
|> DateTime.to_date
50+
end
5551

56-
unix_now + sign * @seconds_per_day * :crypto.rand_uniform(1, abs(days))
57-
|> DateTime.from_unix!()
58-
|> DateTime.to_date()
52+
@doc """
53+
Returns a random date between two dates
54+
"""
55+
@spec between(Date.t, Date.t) :: Date.t
56+
def between(from, to) do
57+
Faker.DateTime.between(from, to)
58+
|> DateTime.to_date
5959
end
6060
end
6161
end

lib/faker/datetime.ex

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
if Version.match?(System.version(), ">= 1.3.0") do
2+
defmodule Faker.DateTime do
3+
@microseconds_per_day 86400000000
4+
5+
@doc """
6+
Returns a random date in the past up to N days, today not included
7+
"""
8+
@spec backward(integer) :: DateTime.t
9+
def backward(days) do
10+
forward(-days)
11+
end
12+
13+
@doc """
14+
Returns a random date in the future up to N days, today not included
15+
"""
16+
@spec forward(integer) :: DateTime.t
17+
def forward(days) do
18+
sign = if days < 0, do: -1, else: 1
19+
20+
today = DateTime.utc_now() |> to_timestamp
21+
from = today + sign * @microseconds_per_day # add or subtract extra day to avoid returning today
22+
to = from + @microseconds_per_day * days
23+
24+
unix_between(from, to)
25+
end
26+
27+
@doc """
28+
Returns a random date & time between two dates
29+
"""
30+
@spec between(Date.t, Date.t) :: DateTime.t
31+
def between(%Date{} = from, %Date{} = to) do
32+
between(date_to_datetime(from), date_to_datetime(to))
33+
end
34+
35+
@doc """
36+
Returns a random `DateTime.t` between two `NaiveDateTime.t`'s
37+
"""
38+
@spec between(NaiveDateTime.t, NaiveDateTime.t) :: DateTime.t
39+
def between(%NaiveDateTime{} = from, %NaiveDateTime{} = to) do
40+
between(naivedatetime_to_datetime(from), naivedatetime_to_datetime(to))
41+
end
42+
43+
@doc """
44+
Returns a random `DateTime.t` between two `DateTime.t`'s
45+
"""
46+
@spec between(DateTime.t, DateTime.t) :: DateTime.t
47+
def between(from, to) do
48+
unix_between(to_timestamp(from), to_timestamp(to))
49+
end
50+
51+
# private
52+
53+
defp date_to_datetime(date) do
54+
%DateTime{calendar: Calendar.ISO, day: date.day, hour: 0, minute: 0,
55+
month: date.month, second: 0, time_zone: "Etc/UTC",
56+
utc_offset: 0, std_offset: 0, year: date.year, zone_abbr: "UTC"}
57+
end
58+
59+
defp naivedatetime_to_datetime(naivedatetime) do
60+
%DateTime{calendar: naivedatetime.calendar, day: naivedatetime.day,
61+
hour: naivedatetime.hour, minute: naivedatetime.minute,
62+
month: naivedatetime.month, second: naivedatetime.second,
63+
time_zone: "Etc/UTC", utc_offset: 0, std_offset: 0,
64+
year: naivedatetime.year, zone_abbr: "UTC"}
65+
end
66+
67+
defp to_timestamp(datetime) do
68+
DateTime.to_unix(datetime, :microseconds)
69+
end
70+
71+
defp unix_between(from, to) do
72+
diff = to - from
73+
sign = if diff < 0, do: -1, else: 1
74+
75+
from + sign * :crypto.rand_uniform(0, abs(diff))
76+
|> DateTime.from_unix!(:microseconds)
77+
end
78+
end
79+
end

lib/faker/naivedatetime.ex

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
if Version.match?(System.version(), ">= 1.3.0") do
2+
defmodule Faker.NaiveDateTime do
3+
@doc """
4+
Returns a random date in the past up to N days, today not included
5+
"""
6+
@spec backward(integer) :: NaiveDateTime.t
7+
def backward(days) do
8+
forward(-days)
9+
end
10+
11+
@doc """
12+
Returns a random date in the future up to N days, today not included
13+
"""
14+
@spec forward(integer) :: NaiveDateTime.t
15+
def forward(days) do
16+
Faker.DateTime.forward(days)
17+
|> DateTime.to_naive
18+
end
19+
20+
@doc """
21+
Returns a random `NaiveDateTime.t` between two `NaiveDateTime.t`'s
22+
"""
23+
@spec between(NaiveDateTime.t, NaiveDateTime.t) :: NaiveDateTime.t
24+
def between(from, to) do
25+
Faker.DateTime.between(from, to)
26+
|> DateTime.to_naive
27+
end
28+
end
29+
end

test/faker/date_test.exs

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ if Version.match?(System.version(), ">= 1.3.0") do
2626
assert now().year > year || now().month > month || now().day > day
2727
end
2828

29+
test "between/2" do
30+
from_date = ~D[2017-01-01]
31+
to_date = ~D[2017-01-10]
32+
between_date = Faker.Date.between(from_date, to_date)
33+
assert %Date{year: year, month: month, day: day} = between_date
34+
assert from_date.year <= year || from_date.month <= month || from_date.day <= day
35+
assert to_date.year >= year || from_date.month >= month || to_date.day >= day
36+
end
37+
2938
defp age(%Date{year: year, month: month, day: day}) do
3039
%Date{ year: current_year, month: current_month, day: current_day } = now()
3140
already_aged_this_year = current_month > month || current_month == month && day >= current_day

test/faker/datetime_test.exs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
if Version.match?(System.version(), ">= 1.3.0") do
2+
defmodule DateTimeTest do
3+
use ExUnit.Case, async: true
4+
5+
test "forward/1" do
6+
now = DateTime.utc_now()
7+
forwarded_date = Faker.DateTime.forward(10)
8+
assert %DateTime{
9+
year: year, month: month, day: day, hour: hour, minute: minute,
10+
second: second, microsecond: microsecond
11+
} = forwarded_date
12+
assert now.year < year || now.month < month
13+
|| now.hour < hour || now.minute < minute
14+
|| now.day < day || now.second < second
15+
|| now.microsecond < microsecond
16+
end
17+
18+
test "backward/1" do
19+
now = DateTime.utc_now()
20+
backward_date = Faker.DateTime.backward(10)
21+
assert %DateTime{
22+
year: year, month: month, day: day, hour: hour, minute: minute,
23+
second: second, microsecond: microsecond
24+
} = backward_date
25+
assert now.year > year || now.month > month
26+
|| now.hour > hour || now.minute > minute
27+
|| now.day > day || now.second > second
28+
|| now.microsecond > microsecond
29+
end
30+
31+
test "between/2 for Date.t" do
32+
from_date = DateTime.utc_now() |> DateTime.to_date
33+
to_date = Faker.DateTime.forward(50) |> DateTime.to_date
34+
between_date = Faker.DateTime.between(from_date, to_date)
35+
assert %DateTime{year: year, month: month, day: day}
36+
= between_date
37+
assert from_date.year <= year || from_date.month <= month
38+
|| from_date.day <= day
39+
assert to_date.year >= year || to_date.month >= month
40+
|| to_date.day >= day
41+
end
42+
43+
test "between/2 for NaiveDateTime.t" do
44+
from_datetime = DateTime.utc_now()
45+
from_date = from_datetime |> DateTime.to_date
46+
from_time = from_datetime |> DateTime.to_time
47+
{:ok, from_naivedatetime} = NaiveDateTime.new(from_date, from_time)
48+
49+
to_datetime = Faker.DateTime.forward(50)
50+
to_date = to_datetime |> DateTime.to_date
51+
to_time = to_datetime |> DateTime.to_time
52+
{:ok, to_naivedatetime} = NaiveDateTime.new(to_date, to_time)
53+
54+
between_date = Faker.DateTime.between(from_naivedatetime, to_naivedatetime)
55+
assert %DateTime{
56+
year: year, month: month, day: day, hour: hour, minute: minute,
57+
second: second, microsecond: microsecond
58+
} = between_date
59+
assert from_naivedatetime.year <= year || from_naivedatetime.month <= month
60+
|| from_naivedatetime.hour <= hour || from_naivedatetime.minute <= minute
61+
|| from_naivedatetime.day <= day || from_naivedatetime.second <= second
62+
|| from_naivedatetime.microsecond <= microsecond
63+
assert to_naivedatetime.year >= year || to_naivedatetime.month >= month
64+
|| to_naivedatetime.hour >= hour || to_naivedatetime.minute >= minute
65+
|| to_naivedatetime.day >= day || to_naivedatetime.second >= second
66+
|| to_naivedatetime.microsecond >= microsecond
67+
end
68+
69+
test "between/2 for DateTime.t" do
70+
from_date = DateTime.utc_now()
71+
to_date = Faker.DateTime.forward(50)
72+
between_date = Faker.DateTime.between(from_date, to_date)
73+
assert %DateTime{
74+
year: year, month: month, day: day, hour: hour, minute: minute,
75+
second: second, microsecond: microsecond
76+
} = between_date
77+
assert from_date.year <= year || from_date.month <= month
78+
|| from_date.hour <= hour || from_date.minute <= minute
79+
|| from_date.day <= day || from_date.second <= second
80+
|| from_date.microsecond <= microsecond
81+
assert to_date.year >= year || to_date.month >= month
82+
|| to_date.hour >= hour || to_date.minute >= minute
83+
|| to_date.day >= day || to_date.second >= second
84+
|| to_date.microsecond >= microsecond
85+
end
86+
end
87+
end

test/faker/naivedatetime_test.exs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
if Version.match?(System.version(), ">= 1.3.0") do
2+
defmodule NaiveDateTimeTest do
3+
use ExUnit.Case, async: true
4+
5+
test "forward/1" do
6+
now_datetime = DateTime.utc_now()
7+
now_date = now_datetime |> DateTime.to_date
8+
now_time = now_datetime |> DateTime.to_time
9+
{:ok, now} = NaiveDateTime.new(now_date, now_time)
10+
11+
forwarded_date = Faker.NaiveDateTime.forward(10)
12+
assert %NaiveDateTime{
13+
year: year, month: month, day: day, hour: hour, minute: minute,
14+
second: second, microsecond: microsecond
15+
} = forwarded_date
16+
assert now.year < year || now.month < month
17+
|| now.hour < hour || now.minute < minute
18+
|| now.day < day || now.second < second
19+
|| now.microsecond < microsecond
20+
end
21+
22+
test "backward/1" do
23+
now_datetime = DateTime.utc_now()
24+
now_date = now_datetime |> DateTime.to_date
25+
now_time = now_datetime |> DateTime.to_time
26+
{:ok, now} = NaiveDateTime.new(now_date, now_time)
27+
28+
backward_date = Faker.NaiveDateTime.backward(10)
29+
assert %NaiveDateTime{
30+
year: year, month: month, day: day, hour: hour, minute: minute,
31+
second: second, microsecond: microsecond
32+
} = backward_date
33+
assert now.year > year || now.month > month
34+
|| now.hour > hour || now.minute > minute
35+
|| now.day > day || now.second > second
36+
|| now.microsecond > microsecond
37+
end
38+
39+
test "between/2" do
40+
from_datetime = DateTime.utc_now()
41+
from_date = from_datetime |> DateTime.to_date
42+
from_time = from_datetime |> DateTime.to_time
43+
{:ok, from_naivedatetime} = NaiveDateTime.new(from_date, from_time)
44+
45+
to_datetime = Faker.DateTime.forward(50)
46+
to_date = to_datetime |> DateTime.to_date
47+
to_time = to_datetime |> DateTime.to_time
48+
{:ok, to_naivedatetime} = NaiveDateTime.new(to_date, to_time)
49+
50+
between_date = Faker.DateTime.between(from_naivedatetime, to_naivedatetime)
51+
assert %DateTime{
52+
year: year, month: month, day: day, hour: hour, minute: minute,
53+
second: second, microsecond: microsecond
54+
} = between_date
55+
assert from_naivedatetime.year <= year || from_naivedatetime.month <= month
56+
|| from_naivedatetime.hour <= hour || from_naivedatetime.minute <= minute
57+
|| from_naivedatetime.day <= day || from_naivedatetime.second <= second
58+
|| from_naivedatetime.microsecond <= microsecond
59+
assert to_naivedatetime.year >= year || to_naivedatetime.month >= month
60+
|| to_naivedatetime.hour >= hour || to_naivedatetime.minute >= minute
61+
|| to_naivedatetime.day >= day || to_naivedatetime.second >= second
62+
|| to_naivedatetime.microsecond >= microsecond
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)