ISO8601 duration strings (e.g., "P3Y6M4DT12H30M5S"
) are a standardized way to represent time durations. While Elixir has strong support for ISO8601 date and time formats via the Date
, Time
, and DateTime
modules, parsing durations requires a bit more work.
Why it matters
You might encounter ISO8601 durations when working with APIs (like Google Calendar or video metadata) that encode durations in this format. To use them effectively in Elixir, you'll want to parse them into something usable like a map or seconds.
Using Timex
The Timex library provides built-in support for parsing ISO8601 durations.
Installation
Add Timex to your mix.exs
:
def deps do
[
{:timex, "~> 3.7"}
]
end
Then run:
mix deps.get
Parsing a Duration
iex> Timex.Duration.parse("P1DT2H3M4S")
{:ok, #<Duration(P1DT2H3M4S)>}
You can convert it to a human-readable format or manipulate it:
{:ok, duration} = Timex.Duration.parse("PT30M")
Timex.Duration.to_minutes(duration)
# => 30.0
Timex.Format.Duration.Formatter.format(d)
# => PT30M
You can also do things like this:
{:ok, d} = Timex.Duration.parse("P3.5M")
d |> Timex.Duration.to_days() |> Timex.Duration.from_days()
#<Duration(P3M15D)>
Manual parsing (regex-based)
If you prefer not to use a dependency, you can parse the string yourself:
defmodule DurationParser do
@regex ~r/P(?:(?<days>\d+)D)?(?:T(?:(?<hours>\d+)H)?(?:(?<minutes>\d+)M)?(?:(?<seconds>\d+)S)?)?/
def parse(iso8601) do
case Regex.named_captures(@regex, iso8601) do
nil -> :error
captures ->
Enum.reduce(captures, 0, fn
{_, nil}, acc -> acc
{"days", v}, acc -> acc + String.to_integer(v) * 86400
{"hours", v}, acc -> acc + String.to_integer(v) * 3600
{"minutes", v}, acc -> acc + String.to_integer(v) * 60
{"seconds", v}, acc -> acc + String.to_integer(v)
end)
end
end
end
DurationParser.parse("P1DT2H3M4S")
# => 93784
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.