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