sysl

Module

sysl.time.tzif

Reading a zone out of the bytes of a TZif file (RFC 8536), which is what the IANA time zone database is distributed as.

Import import sysl.time.tzif

This module asks for no capability, and that is the whole reason it is a module of its own. A zone by name lives in a file, and reading a file needs an operating system – so a decoder written beside sysl.fs would take the whole of sysl.time down with it, exactly as a now() written beside Instant would have. What is here is the decoding, which is arithmetic over bytes somebody else supplied; sysl.posix.time.zone_data is what fetches them on a host.

The split is not bookkeeping — it is what makes a zone reachable on a bare machine. A board with an RTC can carry one zone’s bytes in flash, the zone it was deployed in, and resolve local time with no filesystem anywhere. Weld the decoder to the file reading and a target with no operating system gets nothing at all.

Nothing is copied, and nothing is allocated

A Zone is a handful of offsets into the caller’s bytes, and every lookup reads the packed arrays where they lie. That is what lets a zone be used with no allocator: the storage is the file, which the caller already has, and this adds a few words beside it.

The cost is the ordinary one for a borrowed view: the bytes must outlive the Zone. A Zone whose buffer has been released reads freed memory, exactly as any slice would.

What is decoded and what is skipped

A lookup needs three of the file’s arrays – the transition times, the type index for each, and the types themselves – and the designations for a name. Leap seconds are skipped, because POSIX time ignores them by definition and Instant counts the same way, so a table of them describes a timeline this library does not have. The standard/wall and UT/local indicators are skipped for the same reason they exist: they matter to somebody re-interpreting a POSIX TZ string, and nothing here does that.

The version 2+ block is the one read, where the file has one – its transition times are 8 bytes, and a 4-byte time runs out in 2038. A version 1 file is still read, from its only block. The footer’s TZ string, which extrapolates past the last transition, is not read: a lookup past the end answers with the last type, which is what the table says and is right until a zone changes its rules again.

Index

abbrev_at is_dst_at offset_at parse TzifError Zone Display for TzifError

Functions

abbrev_at

abbrev_at(z: Zone, t: Instant) -> Result[string, TzifError]

The abbreviation the zone used at that instant – EST, EDT, +0545.

It is here because the file already carries it, and it is the one thing a host’s tm_gmtoff route cannot answer without a second call. It is a display string and nothing else: CST names three different offsets around the world, so a timestamp that has to be read back carries the number instead, which is what timestamp_text writes.

is_dst_at

is_dst_at(z: Zone, t: Instant) -> Result[bool, TzifError]

Whether daylight saving was in force at that instant, as the file records it.

offset_at

offset_at(z: Zone, t: Instant) -> Result[Offset, TzifError]

How far this zone’s clocks were set from UTC at that instant.

This is the shape sysl.time.resolve takes, which is the whole point of it: a zone read from a file, a zone talking to a chip and a zone written out by hand are one thing to everything above.

parse

parse(data: []const u8) -> Result[Zone, TzifError]

Reads a zone out of the bytes of a TZif file.

The Zone borrows data – see the note above about what that costs.

A version 2 or later file carries its table twice: once with 4-byte transition times for a reader that predates the format’s second version, and then again with 8-byte ones. The first is stepped over rather than read, which is why the length of it has to be worked out even though nothing in it is wanted.

Types

TzifError

enum TzifError
    BadMagic
    BadVersion(saw: u8)
    Truncated(needed: usize)
    NoTypes

Why a decode refused, at the granularity a caller can act on.

The shape is sysl.encoding.DecodeError‘s and for its reason: the variants are separated by what a caller would do about each. BadMagic is a file that is not one of these at all – the usual cause being a path that named something else. Truncated carries the length that would have been enough, so a caller reading in pieces knows how much more to get rather than doubling until it fits.

Zone

struct Zone
    data: []const u8
    trans_at: usize
    types_at: usize
    ttinfo_at: usize
    desig_at: usize
    timecnt: usize
    typecnt: usize
    wide: bool

A zone, as a view of the bytes it was decoded from.

Implementations

Display for TzifError

impl Display for TzifError

Search

Esc
↑↓ to navigate ↵ to open Esc to close