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