# `Localize.DateTime.Timezone`
[🔗](https://github.com/elixir-localize/localize/blob/v1.4.0/lib/localize/datetime/timezone.ex#L1)

Provides timezone data access and timezone formatting for CLDR
date/time format symbols.

This module combines CLDR short zone code lookups (mapping between
BCP 47 timezone identifiers and IANA timezone names) with the format
symbol handlers for `z`, `Z`, `O`, `v`, `V`, `X`, and `x`.

# `exemplar_city`

```elixir
@spec exemplar_city(String.t(), Localize.locale(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the exemplar city for an IANA timezone identifier.

CLDR names a representative city for most timezones — the city a
reader would recognise the zone by — localized, and sometimes
differing from the city in the identifier: `"America/Godthab"` is
`"Nuuk"`, which is what the place is now called.

### Arguments

* `iana_id` is an IANA timezone identifier such as
  `"America/Los_Angeles"` or `"America/Indiana/Knox"`.

* `locale` is a locale identifier or a `t:Localize.LanguageTag.t/0`.
  The default is `Localize.get_locale/0`.

* `options` is a keyword list of options.

### Options

* `:derive` determines what happens when CLDR names no exemplar city
  for the zone. `true`, the default, derives one from the identifier,
  so `"Pacific/Wallis"` yields `"Wallis"`. `false` returns an error
  instead, which distinguishes a name CLDR vouches for from one this
  library invented.

### Returns

* `{:ok, city}`, or

* `{:error, exception}` if the locale is unknown, or if the zone has
  no exemplar city and `derive: false` was given.

### Examples

    iex> Localize.DateTime.Timezone.exemplar_city("America/Los_Angeles", :en)
    {:ok, "Los Angeles"}

    iex> Localize.DateTime.Timezone.exemplar_city("America/Godthab", :en)
    {:ok, "Nuuk"}

    iex> Localize.DateTime.Timezone.exemplar_city("America/Indiana/Knox", :en)
    {:ok, "Knox, Indiana"}

    iex> Localize.DateTime.Timezone.exemplar_city("Atlantic/Azores", :de)
    {:ok, "Azoren"}

    iex> {:error, exception} =
    ...>   Localize.DateTime.Timezone.exemplar_city("Neverwhere/Nowhere", :en, derive: false)
    iex> exception.__struct__
    Localize.UnknownTimezoneError

# `fetch_short_zone`

```elixir
@spec fetch_short_zone(String.t()) :: {:ok, map()} | {:error, Exception.t()}
```

Returns `{:ok, map}` for a given CLDR short zone code,
or `:error` if no such short code exists.

### Arguments

* `short_zone` is a CLDR short timezone code string.

### Returns

* `{:ok, map}` where map has `:aliases`, `:preferred`, and
  `:territory` keys.

* `{:error, exception}` if the short zone code is not found.

### Examples

    iex> Localize.DateTime.Timezone.fetch_short_zone("ausyd")
    {
      :ok,
      %{
        preferred: nil,
        aliases: ["Australia/Sydney", "Australia/ACT", "Australia/Canberra", "Australia/NSW"],
        territory: :AU
      }
    }

    iex> match?({:error, _}, Localize.DateTime.Timezone.fetch_short_zone("nope"))
    true

# `generic_location_format`

```elixir
@spec generic_location_format(String.t(), Localize.locale()) ::
  {:ok, String.t()} | :error
```

Returns the generic location format for a timezone.

This is the `V` format symbol at width four: the zone's location
substituted into the locale's generic `regionFormat`, so
`Australia/Adelaide` in `en` is "Adelaide Time".

### Arguments

* `iana_id` is an IANA timezone name such as `"Australia/Adelaide"`.

* `locale` is any locale returned by `Localize.all_locale_ids/0` or a
  `t:Localize.LanguageTag.t/0`. The default is `Localize.get_locale/0`.

### Returns

* `{:ok, formatted_string}`.

* `:error` for a zone with no place to name — the `Etc/*` zones, for
  which TR35 falls back to the localized GMT format — or when the
  locale has no `regionFormat`.

### Examples

    iex> Localize.DateTime.Timezone.generic_location_format("Australia/Adelaide", :en)
    {:ok, "Adelaide Time"}

    iex> Localize.DateTime.Timezone.generic_location_format("Europe/Rome", :en)
    {:ok, "Italy Time"}

    iex> Localize.DateTime.Timezone.generic_location_format("Etc/GMT", :en)
    :error

# `get_short_zone`

```elixir
@spec get_short_zone(String.t(), term()) :: map() | term()
```

Returns a timezone map for a given CLDR short zone code,
or a default value.

### Arguments

* `short_zone` is a CLDR short timezone code string.

* `default` is the value to return if the short zone is not
  found. Defaults to `nil`.

### Returns

* A map with `:aliases`, `:preferred`, and `:territory` keys,
  or the default value.

### Examples

    iex> Localize.DateTime.Timezone.get_short_zone("ausyd")
    %{
      preferred: nil,
      aliases: ["Australia/Sydney", "Australia/ACT", "Australia/Canberra", "Australia/NSW"],
      territory: :AU
    }

    iex> Localize.DateTime.Timezone.get_short_zone("nope")
    nil

# `gmt_format`

```elixir
@spec gmt_format(map(), atom(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the localized GMT offset format.

Uses the locale's `gmt_format` pattern (e.g., `["GMT", 0]`) and
`hour_format` pattern to render the datetime's total UTC offset.

### Arguments

* `datetime` is a map with an integer `:utc_offset` in seconds
  and optionally an integer `:std_offset` in seconds (a
  `t:DateTime.t/0` satisfies this shape).

* `locale_id` is a **resolved** locale identifier atom (e.g., `:en`),
  such as the `cldr_locale_id` of a validated
  `t:Localize.LanguageTag.t/0`. It is passed directly to
  `Localize.Locale.get/2` and is not validated or canonicalized
  by this function.

* `options` is a keyword list of options.

### Options

* `:format` is `:long` (e.g., `"GMT+01:00"`) or `:short` (e.g.,
  `"GMT+1"`; minutes are dropped when zero). The default is
  `:long`. TR35 gives the long format a two-digit hour and the short
  format an hour with no leading zero, whichever the locale's
  `hourFormat` writes: `"UTC+05.30"` and `"UTC+5.30"` in `fi`, whose
  pattern is `"+H.mm"`. Both write the seconds of an offset that has
  them, as the offsets zones kept before standard time do:
  `"GMT-07:52:58"` and `"GMT-7:52:58"`.

* `:number_system` is the numbering system whose digits write the
  offset, as TR35 has it written in the locale's digits (`"GMT-४"` in
  `ne`). The default is the locale's default numbering system. A system
  without digits of its own writes 0 to 9.

* `:designation` is `:gmt`, the locale's `gmtFormat`, or `:utc`, CLDR
  49's `alt="utc"` form. The default is `:gmt`. "GMT" names a zone
  connected with London as well as Coordinated Universal Time, so
  `:utc` asks for the locale's unambiguous designation for the latter:
  `"UTC+01:00"` in `en`, `"التوقيت العالمي+١:٠٠"` in `ar` against its
  `"غرينتش+١:٠٠"`. Many locales spell the two alike. Both spellings
  read back through `parse_offset/2`.

* `:offsets` is `:single`, the offset the value carries, or `:dual`,
  the pair the zone keeps joined by CLDR 49's `dualOffsetFormat`. The
  default is `:single`. A zone that moves between standard and
  daylight time is poorly described by whichever offset it happens to
  keep at one time, so `:dual` writes both — Los Angeles is
  `"GMT-8/-7"` where Phoenix, which keeps one offset, stays
  `"GMT-7"`. The pair comes from the time zone database the
  application configures, read across the year around the value's
  time, so a value with no `:time_zone`, or a zone the database
  cannot place, writes the single offset. The result names a zone
  rather than a time and does not read back through `parse_offset/2`.

### Returns

* `{:ok, formatted_string}` (e.g., `"GMT+01:00"`). A zero offset is
  spelled out — `"GMT+00:00"` long, `"GMT+0"` short — which is the
  only style TR35 defines for a known offset.

* `{:ok, unknown}` using the locale's `gmtUnknownFormat` (e.g.
  `"GMT+?"`), or its `alt="utc"` form for `designation: :utc`, when
  `datetime` carries no offset at all. TR35 makes this the second of
  the two localized GMT styles.

* `{:error, exception}` if the locale's timezone data cannot be
  loaded, or if `:designation` or `:offsets` is not one of the values
  above.

### Examples

    iex> Localize.DateTime.Timezone.gmt_format(%{utc_offset: 3600, std_offset: 0}, :en)
    {:ok, "GMT+01:00"}

    iex> Localize.DateTime.Timezone.gmt_format(%{utc_offset: -28800, std_offset: 0}, :en, format: :short)
    {:ok, "GMT-8"}

    iex> Localize.DateTime.Timezone.gmt_format(%{utc_offset: -14400, std_offset: 0}, :ne, format: :short)
    {:ok, "GMT-४"}

    iex> Localize.DateTime.Timezone.gmt_format(%{utc_offset: 3600, std_offset: 0}, :en, designation: :utc)
    {:ok, "UTC+01:00"}

    iex> datetime = DateTime.new!(~D[2026-01-15], ~T[12:00:00], "America/Los_Angeles")
    iex> Localize.DateTime.Timezone.gmt_format(datetime, :en, format: :short, offsets: :dual)
    {:ok, "GMT-8/-7"}

    iex> datetime = DateTime.new!(~D[2026-01-15], ~T[12:00:00], "America/Phoenix")
    iex> Localize.DateTime.Timezone.gmt_format(datetime, :en, format: :short, offsets: :dual)
    {:ok, "GMT-7"}

# `iso_format`

```elixir
@spec iso_format(map(), Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}
```

Returns the ISO 8601 timezone offset format.

This function is locale-independent — ISO 8601 offsets are the
same in every locale.

### Arguments

* `datetime` is a map with an integer `:utc_offset` in seconds
  and optionally an integer `:std_offset` in seconds (a
  `t:DateTime.t/0` satisfies this shape).

* `options` is a keyword list of options.

### Options

* `:format` is `:short` (minutes omitted when zero), `:long`
  (hours and minutes), or `:full` (like `:long`, with seconds
  appended when non-zero). The default is `:long`.

* `:type` is `:basic` (no separator, e.g., `"+0500"`) or
  `:extended` (colon separator, e.g., `"+05:00"`). The default
  is `:basic`.

* `:z_for_zero` is a boolean controlling whether a zero offset
  renders as `"Z"`. The default is `true`.

### Returns

* `{:ok, formatted_string}` (e.g., `"+0500"`, `"Z"`, `"+05:00"`).

### Examples

    iex> Localize.DateTime.Timezone.iso_format(%{utc_offset: 18000, std_offset: 0})
    {:ok, "+0500"}

    iex> Localize.DateTime.Timezone.iso_format(%{utc_offset: 19800, std_offset: 0}, type: :extended)
    {:ok, "+05:30"}

    iex> Localize.DateTime.Timezone.iso_format(%{utc_offset: 0, std_offset: 0})
    {:ok, "Z"}

# `known_timezones`

```elixir
@spec known_timezones() :: [String.t(), ...]
```

Returns the canonical IANA time zone names known to CLDR.

The canonical name is the first alias of each BCP 47 short zone in the CLDR timezone data; short zones with no IANA mapping are omitted. This is the inventory backing ECMA-402's `Intl.supportedValuesOf("timeZone")`.

### Returns

* A sorted list of IANA time zone name strings.

### Examples

    iex> zones = Localize.DateTime.Timezone.known_timezones()
    iex> "Australia/Sydney" in zones and "America/New_York" in zones
    true

    iex> Localize.DateTime.Timezone.known_timezones() |> hd()
    "Africa/Abidjan"

# `location_name`

```elixir
@spec location_name(String.t(), Localize.locale(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the place named by the generic location format for a timezone.

TR35 names a country when the zone is the only one in its territory,
or when CLDR lists it as that territory's primary zone, and names the
zone's exemplar city otherwise. So `Europe/Rome` is "Italy" — Italy
keeps one zone — while `Australia/Adelaide` is "Adelaide".

### Arguments

* `iana_id` is an IANA timezone name such as `"Europe/Rome"`.

* `locale` is any locale returned by `Localize.all_locale_ids/0` or a
  `t:Localize.LanguageTag.t/0`. The default is `Localize.get_locale/0`.

* `options` is a keyword list of options.

### Options

* `:derive` is a boolean determining whether an exemplar city may be
  derived from the timezone identifier when CLDR names none. The
  default is `true`.

### Returns

* `{:ok, place_name}` where the place is a country or a city. A country
  the locale has no name for is its code, as TR35 composes a location.

* `{:error, exception}` if the locale is unknown, or if no city can be
  found or derived for the timezone.

### Examples

    iex> Localize.DateTime.Timezone.location_name("Europe/Rome", :en)
    {:ok, "Italy"}

    iex> Localize.DateTime.Timezone.location_name("Europe/Berlin", :en)
    {:ok, "Germany"}

    iex> Localize.DateTime.Timezone.location_name("Australia/Adelaide", :en)
    {:ok, "Adelaide"}

    iex> Localize.DateTime.Timezone.location_name("America/Havana", :su)
    {:ok, "CU"}

# `metazone_for`

```elixir
@spec metazone_for(String.t(), map() | nil) :: atom() | nil
```

Returns the CLDR metazone for an IANA timezone name.

Zones move between metazones over time (for example
`America/Indiana/Knox` has alternated between the central and
eastern metazones), so the datetime selects the applicable usage
period.

A zone keeps no metazone before 1970. CLDR tells zones apart back to
1970, and where it gives a zone's first period no beginning the
period begins at 00:00 on 1 January 1970 in UTC.
Before that a zone is written by its own names, its offset and its
location: New York in 1965 is "GMT-05:00" and "New York Time".

### Arguments

* `time_zone` is an IANA timezone name (e.g., `"America/New_York"`)
  or any of its CLDR aliases (e.g., `"Asia/Calcutta"`).

* `datetime` is a map that may carry `:year` .. `:second` fields
  selecting the metazone in effect at that instant. The fields are
  read in the map's `:calendar`, `Calendar.ISO` when it has none.
  When the fields are absent (or `datetime` is `nil`), or name no
  day of the calendar, the currently effective metazone is
  returned. The default is `nil`.

### Returns

* The metazone as an atom (e.g., `:america_eastern`), matching the
  keys of the locale `time_zone_names.metazone` data.

* `nil` when the zone has no metazone mapping for the instant.

### Examples

    iex> Localize.DateTime.Timezone.metazone_for("America/New_York")
    :america_eastern

    iex> Localize.DateTime.Timezone.metazone_for("Asia/Calcutta")
    :india

    iex> Localize.DateTime.Timezone.metazone_for("America/Indiana/Knox", ~N[2000-06-01 00:00:00])
    :america_eastern

    iex> Localize.DateTime.Timezone.metazone_for("America/Indiana/Knox", ~N[2020-06-01 00:00:00])
    :america_central

    iex> Localize.DateTime.Timezone.metazone_for("America/New_York", ~N[1969-12-31 23:59:59])
    nil

# `non_location_format`

```elixir
@spec non_location_format(map(), Localize.locale(), Keyword.t()) ::
  {:ok, String.t()} | {:error, Exception.t()}
```

Returns the specific or generic non-location timezone name.

Looks up the zone's own name, then its metazone name, in the
locale's timezone data (e.g., "Eastern Standard Time"). When the
locale carries neither, falls back to `gmt_format/3`.

A metazone's name is shared by every zone that keeps it, so it is
qualified as TR35 gives for the non-location formats, generic and
specific alike: written as it is for the metazone's preferred zone
for the locale's country, with the zone's country where it is that
country's preferred zone ("Pacific Standard Time (Canada)" for
Vancouver in `en`), and with its city otherwise ("Mountain Standard
Time (Phoenix)"). A zone's own name is not qualified.

### Arguments

* `datetime` is a map with `:time_zone`, `:utc_offset`, and
  `:std_offset` keys (a `t:DateTime.t/0` satisfies this shape).

* `locale_id` is a **resolved** locale identifier atom (e.g., `:en`),
  such as the `cldr_locale_id` of a validated
  `t:Localize.LanguageTag.t/0`. It is passed directly to
  `Localize.Locale.get/2` and is not validated or canonicalized
  by this function.

* `options` is a keyword list of options.

### Options

* `:format` is `:short` (e.g., `"EST"`) or `:long` (e.g.,
  `"Eastern Standard Time"`). The default is `:long`.

* `:type` is `:specific` (standard or daylight name chosen from
  the datetime's `:std_offset`), `:generic`, `:standard`, or
  `:daylight`. The default is `:specific`.

* `:number_system` is the numbering system whose digits write the
  offset where the name falls back to `gmt_format/3`. The default is
  the locale's default numbering system.

* `:designation` is `:gmt` or `:utc`, choosing the designation the
  fallback to `gmt_format/3` spells its offset with. The default is
  `:gmt`. It has no effect where the locale names the zone.

### Returns

* `{:ok, timezone_name}` with the localized non-location name, or
  `{:ok, gmt_offset_string}` when falling back to the GMT format.

* `{:error, exception}` if the locale's timezone data cannot be
  loaded, or `:type` or `:designation` is not one of the values
  above.

### Examples

    iex> datetime = %{time_zone: "America/New_York", utc_offset: -18000, std_offset: 0}
    iex> Localize.DateTime.Timezone.non_location_format(datetime, :en, format: :long)
    {:ok, "Eastern Standard Time"}

    iex> datetime = %{time_zone: "America/New_York", utc_offset: -18000, std_offset: 0}
    iex> Localize.DateTime.Timezone.non_location_format(datetime, :en, format: :short)
    {:ok, "EST"}

    iex> datetime = %{time_zone: "America/Vancouver", utc_offset: -28800, std_offset: 0}
    iex> Localize.DateTime.Timezone.non_location_format(datetime, :en)
    {:ok, "Pacific Standard Time (Canada)"}

# `parse_offset`

```elixir
@spec parse_offset(String.t(), Keyword.t()) ::
  {:ok, integer()} | {:error, Exception.t()}
```

Parses a fixed UTC offset from a time zone string.

This is the inverse of `gmt_format/3` and `iso_format/2`. It resolves
only offsets that are pure arithmetic — an ISO 8601 offset, or the
localized GMT format in the locale's own spelling. A named zone
(`"PST"`, `"Asia/Tokyo"`) carries no offset of its own and needs a
time-zone database to resolve; it is rejected here.

The offset is read in the digits of any numbering system, whatever the
locale, as TR35's parsing reads the localized GMT format with "non-Latin
numbers": `gmt_format/3` writes it in the locale's digits.

TR35 has that format's number read leniently: as "03, 3, 330, 3:30,
33045 or 3:30:45", with spaces around its sign, and with "+, -, or
nothing" before it, so a number with no sign after the GMT literal is an
offset east of it. A number before a literal that follows it needs its
sign, being as much a time of day in that zone (`"10:30 GMT"`).

### Arguments

* `zone_string` is the zone portion of a parsed time, such as `"Z"`,
  `"+05:30"`, `"GMT+10:30"` or a locale's own GMT spelling.

* `options` is a keyword list of options.

### Options

* `:locale` is a locale identifier, used to recognise that locale's
  GMT format. The default is the locale returned by
  `Localize.get_locale/0`.

### Returns

* `{:ok, offset}` where `offset` is the number of seconds east of
  UTC, negative for offsets west of it.

* `{:error, exception}`, a `t:Localize.UnknownTimezoneError.t/0`, when
  the string carries no fixed offset.

### Examples

    iex> Localize.DateTime.Timezone.parse_offset("+05:30")
    {:ok, 19800}

    iex> Localize.DateTime.Timezone.parse_offset("Z")
    {:ok, 0}

    iex> Localize.DateTime.Timezone.parse_offset("GMT-8")
    {:ok, -28800}

    iex> Localize.DateTime.Timezone.parse_offset("GMT+५:३०", locale: :ne)
    {:ok, 19800}

    iex> Localize.DateTime.Timezone.parse_offset("GMT 3")
    {:ok, 10800}

    iex> Localize.DateTime.Timezone.parse_offset("Asia/Tokyo")
    {:error, %Localize.UnknownTimezoneError{timezone: "Asia/Tokyo"}}

# `parse_zone`

```elixir
@spec parse_zone(String.t(), Keyword.t()) ::
  {:ok,
   {:offset, integer()} | {:zone, String.t(), :generic | :standard | :daylight}}
  | {:error, Exception.t()}
```

Parses a time zone written in any of the forms a locale formats one in.

TR35's time zone parsing reads a zone as an ISO 8601 offset, the
localized GMT format in the locale's spelling or the global one, in the
digits of any numbering system ("GMT-4", "UTC−4", "غرينتش-4",
"غرينتش-٤"), a zone ID ("America/New_York") or its short form
("usnyc"), an exemplar city ("New York"), a location ("New York Time",
"heure : New York"), or a zone or metazone name, long or short, generic,
standard or daylight ("Eastern Time", "EDT", "heure d’été de l’Est
nord-américain"), alone or with a city or country in the locale's
fallback format ("Pacific Time (Canada)").

A country with one zone stands for that zone, and a city or a zone's own
name is read before a metazone's, as TR35 orders them: "Chile Time (Punta
Arenas)" is `America/Punta_Arenas`. A metazone name stands for the
metazone's zone in the country the string names, else in the locale's
country, else its golden zone, so "Mitteleuropäische Zeit" is
`Europe/Berlin` in `de` and `Europe/Vienna` in `de-AT`, the zone the
formatter writes that name for in each.
Where several metazones share a name ("Greenwich Mean Time") the one with
a zone in that country is read, else the one with zones in the most
countries. A country with several zones stands for its primary zone, as
its location is written ("Germany Time"), where the string names no other
zone.

A country or a city in parentheses qualifies a name the locale writes,
as every qualified name the formatter writes is one: "xyz (Italy)" is no
zone, though TR35's sample process reads it as Italy's whatever stands
before the country.

### Arguments

* `zone_string` is the zone as written.

* `options` is a keyword list of options.

### Options

* `:locale` is the locale whose names are read, any locale returned by
  `Localize.all_locale_ids/0` or a `t:Localize.LanguageTag.t/0`. The
  default is `Localize.get_locale/0`.

### Returns

* `{:ok, {:offset, offset}}` for a fixed offset, in seconds east of UTC.

* `{:ok, {:zone, time_zone, type}}` for a time zone, by CLDR's canonical
  IANA name, where `type` is `:standard` or `:daylight` for a name of that
  time and `:generic` for every other form.

* `{:error, exception}` when the string is not a zone the locale writes.

### Examples

    iex> Localize.DateTime.Timezone.parse_zone("Eastern Daylight Time", locale: :en)
    {:ok, {:zone, "America/New_York", :daylight}}

    iex> Localize.DateTime.Timezone.parse_zone("heure : New York", locale: :fr)
    {:ok, {:zone, "America/New_York", :generic}}

    iex> Localize.DateTime.Timezone.parse_zone("Pacific Time (Canada)", locale: :en)
    {:ok, {:zone, "America/Vancouver", :generic}}

    iex> Localize.DateTime.Timezone.parse_zone("GMT-4", locale: :en)
    {:ok, {:offset, -14400}}

# `resolve`

```elixir
@spec resolve(String.t(), NaiveDateTime.t(), Keyword.t()) ::
  {:ok, DateTime.t()} | {:error, Exception.t()}
```

Resolves a time zone written in any form `parse_zone/2` reads, at a date
and time, to the `t:DateTime.t/0` it names.

A fixed offset resolves on its own, as a `t:DateTime.t/0` at that offset,
whose time zone is the offset itself, `"+05:30"`, and `"Etc/UTC"` for no
offset at all.
A time zone's offset depends on the date, so it needs the time zone
database the application configures, such as `:tz`
(`config :elixir, :time_zone_database, Tz.TimeZoneDatabase`).

A name of standard or daylight time is the zone's own `t:DateTime.t/0` on
a date the zone keeps that time, as the formatter names it: by the offsets
CLDR names standard and daylight for the zone's metazone where it gives
them (Punta Arenas keeps Chile's summer time all year), and else as the
time zone database says. On any other date the name keeps its own offset,
which TR35 allows such a name to read back as ("or to just an offset"):
"10:00 EST" in July is 10:00 at -05:00, a fixed offset,
since New York keeps daylight time then. A standard name whose zone and
metazone have no daylight name in the locale stands for every type, as
TR35's type fallback has it, and follows the zone's clock.

Any other form follows the zone's clock, and a wall time its clocks pass
twice is read in standard time, TR35's example for a generic format
"when the local time maps to two possible GMT times" being one "favoring
standard time". A time they skip is read at the offset before the change;
of that TR35 says only that "there can also be a gap".

Some strings have more than one reading: `it`'s "Ora dell’Europa
orientale (Kaliningrad)" names a metazone no zone has kept since 2014 and
is also Eastern European time in Kaliningrad, and "Malaysia Time" is the
time of Kuching and the location of Kuala Lumpur, which kept another
time until 1982. The date decides between them: the reading taken is the
first whose zone is written as that string on the date, where
`parse_zone/2`, which has no date, gives the first of them.

### Arguments

* `zone_string` is the zone as written.

* `naive_datetime` is the date and time written with it, a
  `t:NaiveDateTime.t/0` in any calendar.

* `options` is a keyword list of options.

### Options

* `:locale` is the locale whose names are read. The default is
  `Localize.get_locale/0`.

### Returns

* `{:ok, datetime}`.

* `{:error, exception}` when the string is not a zone the locale writes,
  or names a time zone the configured database does not resolve, as
  Elixir's default database resolves none but UTC.

### Examples

    iex> {:ok, datetime} =
    ...>   Localize.DateTime.Timezone.resolve("UTC−4", ~N[2023-07-15 10:05:00], locale: :fr)
    iex> {datetime.utc_offset, DateTime.to_naive(datetime)}
    {-14400, ~N[2023-07-15 10:05:00]}

# `short_zone_id`

```elixir
@spec short_zone_id(String.t()) :: String.t()
```

Returns the BCP 47 short timezone identifier for an IANA
timezone name.

This is the value of the `V` format symbol in TR35. Every alias
of a zone resolves to the same short identifier, so both
`"America/New_York"` and its alias `"US/Eastern"` return `"usnyc"`.

### Arguments

* `iana_id` is an IANA timezone name such as `"America/New_York"`.

### Returns

* The BCP 47 short timezone identifier as a string.

* `"unk"`, the Unknown Zone identifier, if `iana_id` is not a known
  timezone. TR35 specifies this as the fallback for the `V` symbol.

### Examples

    iex> Localize.DateTime.Timezone.short_zone_id("America/New_York")
    "usnyc"

    iex> Localize.DateTime.Timezone.short_zone_id("US/Eastern")
    "usnyc"

    iex> Localize.DateTime.Timezone.short_zone_id("Not/AZone")
    "unk"

# `territories_by_timezone`

```elixir
@spec territories_by_timezone() :: %{required(String.t()) =&gt; atom()}
```

Returns a mapping of IANA time zone names to their
known territory.

A time zone can only belong to one territory in CLDR.

### Returns

* A map where each key is an IANA timezone string and each
  value is a territory atom.

### Examples

    iex> territories = Localize.DateTime.Timezone.territories_by_timezone()
    iex> Map.get(territories, "Australia/Sydney")
    :AU

# `timezone_count_for_territory`

```elixir
@spec timezone_count_for_territory(atom()) ::
  {:ok, non_neg_integer()} | {:error, Exception.t()}
```

Returns the count of timezones for a given territory.

### Arguments

* `territory` is a territory atom like `:US` or `:AU`.

### Returns

* `{:ok, count}` where count is the number of timezones.

* `{:error, exception}` if the territory has no known timezones.

### Examples

    iex> {:ok, count} = Localize.DateTime.Timezone.timezone_count_for_territory(:AU)
    iex> count > 0
    true

# `timezones`

```elixir
@spec timezones() :: %{required(String.t()) =&gt; map()}
```

Returns a mapping of CLDR short zone codes to
IANA timezone names.

Each key is a BCP 47 short timezone identifier string and each
value is a map with `:aliases`, `:preferred`, and `:territory`
keys.

### Returns

* A map of `%{String.t() => map()}`.

### Examples

    iex> timezones = Localize.DateTime.Timezone.timezones()
    iex> Map.get(timezones, "ausyd")
    %{preferred: nil, aliases: ["Australia/Sydney", "Australia/ACT", "Australia/Canberra", "Australia/NSW"], territory: :AU}

# `timezones_by_territory`

```elixir
@spec timezones_by_territory() :: %{
  required(atom()) =&gt; [
    %{
      short_zone: String.t(),
      territory: atom(),
      aliases: [term(), ...],
      preferred: nil | String.t()
    },
    ...
  ]
}
```

Returns a mapping of territories to their known IANA
timezone names.

### Returns

* A map where each key is a territory atom and each value is a
  list of timezone maps including `:short_zone`, `:aliases`,
  `:preferred`, and `:territory` keys.

### Examples

    iex> {:ok, zones} = Localize.DateTime.Timezone.timezones_for_territory(:AU)
    iex> Enum.any?(zones, & &1.short_zone == "ausyd")
    true

# `timezones_for_territory`

```elixir
@spec timezones_for_territory(atom()) :: {:ok, [map()]} | {:error, Exception.t()}
```

Returns a list of timezone maps for a given territory.

### Arguments

* `territory` is a territory atom like `:US` or `:AU`.

### Returns

* `{:ok, list}` where list is timezone maps for the territory.

* `{:error, exception}` if the territory has no known timezones.

### Examples

    iex> {:ok, zones} = Localize.DateTime.Timezone.timezones_for_territory(:US)
    iex> is_list(zones)
    true

# `validate_short_zone`

```elixir
@spec validate_short_zone(String.t()) :: {:ok, String.t()} | {:error, Exception.t()}
```

Validates a CLDR short zone code and returns the canonical
IANA timezone name.

### Arguments

* `short_zone` is a CLDR short timezone code string.

### Returns

* `{:ok, iana_name}` where `iana_name` is the canonical IANA
  timezone name string.

* `{:error, exception}` if the short zone code is not valid.

### Examples

    iex> Localize.DateTime.Timezone.validate_short_zone("ausyd")
    {:ok, "Australia/Sydney"}

    iex> Localize.DateTime.Timezone.validate_short_zone("nope")
    {:error, %Localize.UnknownTimezoneError{timezone: "nope"}}

# `zone_for_metazone`

```elixir
@spec zone_for_metazone(atom(), atom()) :: String.t() | nil
```

Returns the IANA timezone that represents a CLDR metazone.

### Arguments

* `metazone` is a metazone atom as returned by `metazone_for/2`
  (e.g., `:america_pacific`).

* `territory` is a territory atom used to select a
  territory-specific representative zone (e.g., `:CA` selects
  `"America/Vancouver"` for `:america_pacific`). The default is
  `:"001"`, the metazone's golden zone.

### Returns

* The IANA timezone name for the territory, falling back to the
  metazone's golden zone when the territory has no specific
  mapping.

* `nil` when the metazone is unknown.

### Examples

    iex> Localize.DateTime.Timezone.zone_for_metazone(:america_pacific)
    "America/Los_Angeles"

    iex> Localize.DateTime.Timezone.zone_for_metazone(:america_pacific, :CA)
    "America/Vancouver"

    iex> Localize.DateTime.Timezone.zone_for_metazone(:no_such_metazone)
    nil

---

*Consult [api-reference.md](api-reference.md) for complete listing*
