const std = @import("std"); const expect = std.testing.expect; const expectEqualStrings = std.testing.expectEqualStrings; const expectEqual = std.testing.expectEqual; const Allocator = std.mem.Allocator; pub const TimeError = error{ParsingError}; pub const Time = struct { /// expecting a date in the format yyyy-MM-dd'T'HH:mm:ss /// returning a unix timestamp /// does not handle leap seconds /// only handles UTC pub fn parseIso8601(data: []const u8) !i64 { const format = "nnnn-nn-nnTnn:nn:nn"; // 0123456789012345678 // validate the format if (data.len != 19) { return TimeError.ParsingError; } for (format, 0..) |formatChar, index| { if (index >= data.len) { return TimeError.ParsingError; } const c = data[index]; switch (formatChar) { 'n' => { if (!isDigit(c)) return TimeError.ParsingError; }, else => { if (formatChar != c) return TimeError.ParsingError; }, } } const year = try std.fmt.parseInt(i64, data[0..4], 10); const month = try std.fmt.parseInt(i64, data[5..7], 10); const day = try std.fmt.parseInt(i64, data[8..10], 10); const hour = try std.fmt.parseInt(i64, data[11..13], 10); const minute = try std.fmt.parseInt(i64, data[14..16], 10); const second = try std.fmt.parseInt(i64, data[17..19], 10); const unixTime: i64 = (year - 1970) * 365 * 24 * 3600 // + leapDays(year, month) * 24 * 3600 // + dayInYear(month, day) * 24 * 3600 // + hour * 3600 // + minute * 60 // + second; return unixTime; } fn isDigit(byte: u8) bool { return byte >= '0' and byte <= '9'; } fn dayInYear(month: i64, day: i64) i64 { const daysPerMonth = [_]u8{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; var result: i64 = 0; var i: u8 = 0; while (i < (month - 1)) : (i += 1) { result += daysPerMonth[i]; } result += day - 1; return result; } fn leapDays(year: i64, month: i64) i64 { var i: u64 = 1972; var result: i64 = 0; while (i < year) : (i += 1) { if (@mod(i, 4) == 0 and (i % 100 != 0 or @mod(i, 400) == 0)) { result += 1; } } if (month > 2 and @mod(year, 4) == 0 and (@mod(year, 100) != 0 or @mod(year, 400) == 0)) { result += 1; } return result; } }; test "parsing error 'x025-01-01T00:00:00'" { try std.testing.expectError(TimeError.ParsingError, Time.parseIso8601("x025-01-01T00:00:00")); } test "parsing error '2025-01-01'" { try std.testing.expectError(TimeError.ParsingError, Time.parseIso8601("2025-01-01T")); } //test "parsing validation successfull" { // const y = try Time.parseIso8601("2025-12-13T01:02:03"); // try expectEqual(12, y); //} test "parse 2025-02-18T18:37:45, expecting 1739903865" { const unixTime = try Time.parseIso8601("2025-02-18T18:37:45"); try expectEqual(1739903865, unixTime); }