Skip to content

Commit 20f9478

Browse files
committed
Fix parsing of fractional durations
The parsing of fractional durations checked for positivity by testing `second > 0`, which reports `false` for all fractions between -Infinity and +1 seconds (exclusive). The JavaScript libraries that I tested disagree whether "-PT-0,6S" represents a positive or negative duration, "moment" and "tinyduration" reported positive, "luxon" (the successor to moment) reported negative. My reading of the specification gives me no reason to doubt that a double negative should result in anything but a positive total result. It is worth mentioning that as the scale units are represented as integers, there is no clean way to implement fractional parsing of non-second units. Currently, fractional non-second durations simply fail to parse. [Wikipedia](https://en.wikipedia.org/wiki/ISO_8601#Durations) gives "P0.5Y" as an example.
1 parent ad3646e commit 20f9478

File tree

2 files changed

+12
-1
lines changed

2 files changed

+12
-1
lines changed

Diff for: lib/elixir/lib/calendar/iso.ex

+9-1
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,15 @@ defmodule Calendar.ISO do
715715
{second, <<delimiter, _::binary>> = rest} when delimiter in [?., ?,] ->
716716
case parse_microsecond(rest) do
717717
{{ms, precision}, "S"} ->
718-
ms = if second > 0, do: ms, else: -ms
718+
ms =
719+
case string do
720+
"-" <> _ ->
721+
-ms
722+
723+
_ ->
724+
ms
725+
end
726+
719727
{:ok, [second: second, microsecond: {ms, precision}] ++ acc}
720728

721729
_ ->

Diff for: lib/elixir/test/elixir/calendar/duration_test.exs

+3
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ defmodule DurationTest do
262262
assert Duration.from_iso8601!("PT6S") == %Duration{second: 6}
263263
assert Duration.from_iso8601!("PT1,6S") == %Duration{second: 1, microsecond: {600_000, 1}}
264264
assert Duration.from_iso8601!("PT-1.6S") == %Duration{second: -1, microsecond: {-600_000, 1}}
265+
assert Duration.from_iso8601!("PT0,6S") == %Duration{second: 0, microsecond: {600_000, 1}}
266+
assert Duration.from_iso8601!("PT-0,6S") == %Duration{second: 0, microsecond: {-600_000, 1}}
267+
assert Duration.from_iso8601!("-PT-0,6S") == %Duration{second: 0, microsecond: {600_000, 1}}
265268
assert Duration.from_iso8601!("-P10DT4H") == %Duration{day: -10, hour: -4}
266269
assert Duration.from_iso8601!("-P10DT-4H") == %Duration{day: -10, hour: 4}
267270
assert Duration.from_iso8601!("P-10D") == %Duration{day: -10}

0 commit comments

Comments
 (0)