.. _library_tzif:

``tzif``
========

The ``tzif`` library loads TZif v1, v2, and v3 files from the IANA time
zone database into inspectable terms and answers UTC-based lookup
queries over one or more zones.

**Requires a backend Prolog compiler with unbounded integer
arithmetic.**

Current feature set:

- Loads sources given as ``file(Path, ZoneId)``, ``files(Root, Paths)``,
  ``directory(Root)``, ``stream(Stream, ZoneId)``,
  ``bytes(Bytes, ZoneId)``, or ``snapshot(File)`` using ``load/1`` or
  ``load/2``.
- Supports TZif v1, v2, and v3, including validated skipping of the v1
  compatibility block in v2/v3 files.
- Parses POSIX footers, including signed-hour and signed-minute offsets.
- Exposes zone-aware lookup predicates over loaded ``tzif(...)`` terms,
  plus cached convenience variants for zone and single-zone queries.
- Exposes strict local civil-time lookup predicates that fail cleanly
  for ambiguous or nonexistent times.
- Exposes ``*_with_resolution`` local civil-time query predicates for
  explicit resolution modes (``strict``, ``first``, ``second``, and
  ``all``).
- Exposes ``*_reified`` local civil-time query predicates that return
  ``unique(...)``, ``ambiguous(...)``, or ``nonexistent`` results.
- Caches successful ``load/1`` calls automatically, while ``load/2``
  returns a loaded term without changing the cache.
- Provides ``cache/1`` for making an already loaded ``tzif(...)`` term
  the active cached term.
- Saves and reloads ``tzif(...)`` terms as plain Prolog snapshot files,
  with ``save/1`` providing a convenience predicate for saving the
  cached term.
- Returns the source term recorded in the active cached term via
  ``cache_source/1``.
- Exposes the active cached term using ``cached_tzif/1``.
- Exposes the cached zone list using ``zones/1``.
- Accepts UTC queries as Unix seconds or ``date_time/6`` terms.
- Validates zone identifiers against bundled IANA TZDB 2026a canonical
  names plus backward-compatible aliases.

API documentation
-----------------

Open the
`../../apis/library_index.html#tzif <../../apis/library_index.html#tzif>`__
link in a web browser.

Loading
-------

To load all entities in this library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(tzif(loader)).

Testing
-------

To test this library predicates, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(tzif(tester)).

Representation
--------------

Loaded sources are returned by ``load/2`` as lists of
``tzif(Zone, Source, ZoneData)`` terms, one term per loaded zone.

Each nested ``ZoneData`` term contains the parsed block data,
leap-second records, and parsed footer for a single zone. These per-zone
``tzif(...)`` terms are stable enough to be serialized directly using
``save/2`` and restored using ``load(snapshot(File), TZifs)``.

For directory and file-set loads, zone identifiers are the relative
paths used to locate the TZif files inside the root directory, and each
loaded term records its own ``file(File, ZoneId)`` source.

When loading a ``directory(Root)``, regular files whose relative paths
are not recognized zone identifiers are ignored. This allows loading
system zoneinfo trees that also contain metadata files such as
``leapseconds``.

For single-zone file, stream, and byte-list loads, the caller must
provide the zone identifier explicitly using ``file(Path, ZoneId)``,
``stream(Stream, ZoneId)``, or ``bytes(Bytes, ZoneId)``.

The cache stores one ``tzif(...)`` term per zone id. ``load/1`` and
``cache/1`` replace only cached entries whose zone ids match the newly
loaded terms.

Zone identifier validation
--------------------------

The library validates zone identifiers using bundled data derived from
the official IANA TZDB 2026a release. Accepted identifiers include both
canonical zone names such as ``America/New_York`` and
backward-compatible aliases such as ``US/Eastern``.

The bundled data is generated from the IANA ``zone1970.tab`` and
``backward`` files and does not consult the host operating system's
installed zoneinfo tree.

To refresh the bundled table for a newer IANA release, run:

::

   $ ./update_zone_ids.sh 2026a

Local Queries
-------------

The library provides three sets of predicates for local civil-time
queries.

Strict predicates:

- ``local_time_type/2-4``
- ``local_offset/2-4``
- ``local_daylight_saving_time/2-4``
- ``local_abbreviation/2-4``

These predicates are intended for local civil times with a unique
interpretation. They fail for ambiguous fold times and nonexistent gap
times.

Explicit-resolution predicates:

- ``local_time_type_with_resolution/3-5``
- ``local_offset_with_resolution/3-5``
- ``local_daylight_saving_time_with_resolution/3-5``
- ``local_abbreviation_with_resolution/3-5``

These predicates accept an explicit resolution mode argument. Supported
modes are ``strict``, ``first``, ``second``, and ``all``.

- ``strict``: succeed only when the local civil time has a unique
  interpretation
- ``first``: select the earliest valid interpretation
- ``second``: select the latest valid interpretation
- ``all``: enumerate all valid interpretations in chronological order

Reified predicates:

- ``local_time_type_reified/2-4``
- ``local_offset_reified/2-4``
- ``local_daylight_saving_time_reified/2-4``
- ``local_abbreviation_reified/2-4``

These predicates never use failure to distinguish ambiguity from
absence. Instead, they return one of the following results:

- ``unique(...)``
- ``ambiguous(...)``
- ``nonexistent``

Examples
--------

Load a single TZif payload without caching it:

::

   | ?- tzif::load(bytes(Bytes, 'America/New_York'), TZifs).

Cache an already loaded term explicitly:

::

     | ?- tzif::load(bytes(Bytes, 'America/New_York'), TZifs), tzif::cache(TZifs).

Load a directory tree of TZif files:

::

   | ?- tzif::load(directory('/usr/share/zoneinfo'), TZifs).

Query a specific zone in a loaded term:

::

   | ?- tzif::load(bytes(Bytes, 'America/New_York'), [TZif]).
   | ?- tzif::offset(TZif, 'America/New_York', date_time(2024, 7, 1, 12, 0, 0), Offset).

Query a specific zone in the cached term:

::

   | ?- tzif::load(directory('/usr/share/zoneinfo')).
   | ?- tzif::offset('America/New_York', date_time(2024, 7, 1, 12, 0, 0), Offset).

Persist and reload a snapshot:

::

   | ?- tzif::save(TZifs, 'snapshot.pl').
   | ?- tzif::load(snapshot('snapshot.pl'), ReloadedTZifs).

Save the cached terms directly:

::

   | ?- tzif::load(directory('/usr/share/zoneinfo')).
   | ?- tzif::save('snapshot.pl').

Load using a backward-compatible alias:

::

   | ?- tzif::load(bytes(Bytes, 'US/Eastern'), TZifs).

Use the automatic cache populated by ``load/1``:

::

   | ?- tzif::load(snapshot('snapshot.pl')).
   | ?- tzif::abbreviation(date_time(2024, 7, 1, 12, 0, 0), Abbreviation).

Resolve an ambiguous local civil time explicitly:

::

   | ?- tzif::local_offset_with_resolution('America/New_York', date_time(2024, 11, 3, 1, 30, 0), first, Offset).

Inspect whether a local civil time is unique, ambiguous, or nonexistent:

::

   | ?- tzif::local_time_type_reified('America/New_York', date_time(2024, 11, 3, 1, 30, 0), Result).

List cached zones directly:

::

   | ?- tzif::load(directory('/usr/share/zoneinfo')).
   | ?- tzif::zones(Zones).

Known issues
------------

Some tests may fail on Windows depending on the backend. When using
SICStus Prolog, test failures result from file name case-normalization
by this system file and directory library predicates. When using
SWI-Prolog, the same issues with file name case-normalization are fixed
in version 10.1.12.
