________________________________________________________________________

This file is part of Logtalk <https://logtalk.org/>
SPDX-FileCopyrightText: 2026 Paulo Moura <pmoura@logtalk.org>
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
________________________________________________________________________


`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.
