# std.Tz - TZif Timezone Database Parsing (Zig 0.16.0) Parse IANA Time Zone Database files (TZif format, RFC 8536) into transitions, time types, leap seconds, and an optional POSIX footer. `std.Tz` stores this data; applications perform their own timestamp lookup and, if needed, interpret future rules from the footer. Primary Zig 0.16 release-note source: https://ziglang.org/download/0.16.0/release-notes.html When reading timezone files in Zig 0.16, use `std.Io.Dir`/`std.Io.File` and explicit `std.Io`; use `std.Io.Reader.fixed(bytes)` style patterns instead of old `std.io.fixedBufferStream`. ## Quick Reference | Type | Description | |------|-------------| | `Tz` | Parsed timezone with transitions, time types, and leap seconds | | `Transition` | Point in time when timezone rules change | | `Timetype` | Timezone offset, DST flag, and abbreviation | | `Leapsecond` | Leap second occurrence and cumulative correction | ## Basic Usage ```zig const std = @import("std"); fn load(io: std.Io, allocator: std.mem.Allocator) !void { // Open system timezone file const file = try std.Io.Dir.openFileAbsolute(io, "/usr/share/zoneinfo/America/New_York", .{}); defer file.close(io); var read_buf: [4096]u8 = undefined; var file_reader = file.reader(io, &read_buf); // Parse TZif data var tz = try std.Tz.parse(allocator, &file_reader.interface); defer tz.deinit(); // Access timezone information std.debug.print("Transitions: {}\n", .{tz.transitions.len}); std.debug.print("Footer (POSIX TZ): {s}\n", .{tz.footer orelse "(none)"}); } ``` ## Parsing from Embedded Data ```zig const std = @import("std"); // Embed TZif file at compile time const tokyo_tz = @embedFile("tz/asia_tokyo.tzif"); pub fn parseEmbedded(allocator: std.mem.Allocator) !void { var reader: std.Io.Reader = .fixed(tokyo_tz); var tz = try std.Tz.parse(allocator, &reader); defer tz.deinit(); // Use timezone data... } ``` ## Tz Struct ```zig pub const Tz = struct { allocator: std.mem.Allocator, transitions: []const Transition, // Sorted by timestamp timetypes: []const Timetype, leapseconds: []const Leapsecond, footer: ?[]const u8, // POSIX TZ string for future dates pub fn parse(allocator: std.mem.Allocator, reader: *std.Io.Reader) !Tz pub fn deinit(self: *Tz) void }; ``` ## Transition A transition marks when timezone rules change (e.g., DST start/end): ```zig pub const Transition = struct { ts: i64, // Unix timestamp (seconds since epoch) timetype: *Timetype, // Pointer to active time type after this transition }; ``` ## Timetype Describes timezone offset and DST status: ```zig pub const Timetype = struct { offset: i32, // UTC offset in seconds (e.g., -18000 for EST = UTC-5) flags: u8, // Packed flags name_data: [6:0]u8, // Null-terminated abbreviation (e.g., "EST", "PDT") pub fn name(self: *const Timetype) [:0]const u8 // Get abbreviation pub fn isDst(self: Timetype) bool // Is daylight saving time? pub fn standardTimeIndicator(self: Timetype) bool pub fn utIndicator(self: Timetype) bool }; ``` ## Leapsecond Leap second corrections for TAI-UTC: ```zig pub const Leapsecond = struct { occurrence: i48, // Unix timestamp when leap second occurs correction: i16, // Cumulative TAI-UTC difference }; ``` ## Look Up Current Timezone Offset ```zig fn getUtcOffset(tz: *const std.Tz, unix_timestamp: i64) i32 { // Find the last transition before or at the given timestamp var result: ?*const std.Tz.Timetype = null; for (tz.transitions) |t| { if (t.ts <= unix_timestamp) { result = t.timetype; } else { break; } } // Return offset or default to first timetype if (result) |tt| { return tt.offset; } else if (tz.timetypes.len > 0) { return tz.timetypes[0].offset; } return 0; } // Usage for a timestamp supplied by the caller const unix_timestamp: i64 = 1_700_000_000; const offset = getUtcOffset(&tz, unix_timestamp); const local_time = unix_timestamp + offset; ``` ## Check if DST is Active ```zig fn isDstActive(tz: *const std.Tz, unix_timestamp: i64) bool { var active: ?*const std.Tz.Timetype = null; for (tz.transitions) |t| { if (t.ts <= unix_timestamp) { active = t.timetype; } else { break; } } if (active) |tt| return tt.isDst(); return tz.timetypes.len > 0 and tz.timetypes[0].isDst(); } ``` ## Get Timezone Abbreviation ```zig fn getTimezoneAbbrev(tz: *const std.Tz, unix_timestamp: i64) []const u8 { var result: ?*const std.Tz.Timetype = null; for (tz.transitions) |t| { if (t.ts <= unix_timestamp) { result = t.timetype; } else { break; } } if (result) |tt| { return tt.name(); } else if (tz.timetypes.len > 0) { return tz.timetypes[0].name(); } return "UTC"; } // Returns "EST", "EDT", "PST", "PDT", "JST", etc. ``` ## List All Transitions ```zig fn printTransitions(tz: *const std.Tz) void { for (tz.transitions) |t| { std.debug.print("{d}: {s} (offset {d}s, DST: {})\n", .{ t.ts, t.timetype.name(), t.timetype.offset, t.timetype.isDst(), }); } } ``` ## Selected Parse Errors | Error | Cause | |-------|-------| | `error.BadHeader` | Invalid TZif magic bytes (not "TZif") | | `error.BadVersion` | Unsupported TZif version (only 0, 2, 3 supported) | | `error.Malformed` | RFC 8536 validation failure | | `error.OverlargeFooter` | Footer exceeded the active reader's capacity while scanning to its newline | Allocation failures and `std.Io.Reader` failures such as `EndOfStream` or `ReadFailed` can also propagate. ## System Timezone Paths | Platform | Path | |----------|------| | Linux/BSD | `/usr/share/zoneinfo//` | | macOS | `/var/db/timezone/zoneinfo//` | Common timezone identifiers: - `America/New_York`, `America/Los_Angeles`, `America/Chicago` - `Europe/London`, `Europe/Paris`, `Europe/Berlin` - `Asia/Tokyo`, `Asia/Shanghai`, `Asia/Kolkata` - `UTC`, `Etc/GMT` ## POSIX TZ Footer Modern TZif files (v2+) may include a POSIX TZ string in the footer. `std.Tz` stores a non-empty footer but does not interpret it to calculate future offsets: ```zig if (tz.footer) |posix_tz| { // e.g., "EST5EDT,M3.2.0,M11.1.0" for US Eastern std.debug.print("POSIX TZ: {s}\n", .{posix_tz}); } ``` ## Notes - Allocates memory for transitions, timetypes, leapseconds, and footer - Call `deinit()` to free allocated memory - Supports TZif version 0 (legacy 32-bit), 2, and 3 (64-bit timestamps) - Timezone abbreviations are limited to 6 characters (POSIX compliance) - Transition timestamps are Unix epoch seconds (signed i64) - Offset is in seconds, negative for west of UTC (e.g., -18000 = UTC-5)