Skip to content

Commit

Permalink
fix(daysUntil): use larger integer type for proper underflow
Browse files Browse the repository at this point in the history
Use a larger integer type for proper underflow in Weekday.daysUntil.

    This is because the algorithm assumes a larger integer than a u3.
    It's relying on underflow to work:

    https://howardhinnant.github.io/date_algorithms.html#weekday_difference

    For Wednesday -> Monday, the math is:

    Monday (1) - Wednesday (3) = -2

    -2 as a u3 = 6, but we are checking if the difference is <= 6 so we
    return 6 instead of adding 7.

    If we do this as a u8, -2 is 254. Add 7 with overflow, and we get 5.

Note that this exposed a bug in the DST calculation for fixed posix
timezones as well. The `days` var suffered from an off-by-one bug. It is
supposed to be the day of the month (1-31), NOT a zero-indexed day.

Fixes: #8
  • Loading branch information
rockorager committed Sep 10, 2024
1 parent 07d180b commit 4a41cf3
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/timezone.zig
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ pub const Posix = struct {
const first_of_month = zeit.weekdayFromDays(days_from_epoch - civil.day + 1);
// days is the first "rule day" of the month (ie the first
// Sunday of the month)
var days: u9 = first_of_month.daysUntil(rule.day);
var days: u9 = first_of_month.daysUntil(rule.day) + 1;
var i: usize = 1;
while (i < rule.week) : (i += 1) {
if (days + 7 >= rule.month.lastDay(civil.year)) break;
Expand Down Expand Up @@ -434,7 +434,7 @@ pub const Posix = struct {
const first_of_month = zeit.weekdayFromDays(days_from_epoch - civil.day + 1);
// days is the first "rule day" of the month (ie the first
// Sunday of the month)
var days: u9 = first_of_month.daysUntil(rule.day);
var days: u9 = first_of_month.daysUntil(rule.day) + 1;
var i: usize = 1;
while (i < rule.week) : (i += 1) {
if (days + 7 >= rule.month.lastDay(civil.year)) break;
Expand Down
4 changes: 3 additions & 1 deletion src/zeit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ pub const Weekday = enum(u3) {

/// number of days from self until other. Returns 0 when self == other
pub fn daysUntil(self: Weekday, other: Weekday) u3 {
const d = @intFromEnum(other) -% @intFromEnum(self);
const d: u8 = @as(u8, @intFromEnum(other)) -% @as(u8, @intFromEnum(self));
return if (d <= 6) @intCast(d) else @intCast(d +% 7);
}

Expand All @@ -446,6 +446,8 @@ pub const Weekday = enum(u3) {
const wed: Weekday = .wed;
try std.testing.expectEqual(0, wed.daysUntil(.wed));
try std.testing.expectEqual(6, wed.daysUntil(.tue));
try std.testing.expectEqual(5, wed.daysUntil(.mon));
try std.testing.expectEqual(4, wed.daysUntil(.sun));
}
};

Expand Down

0 comments on commit 4a41cf3

Please sign in to comment.