Skip to content

Commit b0cf76c

Browse files
fertapricmichalmuskala
authored andcommitted
Fix year in negative datetimes with offset (#8038)
1 parent 7841fbb commit b0cf76c

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

lib/elixir/lib/calendar/datetime.ex

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,30 +513,28 @@ defmodule DateTime do
513513
@doc since: "1.4.0"
514514
@spec from_iso8601(String.t(), Calendar.calendar()) ::
515515
{:ok, t, Calendar.utc_offset()} | {:error, atom}
516-
517516
def from_iso8601(string, calendar \\ Calendar.ISO)
518517

519518
def from_iso8601(<<?-, rest::binary>>, calendar) do
520-
with {:ok, %{year: year} = datetime, offset} <- raw_from_iso8601(rest, calendar) do
521-
{:ok, %{datetime | year: -year}, offset}
522-
end
519+
raw_from_iso8601(rest, calendar, true)
523520
end
524521

525522
def from_iso8601(<<rest::binary>>, calendar) do
526-
raw_from_iso8601(rest, calendar)
523+
raw_from_iso8601(rest, calendar, false)
527524
end
528525

529526
@sep [?\s, ?T]
530527
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
531528
[match_time, guard_time, read_time] = Calendar.ISO.__match_time__()
532529

533-
defp raw_from_iso8601(string, calendar) do
530+
defp raw_from_iso8601(string, calendar, is_negative_datetime) do
534531
with <<unquote(match_date), sep, unquote(match_time), rest::binary>> <- string,
535532
true <- unquote(guard_date) and sep in @sep and unquote(guard_time),
536533
{microsecond, rest} <- Calendar.ISO.parse_microsecond(rest),
537534
{offset, ""} <- Calendar.ISO.parse_offset(rest) do
538535
{year, month, day} = unquote(read_date)
539536
{hour, minute, second} = unquote(read_time)
537+
year = if is_negative_datetime, do: -year, else: year
540538

541539
cond do
542540
not calendar.valid_date?(year, month, day) ->

lib/elixir/test/elixir/calendar_test.exs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,21 @@ defmodule DateTimeTest do
322322
minute: 50,
323323
second: 7
324324
}
325+
326+
assert DateTime.from_iso8601("0000-01-01T01:22:07+10:30") |> elem(1) ==
327+
%DateTime{
328+
microsecond: {0, 0},
329+
month: 12,
330+
std_offset: 0,
331+
time_zone: "Etc/UTC",
332+
utc_offset: 0,
333+
year: -1,
334+
zone_abbr: "UTC",
335+
day: 31,
336+
hour: 14,
337+
minute: 52,
338+
second: 7
339+
}
325340
end
326341

327342
test "from_iso8601/1 handles negative dates" do
@@ -354,6 +369,51 @@ defmodule DateTimeTest do
354369
minute: 50,
355370
second: 7
356371
}
372+
373+
assert DateTime.from_iso8601("-0001-01-01T01:22:07+10:30") |> elem(1) ==
374+
%DateTime{
375+
microsecond: {0, 0},
376+
month: 12,
377+
std_offset: 0,
378+
time_zone: "Etc/UTC",
379+
utc_offset: 0,
380+
year: -2,
381+
zone_abbr: "UTC",
382+
day: 31,
383+
hour: 14,
384+
minute: 52,
385+
second: 7
386+
}
387+
388+
assert DateTime.from_iso8601("-0001-01-01T01:22:07-10:30") |> elem(1) ==
389+
%DateTime{
390+
microsecond: {0, 0},
391+
month: 1,
392+
std_offset: 0,
393+
time_zone: "Etc/UTC",
394+
utc_offset: 0,
395+
year: -1,
396+
zone_abbr: "UTC",
397+
day: 1,
398+
hour: 11,
399+
minute: 52,
400+
second: 7
401+
}
402+
403+
assert DateTime.from_iso8601("-0001-12-31T23:22:07-10:30") |> elem(1) ==
404+
%DateTime{
405+
microsecond: {0, 0},
406+
month: 1,
407+
std_offset: 0,
408+
time_zone: "Etc/UTC",
409+
utc_offset: 0,
410+
year: 0,
411+
zone_abbr: "UTC",
412+
day: 1,
413+
hour: 9,
414+
minute: 52,
415+
second: 7
416+
}
357417
end
358418

359419
test "from_unix/2" do

0 commit comments

Comments
 (0)