-
Notifications
You must be signed in to change notification settings - Fork 766
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
How to implement PyDateTime From<chrono::DateTime> #884
Comments
Hi @milesgranger, good question! This would absolutely be welcome as a PR - you would have to implement There's a few recent PRs which added similar conversions for Any questions just pop them on here or open a WIP review and I can comment directly on the code. |
@milesgranger |
I guess I can begrudgingly accept why you'd want to do this, but is there any way this can be a third-party library providing this trait instead, or does it have to be provided in one of Perhaps irrationally, I oppose this because in my experience I'm also not sure how well it would naturally translate to |
@pganssle : it can be provided in |
I'm not concerned about taking on a dependency on |
Interesting to see the pitfalls of doing this. Should we rather focus on making the API of things like |
Interesting. Sorry for my ignorance about that.
#[pyclass(extends=PyTzInfo)]
pub struct TzClass {}
#[pymethods]
impl TzClass {
#[new]
fn new() -> Self {
TzClass {}
}
fn utcoffset<'p>(&self, py: Python<'p>, _dt: &PyDateTime) -> PyResult<&'p PyDelta> {
PyDelta::new(py, 0, 3600, 0, true)
}
fn tzname(&self, _py: Python<'_>, _dt: &PyDateTime) -> PyResult<String> {
Ok(String::from("+01:00"))
}
fn dst(&self, _py: Python<'_>, _dt: &PyDateTime) -> PyResult<Option<&PyDelta>> {
Ok(None)
}
} So it is not actually straightforward to cooperate with chrono, in which |
I kinda agree with this comment, but I don't think we have a solution for that right now. In the future we might be able to do something like serde remote derive to make it possible for separate compatibility crates to exist. |
@davidhewitt how would you see this working under the "remote derive" design, such that we get a real We can write the wrapper newtype without assistance from PyO3, but it's painful to manually unwrap and wrap across the Python boundary. |
To be honest, I haven't put time into thinking about it at all. If anyone wants to take ownership of researching how serde does it and how we might be able to introduce similar to pyO3, I'd be keen to be a sounding board to bounce ideas off. |
There seems to be two key points to the remote derive design:
serde is able to achieve the above (and more) because it allows you to attach attributes to fields within your type: specifically, PyO3 relies fully on trait impls for any customisation of method arguments and return values. I'm not aware of any hook mechanism similar to serde's field attributes. I could imagine an equivalent mechanism where I put a |
Hmm interesting idea. I think I'd prefer the attribute to be
... but other than that it's a cool suggestion and would love to see it! |
For future visitors: an implementation of a newtype around NaiveDateTime that can be passed to Python EDIT: Use the pyo3-chrono crate instead, which includes conversions for other data types, Python->Rust conversions, and is leap-second aware. use chrono::{Datelike as _, Timelike as _};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct DateTime(pub chrono::NaiveDateTime);
impl pyo3::ToPyObject for DateTime {
fn to_object(&self, py: pyo3::Python) -> pyo3::PyObject {
pyo3::types::PyDateTime::new(
py,
self.0.year(),
self.0.month() as u8,
self.0.day() as u8,
self.0.hour() as u8,
self.0.minute() as u8,
self.0.second() as u8,
self.0.timestamp_subsec_micros(),
None,
)
.unwrap()
.to_object(py)
}
}
impl pyo3::IntoPy<pyo3::PyObject> for DateTime {
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
pyo3::ToPyObject::to_object(&self, py)
}
} |
@kangalioo, could you please add the Here is mine: impl<'source> FromPyObject<'source> for DateTime {
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let d = PyDateTime::try_from(ob)?;
Ok(Self(NaiveDate::from_ymd(
d.get_year() as i32, d.get_month() as u32, d.get_day() as u32,
).and_hms_micro(
d.get_hour() as u32, d.get_minute() as u32, d.get_second() as u32,
d.get_microsecond(),
)))
}
} But I am new to Rust, so the above code could probably be made simpler. |
Okay, I made a crate that includes all conversions to and from NaiveDateTime, NaiveDate, NaiveTime, and Duration. Everything is rigorously tested and documented ^.^ https://crates.io/crates/pyo3-chrono It doesn't handle timezones because, as far as I can tell, Python has no real understanding of timezones, you can merely "tack on" a timezone to an otherwise naive datetime. This makes for ambiguity when converting from a timezone-aware If I'm wrong on this, I'd gladly learn how to unambiguously convert a |
Thank you @kangalioo, it works. |
I am trying to add integration into chrono directly, we can use the internals without having a separate newtype. chronotope/chrono#542 |
Now trying to add timezone but realized that timezone bindings are not available yet. #207 Maybe I will also try to contribute to that as well. |
We added |
This means |
Yes, I believe we now cover all the functionality in |
In the implementation of When I started using that, I noticed I can't really reconcile results. For example, if I pass Hopefully I didn't miss anything important that would explain those odd results. When I looked at tests for that, they follow the same logic, so they test if extract() worked, but not really if the logic is correct. |
I would glad if you could open a new issue or better yet a pull request detailing the changes to the current implementing and its tests. Additional comments on old issues make it significantly harder for us to keep things organized. And a concrete proposal for code changes is also often a good starting for further discussion. |
Sure, happy to |
❓ Question
What is the best way to do the following?
This obviously doesn't work now, and currently doing silly things to get the same effect. Most the the problem is getting
chrono::offset::Utc
into aPyTzInfo
forPyDateTime::from_timestamp
There must be a better way. If not, I'd be happy to provide a PR (with a
chrono
feature flag(?)) with some pointers on where to start.The text was updated successfully, but these errors were encountered: