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); }