Skip to content

Commit f5773fe

Browse files
josevalimJosé Valim
authored andcommitted
Take negative years into account in DateTime (#8033)
Signed-off-by: José Valim <[email protected]>
1 parent e2b8ab9 commit f5773fe

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

lib/elixir/lib/calendar/datetime.ex

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,14 @@ defmodule DateTime do
485485
iex> datetime
486486
#DateTime<2015-01-23 21:20:07.123Z>
487487
488+
iex> {:ok, datetime, 0} = DateTime.from_iso8601("-2015-01-23T23:50:07Z")
489+
iex> datetime
490+
#DateTime<-2015-01-23 23:50:07Z>
491+
492+
iex> {:ok, datetime, 9000} = DateTime.from_iso8601("-2015-01-23T23:50:07,123+02:30")
493+
iex> datetime
494+
#DateTime<-2015-01-23 21:20:07.123Z>
495+
488496
iex> DateTime.from_iso8601("2015-01-23P23:50:07")
489497
{:error, :invalid_format}
490498
iex> DateTime.from_iso8601("2015-01-23 23:50:07A")
@@ -506,11 +514,23 @@ defmodule DateTime do
506514
@spec from_iso8601(String.t(), Calendar.calendar()) ::
507515
{:ok, t, Calendar.utc_offset()} | {:error, atom}
508516

517+
def from_iso8601(string, calendar \\ Calendar.ISO)
518+
519+
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
523+
end
524+
525+
def from_iso8601(<<rest::binary>>, calendar) do
526+
raw_from_iso8601(rest, calendar)
527+
end
528+
509529
@sep [?\s, ?T]
510530
[match_date, guard_date, read_date] = Calendar.ISO.__match_date__()
511531
[match_time, guard_time, read_time] = Calendar.ISO.__match_time__()
512532

513-
def from_iso8601(string, calendar \\ Calendar.ISO) when is_binary(string) do
533+
defp raw_from_iso8601(string, calendar) do
514534
with <<unquote(match_date), sep, unquote(match_time), rest::binary>> <- string,
515535
true <- unquote(guard_date) and sep in @sep and unquote(guard_time),
516536
{microsecond, rest} <- Calendar.ISO.parse_microsecond(rest),

lib/elixir/test/elixir/calendar_test.exs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,7 @@ defmodule DateTimeTest do
293293
end
294294

295295
test "from_iso8601/1 handles positive and negative offsets" do
296-
assert DateTime.from_iso8601("2015-01-24T09:50:07-10:00")
297-
|> elem(1) ==
296+
assert DateTime.from_iso8601("2015-01-24T09:50:07-10:00") |> elem(1) ==
298297
%DateTime{
299298
microsecond: {0, 0},
300299
month: 1,
@@ -309,8 +308,7 @@ defmodule DateTimeTest do
309308
second: 7
310309
}
311310

312-
assert DateTime.from_iso8601("2015-01-24T09:50:07+10:00")
313-
|> elem(1) ==
311+
assert DateTime.from_iso8601("2015-01-24T09:50:07+10:00") |> elem(1) ==
314312
%DateTime{
315313
microsecond: {0, 0},
316314
month: 1,
@@ -326,6 +324,38 @@ defmodule DateTimeTest do
326324
}
327325
end
328326

327+
test "from_iso8601/1 handles negative dates" do
328+
assert DateTime.from_iso8601("-2015-01-24T09:50:07-10:00") |> elem(1) ==
329+
%DateTime{
330+
microsecond: {0, 0},
331+
month: 1,
332+
std_offset: 0,
333+
time_zone: "Etc/UTC",
334+
utc_offset: 0,
335+
year: -2015,
336+
zone_abbr: "UTC",
337+
day: 24,
338+
hour: 19,
339+
minute: 50,
340+
second: 7
341+
}
342+
343+
assert DateTime.from_iso8601("-2015-01-24T09:50:07+10:00") |> elem(1) ==
344+
%DateTime{
345+
microsecond: {0, 0},
346+
month: 1,
347+
std_offset: 0,
348+
time_zone: "Etc/UTC",
349+
utc_offset: 0,
350+
year: -2015,
351+
zone_abbr: "UTC",
352+
day: 23,
353+
hour: 23,
354+
minute: 50,
355+
second: 7
356+
}
357+
end
358+
329359
test "from_unix/2" do
330360
min_datetime = %DateTime{
331361
calendar: Calendar.ISO,

0 commit comments

Comments
 (0)