Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jiff into/from python convertions #4823

Merged
merged 4 commits into from
Jan 20, 2025
Merged

Conversation

bschoenmaeckers
Copy link
Contributor

@bschoenmaeckers bschoenmaeckers commented Dec 24, 2024

closes #4510

@bschoenmaeckers
Copy link
Contributor Author

Jiff requires rust 1.70 or newer, but pyo3's minimum version is 1.63.0. Is it acceptable to increase our msrv or can we make this conditional somehow?

@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 2 times, most recently from 5b523f0 to c82a970 Compare December 24, 2024 15:00
@Icxolu
Copy link
Contributor

Icxolu commented Dec 25, 2024

I don't think we can bump MSRV, but having an off-by-default feature, which requires a newer version is probably ok (if we document that). I think this therefore requires changes in the testing workflows, because it can't be part of full but still should run on everything except the MSRV workflow.

@bschoenmaeckers
Copy link
Contributor Author

Fixed the CI and all other TODO's so it is ready for review

@bschoenmaeckers bschoenmaeckers marked this pull request as ready for review January 3, 2025 00:04
@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 2 times, most recently from c0e5ddb to d2e6ac6 Compare January 3, 2025 11:17
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

Thank you! This looks good to me, just some minor comments.

src/conversions/jiff.rs Outdated Show resolved Hide resolved
src/conversions/jiff.rs Outdated Show resolved Hide resolved
let fold = match ambiguous_offset {
AmbiguousOffset::Unambiguous { .. } => false,
AmbiguousOffset::Fold { after, .. } => after == self.offset(),
AmbiguousOffset::Gap { .. } => unreachable!(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a comment here on why this is unreachable.

Choose a reason for hiding this comment

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

Here's my attempt:

// This case isn't reachable because if it were, it
// would imply that the `Zoned` value has a civil
// time that never appears on a clock in this time
// zone. Jiff's API prevents this from happening,
// so this case is unreachable.

An alternative for discovering whether a datetime is in a fold or not might be to use the new TimeZone::preceding and TimeZone::following APIs. It's likely faster but more difficult to get right. The way it's done here is probably the clearest way to do it given Jiff's current API.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me 👍, thank you for the suggestion!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

An alternative for discovering whether a datetime is in a fold or not might be to use the new TimeZone::preceding and TimeZone::following APIs.

Switched to the TimeZone iter API & added a proptest to verify. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be a nice api addition to jiff if one could get the start timestamp of the current offset transition.

I currently do it using the following snippet but it could probably be done cleaner with some internal api?

            let prev = zoned.time_zone().preceding(zoned.timestamp()).next()?;
            let start_of_current_offset = zoned
                .time_zone()
                .following(prev.timestamp())
                .next()?
                .timestamp();

Choose a reason for hiding this comment

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

I think that's actually not quite right. Try this:

use jiff::tz::{TimeZone, TimeZoneTransition};

fn main() -> anyhow::Result<()> {
    let tz = TimeZone::system();
    let zdt = jiff::Zoned::now();
    let cur = current_transition(&tz, zdt.timestamp()).unwrap();
    dbg!(cur.timestamp().to_zoned(tz));
    Ok(())
}

fn current_transition(
    tz: &TimeZone,
    ts: jiff::Timestamp,
) -> Option<TimeZoneTransition> {
    let prev = tz.preceding(ts).next()?;
    let next = tz.following(ts).next()?;
    if next.timestamp() == ts {
        Some(next)
    } else {
        Some(prev)
    }
}

Jiff actually doesn't have any internal APIs that would make this easier. It might be worth adding a new routine for this, but I'd like to see how common the need is for it first.

Copy link
Contributor Author

@bschoenmaeckers bschoenmaeckers Jan 7, 2025

Choose a reason for hiding this comment

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

When ts is exactly on a timezone transition both preceding and following will not contain the current transition. preceding will contain all transitions strictly earlier and following will contain all transitions strictly after the current one. So it is impossible for the case 2020-10-25T01:00:00+00:00[Europe/London] to get the current transition without going back one transition and going forward again.

zoned: 2020-10-25T01:00:00+00:00[Europe/London]
zoned.timestamp(): 2020-10-25T01:00:00Z
next.timestamp(): 2021-03-28T01:00:00Z
prev.timestamp(): 2020-03-29T01:00:00Z

Choose a reason for hiding this comment

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

Yeah my code does do that! But I did indeed have a bug in it. Try this instead:

fn current_transition(
    tz: &TimeZone,
    ts: Timestamp,
) -> Option<TimeZoneTransition> {
    let prev = tz.preceding(ts).next()?;
    let next = tz.following(prev.timestamp()).next()?;
    if next.timestamp() == ts {
        Some(next)
    } else {
        Some(prev)
    }
}

That should work. In your original code, you go back and forward unconditionally, which only works in the case where the original timestamp is precisely at the boundary. But the above snippet only uses next (after going back) if the given timestamp is at the boundary, otherwise, the immediately previous transition is the correct one.

Your code is correct if you know the timestamp given is always at a boundary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks you are right and thanks for pointing this out. Code looks way cleaner this way.

src/conversions/mod.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated
@@ -39,6 +39,7 @@ either = { version = "1.9", optional = true }
eyre = { version = ">= 0.6.8, < 0.7", optional = true }
hashbrown = { version = ">= 0.14.5, < 0.16", optional = true }
indexmap = { version = ">= 2.5.0, < 3", optional = true }
jiff = { version = "0.1.17", optional = true }

Choose a reason for hiding this comment

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

I would suggest ensuring that the feature is named jiff-01, either by introducing a new feature or renaming the package. For example, this is what rust-postgres does.

This ensures there is a paved path to supporting multiple versions of Jiff when a semver incompatible release comes out without making a semver incompatible release of pyo3.

Arguably, this ought to be done for other dependencies, but maybe they don't do incompatible releases often. For Jiff, I'd like get 1.0 out this summer, and then it should sit there for a long while.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good idea (especially if we know there will be a major version bump soon-ish), thanks for bringing that up. Renaming the package looks like a good option to me.

@Icxolu
Copy link
Contributor

Icxolu commented Jan 5, 2025

We should also add this to the feature docs in the guide, and mention the different MSRV for this feature there.

@BurntSushi
Copy link

It might be worth waiting on this a little bit. I'm planning to get a jiff 0.2 release out in the next month or so. So if this gets merged for jiff 0.1, there will likely be some upcoming churn pretty soon.

My plan is to get jiff 0.2 out with some breaking changes we've discovered. And if that works out okay for the next six months or so, then I want to get Jiff 1.0 out in the summer and I plan to commit to that longer term (~years, or ideally, indefinitely).

@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 3 times, most recently from 0883ab8 to aa2a4ba Compare January 7, 2025 14:02
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

It might be worth waiting on this a little bit. I'm planning to get a jiff 0.2 release out in the next month or so.

I'd be fine with that. (Something like ">= 0.1, < 0.3" could also be an option, but that depends on the changes)

Cargo.toml Outdated Show resolved Hide resolved
@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 4 times, most recently from 4f71daa to 0dba0f5 Compare January 13, 2025 16:06
Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks, this is looking good to me (I didn't manage to read the meat of the conversions in much depth, it looks like they have already been reviewed, thanks @Icxolu).

I'm ok with either merging this for jiff 0.1 or waiting until jiff 0.2 as you prefer @bschoenmaeckers.

Just a few small thoughts.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe this is better named as datetime_abi3? It's not really an ABI here but it is code specific to how we deal with abi3.

noxfile.py Outdated
Comment on lines 134 to 135
jiff_features_set = (("--features=jiff-01",), ("--features=jiff-01 abi3",))
for feature_set in _get_feature_sets() + jiff_features_set:
Copy link
Member

Choose a reason for hiding this comment

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

The clippy jobs are already quite slow. Rather than adding additional jobs here, please can you modify _get_feature_sets() to be rust version aware and only set the jiff feature if the compiler is new enough?

//! # Example: Convert a `datetime.datetime` to jiff `Zoned`
//!
//! ```rust
//! # use jiff_01 as jiff;
Copy link
Member

Choose a reason for hiding this comment

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

You might need to cfg this doctest as not windows, IIRC windows needs a tzdata package but it's quite a faff to bother requiring it.

@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 2 times, most recently from 2d1427d to d3e4fb4 Compare January 17, 2025 09:09
@bschoenmaeckers
Copy link
Contributor Author

I'm ok with either merging this for jiff 0.1 or waiting until jiff 0.2 as you prefer @bschoenmaeckers.

I'm fine by merging it for 0.1, adding support for 0.2 should be very easy when the time comes.

@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 3 times, most recently from 6ac96ca to a7b26a3 Compare January 17, 2025 09:51
@bschoenmaeckers bschoenmaeckers force-pushed the jiff branch 6 times, most recently from a53a115 to c2dcb3a Compare January 17, 2025 11:16
src/conversions/jiff.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

Thanks again for implementing! Let's get this merged now 🚀

guide/src/features.md Outdated Show resolved Hide resolved
@Icxolu Icxolu enabled auto-merge January 19, 2025 16:37
@Icxolu Icxolu added this pull request to the merge queue Jan 19, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 19, 2025
@Icxolu
Copy link
Contributor

Icxolu commented Jan 19, 2025

Looks like the feature combinations for CI are not quite right yet.

@bschoenmaeckers
Copy link
Contributor Author

Looks like the feature combinations for CI are not quite right yet.

Please try again

@Icxolu Icxolu added this pull request to the merge queue Jan 20, 2025
Merged via the queue into PyO3:main with commit e7041ba Jan 20, 2025
45 of 46 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.

Add conversion support for the jiff datetime library
4 participants