defmodule Cldr.Timezone do @moduledoc """ Functions to map between the CLDR short time zone code and the IANA timezone names. The Unicode [locale](https://unicode.org/reports/tr35/#Locale) [extension U](https://unicode.org/reports/tr35/#u_Extension) allows the specification of the time zone requested for the provided locale. This short timezone codes never change even if the IANA names change over time. Therefore these short codes are always stable between CLDR releases. """ @timezones_file "cldr/timezones.json" @timezones Path.join(:code.priv_dir(Cldr.Config.app_name()), @timezones_file) |> File.read!() |> Cldr.Config.json_library().decode! |> Enum.reject(fn {_k, v} -> v == [""] end) |> Map.new @timezones_for_territory @timezones |> Enum.group_by(fn {k, _v} -> String.slice(k, 0, 2) end, fn {_k, v} -> v end) |> Enum.map(fn {k, v} -> case Cldr.validate_territory(k) do {:ok, territory} -> {territory, List.flatten(v)} {:error, _} -> nil end end) |> Enum.reject(&is_nil/1) |> Map.new @doc """ Returns a mapping of CLDR short zone codes to IANA timezone names. """ @spec timezones() :: map() def timezones do @timezones end @doc """ Returns a mapping of territories to their known IANA timezone names. """ @spec timezones_for_territory() :: map() def timezones_for_territory do @timezones_for_territory end @doc """ Returns a list of IANA time zone names for a given CLDR short zone code, or `nil` ### Examples iex> Cldr.Timezone.fetch("ausyd") ["Australia/Sydney", "Australia/ACT", "Australia/Canberra", "Australia/NSW"]} iex> Cldr.Timezone.fetch("nope") nil """ @spec get(String.t(), String.t() | nil) :: [String.t()] | nil def get(short_zone, default \\ nil) do Map.get(timezones(), short_zone, default) end @doc """ Returns a `:{:ok, list}` where list is a list of IANA timezone names for a given CLDR short zone code. If no such short code exists then `:error` is returned. ### Example iex> Cldr.Timezone.fetch("ausyd") {:ok, ["Australia/Sydney", "Australia/ACT", "Australia/Canberra", "Australia/NSW"]} iex> Cldr.Timezone.fetch("nope") :error """ @spec fetch(String.t()) :: {:ok, [String.t()]} | :error def fetch(short_zone) do Map.fetch(timezones(), short_zone) end @doc false @spec validate_timezone(String.t()) :: {:ok, String.t()} | {:error, String.t()} def validate_timezone(short_zone) do case fetch(short_zone) do {:ok, [first_zone | _others]} -> {:ok, first_zone} :error -> {:error, short_zone} end end @doc false def timezones_for_territory(territory) do timezones_for_territory() |> Map.fetch(territory) end end