-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add Duration.from_iso8601/1
#13473
add Duration.from_iso8601/1
#13473
Conversation
ec9a2a5
to
1072450
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm definitely in favor of having the parse
function. Perhaps we could name it from_iso8601
in order to be consistent with other calendar methods, WDYT?
Regarding the sigil, it is a bit cryptic indeed.
I've been thinking, if we prefer something more spelled out, an alternative could be a macro rather than a sigil.
Kernel.duration/1
, basically would work like new!
but would run at compile time: duration(month: 1, minute: 1)
.
Duration.parse/1
and sigil_P
Duration.from_iso8601/1
I've dropped the sigil for now, and i like the suggestion of adding |
duration/1 macro is a nice idea. It’s just couple characters less than either %Duration{…} or Duration.new!(…) but doing things at compile time is potentially appealing. (Eg if all kwargs are literals call Duration.new!/1 at compile time.) Such macro would have pretty strong record vibes and in general be pretty uncommon FWIW. |
And I think it’d be a closer call whether to support both or just the latter: DateTime.utc_ago(hour: 42)
DateTime.utc_ago(duration(hour: 42)) |
Also in addition, there is also the fact it could be used in pattern-matching.
I agree, although sigils have been the typical way of achieving this which are technically macros, but unlike other calendar structs I'm not sure a sigil would feel very natural. And there's |
There is something the Erlang compiler does, and maybe we should start doing it too, which is to inline pure functions in stdlib. If we see Regarding this PR, I agree the sigil is not readable and, while we could add I would love a human language sigil, I think ~P[3 hours] and |
@tfiedlerdejanze, I am reopening so we add this function, but without the sigil. Can you please update it accordingly? |
That's great @josevalim i'll be able to update the PR this afternoon or tomorrow by the latest, thanks! |
Was reading the code, we'll need to update https://github.com/elixir-lang/elixir/blob/main/lib/elixir/lib/calendar/iso.ex#L32 as well:
|
FYI I've run a benchmark comparing this version with the following (incomplete) regex implementation: def from_iso8601_with_regex(string) do
~r"^P((?<year>\d+)Y)?((?<month>\d+)M)?((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$"
|> Regex.named_captures(string)
|> case do
%{} = named_captures ->
fields =
for {component_str, value_str} <- named_captures, value_str != "" do
{String.to_atom(component_str), value_str |> Integer.parse() |> elem(0)}
end
{:ok, struct(Duration, fields)}
nil ->
{:error, :invalid_format}
end
end test_durations = ~w|P3WT5H3M PT5H3M P1Y2M3D PT4H5M6S P1Y2M P3D PT4H5M PT6S P4Y2W3Y P5HT4MT3S P5H3HT4M invalid|
Benchee.run(
%{
"Elixir parser" => fn -> test_durations |> Enum.each(&Duration.from_iso8601(&1)) end,
"Regex" => fn -> test_durations |> Enum.each(&Duration.from_iso8601_with_regex(&1)) end
},
time: 100,
memory_time: 2
) Results:
|
@josevalim i've reworked the implementation so the parser now takes takes order into account. By parsing the duration components one by one, we can now also keep track of the period designator prefix, so we could more easily negate all the fields after parsing all individual components, in case we decide to support the prefix, in a different PR maybe. @tanguilp thanks for the benchmarks! they look similar on my machine after the rework, but feel free to double check of course. Finally @NickNeck thanks for your review and input on approaching the parsing. I definitely took some inspiration from it, as far as in grouping and generating the parse functions for the different duration components. Thanks for taking the time reviewing, it's much appreciated! :) |
💚 💙 💜 💛 ❤️ |
[Proposal] This is a draft for a proposal which may become relevant in the future if the community finds it beneficial.
This PR implements a parser for ISO 8601-2 formatted duration strings to create
Duration.t
structs. The extended specification is relatively lose and can be found here. One interesting discussion around the standard can be found here.The current state of the PR:
supports sign prefixes-P1D
,-P1M-3D
andP1M-3D
introduces the~P
sigil as~P[PY4]
. While the secondP
may seem redundant it is more intuitive to use with sign prefixes and allows to easily copy/paste duration strings to or from a sigil, just like the other calendar ISO formatted date/time sigils