TzWorld (TzWorld v2.5.0)

Copy Markdown View Source

Resolves the time zone at a location from its coordinates.

Time zone boundaries come from the timezone-boundary-builder project. They are not included in the package: mix tz_world.update downloads and installs them, and lookups return {:error, :enoent} until it has run.

Lookups are answered by a backend running in the application's supervision tree, normally TzWorld.Backend.SpatialIndex.

Public API

Summary

Types

A backend module implementing TzWorld.Backend.

A location as a Geo.Point, a Geo.PointZ or a {longitude, latitude} tuple, in degrees.

Functions

Returns every time zone at a point.

Returns the OTP application name of tz_world.

Reload the timezone geometry data from the on-disk files.

Returns the time zone at a point.

Returns the version of the installed time zone data.

Types

backend()

@type backend() :: module()

A backend module implementing TzWorld.Backend.

backend_result()

@type backend_result() :: {:ok, term()} | {:error, term()}

point()

@type point() :: Geo.Point.t() | Geo.PointZ.t() | {lng :: number(), lat :: number()}

A location as a Geo.Point, a Geo.PointZ or a {longitude, latitude} tuple, in degrees.

Functions

all_timezones_at(point, backend \\ fetch_backend())

@spec all_timezones_at(point(), backend()) :: {:ok, [String.t()]} | {:error, atom()}

Returns every time zone at a point.

Most points are in exactly one time zone. A few, in disputed territories, are in more than one, and points outside every time zone are in none.

Arguments

  • point is a Geo.Point, a Geo.PointZ or a {longitude, latitude} tuple, in degrees. Longitude always comes first.

  • backend is the backend module to query. The default is the running backend, preferring the configured :default_backend. A RuntimeError is raised if no backend is running.

Returns

  • {:ok, time_zones} where time_zones is a list of time zone names in the order they were found, empty if no time zone contains the point.

  • {:error, :invalid_point} if point is not one of the forms above, or its longitude is outside -180..180 or its latitude outside -90..90.

  • {:error, :enoent} if the time zone data has not been installed. Run mix tz_world.update to install it.

Examples

iex> TzWorld.all_timezones_at({3.2, 45.32})
{:ok, ["Europe/Paris"]}

iex> TzWorld.all_timezones_at({87.6168, 43.8256})
{:ok, ["Asia/Shanghai", "Asia/Urumqi"]}

iex> TzWorld.all_timezones_at({0.0, 0.0})
{:ok, []}

app_name()

@spec app_name() :: :tz_world

Returns the OTP application name of tz_world.

Returns

  • :tz_world.

Examples

iex> TzWorld.app_name()
:tz_world

reload_timezone_data()

@spec reload_timezone_data() ::
  {:ok, [{module(), backend_result()}]} | {:error, [{module(), term()}]}

Reload the timezone geometry data from the on-disk files.

Iterates the list of known backends and asks each one that is currently running in this node to reload itself from disk. Backends that are not running are skipped — the function is safe to call regardless of which backends the host application has added to its supervision tree.

Returns

  • {:ok, results} when every running backend reloaded successfully. results is a list of {backend, result} pairs in reload order.

  • {:error, failures} when one or more running backends failed to reload. failures is a list of {backend, reason} pairs for the backends that did not return {:ok, _}.

Notes

  • Reload is performed sequentially in a fixed order (DetsWithIndexCache, EtsWithIndexCache, SpatialIndex) because EtsWithIndexCache reads from the DETS file rebuilt by DetsWithIndexCache.

  • Each backend's reload runs inside its own GenServer call, so concurrent reload requests against a single backend are serialized at its mailbox.

  • Lookups during reload are safe: SpatialIndex swaps its persistent-term entry atomically, and the other backends process lookup messages only after the reload returns.

Telemetry

The reload emits the following events:

  • [:tz_world, :reload, :start | :stop | :exception] — wraps the full call. :stop measurements include :duration. :stop metadata includes :result (the return value), :backends (list of running backends that participated), and :failure_count.

  • [:tz_world, :reload, :backend, :start | :stop | :exception] — wraps each per-backend reload. Metadata includes :backend (the module). :stop metadata also includes :result.

Examples

In an application running only the default backend:

iex> TzWorld.reload_timezone_data()
{:ok, [{TzWorld.Backend.SpatialIndex, {:ok, :loaded}}]}

timezone_at(point, backend \\ fetch_backend())

@spec timezone_at(point(), backend()) :: {:ok, String.t()} | {:error, atom()}

Returns the time zone at a point.

Where time zones overlap, which happens in a few disputed territories, the first one found is returned. all_timezones_at/2 returns them all.

Arguments

  • point is a Geo.Point, a Geo.PointZ or a {longitude, latitude} tuple, in degrees. Longitude always comes first.

  • backend is the backend module to query. The default is the running backend, preferring the configured :default_backend. A RuntimeError is raised if no backend is running.

Returns

  • {:ok, time_zone} where time_zone is the name of the time zone.

  • {:error, :time_zone_not_found} if no time zone contains the point.

  • {:error, :invalid_point} if point is not one of the forms above, or its longitude is outside -180..180 or its latitude outside -90..90.

  • {:error, :enoent} if the time zone data has not been installed. Run mix tz_world.update to install it.

Examples

iex> TzWorld.timezone_at(%Geo.Point{coordinates: {3.2, 45.32}})
{:ok, "Europe/Paris"}

iex> TzWorld.timezone_at({3.2, 45.32})
{:ok, "Europe/Paris"}

iex> TzWorld.timezone_at({0.0, 0.0})
{:error, :time_zone_not_found}

iex> TzWorld.timezone_at({200.0, 45.32})
{:error, :invalid_point}

version()

@spec version() :: {:ok, String.t()} | {:error, :enoent}

Returns the version of the installed time zone data.

The version is read from the running backend.

Returns

  • {:ok, version} where version is the name of the upstream data release, such as "2026d".

  • {:error, :enoent} if the time zone data has not been installed.

Examples

iex> {:ok, version} = TzWorld.version()
iex> is_binary(version)
true