6.8 KiB
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
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
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
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):
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:
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:
pub const Leapsecond = struct {
occurrence: i48, // Unix timestamp when leap second occurs
correction: i16, // Cumulative TAI-UTC difference
};
Look Up Current Timezone Offset
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
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
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
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/<Region>/<City> |
| macOS | /var/db/timezone/zoneinfo/<Region>/<City> |
Common timezone identifiers:
America/New_York,America/Los_Angeles,America/ChicagoEurope/London,Europe/Paris,Europe/BerlinAsia/Tokyo,Asia/Shanghai,Asia/KolkataUTC,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:
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)