Skip to content

Commit 3d7f4dd

Browse files
authored
Allow Date to accept year outside of -9999..9999 range (#13551)
1 parent 4226a8a commit 3d7f4dd

File tree

4 files changed

+67
-22
lines changed

4 files changed

+67
-22
lines changed

lib/elixir/lib/calendar/date.ex

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,10 +1157,20 @@ defmodule Date do
11571157
end
11581158

11591159
defimpl Inspect do
1160-
def inspect(%{calendar: calendar, year: year, month: month, day: day}, _) do
1160+
def inspect(%{calendar: calendar, year: year, month: month, day: day}, _)
1161+
when year in -9999..9999 do
11611162
"~D[" <> calendar.date_to_string(year, month, day) <> suffix(calendar) <> "]"
11621163
end
11631164

1165+
def inspect(%{calendar: calendar, year: year, month: month, day: day}, _)
1166+
when calendar == Calendar.ISO do
1167+
"Date.new!(#{Integer.to_string(year)}, #{Integer.to_string(month)}, #{Integer.to_string(day)})"
1168+
end
1169+
1170+
def inspect(%{calendar: calendar, year: year, month: month, day: day}, _) do
1171+
"Date.new!(#{Integer.to_string(year)}, #{Integer.to_string(month)}, #{Integer.to_string(day)}, #{inspect(calendar)})"
1172+
end
1173+
11641174
defp suffix(Calendar.ISO), do: ""
11651175
defp suffix(calendar), do: " " <> inspect(calendar)
11661176
end

lib/elixir/lib/calendar/iso.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,9 @@ defmodule Calendar.ISO do
230230
]
231231
end
232232

233-
defguardp is_year(year) when year in -9999..9999
234-
defguardp is_year_BCE(year) when year in -9999..0
235-
defguardp is_year_CE(year) when year in 1..9999
233+
defguardp is_year(year) when is_integer(year)
234+
defguardp is_year_BCE(year) when year <= 0
235+
defguardp is_year_CE(year) when year >= 1
236236
defguardp is_month(month) when month in 1..12
237237
defguardp is_day(day) when day in 1..31
238238
defguardp is_hour(hour) when hour in 0..23
@@ -809,7 +809,7 @@ defmodule Calendar.ISO do
809809

810810
# Converts count of days since 0000-01-01 to {year, month, day} tuple.
811811
@doc false
812-
def date_from_iso_days(days) when days in -3_652_059..3_652_424 do
812+
def date_from_iso_days(days) do
813813
{year, day_of_year} = days_to_year(days)
814814
extra_day = if leap_year?(year), do: 1, else: 0
815815
{month, day_in_month} = year_day_to_year_date(extra_day, day_of_year)

lib/elixir/test/elixir/calendar/date_test.exs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ defmodule DateTest do
4242

4343
assert to_string(%{date | calendar: FakeCalendar}) == "1/1/2000"
4444
assert Date.to_string(%{date | calendar: FakeCalendar}) == "1/1/2000"
45+
46+
date2 = Date.new!(5_874_897, 12, 31)
47+
assert to_string(date2) == "5874897-12-31"
48+
assert Date.to_string(date2) == "5874897-12-31"
49+
assert Date.to_string(Map.from_struct(date2)) == "5874897-12-31"
50+
51+
assert to_string(%{date2 | calendar: FakeCalendar}) == "31/12/5874897"
52+
assert Date.to_string(%{date2 | calendar: FakeCalendar}) == "31/12/5874897"
4553
end
4654

4755
test "inspect/1" do
@@ -50,29 +58,56 @@ defmodule DateTest do
5058

5159
date = %{~D[2000-01-01] | calendar: FakeCalendar}
5260
assert inspect(date) == "~D[1/1/2000 FakeCalendar]"
61+
62+
assert inspect(Date.new!(5_874_897, 12, 31)) == "Date.new!(5874897, 12, 31)"
63+
assert inspect(Date.new!(-5_874_897, 1, 1)) == "Date.new!(-5874897, 1, 1)"
64+
65+
date2 = %{Date.new!(5_874_897, 12, 31) | calendar: FakeCalendar}
66+
67+
assert inspect(%{date2 | calendar: FakeCalendar}) ==
68+
"Date.new!(5874897, 12, 31, FakeCalendar)"
5369
end
5470

5571
test "compare/2" do
5672
date1 = ~D[-0001-12-30]
5773
date2 = ~D[-0001-12-31]
5874
date3 = ~D[0001-01-01]
75+
date4 = Date.new!(5_874_897, 12, 31)
76+
date5 = Date.new!(-4713, 1, 1)
77+
5978
assert Date.compare(date1, date1) == :eq
6079
assert Date.compare(date1, date2) == :lt
6180
assert Date.compare(date2, date1) == :gt
6281
assert Date.compare(date3, date3) == :eq
6382
assert Date.compare(date2, date3) == :lt
6483
assert Date.compare(date3, date2) == :gt
84+
assert Date.compare(date4, date1) == :gt
85+
assert Date.compare(date1, date4) == :lt
86+
assert Date.compare(date4, date4) == :eq
87+
assert Date.compare(date4, date5) == :gt
88+
assert Date.compare(date5, date4) == :lt
89+
assert Date.compare(date5, date5) == :eq
6590
end
6691

6792
test "before?/2 and after?/2" do
6893
date1 = ~D[2022-11-01]
6994
date2 = ~D[2022-11-02]
95+
date3 = Date.new!(5_874_897, 12, 31)
96+
date4 = Date.new!(-4713, 1, 1)
7097

7198
assert Date.before?(date1, date2)
99+
assert Date.before?(date1, date3)
100+
assert Date.before?(date4, date1)
72101
assert not Date.before?(date2, date1)
102+
assert not Date.before?(date3, date1)
103+
assert not Date.before?(date1, date4)
73104

74105
assert Date.after?(date2, date1)
106+
assert Date.after?(date3, date2)
107+
assert Date.after?(date2, date4)
75108
assert not Date.after?(date1, date2)
109+
assert not Date.after?(date2, date3)
110+
assert not Date.after?(date4, date2)
76111
end
77112

78113
test "compare/2 across calendars" do
@@ -149,22 +184,16 @@ defmodule DateTest do
149184

150185
test "add/2" do
151186
assert Date.add(~D[0000-01-01], 3_652_424) == ~D[9999-12-31]
152-
153-
assert_raise FunctionClauseError, fn ->
154-
Date.add(~D[0000-01-01], 3_652_425)
155-
end
156-
187+
assert Date.add(~D[0000-01-01], 3_652_425) == Date.new!(10000, 1, 1)
157188
assert Date.add(~D[0000-01-01], -1) == ~D[-0001-12-31]
158189
assert Date.add(~D[0000-01-01], -365) == ~D[-0001-01-01]
159190
assert Date.add(~D[0000-01-01], -366) == ~D[-0002-12-31]
160191
assert Date.add(~D[0000-01-01], -(365 * 4)) == ~D[-0004-01-02]
161192
assert Date.add(~D[0000-01-01], -(365 * 5)) == ~D[-0005-01-02]
162193
assert Date.add(~D[0000-01-01], -(365 * 100)) == ~D[-0100-01-25]
163194
assert Date.add(~D[0000-01-01], -3_652_059) == ~D[-9999-01-01]
164-
165-
assert_raise FunctionClauseError, fn ->
166-
Date.add(~D[0000-01-01], -3_652_060)
167-
end
195+
assert Date.add(~D[0000-01-01], -3_652_060) == Date.new!(-10000, 12, 31)
196+
assert Date.add(Date.new!(5_874_897, 12, 31), 1) == Date.new!(5_874_898, 1, 1)
168197
end
169198

170199
test "diff/2" do
@@ -174,6 +203,9 @@ defmodule DateTest do
174203
assert Date.diff(~D[0000-01-01], ~D[-0001-01-01]) == 365
175204
assert Date.diff(~D[-0003-01-01], ~D[-0004-01-01]) == 366
176205

206+
assert Date.diff(Date.new!(5_874_898, 1, 1), Date.new!(5_874_897, 1, 1)) == 365
207+
assert Date.diff(Date.new!(5_874_905, 1, 1), Date.new!(5_874_904, 1, 1)) == 366
208+
177209
date1 = ~D[2000-01-01]
178210
date2 = Calendar.Holocene.date(12000, 01, 14)
179211
assert Date.diff(date1, date2) == -13
@@ -194,6 +226,17 @@ defmodule DateTest do
194226
assert Date.shift(~D[2000-01-01], month: 12) == ~D[2001-01-01]
195227
assert Date.shift(~D[0000-01-01], day: 2, year: 1, month: 37) == ~D[0004-02-03]
196228

229+
assert Date.shift(Date.new!(5_874_904, 2, 29), day: -1) == Date.new!(5_874_904, 2, 28)
230+
assert Date.shift(Date.new!(5_874_904, 2, 29), month: -2) == Date.new!(5_874_903, 12, 29)
231+
assert Date.shift(Date.new!(5_874_904, 2, 29), week: -9) == Date.new!(5_874_903, 12, 28)
232+
assert Date.shift(Date.new!(5_874_904, 2, 29), month: 1) == Date.new!(5_874_904, 3, 29)
233+
assert Date.shift(Date.new!(5_874_904, 2, 29), year: -1) == Date.new!(5_874_903, 2, 28)
234+
assert Date.shift(Date.new!(5_874_904, 2, 29), year: -4) == Date.new!(5_874_900, 2, 28)
235+
assert Date.shift(Date.new!(5_874_904, 2, 29), year: 4) == Date.new!(5_874_908, 2, 29)
236+
237+
assert Date.shift(Date.new!(5_874_904, 2, 29), day: 1, year: 4, month: 2) ==
238+
Date.new!(5_874_908, 4, 30)
239+
197240
assert_raise ArgumentError,
198241
"unsupported unit :second. Expected :year, :month, :week, :day",
199242
fn -> Date.shift(~D[2012-02-29], second: 86400) end

lib/elixir/test/elixir/calendar/iso_test.exs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,6 @@ defmodule Calendar.ISOTest do
114114

115115
random_positive_year = Enum.random(1..9999)
116116
assert Calendar.ISO.year_of_era(random_positive_year, 1, 1) == {random_positive_year, 1}
117-
118-
assert_raise FunctionClauseError, fn ->
119-
Calendar.ISO.year_of_era(10000, 1, 1)
120-
end
121-
122-
assert_raise FunctionClauseError, fn ->
123-
Calendar.ISO.year_of_era(-10000, 12, 1)
124-
end
125117
end
126118

127119
defp iso_day_roundtrip(year, month, day) do

0 commit comments

Comments
 (0)