Skip to content

Commit

Permalink
revised datetime's 'now' methods
Browse files Browse the repository at this point in the history
  • Loading branch information
FObersteiner committed Sep 20, 2024
1 parent e80de1d commit f138241
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 44 deletions.
4 changes: 3 additions & 1 deletion change.log
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/ex_datetime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 7 additions & 7 deletions examples/ex_duration.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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});
}

Expand Down
4 changes: 2 additions & 2 deletions examples/ex_timezones.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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() });
Expand Down
56 changes: 27 additions & 29 deletions lib/Datetime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 <https://en.wikipedia.org/wiki/ISO_week_date>.
pub fn toISOCalendar(dt: Datetime) ISOCalendar {
Expand All @@ -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;
Expand Down
15 changes: 11 additions & 4 deletions tests/test_timezone.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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(), ""));
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit f138241

Please sign in to comment.