From f13824181916e49c12e3a8175d0d8569a863110f Mon Sep 17 00:00:00 2001 From: FObersteiner Date: Fri, 20 Sep 2024 15:15:45 +0200 Subject: [PATCH] revised datetime's 'now' methods --- change.log | 4 ++- examples/ex_datetime.zig | 2 +- examples/ex_duration.zig | 14 +++++----- examples/ex_timezones.zig | 4 +-- lib/Datetime.zig | 56 +++++++++++++++++++-------------------- tests/test_timezone.zig | 15 ++++++++--- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/change.log b/change.log index 1708d89..379771c 100644 --- a/change.log +++ b/change.log @@ -6,7 +6,9 @@ - Datetime now has `toString` and `fromString`, as well as `fromISO8601` as a shortcut for ISO8601-compatible input - string.zig and calendar.zig aren't exported anymore (stringIO.zig is now string.zig), to keep the API concise - `toString`: correctly handle naive datetime with 'i', 'z' and 'Z' directives -- add method `tzDeinit()` for datetime instance that frees memory allocated for a timezone - required for nowLocal with timezone +- remove method `nowLocal` from Datetime; this can be achieved with `now` plus `Timezone.tzLocal` +- added method `nowUTC` to Datetime that returns UTC (aware datetime) without needing a timezone argument and returns no error +- change `now` to return an error union, in case loading the timezone causes an error ## 2024-09-15, v0.2.3 diff --git a/examples/ex_datetime.zig b/examples/ex_datetime.zig index 262f14c..8356053 100644 --- a/examples/ex_datetime.zig +++ b/examples/ex_datetime.zig @@ -27,7 +27,7 @@ pub fn main() !void { println("", .{}); println("---> (usage) Now: datetime from system time", .{}); - const now = Datetime.now(Tz.UTC); + const now = Datetime.nowUTC(); println("'now', UTC : {s}", .{now}); println("'now', UTC : {s:.3} (only ms shown)", .{now}); const now_s = try now.floorTo(Duration.Timespan.second); diff --git a/examples/ex_duration.zig b/examples/ex_duration.zig index 9ad0ad8..ebd992b 100644 --- a/examples/ex_duration.zig +++ b/examples/ex_duration.zig @@ -15,19 +15,19 @@ pub fn main() !void { } println("", .{}); - const now = zdt.Datetime.now(zdt.Timezone.UTC); - println("now, UTC : {s}", .{now}); - const past_midnight = try now.floorTo(zdt.Duration.Timespan.day); + const now_utc = zdt.Datetime.nowUTC(); + println("now, UTC : {s}", .{now_utc}); + const past_midnight = try now_utc.floorTo(zdt.Duration.Timespan.day); println( "{d:.3} seconds have passed since midnight ({s})\n", - .{ now.diff(past_midnight).totalSeconds(), past_midnight }, + .{ now_utc.diff(past_midnight).totalSeconds(), past_midnight }, ); - const tomorrow = try now.add(zdt.Duration.fromTimespanMultiple(1, zdt.Duration.Timespan.day)); + const tomorrow = try now_utc.add(zdt.Duration.fromTimespanMultiple(1, zdt.Duration.Timespan.day)); println("tomorrow, same time : {s}", .{tomorrow}); - println("tomorrow, same time, is {d} seconds away from now\n", .{tomorrow.diff(now).asSeconds()}); + println("tomorrow, same time, is {d} seconds away from now\n", .{tomorrow.diff(now_utc).asSeconds()}); - const two_weeks_ago = try now.sub(zdt.Duration.fromTimespanMultiple(2, zdt.Duration.Timespan.week)); + const two_weeks_ago = try now_utc.sub(zdt.Duration.fromTimespanMultiple(2, zdt.Duration.Timespan.week)); println("two weeks ago : {s}", .{two_weeks_ago}); } diff --git a/examples/ex_timezones.zig b/examples/ex_timezones.zig index 83eb41c..38ec4f5 100644 --- a/examples/ex_timezones.zig +++ b/examples/ex_timezones.zig @@ -25,8 +25,8 @@ pub fn main() !void { var tz_berlin: Tz = try Tz.fromTzdata("Europe/Berlin", allocator); defer tz_berlin.deinit(); - var now_berlin: Datetime = Datetime.now(tz_berlin); - const now_utc: Datetime = Datetime.now(Tz.UTC); + var now_berlin: Datetime = try Datetime.now(tz_berlin); + const now_utc: Datetime = Datetime.nowUTC(); println("Now, UTC time : {s}", .{now_utc}); println("Now, Berlin time : {s} ({s})", .{ now_berlin, now_berlin.tzinfo.?.abbreviation() }); println("Datetimes have timezone? {}, {}\n", .{ now_utc.isAware(), now_berlin.isAware() }); diff --git a/lib/Datetime.zig b/lib/Datetime.zig index 414f9c2..8b84ec2 100644 --- a/lib/Datetime.zig +++ b/lib/Datetime.zig @@ -351,11 +351,6 @@ pub fn tzConvert(dt: Datetime, new_tz: Timezone) ZdtError!Datetime { ); } -/// Wrapper to deinit the timezone of a datetime -pub fn tzDeinit(dt: *Datetime) void { - if (dt.isAware()) (&dt.tzinfo.?).deinit(); -} - /// Floor a datetime to a certain timespan. Creates a new datetime instance. pub fn floorTo(dt: Datetime, timespan: Duration.Timespan) !Datetime { // any other timespan than second can lead to ambiguous or non-existent @@ -400,18 +395,15 @@ pub fn floorTo(dt: Datetime, timespan: Duration.Timespan) !Datetime { /// The current time with nanosecond resolution. /// If 'null' is supplied as tzinfo, naive datetime resembling UTC is returned. -pub fn now(tzinfo: ?Timezone) Datetime { +pub fn now(tzinfo: ?Timezone) ZdtError!Datetime { const t = std.time.nanoTimestamp(); - return Datetime.fromUnix(@intCast(t), Duration.Resolution.nanosecond, tzinfo) catch Datetime{}; + return Datetime.fromUnix(@intCast(t), Duration.Resolution.nanosecond, tzinfo); } -/// Try to obtain datetime in the local time zone. -/// Requires allocator for the time zone object; must be de-initialized by the caller. -/// User the 'tzDeinit()' method of the datetime to do so. -pub fn nowLocal(allocator: std.mem.Allocator) !Datetime { - const tz = try Timezone.tzLocal(allocator); +/// Current UTC time is fail-safe since it contains a pre-defined time zone. +pub fn nowUTC() Datetime { const t = std.time.nanoTimestamp(); - return Datetime.fromUnix(@intCast(t), Duration.Resolution.nanosecond, tz); + return Datetime.fromUnix(@intCast(t), Duration.Resolution.nanosecond, Timezone.UTC) catch unreachable; } /// Compare two instances with respect to their Unix time. @@ -556,21 +548,6 @@ pub fn weekOfYearMon(dt: Datetime) u8 { return @truncate(@divFloor((doy + 7 - if (dow > 0) dow - 1 else 6), 7)); } -/// Parse a datetime from a string -pub fn fromString(dt_string: []const u8, comptime fmt: []const u8) !Datetime { - return str.parseToDatetime(fmt, dt_string); -} - -/// Parse a datetime from a string -pub fn fromISO8601(dt_string: []const u8) !Datetime { - return str.parseISO8601(dt_string); -} - -/// Format a datetime into a string -pub fn toString(dt: Datetime, fmt: []const u8, writer: anytype) !void { - return str.formatToString(writer, fmt, dt); -} - /// Calculate the ISO week of the year and generate ISOCalendar. /// Algorithm from . pub fn toISOCalendar(dt: Datetime) ISOCalendar { @@ -591,8 +568,29 @@ pub fn toISOCalendar(dt: Datetime) ISOCalendar { return isocal; } +/// Parse a datetime from a string +pub fn fromString(dt_string: []const u8, comptime fmt: []const u8) !Datetime { + return str.parseToDatetime(fmt, dt_string); +} + +/// Parse a datetime from a string +pub fn fromISO8601(dt_string: []const u8) !Datetime { + return str.parseISO8601(dt_string); +} + +/// Format a datetime into a string +pub fn toString(dt: Datetime, fmt: []const u8, writer: anytype) !void { + return str.formatToString(writer, fmt, dt); +} + +pub fn tzName(dt: *Datetime) []const u8 { + return @constCast(&dt.tzinfo.?).name(); +} +pub fn tzAbbreviation(dt: *Datetime) []const u8 { + return @constCast(&dt.tzinfo.?).abbreviation(); +} + /// Formatted printing for UTC offset -// TODO : does this need to be pub ? pub fn formatOffset(dt: Datetime, writer: anytype) !void { // if the tzinfo or tzOffset is null, we cannot do anything: if (dt.isNaive()) return; diff --git a/tests/test_timezone.zig b/tests/test_timezone.zig index 1616c1e..e143b5a 100644 --- a/tests/test_timezone.zig +++ b/tests/test_timezone.zig @@ -16,6 +16,11 @@ test "utc" { try testing.expect(utc.tzOffset.?.seconds_east == 0); try testing.expectEqualStrings(utc.name(), "UTC"); try testing.expectEqualStrings(utc.abbreviation(), "Z"); + var utc_now = Datetime.nowUTC(); + try testing.expectEqualStrings(@constCast(&utc_now.tzinfo.?).name(), "UTC"); + try testing.expectEqualStrings(@constCast(&utc_now.tzinfo.?).abbreviation(), "Z"); + try testing.expectEqualStrings(utc_now.tzName(), "UTC"); + try testing.expectEqualStrings(utc_now.tzAbbreviation(), "Z"); } test "offset tz never changes offset" { @@ -117,8 +122,10 @@ test "invalid tzfile name" { } test "local tz" { - var now = try Datetime.nowLocal(testing.allocator); - defer now.tzDeinit(); + var tzinfo = try Tz.tzLocal(testing.allocator); + defer tzinfo.deinit(); + var now = try Datetime.now(tzinfo); + try testing.expect(now.tzinfo != null); try testing.expect(!std.mem.eql(u8, now.tzinfo.?.name(), "")); try testing.expect(!std.mem.eql(u8, now.tzinfo.?.abbreviation(), "")); @@ -233,11 +240,11 @@ test "tz name and abbr correct after localize" { var tz_ny = try Tz.fromTzfile("America/New_York", testing.allocator); defer _ = tz_ny.deinit(); - var now_local: Datetime = Datetime.now(tz_ny); + var now_local: Datetime = try Datetime.now(tz_ny); try testing.expectEqualStrings("America/New_York", now_local.tzinfo.?.name()); try testing.expect(now_local.tzinfo.?.abbreviation().len > 0); - now_local = Datetime.now(null); + now_local = try Datetime.now(null); now_local = try now_local.tzLocalize(tz_ny); try testing.expectEqualStrings("America/New_York", now_local.tzinfo.?.name()); try testing.expect(now_local.tzinfo.?.abbreviation().len > 0);