Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Date to accept year outside ISO8601 range #13551

Merged
merged 5 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/elixir/lib/calendar/date.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1157,10 +1157,20 @@ defmodule Date do
end

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

def inspect(%{calendar: calendar, year: year, month: month, day: day}, _)
when calendar == Calendar.ISO do
"Date.new!(#{Integer.to_string(year)}, #{Integer.to_string(month)}, #{Integer.to_string(day)})"
end

def inspect(%{calendar: calendar, year: year, month: month, day: day}, _) do
"Date.new!(#{Integer.to_string(year)}, #{Integer.to_string(month)}, #{Integer.to_string(day)}, #{inspect(calendar)})"
end

defp suffix(Calendar.ISO), do: ""
defp suffix(calendar), do: " " <> inspect(calendar)
end
Expand Down
8 changes: 4 additions & 4 deletions lib/elixir/lib/calendar/iso.ex
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ defmodule Calendar.ISO do
]
end

defguardp is_year(year) when year in -9999..9999
defguardp is_year_BCE(year) when year in -9999..0
defguardp is_year_CE(year) when year in 1..9999
defguardp is_year(year) when is_integer(year)
defguardp is_year_BCE(year) when year <= 0
defguardp is_year_CE(year) when year >= 1
defguardp is_month(month) when month in 1..12
defguardp is_day(day) when day in 1..31
defguardp is_hour(hour) when hour in 0..23
Expand Down Expand Up @@ -809,7 +809,7 @@ defmodule Calendar.ISO do

# Converts count of days since 0000-01-01 to {year, month, day} tuple.
@doc false
def date_from_iso_days(days) when days in -3_652_059..3_652_424 do
def date_from_iso_days(days) do
{year, day_of_year} = days_to_year(days)
extra_day = if leap_year?(year), do: 1, else: 0
{month, day_in_month} = year_day_to_year_date(extra_day, day_of_year)
Expand Down
61 changes: 52 additions & 9 deletions lib/elixir/test/elixir/calendar/date_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ defmodule DateTest do

assert to_string(%{date | calendar: FakeCalendar}) == "1/1/2000"
assert Date.to_string(%{date | calendar: FakeCalendar}) == "1/1/2000"

date2 = Date.new!(5_874_897, 12, 31)
assert to_string(date2) == "5874897-12-31"
assert Date.to_string(date2) == "5874897-12-31"
assert Date.to_string(Map.from_struct(date2)) == "5874897-12-31"

assert to_string(%{date2 | calendar: FakeCalendar}) == "31/12/5874897"
assert Date.to_string(%{date2 | calendar: FakeCalendar}) == "31/12/5874897"
end

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

date = %{~D[2000-01-01] | calendar: FakeCalendar}
assert inspect(date) == "~D[1/1/2000 FakeCalendar]"

assert inspect(Date.new!(5_874_897, 12, 31)) == "Date.new!(5874897, 12, 31)"
assert inspect(Date.new!(-5_874_897, 1, 1)) == "Date.new!(-5874897, 1, 1)"

date2 = %{Date.new!(5_874_897, 12, 31) | calendar: FakeCalendar}

assert inspect(%{date2 | calendar: FakeCalendar}) ==
"Date.new!(5874897, 12, 31, FakeCalendar)"
end

test "compare/2" do
date1 = ~D[-0001-12-30]
date2 = ~D[-0001-12-31]
date3 = ~D[0001-01-01]
date4 = Date.new!(5_874_897, 12, 31)
date5 = Date.new!(-4713, 1, 1)

assert Date.compare(date1, date1) == :eq
assert Date.compare(date1, date2) == :lt
assert Date.compare(date2, date1) == :gt
assert Date.compare(date3, date3) == :eq
assert Date.compare(date2, date3) == :lt
assert Date.compare(date3, date2) == :gt
assert Date.compare(date4, date1) == :gt
assert Date.compare(date1, date4) == :lt
assert Date.compare(date4, date4) == :eq
assert Date.compare(date4, date5) == :gt
assert Date.compare(date5, date4) == :lt
assert Date.compare(date5, date5) == :eq
end

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

assert Date.before?(date1, date2)
assert Date.before?(date1, date3)
assert Date.before?(date4, date1)
assert not Date.before?(date2, date1)
assert not Date.before?(date3, date1)
assert not Date.before?(date1, date4)

assert Date.after?(date2, date1)
assert Date.after?(date3, date2)
assert Date.after?(date2, date4)
assert not Date.after?(date1, date2)
assert not Date.after?(date2, date3)
assert not Date.after?(date4, date2)
end

test "compare/2 across calendars" do
Expand Down Expand Up @@ -149,22 +184,16 @@ defmodule DateTest do

test "add/2" do
assert Date.add(~D[0000-01-01], 3_652_424) == ~D[9999-12-31]

assert_raise FunctionClauseError, fn ->
Date.add(~D[0000-01-01], 3_652_425)
end

assert Date.add(~D[0000-01-01], 3_652_425) == Date.new!(10000, 1, 1)
assert Date.add(~D[0000-01-01], -1) == ~D[-0001-12-31]
assert Date.add(~D[0000-01-01], -365) == ~D[-0001-01-01]
assert Date.add(~D[0000-01-01], -366) == ~D[-0002-12-31]
assert Date.add(~D[0000-01-01], -(365 * 4)) == ~D[-0004-01-02]
assert Date.add(~D[0000-01-01], -(365 * 5)) == ~D[-0005-01-02]
assert Date.add(~D[0000-01-01], -(365 * 100)) == ~D[-0100-01-25]
assert Date.add(~D[0000-01-01], -3_652_059) == ~D[-9999-01-01]

assert_raise FunctionClauseError, fn ->
Date.add(~D[0000-01-01], -3_652_060)
end
assert Date.add(~D[0000-01-01], -3_652_060) == Date.new!(-10000, 12, 31)
assert Date.add(Date.new!(5_874_897, 12, 31), 1) == Date.new!(5_874_898, 1, 1)
end

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

assert Date.diff(Date.new!(5_874_898, 1, 1), Date.new!(5_874_897, 1, 1)) == 365
assert Date.diff(Date.new!(5_874_905, 1, 1), Date.new!(5_874_904, 1, 1)) == 366

date1 = ~D[2000-01-01]
date2 = Calendar.Holocene.date(12000, 01, 14)
assert Date.diff(date1, date2) == -13
Expand All @@ -194,6 +226,17 @@ defmodule DateTest do
assert Date.shift(~D[2000-01-01], month: 12) == ~D[2001-01-01]
assert Date.shift(~D[0000-01-01], day: 2, year: 1, month: 37) == ~D[0004-02-03]

assert Date.shift(Date.new!(5_874_904, 2, 29), day: -1) == Date.new!(5_874_904, 2, 28)
assert Date.shift(Date.new!(5_874_904, 2, 29), month: -2) == Date.new!(5_874_903, 12, 29)
assert Date.shift(Date.new!(5_874_904, 2, 29), week: -9) == Date.new!(5_874_903, 12, 28)
assert Date.shift(Date.new!(5_874_904, 2, 29), month: 1) == Date.new!(5_874_904, 3, 29)
assert Date.shift(Date.new!(5_874_904, 2, 29), year: -1) == Date.new!(5_874_903, 2, 28)
assert Date.shift(Date.new!(5_874_904, 2, 29), year: -4) == Date.new!(5_874_900, 2, 28)
assert Date.shift(Date.new!(5_874_904, 2, 29), year: 4) == Date.new!(5_874_908, 2, 29)

assert Date.shift(Date.new!(5_874_904, 2, 29), day: 1, year: 4, month: 2) ==
Date.new!(5_874_908, 4, 30)

assert_raise ArgumentError,
"unsupported unit :second. Expected :year, :month, :week, :day",
fn -> Date.shift(~D[2012-02-29], second: 86400) end
Expand Down
8 changes: 0 additions & 8 deletions lib/elixir/test/elixir/calendar/iso_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,6 @@ defmodule Calendar.ISOTest do

random_positive_year = Enum.random(1..9999)
assert Calendar.ISO.year_of_era(random_positive_year, 1, 1) == {random_positive_year, 1}

assert_raise FunctionClauseError, fn ->
Calendar.ISO.year_of_era(10000, 1, 1)
end

assert_raise FunctionClauseError, fn ->
Calendar.ISO.year_of_era(-10000, 12, 1)
end
end

defp iso_day_roundtrip(year, month, day) do
Expand Down
Loading