Skip to content

feat: Move to jiff datetime library. - #744

Merged
jetuk merged 2 commits into
mainfrom
switch-chrono-jiff
Aug 14, 2026
Merged

feat: Move to jiff datetime library.#744
jetuk merged 2 commits into
mainfrom
switch-chrono-jiff

Conversation

@jetuk

@jetuk jetuk commented Aug 5, 2026

Copy link
Copy Markdown
Member

Chrono is now soft-deprecated. Much of the Rust ecosystem is switching to jiff. These changes move to Jiff for all but the interface with polars. Polars is still using chrono, and we cannot drop the chrono dependency until polars does.

There are some minor integer type changes. The biggest change is the removal of the Date enum in favour of just using jiff::civil::DateTime directly. However, this changes the roundtrip deserlisation/serialisation behaviour losing the information on whether the user provided a simple date or a date-time.

The conversion tests currently fail because they are not updated to reflect this change.

@jetuk
jetuk requested a review from Batch21 August 5, 2026 15:49
@jetuk
jetuk force-pushed the switch-chrono-jiff branch from e8d5263 to 30853b9 Compare August 10, 2026 10:35
@jetuk
jetuk marked this pull request as ready for review August 10, 2026 10:35
@jetuk

jetuk commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

I have put a separate commit on here to bump pywr-v1-schema to the latest version which also does the jiff migration.

@Batch21 Batch21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few queries but runs ok for me.

Comment thread pywr-core/src/timestep.rs Outdated
let mut timesteps: Vec<Timestep> = Vec::new();
let mut current = self.start;
while current <= self.end {
let next = current + span;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think next needs to be

let next = self.start + span * (timesteps.len() as i64 + 1);

If the timestep is a monthly it will currently go 02-28 -> 03-28 instead of 02-28 -> 03-31

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this any different to polars::time::date_range?

>>> pl.date_range(date(2000, 2, 28), date(2000, 8, 31), '1mo', eager=True).alias("date")
shape: (7,)
Series: 'date' [date]
[
        2000-02-28
        2000-03-28
        2000-04-28
        2000-05-28
        2000-06-28
        2000-07-28
        2000-08-28
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue is with 29th–31st start days, where the date will get clamped to the 28th for all months after the first Feb.

Comment thread pywr-core/src/timestep.rs
@@ -330,44 +338,28 @@ impl TimeDomainBuilder {
let duration = polars::time::Duration::parse(frequency);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check here that the duration is not zero or negative? Previously this would have be done by the polars range function.

}
VirtualStorageReset::Annual(annual) => pywr_core::virtual_storage::VirtualStorageReset::DayOfYear {
day: annual.day,
month: annual.month,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the core month values are i8 not chrono Months should we have some validation here that the values are in the correct range? Same for the other month fields below

Comment thread pywr-core/src/timestep.rs Outdated
fn eq(&self, other: &Span) -> bool {
self.0
.compare(SpanCompare::from(other).days_are_24_hours())
.expect("Span comparison failed!")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are the expects ok in these methods? one way to avoid them would be to switch to using SignedDuration instead of Span as the innner type but I don't know if that removes some required functinality?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wrap SignedDuration in PywrDuration then we lose the knowledge it was "1 month" or "2 days". It just becomes a (possibly) different duration for each timestep. This is possibly OK as mainly just need to know the length of each one anyway. However, it means that each timestep is no longer equal. Currently we have a PartialEq implementation that is used to validate each timestep is equal in the series. I am not sure that is really required though.

@jetuk
jetuk force-pushed the switch-chrono-jiff branch from 9ff62c6 to f80642b Compare August 12, 2026 11:22
jetuk added 2 commits August 12, 2026 12:22
Chrono is now soft-deprecated. Much of the Rust ecosystem is
switching to jiff. These changes move to Jiff for all but the
interface with polars. Polars is still using chrono, and we
cannot drop the chrono dependency until polars does.

There are some minor integer type changes. The biggest change
is the removal of the `Date` enum in favour of just using
`jiff::civil::DateTime` directly. However, this changes the
roundtrip deserlisation/serialisation behaviour losing the
information on whether the user provided a simple date or a
date-time.

We now Use jiff::SignedDuration internally for the
`PywrDuration` this is consistent with the use of 24-hour
days. The `TimeDomain` is updated to remove the assumption
that it has a fixed duration. Instead a helper method
can return the fixed duration *if* there is one.

The conversion tests have been updated to reflect this change.
@jetuk
jetuk force-pushed the switch-chrono-jiff branch from f80642b to 8d4af80 Compare August 12, 2026 11:24

@Batch21 Batch21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes look good

@jetuk
jetuk merged commit 184f3a4 into main Aug 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants