aboutsummaryrefslogtreecommitdiff
path: root/src/time.zig
diff options
context:
space:
mode:
authortsne <tsne.dev@outlook.com>2025-01-01 14:58:28 +0100
committertsne <tsne.dev@outlook.com>2025-10-30 08:32:49 +0100
commit44e5ad763794a438ecfd50c8b7f6ea760ea82da5 (patch)
tree575a1fc72ceb1d7f052cf582abc1e038e27f69a3 /src/time.zig
downloadporteur-main.tar.gz
initial commitHEADmain
Diffstat (limited to 'src/time.zig')
-rw-r--r--src/time.zig134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/time.zig b/src/time.zig
new file mode 100644
index 0000000..290c9de
--- /dev/null
+++ b/src/time.zig
@@ -0,0 +1,134 @@
+const std = @import("std");
+
+pub const Time = struct {
+ year: u16,
+ month: u8,
+ day: u8,
+ hour: u8,
+ minute: u8,
+ second: u8,
+
+ pub fn from_timestamp(timestamp: u64) Time {
+ // Source: https://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c
+ // Copyright: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+
+ const leapoch = 946684800 + 86400 * (31 + 29); // 2000-03-01 (mod 400 year, immediately after feb29)
+ const days_per_400y = 365 * 400 + 97;
+ const days_per_100y = 365 * 100 + 24;
+ const days_per_4y = 365 * 4 + 1;
+ const days_in_month = [_]i64{ 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29 };
+
+ var days: i64 = undefined;
+ var remsecs: i64 = undefined;
+ if (timestamp < leapoch) {
+ const secs: i64 = @as(i64, @intCast(timestamp)) - leapoch;
+ days = @divTrunc(secs, 86400);
+ remsecs = @rem(secs, 86400);
+ } else {
+ const secs: u64 = timestamp - leapoch;
+ days = @intCast(secs / 86400);
+ remsecs = @intCast(@rem(secs, 86400));
+ }
+ if (remsecs < 0) {
+ remsecs += 86400;
+ days -= 1;
+ }
+
+ var qc_cycles = @divTrunc(days, days_per_400y);
+ var remdays = @rem(days, days_per_400y);
+ if (remdays < 0) {
+ remdays += days_per_400y;
+ qc_cycles = -1;
+ }
+
+ var c_cycles = @divTrunc(remdays, days_per_100y);
+ if (c_cycles == 4) {
+ c_cycles -= 1;
+ }
+ remdays -= c_cycles * days_per_100y;
+
+ var q_cycles = @divTrunc(remdays, days_per_4y);
+ if (q_cycles == 25) {
+ q_cycles -= 1;
+ }
+ remdays -= q_cycles * days_per_4y;
+
+ var remyears = @divTrunc(remdays, 365);
+ if (remyears == 4) {
+ remyears -= 1;
+ }
+ remdays -= remyears * 365;
+
+ var years = remyears + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles;
+ var months: u64 = 0;
+ while (remdays >= days_in_month[months]) {
+ remdays -= days_in_month[months];
+ months += 1;
+ }
+
+ if (months < 10) {
+ months += 3;
+ } else {
+ months -= 9;
+ years += 1;
+ }
+
+ return .{
+ .year = @intCast(years + 2000),
+ .month = @intCast(months),
+ .day = @intCast(remdays + 1),
+ .hour = @intCast(@divTrunc(remsecs, 3600)),
+ .minute = @intCast(@rem(@divTrunc(remsecs, 60), 60)),
+ .second = @intCast(@rem(remsecs, 60)),
+ };
+ }
+
+ pub fn format(self: Time, w: *std.io.Writer) std.io.Writer.Error!void {
+ try w.print(
+ "{d:0>4}-{d:0>2}-{d:0>2} {d:0>2}:{d:0>2}:{d:0>2} UTC",
+ .{ self.year, self.month, self.day, self.hour, self.minute, self.second },
+ );
+ }
+};
+
+pub fn now() u64 {
+ const ts = std.posix.clock_gettime(.REALTIME) catch return 0;
+ return if (ts.sec < 0) 0 else @intCast(ts.sec);
+}
+
+test "time from timestamp" {
+ var t: Time = undefined;
+ const leapoch = 946684800 + 86400 * (31 + 29);
+
+ t = .from_timestamp(0);
+ try std.testing.expectEqual(1970, t.year);
+ try std.testing.expectEqual(1, t.month);
+ try std.testing.expectEqual(1, t.day);
+ try std.testing.expectEqual(0, t.hour);
+ try std.testing.expectEqual(0, t.minute);
+ try std.testing.expectEqual(0, t.second);
+
+ t = .from_timestamp(leapoch - 1);
+ try std.testing.expectEqual(2000, t.year);
+ try std.testing.expectEqual(2, t.month);
+ try std.testing.expectEqual(29, t.day);
+ try std.testing.expectEqual(23, t.hour);
+ try std.testing.expectEqual(59, t.minute);
+ try std.testing.expectEqual(59, t.second);
+
+ t = .from_timestamp(leapoch);
+ try std.testing.expectEqual(2000, t.year);
+ try std.testing.expectEqual(3, t.month);
+ try std.testing.expectEqual(1, t.day);
+ try std.testing.expectEqual(0, t.hour);
+ try std.testing.expectEqual(0, t.minute);
+ try std.testing.expectEqual(0, t.second);
+
+ t = .from_timestamp(leapoch + 1);
+ try std.testing.expectEqual(2000, t.year);
+ try std.testing.expectEqual(3, t.month);
+ try std.testing.expectEqual(1, t.day);
+ try std.testing.expectEqual(0, t.hour);
+ try std.testing.expectEqual(0, t.minute);
+ try std.testing.expectEqual(1, t.second);
+}