From 44e5ad763794a438ecfd50c8b7f6ea760ea82da5 Mon Sep 17 00:00:00 2001 From: tsne Date: Wed, 1 Jan 2025 14:58:28 +0100 Subject: initial commit --- src/time.zig | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/time.zig (limited to 'src/time.zig') 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); +} -- cgit v1.2.3