Skip to content
Closed
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
67 changes: 40 additions & 27 deletions lib/elixir/lib/calendar/iso.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
defmodule Ascii do
defmacro int(x) do
quote do
<<3::4, unquote(x)::4>>
end
end
end

defmodule Calendar.ISO do
@moduledoc """
The default calendar implementation, a Gregorian calendar following ISO 8601.
Expand Down Expand Up @@ -200,33 +208,34 @@ defmodule Calendar.ISO do
# on ~D[0001-01-01] which is 366 days later.
@iso_epoch 366

import Ascii

[match_basic_date, match_ext_date, guard_date, read_date] =
quote do
[
<<y1, y2, y3, y4, m1, m2, d1, d2>>,
<<y1, y2, y3, y4, @ext_date_sep, m1, m2, @ext_date_sep, d1, d2>>,
y1 >= ?0 and y1 <= ?9 and y2 >= ?0 and y2 <= ?9 and y3 >= ?0 and y3 <= ?9 and y4 >= ?0 and
y4 <= ?9 and m1 >= ?0 and m1 <= ?9 and m2 >= ?0 and m2 <= ?9 and d1 >= ?0 and d1 <= ?9 and
d2 >= ?0 and d2 <= ?9,
<<int(y1), int(y2), int(y3), int(y4), int(m1), int(m2), int(d1), int(d2)>>,
<<int(y1), int(y2), int(y3), int(y4), @ext_date_sep, int(m1), int(m2), @ext_date_sep,
int(d1), int(d2)>>,
y1 <= 9 and y2 <= 9 and y3 <= 9 and y4 <= 9 and m1 <= 9 and m2 <= 9 and d1 <= 9 and
d2 <= 9,
{
(y1 - ?0) * 1000 + (y2 - ?0) * 100 + (y3 - ?0) * 10 + (y4 - ?0),
(m1 - ?0) * 10 + (m2 - ?0),
(d1 - ?0) * 10 + (d2 - ?0)
y1 * 1000 + y2 * 100 + y3 * 10 + y4,
m1 * 10 + m2,
d1 * 10 + d2
}
]
end

[match_basic_time, match_ext_time, guard_time, read_time] =
quote do
[
<<h1, h2, i1, i2, s1, s2>>,
<<h1, h2, @ext_time_sep, i1, i2, @ext_time_sep, s1, s2>>,
h1 >= ?0 and h1 <= ?9 and h2 >= ?0 and h2 <= ?9 and i1 >= ?0 and i1 <= ?9 and i2 >= ?0 and
i2 <= ?9 and s1 >= ?0 and s1 <= ?9 and s2 >= ?0 and s2 <= ?9,
<<int(h1), int(h2), int(i1), int(i2), int(s1), int(s2)>>,
<<int(h1), int(h2), @ext_time_sep, int(i1), int(i2), @ext_time_sep, int(s1), int(s2)>>,
h1 <= 9 and h2 <= 9 and i1 <= 9 and i2 <= 9 and s1 <= 9 and s2 <= 9,
{
(h1 - ?0) * 10 + (h2 - ?0),
(i1 - ?0) * 10 + (i2 - ?0),
(s1 - ?0) * 10 + (s2 - ?0)
h1 * 10 + h2,
i1 * 10 + i2,
s1 * 10 + s2
}
]
end
Expand Down Expand Up @@ -2021,34 +2030,38 @@ defmodule Calendar.ISO do
defp parse_offset("Z"), do: {0, ""}
defp parse_offset("-00:00"), do: :error

defp parse_offset(<<?+, h1, h2, ?:, m1, m2, rest::binary>>),
defp parse_offset(<<?+, int(h1), int(h2), ?:, int(m1), int(m2), rest::binary>>),
do: parse_offset(1, h1, h2, m1, m2, rest)

defp parse_offset(<<?-, h1, h2, ?:, m1, m2, rest::binary>>),
defp parse_offset(<<?-, int(h1), int(h2), ?:, int(m1), int(m2), rest::binary>>),
do: parse_offset(-1, h1, h2, m1, m2, rest)

defp parse_offset(<<?+, h1, h2, m1, m2, rest::binary>>),
defp parse_offset(<<?+, int(h1), int(h2), int(m1), int(m2), rest::binary>>),
do: parse_offset(1, h1, h2, m1, m2, rest)

defp parse_offset(<<?-, h1, h2, m1, m2, rest::binary>>),
defp parse_offset(<<?-, int(h1), int(h2), int(m1), int(m2), rest::binary>>),
do: parse_offset(-1, h1, h2, m1, m2, rest)

defp parse_offset(<<?+, h1, h2, rest::binary>>), do: parse_offset(1, h1, h2, ?0, ?0, rest)
defp parse_offset(<<?-, h1, h2, rest::binary>>), do: parse_offset(-1, h1, h2, ?0, ?0, rest)
defp parse_offset(<<?+, int(h1), int(h2), rest::binary>>),
do: parse_offset(1, h1, h2, 0, 0, rest)

defp parse_offset(<<?-, int(h1), int(h2), rest::binary>>),
do: parse_offset(-1, h1, h2, 0, 0, rest)

defp parse_offset(_), do: :error

defp parse_offset(sign, h1, h2, m1, m2, rest) do
with true <- h1 in ?0..?2 and h2 in ?0..?9,
true <- m1 in ?0..?5 and m2 in ?0..?9,
hour = (h1 - ?0) * 10 + h2 - ?0,
min = (m1 - ?0) * 10 + m2 - ?0,
true <- hour < 24 do
defp parse_offset(sign, h1, h2, m1, m2, rest)
when h2 < 10 and m1 < 6 and m2 < 10 do
with hour when hour < 24 <- h1 * 10 + h2,
min = m1 * 10 + m2 do
{(hour * 60 + min) * 60 * sign, rest}
else
_ -> :error
end
end

defp parse_offset(_, _, _, _, _, _), do: :error

@doc false
def gregorian_seconds_to_iso_days(seconds, microsecond) do
{days, rest_seconds} = div_rem(seconds, @seconds_per_day)
Expand Down