feature. See also
. The project being documented here (as the example) is the Zig library itself.
tz.Tz
pub const Tz = struct
File
Code
pub const Tz = struct {
allocator: Allocator,
transitions: []const Transition,
timetypes: []const Timetype,
leapseconds: []const Leapsecond,
footer: ?[]const u8,
const Header = extern struct {
magic: [4]u8,
version: u8,
reserved: [15]u8,
counts: extern struct {
isutcnt: u32,
isstdcnt: u32,
leapcnt: u32,
timecnt: u32,
typecnt: u32,
charcnt: u32,
},
};
pub fn parse(allocator: Allocator, reader: *Reader) !Tz {
const legacy_header = try reader.takeStruct(Header, .big);
if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader;
if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3')
return error.BadVersion;
if (legacy_header.version == 0)
return parseBlock(allocator, reader, legacy_header, true);
const skip_n = legacy_header.counts.timecnt * 5 +
legacy_header.counts.typecnt * 6 +
legacy_header.counts.charcnt + legacy_header.counts.leapcnt * 8 +
legacy_header.counts.isstdcnt + legacy_header.counts.isutcnt;
try reader.discardAll(skip_n);
var header = try reader.takeStruct(Header, .big);
if (!std.mem.eql(u8, &header.magic, "TZif")) return error.BadHeader;
if (header.version != '2' and header.version != '3') return error.BadVersion;
return parseBlock(allocator, reader, header, false);
}
fn parseBlock(allocator: Allocator, reader: *Reader, header: Header, legacy: bool) !Tz {
if (header.counts.isstdcnt != 0 and header.counts.isstdcnt != header.counts.typecnt) return error.Malformed;
if (header.counts.isutcnt != 0 and header.counts.isutcnt != header.counts.typecnt) return error.Malformed;
if (header.counts.typecnt == 0) return error.Malformed;
if (header.counts.charcnt == 0) return error.Malformed;
if (header.counts.charcnt > 256 + 6) return error.Malformed;
var leapseconds = try allocator.alloc(Leapsecond, header.counts.leapcnt);
errdefer allocator.free(leapseconds);
var transitions = try allocator.alloc(Transition, header.counts.timecnt);
errdefer allocator.free(transitions);
var timetypes = try allocator.alloc(Timetype, header.counts.typecnt);
errdefer allocator.free(timetypes);
var i: usize = 0;
while (i < header.counts.timecnt) : (i += 1) {
transitions[i].ts = if (legacy) try reader.takeInt(i32, .big) else try reader.takeInt(i64, .big);
}
i = 0;
while (i < header.counts.timecnt) : (i += 1) {
const tt = try reader.takeByte();
if (tt >= timetypes.len) return error.Malformed;
transitions[i].timetype = &timetypes[tt];
}
i = 0;
while (i < header.counts.typecnt) : (i += 1) {
const offset = try reader.takeInt(i32, .big);
if (offset < -2147483648) return error.Malformed;
const dst = try reader.takeByte();
if (dst != 0 and dst != 1) return error.Malformed;
const idx = try reader.takeByte();
if (idx > header.counts.charcnt - 1) return error.Malformed;
timetypes[i] = .{
.offset = offset,
.flags = dst,
.name_data = undefined,
};
timetypes[i].name_data[0] = idx;
}
var designators_data: [256 + 6]u8 = undefined;
try reader.readSliceAll(designators_data[0..header.counts.charcnt]);
const designators = designators_data[0..header.counts.charcnt];
if (designators[designators.len - 1] != 0) return error.Malformed;
// Iterate through the timetypes again, setting the designator names
for (timetypes) |*tt| {
const name = std.mem.sliceTo(designators[tt.name_data[0]..], 0);
if (name.len > 6) return error.Malformed;
@memcpy(tt.name_data[0..name.len], name);
tt.name_data[name.len] = 0;
}
i = 0;
while (i < header.counts.leapcnt) : (i += 1) {
const occur: i64 = if (legacy) try reader.takeInt(i32, .big) else try reader.takeInt(i64, .big);
if (occur < 0) return error.Malformed;
if (i > 0 and leapseconds[i - 1].occurrence + 2419199 > occur) return error.Malformed;
if (occur > std.math.maxInt(i48)) return error.Malformed;
const corr = try reader.takeInt(i32, .big);
if (i == 0 and corr != -1 and corr != 1) return error.Malformed;
if (i > 0 and leapseconds[i - 1].correction != corr + 1 and leapseconds[i - 1].correction != corr - 1) return error.Malformed;
if (corr > std.math.maxInt(i16)) return error.Malformed;
leapseconds[i] = .{
.occurrence = @as(i48, @intCast(occur)),
.correction = @as(i16, @intCast(corr)),
};
}
i = 0;
while (i < header.counts.isstdcnt) : (i += 1) {
const stdtime = try reader.takeByte();
if (stdtime == 1) {
timetypes[i].flags |= 0x02;
}
}
i = 0;
while (i < header.counts.isutcnt) : (i += 1) {
const ut = try reader.takeByte();
if (ut == 1) {
timetypes[i].flags |= 0x04;
if (!timetypes[i].standardTimeIndicator()) return error.Malformed;
}
}
var footer: ?[]u8 = null;
if (!legacy) {
if ((try reader.takeByte()) != '\n') return error.Malformed;
const footer_mem = reader.takeSentinel('\n') catch |err| switch (err) {
error.StreamTooLong => return error.OverlargeFooter,
else => return err,
};
if (footer_mem.len != 0) {
footer = try allocator.dupe(u8, footer_mem);
}
}
errdefer if (footer) |ft| allocator.free(ft);
return .{
.allocator = allocator,
.transitions = transitions,
.timetypes = timetypes,
.leapseconds = leapseconds,
.footer = footer,
};
}
pub fn deinit(self: *Tz) void {
if (self.footer) |footer| {
self.allocator.free(footer);
}
self.allocator.free(self.leapseconds);
self.allocator.free(self.transitions);
self.allocator.free(self.timetypes);
}
}