Tzinfo - Parsing the time zone information format (zonefiles)

Over the holidays I worked on a library to parse zonefiles, which are present on most UNIX systems, as a potential source of timezone information. This might be useful if your elixir system does need to share timezone information with other systems, which also rely on the OS provided zonefiles, where tzdata doesn’t fit as it’s downloading/using its own database from the IANA.

The package also includes a parser for POSIX time zone strings, which is what zonefiles hold in their footer part of version 2/3.

It does not yet include code for searching the data though, which also means there’s not yet an implementation for Calendar.TimeZoneDatabase like there is for tzdata.

# https://tools.ietf.org/html/rfc8536#appendix-B.2
> Tzinfo.Parser.parse(binary_contents)
{:ok, 
%{
  bin: "",
  version: 2,
  data: %{
    designations: %{
      0 => "LMT",
      4 => "HST",
      8 => "HDT",
      12 => "HWT",
      16 => "HPT"
    },
    leap_seconds: [],
    std_wall_indicators: [0, 0, 0, 0, 1, 0],
    transition_types: [1, 2, 1, 3, 4, 1, 5],
    transitions: [
      -2_334_101_314,
      -1_157_283_000,
      -1_155_436_200,
      -880_198_200,
      -769_395_600,
      -765_376_200,
      -712_150_200
    ],
    types: [
      %{designation_index: 0, dst: false, offset: -37886},
      %{designation_index: 4, dst: false, offset: -37800},
      %{designation_index: 8, dst: true, offset: -34200},
      %{designation_index: 12, dst: true, offset: -34200},
      %{designation_index: 16, dst: true, offset: -34200},
      %{designation_index: 4, dst: false, offset: -36000}
    ],
    ut_local_indicators: [0, 0, 0, 0, 1, 0]
  },
  footer: "HST10"
}}

Source file: https://tools.ietf.org/html/rfc8536#appendix-B.2

iex(1)> Tzinfo.Posix.parse("PST8PDT,M3.2.0/2:00:00,M11.1.0/2:00:00")
{:ok,
 %{
   std: %{abbr: "PST", std_offset: 0, utc_offset: 28800},
   dst: %{
     abbr: "PDT",
     std_offset: 3600,
     utc_offset: 28800,
     start: %{
       midnight_offset: 7200,
       type: :month_based,
       value: %{day: 0, month: 3, week: 2}
     },
     end: %{
       midnight_offset: 7200,
       type: :month_based,
       value: %{day: 0, month: 11, week: 1}
     }
   }
 }}
6 Likes

Nice project! For anyone curious in trying it out with zone info files shipping with macOS you can do the following:

iex> Tzinfo.Parser.parse(File.read!("/usr/share/zoneinfo/America/New_York"))
{:ok,
 %{
   bin: "",
   counters: %{char: 20, isstd: 5, isut: 5, leap: 0, time: 236, type: 5},
   data: %{
     designations: %{
       0 => "LMT",
       4 => "EDT",
       8 => "EST",
       12 => "EWT",
       16 => "EPT"
     },
     ...
 }}
3 Likes