Skip to content

Commit

Permalink
add unit to timestamp fields and args
Browse files Browse the repository at this point in the history
Annotate timestamp fields and args with the unit they require (s, ns,
etc).
  • Loading branch information
rockorager committed Oct 31, 2024
1 parent 54d8d5c commit a5d5739
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 36 deletions.
30 changes: 17 additions & 13 deletions src/timezone.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ pub const TimeZone = union(enum) {
else => Noop,
},

pub fn adjust(self: TimeZone, timestamp: i64) AdjustedTime {
pub fn adjust(self: TimeZone, timestamp_s: i64) AdjustedTime {
return switch (self) {
inline else => |tz| tz.adjust(timestamp),
inline else => |tz| tz.adjust(timestamp_s),
};
}

Expand All @@ -38,7 +38,7 @@ pub const TimeZone = union(enum) {

pub const AdjustedTime = struct {
designation: []const u8,
timestamp: i64,
timestamp_s: i64,
is_dst: bool,
};

Expand All @@ -47,7 +47,7 @@ pub const Noop = struct {
pub fn adjust(_: Noop, timestamp: i64) AdjustedTime {
return .{
.designation = "noop",
.timestamp = timestamp,
.timestamp_s = timestamp,
.is_dst = false,
};
}
Expand All @@ -64,7 +64,7 @@ pub const Fixed = struct {
pub fn adjust(self: Fixed, timestamp: i64) AdjustedTime {
return .{
.designation = self.name,
.timestamp = timestamp + self.offset,
.timestamp_s = timestamp + self.offset,
.is_dst = self.is_dst,
};
}
Expand Down Expand Up @@ -455,13 +455,13 @@ pub const Posix = struct {
if (self.isDST(timestamp)) {
return .{
.designation = self.dst orelse self.std,
.timestamp = timestamp - (self.dst_offset orelse self.std_offset - s_per_hour),
.timestamp_s = timestamp - (self.dst_offset orelse self.std_offset - s_per_hour),
.is_dst = true,
};
}
return .{
.designation = self.std,
.timestamp = timestamp - self.std_offset,
.timestamp_s = timestamp - self.std_offset,
.is_dst = false,
};
}
Expand Down Expand Up @@ -704,7 +704,7 @@ pub const TZInfo = struct {
} else self.transitions[self.transitions.len - 1];
return .{
.designation = transition.timetype.name(),
.timestamp = timestamp + transition.timetype.offset,
.timestamp_s = timestamp + transition.timetype.offset,
.is_dst = transition.timetype.isDst(),
};
}
Expand Down Expand Up @@ -829,7 +829,7 @@ pub const Windows = struct {
const is_dst = isDST(timestamp, &tzi, &localtime);
return .{
.designation = if (is_dst) self.dst_name else self.standard_name,
.timestamp = systemtimeToUnixTimestamp(localtime),
.timestamp_s = systemtimeToUnixTimestamp(localtime),
.is_dst = is_dst,
};
}
Expand Down Expand Up @@ -943,7 +943,7 @@ test "timezone.zig: test Fixed" {
.is_dst = false,
};
const adjusted = fixed.adjust(0);
try std.testing.expectEqual(-600, adjusted.timestamp);
try std.testing.expectEqual(-600, adjusted.timestamp_s);
}

test "timezone.zig: Posix.isDST" {
Expand Down Expand Up @@ -1062,17 +1062,17 @@ test "timezone.zig: Posix.adjust" {
{
const t = try Posix.parse("UTC+1");
const adjusted = t.adjust(0);
try std.testing.expectEqual(-3600, adjusted.timestamp);
try std.testing.expectEqual(-3600, adjusted.timestamp_s);
}

{
const t = try Posix.parse("CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00");
const adjusted = t.adjust(1704088800);
try std.testing.expectEqual(1704067200, adjusted.timestamp);
try std.testing.expectEqual(1704067200, adjusted.timestamp_s);
try std.testing.expectEqualStrings("CST", adjusted.designation);

const adjusted_dst = t.adjust(1710057600);
try std.testing.expectEqual(1710039600, adjusted_dst.timestamp);
try std.testing.expectEqual(1710039600, adjusted_dst.timestamp_s);
try std.testing.expectEqualStrings("CDT", adjusted_dst.designation);
}
}
Expand All @@ -1099,3 +1099,7 @@ test "timezone.zig: Posix.DSTSpec.parse" {
try std.testing.expectEqual(.sun, spec.mwd.day);
}
}

test {
std.testing.refAllDecls(@This());
}
50 changes: 27 additions & 23 deletions src/zeit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn loadTimeZone(
/// always carry with them a timezone.
pub const Instant = struct {
/// the instant of time, in nanoseconds
timestamp: i128,
timestamp_ns: i128,
/// every instant occurs in a timezone. This is the timezone
timezone: *const TimeZone,

Expand Down Expand Up @@ -175,30 +175,30 @@ pub const Instant = struct {
/// convert this Instant to another timezone
pub fn in(self: Instant, zone: *const TimeZone) Instant {
return .{
.timestamp = self.timestamp,
.timestamp_ns = self.timestamp_ns,
.timezone = zone,
};
}

// convert the nanosecond timestamp into a unix timestamp (in seconds)
pub fn unixTimestamp(self: Instant) i64 {
return @intCast(@divFloor(self.timestamp, ns_per_s));
return @intCast(@divFloor(self.timestamp_ns, ns_per_s));
}

// generate a calendar date and time for this instant
pub fn time(self: Instant) Time {
const adjusted = self.timezone.adjust(self.unixTimestamp());
const days = daysSinceEpoch(adjusted.timestamp);
const days = daysSinceEpoch(adjusted.timestamp_s);
const date = civilFromDays(days);

var seconds = @mod(adjusted.timestamp, s_per_day);
var seconds = @mod(adjusted.timestamp_s, s_per_day);
const hours = @divFloor(seconds, s_per_hour);
seconds -= hours * s_per_hour;
const minutes = @divFloor(seconds, s_per_min);
seconds -= minutes * s_per_min;

// get the nanoseconds from the original timestamp
var nanos = @mod(self.timestamp, ns_per_s);
var nanos = @mod(self.timestamp_ns, ns_per_s);
const millis = @divFloor(nanos, ns_per_ms);
nanos -= millis * ns_per_ms;
const micros = @divFloor(nanos, ns_per_us);
Expand All @@ -214,7 +214,7 @@ pub const Instant = struct {
.millisecond = @intCast(millis),
.microsecond = @intCast(micros),
.nanosecond = @intCast(nanos),
.offset = @intCast(adjusted.timestamp - self.unixTimestamp()),
.offset = @intCast(adjusted.timestamp_s - self.unixTimestamp()),
.designation = adjusted.designation,
};
}
Expand All @@ -224,11 +224,11 @@ pub const Instant = struct {
const ns = try duration.inNanoseconds();

// check for addition with overflow
const timestamp = @addWithOverflow(self.timestamp, ns);
const timestamp = @addWithOverflow(self.timestamp_ns, ns);
if (timestamp[1] == 1) return error.Overflow;

return .{
.timestamp = timestamp[0],
.timestamp_ns = timestamp[0],
.timezone = self.timezone,
};
}
Expand All @@ -238,11 +238,11 @@ pub const Instant = struct {
const ns = try duration.inNanoseconds();

// check for subtraction with overflow
const timestamp = @subWithOverflow(self.timestamp, ns);
const timestamp = @subWithOverflow(self.timestamp_ns, ns);
if (timestamp[1] == 1) return error.Overflow;

return .{
.timestamp = timestamp[0],
.timestamp_ns = timestamp[0],
.timezone = self.timezone,
};
}
Expand All @@ -254,34 +254,34 @@ pub fn instant(cfg: Instant.Config) !Instant {
.now => std.time.nanoTimestamp(),
.unix_timestamp => |unix| unix * ns_per_s,
.unix_nano => |nano| nano,
.time => |time| time.instant().timestamp,
.time => |time| time.instant().timestamp_ns,
.iso8601,
.rfc3339,
=> |iso| blk: {
const t = try Time.fromISO8601(iso);
break :blk t.instant().timestamp;
break :blk t.instant().timestamp_ns;
},
.rfc2822,
.rfc5322,
=> |eml| blk: {
const t = try Time.fromRFC5322(eml);
break :blk t.instant().timestamp;
break :blk t.instant().timestamp_ns;
},
};
return .{
.timestamp = ts,
.timestamp_ns = ts,
.timezone = cfg.timezone,
};
}

test "instant" {
const original = Instant{
.timestamp = std.time.nanoTimestamp(),
.timestamp_ns = std.time.nanoTimestamp(),
.timezone = &utc,
};
const time = original.time();
const round_trip = time.instant();
try std.testing.expectEqual(original.timestamp, round_trip.timestamp);
try std.testing.expectEqual(original.timestamp_ns, round_trip.timestamp_ns);
}

pub const Month = enum(u4) {
Expand Down Expand Up @@ -485,7 +485,7 @@ pub const Time = struct {
.day = self.day,
});
return .{
.timestamp = @as(i128, days) * ns_per_day +
.timestamp_ns = @as(i128, days) * ns_per_day +
@as(i128, self.hour) * ns_per_hour +
@as(i128, self.minute) * ns_per_min +
@as(i128, self.second) * ns_per_s +
Expand Down Expand Up @@ -1281,9 +1281,9 @@ pub const Time = struct {
const self_instant = self.instant();
const time_instant = time.instant();

if (self_instant.timestamp > time_instant.timestamp) {
if (self_instant.timestamp_ns > time_instant.timestamp_ns) {
return .after;
} else if (self_instant.timestamp < time_instant.timestamp) {
} else if (self_instant.timestamp_ns < time_instant.timestamp_ns) {
return .before;
} else {
return .equal;
Expand All @@ -1293,19 +1293,19 @@ pub const Time = struct {
pub fn after(self: Time, time: Time) bool {
const self_instant = self.instant();
const time_instant = time.instant();
return self_instant.timestamp > time_instant.timestamp;
return self_instant.timestamp_ns > time_instant.timestamp_ns;
}

pub fn before(self: Time, time: Time) bool {
const self_instant = self.instant();
const time_instant = time.instant();
return self_instant.timestamp < time_instant.timestamp;
return self_instant.timestamp_ns < time_instant.timestamp_ns;
}

pub fn eql(self: Time, time: Time) bool {
const self_instant = self.instant();
const time_instant = time.instant();
return self_instant.timestamp == time_instant.timestamp;
return self_instant.timestamp_ns == time_instant.timestamp_ns;
}
};

Expand Down Expand Up @@ -1524,3 +1524,7 @@ test "gofmt" {
try time3.gofmt(writer, "frac .999999999");
try std.testing.expectEqualStrings("frac .123456", fbs.getWritten());
}

test {
std.testing.refAllDecls(@This());
}

0 comments on commit a5d5739

Please sign in to comment.