When working with date and time in Elixir, you'll often need to parse strings into DateTime structures. One powerful library for handling these operations is Timex. It provides an extensive set of tools for parsing, formatting, and manipulating date and time data. In this post, we'll look at how to use Timex to handle different date formats and parse them into Elixir's native DateTime structure.

Problem: parsing various date formats

Sometimes, we need to parse a date string that could come in multiple formats. Rather than writing custom parsing logic for each one, we can utilize a list of possible formats and test them against the date string until we find a match. Here's an example to illustrate how to do this using Timex:

Sample code

formats = [
    "{ISO:Extended}",
    "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} {Z}",
    "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} {Zname}",
    "{WDshort}, {D} {Mfull} {YYYY} {h24}:{m}:{s} {Z}",
    "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}{Z}",
    "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}{Z:optional}",
    "{YYYY}-{0M}-{0D}",
    "{0D} {Mshort} {YYYY} {h24}:{m} {Z}"
]

datetime_string = "2021-10-16T17:00:00Z"

Enum.find_value(formats, fn format ->
    case Timex.parse(datetime_string, format) do
    {:ok, datetime} -> datetime
    {:error, _} -> nil
    end
end)
|> Timex.to_datetime("UTC")

Code breakdown

  • Date Formats: We define a list of potential formats that the date string might be in. These formats include common patterns like:

    • ISO 8601 Extended format ({ISO:Extended})
    • A variety of formats with day names, months, and time zones.
  • Date String: The string datetime_string = "2021-10-16T17:00:00Z" is the date and time we want to parse. It follows the ISO 8601 standard, which includes both date and time, along with the UTC timezone (Z).

  • Enumerating Formats: The Enum.find_value function loops over the list of formats. For each format, it tries to parse the datetime_string using Timex.parse.

    • If Timex.parse succeeds, it returns {:ok, datetime}, and we capture the resulting datetime.
    • If Timex.parse fails, it returns {:error, _}, and the loop continues with the next format.
  • Resulting DateTime: After a successful match, the parsed date is converted into a DateTime structure in UTC using Timex.to_datetime("UTC"). This ensures the result is in the correct timezone.

Why use Timex?

  • Flexible Parsing: Timex allows you to define custom formats and handles many date formats right out of the box.
  • Timezone Handling: It can easily work with timezones, including converting dates to UTC or any other timezone.
  • Rich Feature Set: Beyond parsing, Timex offers functions for formatting, adding/subtracting time, working with durations, and more.

Conclusion

In this post, we explored how to use Timex to parse a date string that could be in various formats. The approach of enumerating formats ensures flexibility and adaptability when working with date strings from multiple sources.

Timex makes working with dates and times in Elixir much easier. If you often need to deal with date parsing, formatting, or timezone conversions, it's worth including Timex in your toolset.