Allow for minutes in TimeZone offsets

This commit is contained in:
Nathan Fisher 2023-06-13 00:21:36 -04:00
parent 2029bf3778
commit 2abed40946

View File

@ -135,37 +135,95 @@ pub const TimeZoneTag = enum(u1) {
offset, offset,
}; };
pub const TimeZone = union(TimeZoneTag) { pub const Sign = enum(u1) {
utc: void, positive,
offset: i8, negative,
};
pub const HoursMinutes = struct {
hours: u4,
minutes: ?u6,
};
pub const Offset = union(Sign) {
positive: HoursMinutes,
negative: HoursMinutes,
const Self = @This(); const Self = @This();
pub fn new(offset: ?i8) ?Self { fn new(hours: i8, minutes: ?u6) ?Self {
return if (offset) |ofs| blk: { if (hours > 12 or hours < -12) {
if (ofs < -12 or ofs > 12) { return null;
break :blk null;
} else if (ofs == 0) {
break :blk .utc;
} else {
break :blk Self{ .offset = ofs };
} }
} else .utc; if (minutes) |m| {
if (m > 59) return null;
if (hours == 0 and m == 0) return null;
} else if (hours == 0) return null;
if (hours < 0) {
const h = @intCast(u4, @as(i8, hours) * -1);
return Self{ .negative = .{ .hours = h, .minutes = minutes } };
} else {
return Self{ .positive = .{ .hours = @intCast(u4, hours), .minutes = minutes } };
}
}
fn as_seconds(self: Self) i64 {
return switch (self) {
.positive => |ofs| blk: {
var seconds = @as(i64, ofs.hours) * 3600;
if (ofs.minutes) |m| seconds += (@as(i64, m) * 60);
break :blk seconds;
},
.negative => |ofs| blk: {
var seconds = @as(i64, ofs.hours) * 3600;
if (ofs.minutes) |m| seconds += (@as(i64, m) * 60);
break :blk seconds * -1;
},
};
}
};
test "new offsets" {
try testing.expectEqual(Offset.new(-5, null), Offset{ .negative = .{ .hours = 5, .minutes = null } });
try testing.expectEqual(Offset.new(3, null), Offset{ .positive = .{ .hours = 3, .minutes = null } });
}
test "as seconds" {
try testing.expectEqual(Offset.new(-4, 30).?.as_seconds(), -16200);
try testing.expectEqual(Offset.new(3, null).?.as_seconds(), 10800);
}
pub const TimeZone = union(TimeZoneTag) {
utc: void,
offset: Offset,
const Self = @This();
pub fn new(hours: ?i8, minutes: ?u6) ?Self {
return if (hours) |h| blk: {
if (h == 0) {
break :blk .utc;
} else if (Offset.new(h, minutes)) |ofs| {
break :blk Self{ .offset = ofs };
} else {
break :blk null;
}
} else if (minutes) |m| Self{ .offset = Offset.new(0, m).? } else .utc;
} }
}; };
test "new timezone" { test "new timezone" {
const tz = TimeZone.new(-5).?; const tz = TimeZone.new(-5, null).?;
try testing.expectEqual(@as(TimeZoneTag, tz), .offset); try testing.expectEqual(@as(TimeZoneTag, tz), .offset);
switch (tz) { switch (tz) {
.offset => |ofs| try testing.expectEqual(ofs, -5), .offset => |ofs| try testing.expectEqual(ofs, Offset{ .negative = .{ .hours = 5, .minutes = null } }),
else => unreachable, else => unreachable,
} }
} }
test "new timezone utc" { test "new timezone utc" {
const tz0 = TimeZone.new(null).?; const tz0 = TimeZone.new(null, null).?;
const tz1 = TimeZone.new(0).?; const tz1 = TimeZone.new(0, null).?;
try testing.expectEqual(@as(TimeZoneTag, tz0), .utc); try testing.expectEqual(@as(TimeZoneTag, tz0), .utc);
try testing.expectEqual(@as(TimeZoneTag, tz1), .utc); try testing.expectEqual(@as(TimeZoneTag, tz1), .utc);
} }
@ -186,7 +244,7 @@ pub const DateTime = struct {
return self.year.get(); return self.year.get();
} }
pub fn getOffset(self: Self) ?i8 { pub fn getOffset(self: Self) ?Offset {
return switch (self.tz) { return switch (self.tz) {
.utc => null, .utc => null,
.offset => |ofs| ofs, .offset => |ofs| ofs,
@ -203,7 +261,7 @@ test "get year" {
.minute = 5, .minute = 5,
.second = 14, .second = 14,
.nanos = null, .nanos = null,
.tz = TimeZone.new(-5).?, .tz = TimeZone.new(-5, null).?,
}; };
try testing.expectEqual(dt.getYear(), 2023); try testing.expectEqual(dt.getYear(), 2023);
} }
@ -217,7 +275,7 @@ test "get offset" {
.minute = 5, .minute = 5,
.second = 14, .second = 14,
.nanos = null, .nanos = null,
.tz = TimeZone.new(-5).?, .tz = TimeZone.new(-5, null).?,
}; };
try testing.expectEqual(dt.getOffset().?, -5); try testing.expectEqual(dt.getOffset().?, Offset{ .negative = .{ .hours = 5, .minutes = null } });
} }