From 2abed409462be3dca0f2bc9577426e9ad4670822 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 13 Jun 2023 00:21:36 -0400 Subject: [PATCH] Allow for minutes in TimeZone offsets --- src/main.zig | 94 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/main.zig b/src/main.zig index 387e9aa..cb3f837 100644 --- a/src/main.zig +++ b/src/main.zig @@ -135,37 +135,95 @@ pub const TimeZoneTag = enum(u1) { offset, }; -pub const TimeZone = union(TimeZoneTag) { - utc: void, - offset: i8, +pub const Sign = enum(u1) { + positive, + negative, +}; + +pub const HoursMinutes = struct { + hours: u4, + minutes: ?u6, +}; + +pub const Offset = union(Sign) { + positive: HoursMinutes, + negative: HoursMinutes, const Self = @This(); - pub fn new(offset: ?i8) ?Self { - return if (offset) |ofs| blk: { - if (ofs < -12 or ofs > 12) { - break :blk null; - } else if (ofs == 0) { + fn new(hours: i8, minutes: ?u6) ?Self { + if (hours > 12 or hours < -12) { + return null; + } + 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 { + } else if (Offset.new(h, minutes)) |ofs| { break :blk Self{ .offset = ofs }; + } else { + break :blk null; } - } else .utc; + } else if (minutes) |m| Self{ .offset = Offset.new(0, m).? } else .utc; } }; test "new timezone" { - const tz = TimeZone.new(-5).?; + const tz = TimeZone.new(-5, null).?; try testing.expectEqual(@as(TimeZoneTag, tz), .offset); switch (tz) { - .offset => |ofs| try testing.expectEqual(ofs, -5), + .offset => |ofs| try testing.expectEqual(ofs, Offset{ .negative = .{ .hours = 5, .minutes = null } }), else => unreachable, } } test "new timezone utc" { - const tz0 = TimeZone.new(null).?; - const tz1 = TimeZone.new(0).?; + const tz0 = TimeZone.new(null, null).?; + const tz1 = TimeZone.new(0, null).?; try testing.expectEqual(@as(TimeZoneTag, tz0), .utc); try testing.expectEqual(@as(TimeZoneTag, tz1), .utc); } @@ -186,7 +244,7 @@ pub const DateTime = struct { return self.year.get(); } - pub fn getOffset(self: Self) ?i8 { + pub fn getOffset(self: Self) ?Offset { return switch (self.tz) { .utc => null, .offset => |ofs| ofs, @@ -203,7 +261,7 @@ test "get year" { .minute = 5, .second = 14, .nanos = null, - .tz = TimeZone.new(-5).?, + .tz = TimeZone.new(-5, null).?, }; try testing.expectEqual(dt.getYear(), 2023); } @@ -217,7 +275,7 @@ test "get offset" { .minute = 5, .second = 14, .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 } }); }