Skip to content

Commit

Permalink
fix round() behaving incorrectly when increment is not passed (#209)
Browse files Browse the repository at this point in the history
  • Loading branch information
ariebovenberg committed Feb 25, 2025
1 parent 7ca14ed commit b14b7b9
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
🚀 Changelog
============

0.7.2 (2025-02-25)
------------------

- Fixed ``round()`` method behaving incorrectly when ``increment`` argument
is not passed explicitly (#209)

0.7.1 (2025-02-24)
------------------

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ maintainers = [
{name = "Arie Bovenberg", email = "[email protected]"},
]
readme = "README.md"
version = "0.7.1"
version = "0.7.2"
license = {text = "MIT"}
description = "Modern datetime library for Python"
requires-python = ">=3.9"
Expand Down
2 changes: 1 addition & 1 deletion pysrc/whenever/_pywhenever.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
# - It saves some overhead
from __future__ import annotations

__version__ = "0.7.1"
__version__ = "0.7.2"

import enum
import re
Expand Down
10 changes: 7 additions & 3 deletions src/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ impl Instant {
nanos,
}: Time,
) -> Self {
let secs =
date.ord() as i64 * 86400 + hour as i64 * 3600 + minute as i64 * 60 + second as i64;
Instant { secs, nanos }
Instant {
secs: date.ord() as i64 * 86400
+ hour as i64 * 3600
+ minute as i64 * 60
+ second as i64,
nanos,
}
}

pub(crate) fn diff(self, other: Self) -> TimeDelta {
Expand Down
14 changes: 13 additions & 1 deletion src/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ impl Unit {
.ok_or_value_err("Increment must be 1 for 'day' unit"),
}
}

unsafe fn default_increment(self) -> i64 {
match self {
Unit::Nanosecond => 1,
Unit::Microsecond => 1_000,
Unit::Millisecond => 1_000_000,
Unit::Second => 1_000_000_000,
Unit::Minute => 60 * 1_000_000_000,
Unit::Hour => 3_600 * 1_000_000_000,
Unit::Day => 86_400 * 1_000_000_000,
}
}
}

// NOTE: the caller still needs to check whenever 'day' is valid for them
Expand Down Expand Up @@ -216,7 +228,7 @@ pub(crate) unsafe fn parse_args(
let increment = arg_obj[1]
.map(|v| unit.increment_from_py(v.as_ptr(), hours_largest_unit))
.transpose()?
.unwrap_or(1_000_000_000);
.unwrap_or_else(|| unit.default_increment());
let mode = arg_obj[2]
.map(|v| {
Mode::from_py(
Expand Down
4 changes: 4 additions & 0 deletions tests/test_time_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,10 @@ def test_default_half_even_seconds(self):
seconds=4
)

def test_default_increment(self):
d = TimeDelta(seconds=2, nanoseconds=800)
assert d.round("microsecond") == TimeDelta(seconds=2, microseconds=1)

def test_invalid_unit(self):
t = TimeDelta.ZERO
with pytest.raises(ValueError, match="Invalid.*unit.*foo"):
Expand Down
9 changes: 9 additions & 0 deletions tests/test_zoned_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,15 @@ def test_default(self):
2023, 7, 14, 1, 2, 8, tz="Europe/Paris"
)

# TODO: test this robustly on all classes
def test_default_increment(self):
d = ZonedDateTime(
2023, 7, 14, 1, 2, 3, nanosecond=800_000, tz="Europe/Paris"
)
assert d.round("millisecond") == ZonedDateTime(
2023, 7, 14, 1, 2, 3, nanosecond=1_000_000, tz="Europe/Paris"
)

def test_invalid_mode(self):
d = ZonedDateTime(
2023, 7, 14, 1, 2, 3, nanosecond=4_000, tz="Europe/Paris"
Expand Down

0 comments on commit b14b7b9

Please sign in to comment.