From df09ed2bddec3a372806d37f2fd909655a646bad Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Thu, 15 May 2025 11:48:41 +1000 Subject: [PATCH 1/5] refactor: parse iso dates using zeit --- lib/date/src/parsing.zig | 45 ++-------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/lib/date/src/parsing.zig b/lib/date/src/parsing.zig index d41411b..d3a26b7 100644 --- a/lib/date/src/parsing.zig +++ b/lib/date/src/parsing.zig @@ -69,49 +69,8 @@ const IsoParsingState = enum { Start, Year, Month, Day, Hour, Minute, Second, Mi /// Converts a string to a timestamp value. May not handle dates before the /// epoch pub fn parseIso8601ToDateTime(data: []const u8) !DateTime { - // Basic format YYYYMMDDThhmmss - if (data.len == "YYYYMMDDThhmmss".len and data[8] == 'T') - return try parseIso8601BasicFormatToDateTime(data); - if (data.len == "YYYYMMDDThhmmssZ".len and data[8] == 'T') - return try parseIso8601BasicFormatToDateTime(data); - - var start: usize = 0; - var state = IsoParsingState.Start; - // Anything not explicitly set by our string would be 0 - var rc = DateTime{ .year = 0, .month = 0, .day = 0, .hour = 0, .minute = 0, .second = 0 }; - var zulu_time = false; - for (data, 0..) |ch, i| { - switch (ch) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { - if (state == .Start) state = .Year; - }, - '?', '~', '%' => { - // These characters all specify the type of time (approximate, etc) - // and we will ignore - }, - '.', '-', ':', 'T' => { - // State transition - - // We're going to coerce and this might not go well, but we - // want the compiler to create checks, so we'll turn on - // runtime safety for this block, forcing checks in ReleaseSafe - // ReleaseFast modes. - const next_state = try endIsoState(state, &rc, data[start..i]); - state = next_state; - start = i + 1; - }, - 'Z' => zulu_time = true, - else => { - log.err("Invalid character: {c}", .{ch}); - return error.InvalidCharacter; - }, - } - } - if (!zulu_time) return error.LocalTimeNotSupported; - // We know we have a Z at the end of this, so let's grab the last bit - // of the string, minus the 'Z', and fly, eagles, fly! - _ = try endIsoState(state, &rc, data[start .. data.len - 1]); - return rc; + const ins = try zeit.instant(.{ .source = .{ .iso8601 = data } }); + return DateTime.fromInstant(ins); } fn parseIso8601BasicFormatToDateTime(data: []const u8) !DateTime { From c828dfdcb0c54c6754f870837d1054233f89b4aa Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Thu, 15 May 2025 11:49:53 +1000 Subject: [PATCH 2/5] chore: update zeit with pre-release fix for iso 8601 basic format --- build.zig.zon | 4 ++-- lib/date/build.zig.zon | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index bfc99ca..b53db2c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -22,8 +22,8 @@ .hash = "N-V-__8AAKWdeiawujEcrfukQbb8lLAiQIRT0uG5gCcm4b7W", }, .zeit = .{ - .url = "git+https://github.com/rockorager/zeit#fb6557ad4bd0cd0f0f728ae978061d7fe992c528", - .hash = "zeit-0.6.0-5I6bk29nAgDhK6AVMtXMWhkKTYgUncrWjnlI_8X9DPSd", + .url = "https://github.com/deevus/zeit/archive/refs/heads/iso8601-basic.tar.gz", + .hash = "zeit-0.6.0-5I6bkzt5AgC1_BCuSzXkV0JHeF4Mhti1Z_jFC7E_nmD2", }, .date = .{ .path = "lib/date", diff --git a/lib/date/build.zig.zon b/lib/date/build.zig.zon index ef4c86f..d2ef3b8 100644 --- a/lib/date/build.zig.zon +++ b/lib/date/build.zig.zon @@ -5,8 +5,8 @@ .minimum_zig_version = "0.14.0", .dependencies = .{ .zeit = .{ - .url = "git+https://github.com/rockorager/zeit#fb6557ad4bd0cd0f0f728ae978061d7fe992c528", - .hash = "zeit-0.6.0-5I6bk29nAgDhK6AVMtXMWhkKTYgUncrWjnlI_8X9DPSd", + .url = "https://github.com/deevus/zeit/archive/refs/heads/iso8601-basic.tar.gz", + .hash = "zeit-0.6.0-5I6bkzt5AgC1_BCuSzXkV0JHeF4Mhti1Z_jFC7E_nmD2", }, .json = .{ .path = "../json", From 307e4b985fb5fe988a47193b8362531fb3652158 Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Thu, 15 May 2025 11:57:20 +1000 Subject: [PATCH 3/5] refactor: remove old code --- lib/date/src/parsing.zig | 42 ---------------------------------------- 1 file changed, 42 deletions(-) diff --git a/lib/date/src/parsing.zig b/lib/date/src/parsing.zig index d3a26b7..e262778 100644 --- a/lib/date/src/parsing.zig +++ b/lib/date/src/parsing.zig @@ -73,48 +73,6 @@ pub fn parseIso8601ToDateTime(data: []const u8) !DateTime { return DateTime.fromInstant(ins); } -fn parseIso8601BasicFormatToDateTime(data: []const u8) !DateTime { - return DateTime{ - .year = try std.fmt.parseUnsigned(u16, data[0..4], 10), - .month = try std.fmt.parseUnsigned(u8, data[4..6], 10), - .day = try std.fmt.parseUnsigned(u8, data[6..8], 10), - .hour = try std.fmt.parseUnsigned(u8, data[9..11], 10), - .minute = try std.fmt.parseUnsigned(u8, data[11..13], 10), - .second = try std.fmt.parseUnsigned(u8, data[13..15], 10), - }; -} - -fn endIsoState(current_state: IsoParsingState, date: *DateTime, prev_data: []const u8) !IsoParsingState { - var next_state: IsoParsingState = undefined; - log.debug("endIsoState. Current state '{}', data: {s}", .{ current_state, prev_data }); - - // Using two switches is slightly less efficient, but more readable - switch (current_state) { - .Start, .End => return error.IllegalStateTransition, - .Year => next_state = .Month, - .Month => next_state = .Day, - .Day => next_state = .Hour, - .Hour => next_state = .Minute, - .Minute => next_state = .Second, - .Second => next_state = .Millisecond, - .Millisecond => next_state = .End, - } - - // TODO: This won't handle signed, which Iso supports. For now, let's fail - // explictly - switch (current_state) { - .Year => date.year = try std.fmt.parseUnsigned(u16, prev_data, 10), - .Month => date.month = try std.fmt.parseUnsigned(u8, prev_data, 10), - .Day => date.day = try std.fmt.parseUnsigned(u8, prev_data, 10), - .Hour => date.hour = try std.fmt.parseUnsigned(u8, prev_data, 10), - .Minute => date.minute = try std.fmt.parseUnsigned(u8, prev_data, 10), - .Second => date.second = try std.fmt.parseUnsigned(u8, prev_data, 10), - .Millisecond => {}, // We'll throw that away - our granularity is 1 second - .Start, .End => return error.InvalidState, - } - return next_state; -} - pub fn dateTimeToTimestamp(datetime: DateTime) !zeit.Seconds { return (try datetime.instant()).unixTimestamp(); } From cab0d70c3321478f385342776dffbc058ae3547f Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Thu, 15 May 2025 12:02:35 +1000 Subject: [PATCH 4/5] chore: removed now irrelevant comment --- lib/date/src/parsing.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/date/src/parsing.zig b/lib/date/src/parsing.zig index e262778..adfac46 100644 --- a/lib/date/src/parsing.zig +++ b/lib/date/src/parsing.zig @@ -1,7 +1,3 @@ -// From https://gist.github.com/WoodyAtHome/3ef50b17f0fa2860ac52b97af12f8d15 -// Translated from German. We don't need any local time for this use case, and conversion -// really requires the TZ DB. - const std = @import("std"); const log = std.log.scoped(.date); const zeit = @import("zeit"); From e98c7922448f53005ec26c0a3f5e4f5e5cef701d Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Thu, 15 May 2025 13:32:35 +1000 Subject: [PATCH 5/5] chore: update zeit to master --- build.zig.zon | 2 +- lib/date/build.zig.zon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index b53db2c..841828c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -22,7 +22,7 @@ .hash = "N-V-__8AAKWdeiawujEcrfukQbb8lLAiQIRT0uG5gCcm4b7W", }, .zeit = .{ - .url = "https://github.com/deevus/zeit/archive/refs/heads/iso8601-basic.tar.gz", + .url = "git+https://github.com/rockorager/zeit#f86d568b89a5922f084dae524a1eaf709855cd5e", .hash = "zeit-0.6.0-5I6bkzt5AgC1_BCuSzXkV0JHeF4Mhti1Z_jFC7E_nmD2", }, .date = .{ diff --git a/lib/date/build.zig.zon b/lib/date/build.zig.zon index d2ef3b8..b4b9ca8 100644 --- a/lib/date/build.zig.zon +++ b/lib/date/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.14.0", .dependencies = .{ .zeit = .{ - .url = "https://github.com/deevus/zeit/archive/refs/heads/iso8601-basic.tar.gz", + .url = "git+https://github.com/rockorager/zeit#f86d568b89a5922f084dae524a1eaf709855cd5e", .hash = "zeit-0.6.0-5I6bkzt5AgC1_BCuSzXkV0JHeF4Mhti1Z_jFC7E_nmD2", }, .json = .{