Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
FObersteiner committed Nov 4, 2024
1 parent ca7548a commit 9d26fde
Show file tree
Hide file tree
Showing 28 changed files with 193 additions and 127 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@ Types of changes

## Unreleased

## 2024-11-04, v0.4.2

### Added

- ISO8601 duration parser

### Changed

- revised examples; type infos now in a separate file 'ex_zdt-types.zig'

### Fixed

- default formatter of the Duration type to 'ISO8601 duration'-like string
- default formatter of the Duration type to 'ISO8601 duration'-like string, correct output
- datetime parsing / from fields: leap seconds are now validated, i.e. a random datetime cannot have seconds == 60 anymore

## 2024-10-28, v0.4.1

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ std.debug.print(
"Wall clock time difference: {s}\nAbsolute time difference: {s}\n",
.{ wall_diff, abs_diff },
);
// Wall clock time difference: PT09H00M00S
// Absolute time difference: PT00H00M00S
// Wall clock time difference: PT9H
// Absolute time difference: PT0S
```

More examples in the `./examples` directory. There's a build-step to build them all;
Expand All @@ -76,7 +76,7 @@ See [changelog](https://github.com/FObersteiner/zdt/blob/master/CHANGELOG.md)

## Zig version

This library is developed with Zig `0.14.0-dev` aka 'master', might not compile with older versions. As of 2024-10-27, Zig-0.13 stable or higher should work.
This library is developed with Zig `0.14.0-dev` aka 'master', might not compile with older versions. As of 2024-11-04, Zig-0.13 stable or higher should work.

## IANA timezone database version

Expand Down
7 changes: 4 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ const log = std.log.scoped(.zdt_build);
const zdt_version = std.SemanticVersion{ .major = 0, .minor = 4, .patch = 2 };

const example_files = [_][]const u8{
"ex_demo",
"demo",
"ex_datetime",
"ex_duration",
"ex_locale",
"ex_offsetTz",
"ex_strings",
"ex_timezones",
"ex_zdt-types",
};

const test_files = [_][]const u8{
Expand Down Expand Up @@ -90,7 +91,7 @@ pub fn build(b: *std.Build) !void {
);
var gen_tzdb_prefix = b.addExecutable(.{
.name = "gen_tzdb_prefix",
.root_source_file = b.path("util/gen_tzdb_prefix.zig"),
.root_source_file = b.path("scripts/gen_tzdb_prefix.zig"),
.target = b.host,
});
const run_gen_prefix = b.addRunArtifact(gen_tzdb_prefix);
Expand Down Expand Up @@ -122,7 +123,7 @@ pub fn build(b: *std.Build) !void {
{
var gen_tzdb = b.addExecutable(.{
.name = "gen_tzdb",
.root_source_file = b.path("util/gen_tzdb.zig"),
.root_source_file = b.path("scripts/gen_tzdb.zig"),
.target = b.host,
});

Expand Down
5 changes: 2 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
.paths = .{
"zdt.zig",
"lib", // anything from lib
"util/gen_tzdb_prefix.zig",
"util/gen_tzdb_version.zig",
"README.md",
"scripts/gen_tzdb_prefix.zig",
"scripts/gen_tzdb_version.zig",
"LICENSE",
"build.zig",
"build.zig.zon",
Expand Down
4 changes: 2 additions & 2 deletions examples/ex_demo.zig → examples/demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ pub fn main() !void {
"Wall clock time difference: {s}\nAbsolute time difference: {s}\n",
.{ wall_diff, abs_diff },
);
// Wall clock time difference: PT9H0M0S
// Absolute time difference: PT0H0M0S
// Wall clock time difference: PT9H
// Absolute time difference: PT0S
}
13 changes: 2 additions & 11 deletions examples/ex_datetime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,16 @@ const Tz = zdt.Timezone;

pub fn main() !void {
println("---> datetime example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}\n", .{builtin.zig_version_string});

println("datetime type info:", .{});
println("size of {s}: {}", .{ @typeName(Datetime), @sizeOf(Datetime) });
inline for (std.meta.fields(Datetime)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(Datetime, field.name) });
}
println("", .{});

println("---> (usage) Unix epoch: datetime from timestamp", .{});
println("---> Unix epoch: datetime from timestamp", .{});
const unix_epoch_naive = try Datetime.fromUnix(0, Duration.Resolution.second, null);
println("'Unix epoch', naive datetime : {s}", .{unix_epoch_naive});
var unix_epoch_correct = try Datetime.fromUnix(0, Duration.Resolution.second, .{ .tz = &Tz.UTC });
println("'Unix epoch', aware datetime : {s}", .{unix_epoch_correct});
println("'Unix epoch', tz name : {s}", .{unix_epoch_correct.tzName()});

println("", .{});
println("---> (usage) Now: datetime from system time", .{});
println("---> Now: datetime from system time", .{});
const now = Datetime.nowUTC();
println("'now', UTC : {s}", .{now});
println("'now', UTC : {s:.3} (only ms shown)", .{now});
Expand Down
21 changes: 7 additions & 14 deletions examples/ex_duration.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@ const std = @import("std");
const builtin = @import("builtin");

const zdt = @import("zdt");
const Datetime = zdt.Datetime;
const Duration = zdt.Duration;

pub fn main() !void {
println("---> duration example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}\n", .{builtin.zig_version_string});

println("Duration type info:", .{});
println("size of {s}: {}", .{ @typeName(zdt.Duration), @sizeOf(zdt.Duration) });
inline for (std.meta.fields(zdt.Duration)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(zdt.Duration, field.name) });
}
println("", .{});

const now_utc = zdt.Datetime.nowUTC();
const now_utc = Datetime.nowUTC();
println("now, UTC : {s}", .{now_utc});
const past_midnight = try now_utc.floorTo(zdt.Duration.Timespan.day);
const past_midnight = try now_utc.floorTo(Duration.Timespan.day);

// difference between two datetimes expressed as Duration:
println(
Expand All @@ -26,16 +19,16 @@ pub fn main() !void {
);

// Durations from Timespans:
const tomorrow = try now_utc.add(zdt.Duration.fromTimespanMultiple(1, zdt.Duration.Timespan.day));
const tomorrow = try now_utc.add(Duration.fromTimespanMultiple(1, Duration.Timespan.day));
println("tomorrow, same time : {s}", .{tomorrow});
println("tomorrow, same time, is {d} seconds away from now\n", .{tomorrow.diff(now_utc).asSeconds()});

// Timespan units range from nanoseconds to weeks:
const two_weeks_ago = try now_utc.sub(zdt.Duration.fromTimespanMultiple(2, zdt.Duration.Timespan.week));
const two_weeks_ago = try now_utc.sub(Duration.fromTimespanMultiple(2, Duration.Timespan.week));
println("two weeks ago : {s}", .{two_weeks_ago});

// ISO8601-duration parser on-board:
const one_wk_one_h = try zdt.Duration.fromISO8601Duration("P7DT1H");
const one_wk_one_h = try Duration.fromISO8601Duration("P7DT1H");
const in_a_week = try now_utc.add(one_wk_one_h);
println("in a week and an hour : {s}", .{in_a_week});
}
Expand Down
2 changes: 0 additions & 2 deletions examples/ex_locale.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ const Datetime = zdt.Datetime;

pub fn main() !void {
println("---> locale example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}", .{builtin.zig_version_string});

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
Expand Down
4 changes: 1 addition & 3 deletions examples/ex_offsetTz.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ const Datetime = zdt.Datetime;
const UTCoffset = zdt.UTCoffset;

pub fn main() !void {
println("---> UTC offset time zone example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}\n", .{builtin.zig_version_string});
println("---> UTC offset example", .{});

const offset = try UTCoffset.fromSeconds(3600, "UTC+1");
var a_date = try Datetime.fromFields(.{ .year = 1970, .tz_options = .{ .utc_offset = offset } });
Expand Down
5 changes: 1 addition & 4 deletions examples/ex_strings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ const assert = std.debug.assert;

const zdt = @import("zdt");
const Datetime = zdt.Datetime;
const Duration = zdt.Duration;
const Timezone = zdt.Timezone;

pub fn main() !void {
println("---> datetime example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}", .{builtin.zig_version_string});
println("---> datetime strings example", .{});

// the easiest input format is probably ISO8601. This can directly
// be parsed; schema is infered at runtime.
Expand Down
21 changes: 6 additions & 15 deletions examples/ex_timezones.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,35 @@ const builtin = @import("builtin");

const zdt = @import("zdt");
const Datetime = zdt.Datetime;
const Tz = zdt.Timezone;
const Timezone = zdt.Timezone;

pub fn main() !void {
println("---> time zones example", .{});
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}\n", .{builtin.zig_version_string});

println("TZ type info:", .{});
println("size of {s}: {}", .{ @typeName(Tz), @sizeOf(Tz) });
inline for (std.meta.fields(Tz)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(Tz, field.name) });
}
println("", .{});

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

println("time zone database version: {s}", .{Tz.tzdb_version});
println("path to local tz database: {s}\n", .{Tz.tzdb_prefix});
println("IANA time zone database version: {s}", .{Timezone.tzdb_version});
println("path to local tz database: {s}\n", .{Timezone.tzdb_prefix});

var tz_berlin: Tz = try Tz.fromTzdata("Europe/Berlin", allocator);
var tz_berlin: Timezone = try Timezone.fromTzdata("Europe/Berlin", allocator);
defer tz_berlin.deinit();
var now_berlin: Datetime = try Datetime.now(.{ .tz = &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.tzAbbreviation() });
println("Datetimes have UTC offset / time zone? : {}, {}\n", .{ now_utc.isAware(), now_berlin.isAware() });

var my_tz: Tz = try Tz.tzLocal(allocator);
var my_tz: Timezone = try Timezone.tzLocal(allocator);
defer my_tz.deinit();
var now_local = try now_berlin.tzConvert(.{ .tz = &my_tz });
println("My time zone : {s}", .{my_tz.name()});

println("Now, my time zone : {s} ({s})", .{ now_local, now_local.tzAbbreviation() });
println("", .{});

var tz_ny = try Tz.fromTzdata("America/New_York", allocator);
var tz_ny = try Timezone.fromTzdata("America/New_York", allocator);
defer tz_ny.deinit();
var now_ny: Datetime = try now_local.tzConvert(.{ .tz = &tz_ny });
println("Now in New York : {s} ({s})", .{ now_ny, now_ny.tzAbbreviation() });
Expand Down
46 changes: 46 additions & 0 deletions examples/ex_zdt-types.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const std = @import("std");
const builtin = @import("builtin");

const zdt = @import("zdt");
const Datetime = zdt.Datetime;
const Duration = zdt.Duration;
const Timezone = zdt.Timezone;
const UTCoffset = zdt.UTCoffset;

pub fn main() !void {
println("OS / architecture: {s} / {s}", .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch) });
println("Zig version: {s}\n", .{builtin.zig_version_string});

println("---> Datetime", .{});
println("size of {s}: {} bytes", .{ @typeName(Datetime), @sizeOf(Datetime) });
inline for (std.meta.fields(Datetime)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(Datetime, field.name) });
}
println("", .{});

println("---> Duration", .{});
println("size of {s}: {} bytes", .{ @typeName(zdt.Duration), @sizeOf(zdt.Duration) });
inline for (std.meta.fields(zdt.Duration)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(zdt.Duration, field.name) });
}
println("", .{});

println("---> Timezone", .{});
println("size of {s}: {} bytes", .{ @typeName(Timezone), @sizeOf(Timezone) });
inline for (std.meta.fields(Timezone)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(Timezone, field.name) });
}
println("", .{});

println("---> UTCoffset", .{});
println("size of {s}: {} bytes", .{ @typeName(UTCoffset), @sizeOf(Datetime) });
inline for (std.meta.fields(UTCoffset)) |field| {
println(" field {s} byte offset: {}", .{ field.name, @offsetOf(UTCoffset, field.name) });
}
println("", .{});
}

fn println(comptime fmt: []const u8, args: anytype) void {
const stdout = std.io.getStdOut().writer();
nosuspend stdout.print(fmt ++ "\n", args) catch return;
}
17 changes: 10 additions & 7 deletions lib/Datetime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -239,20 +239,18 @@ pub const OptFields = struct {

/// Make a valid datetime from fields.
pub fn fromFields(fields: Fields) ZdtError!Datetime {

// NOTE : should this only be called on demand or only in debug builds ?
_ = try fields.validate();

const d = cal.dateToRD([_]u16{ fields.year, fields.month, fields.day });
// Note : need to truncate seconds to 59 so that Unix time is 'correct'
const s = if (fields.second == 60) 59 else fields.second;
var dt = Datetime{
.year = fields.year,
.month = @truncate(fields.month),
.day = @truncate(fields.day),
.hour = @truncate(fields.hour),
.minute = @truncate(fields.minute),
.second = @truncate(fields.second),
.month = fields.month,
.day = fields.day,
.hour = fields.hour,
.minute = fields.minute,
.second = fields.second,
.nanosecond = fields.nanosecond,
.unix_sec = ( //
@as(i40, d) * s_per_day +
Expand All @@ -261,6 +259,9 @@ pub fn fromFields(fields: Fields) ZdtError!Datetime {
),
};

// verify that provided leap second datetime is valid
if (dt.second == 60) _ = try dt.validateLeap();

if (fields.tz_options) |opts| {
switch (opts) {
.utc_offset => {
Expand Down Expand Up @@ -649,6 +650,8 @@ pub fn diffWall(this: Datetime, other: Datetime) !Duration {

/// Validate a datetime in terms of leap seconds;
/// Returns an error if the datetime has seconds == 60 but is NOT a leap second datetime.
//
// TODO : might be private
pub fn validateLeap(this: *const Datetime) !void {
if (this.second != 60) return;
if (cal.mightBeLeap(this.unix_sec)) return;
Expand Down
Loading

0 comments on commit 9d26fde

Please sign in to comment.