Skip to content

Commit

Permalink
support sign prefixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tfiedlerdejanze committed Apr 4, 2024
1 parent 187b4bf commit 1072450
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
25 changes: 19 additions & 6 deletions lib/elixir/lib/calendar/duration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,31 @@ defmodule Duration do
iex> Duration.parse("P1Y2M3DT4H5M6S")
{:ok, %Duration{year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6}}
iex> Duration.parse("PT10H30M")
{:ok, %Duration{hour: 10, minute: 30, second: 0}}
iex> Duration.parse("P3Y-2MT3H")
{:ok, %Duration{year: 3, month: -2, hour: 3}}
iex> Duration.parse("-P3Y2MT3H")
{:ok, %Duration{year: -3, month: -2, hour: -3}}
iex> Duration.parse("-P3Y-2MT3H")
{:ok, %Duration{year: -3, month: 2, hour: -3}}
"""
@spec parse(String.t()) :: {:ok, t} | {:error, String.t()}
def parse("P" <> duration_string) do
parse(duration_string, %{}, "", false)
end

def parse("-P" <> duration_string) do
case parse(duration_string, %{}, "", false) do
{:ok, duration} ->
{:ok, negate(duration)}

error ->
error
end
end

def parse(_) do
{:error, "invalid duration string"}
end
Expand All @@ -227,9 +242,6 @@ defmodule Duration do
iex> Duration.parse!("P1Y2M3DT4H5M6S")
%Duration{year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6}
iex> Duration.parse!("PT10H30M")
%Duration{hour: 10, minute: 30, second: 0}
"""
@spec parse!(String.t()) :: t
def parse!(duration_string) do
Expand All @@ -242,9 +254,10 @@ defmodule Duration do
end
end

defp parse(<<>>, duration, "", _), do: {:ok, new(Enum.into(duration, []))}
defp parse(<<>>, duration, "", _), do: {:ok, new!(Enum.into(duration, []))}

defp parse(<<c::utf8, rest::binary>>, duration, buffer, is_time) when c in ?0..?9 or c == ?. do
defp parse(<<c::utf8, rest::binary>>, duration, buffer, is_time)
when c in ?0..?9 or c in [?., ?-] do
parse(rest, duration, <<buffer::binary, c::utf8>>, is_time)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/elixir/pages/getting-started/sigils.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ iex> time_zone

### Duration

A [%Duration{}](`Duration`) struct represents a collection of time scale units, The `~P` sigil allows developers to create Durations from an ISO 8601-2 formatted duration string:
A [%Duration{}](`Duration`) struct represents a collection of time scale units.
The `~P` sigil allows developers to create Durations from an ISO 8601-2 formatted duration string:
```elixir
iex> ~P[P1Y2M3DT4H5M6S]
Expand Down
4 changes: 4 additions & 0 deletions lib/elixir/test/elixir/calendar/duration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ defmodule DurationTest do
assert Duration.parse!("PT6S") == %Duration{second: 6}
assert Duration.parse!("PT1.6S") == %Duration{second: 1, microsecond: {600_000, 6}}
assert Duration.parse!("PT1.12345678S") == %Duration{second: 1, microsecond: {123_456, 6}}
assert Duration.parse!("P3Y4W-3DT-6S") == %Duration{year: 3, week: 4, day: -3, second: -6}
assert Duration.parse!("PT-4.23S") == %Duration{second: -4, microsecond: {-230_000, 6}}
assert Duration.parse!("-P3WT5H3M") == %Duration{week: -3, hour: -5, minute: -3}
assert Duration.parse!("-P-3WT5H3M") == %Duration{week: 3, hour: -5, minute: -3}

assert_raise ArgumentError,
~s/failed to parse duration. reason: "unexpected character: H"/,
Expand Down

0 comments on commit 1072450

Please sign in to comment.