Merge pull request #3730 from Tpt/chrono-tz
Conversion between chrono_tz::Tz and zoneinfo.ZoneInfo
This commit is contained in:
commit
7366b1a386
|
@ -35,6 +35,7 @@ inventory = { version = "0.3.0", optional = true }
|
|||
# crate integrations that can be added using the eponymous features
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
chrono = { version = "0.4.25", default-features = false, optional = true }
|
||||
chrono-tz = { version = ">= 0.6, < 0.9", default-features = false, optional = true }
|
||||
either = { version = "1.9", optional = true }
|
||||
eyre = { version = ">= 0.4, < 0.7", optional = true }
|
||||
hashbrown = { version = ">= 0.9, < 0.15", optional = true }
|
||||
|
@ -47,7 +48,8 @@ smallvec = { version = "1.0", optional = true }
|
|||
|
||||
[dev-dependencies]
|
||||
assert_approx_eq = "1.1.0"
|
||||
chrono = { version = "0.4.25" }
|
||||
chrono = "0.4.25"
|
||||
chrono-tz = ">= 0.6, < 0.9"
|
||||
# Required for "and $N others" normalization
|
||||
trybuild = ">=1.0.70"
|
||||
proptest = { version = "1.0", default-features = false, features = ["std"] }
|
||||
|
@ -108,6 +110,7 @@ full = [
|
|||
"macros",
|
||||
# "multiple-pymethods", # TODO re-add this when MSRV is greater than 1.62
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"hashbrown",
|
||||
|
|
|
@ -115,6 +115,12 @@ Adds a dependency on [chrono](https://docs.rs/chrono). Enables a conversion from
|
|||
- [NaiveTime](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html) -> [`PyTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyTime.html)
|
||||
- [DateTime](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) -> [`PyDateTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDateTime.html)
|
||||
|
||||
### `chrono-tz`
|
||||
|
||||
Adds a dependency on [chrono-tz](https://docs.rs/chrono-tz).
|
||||
Enables conversion from and to [`Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html).
|
||||
It requires at least Python 3.9.
|
||||
|
||||
### `either`
|
||||
|
||||
Adds a dependency on [either](https://docs.rs/either). Enables a conversions into [either](https://docs.rs/either)’s [`Either`](https://docs.rs/either/latest/either/struct.Report.html) type.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
`chrono-tz` feature allowing conversion between `chrono_tz::Tz` and `zoneinfo.ZoneInfo`
|
|
@ -9,8 +9,6 @@
|
|||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # change * to the latest versions
|
||||
//! pyo3 = { version = "*", features = ["chrono"] }
|
||||
//! chrono = "0.4"
|
||||
#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"chrono\"] }")]
|
||||
//! ```
|
||||
|
@ -18,7 +16,7 @@
|
|||
//! Note that you must use compatible versions of chrono and PyO3.
|
||||
//! The required chrono version may vary based on the version of PyO3.
|
||||
//!
|
||||
//! # Example: Convert a `PyDateTime` to chrono's `DateTime<Utc>`
|
||||
//! # Example: Convert a `datetime.datetime` to chrono's `DateTime<Utc>`
|
||||
//!
|
||||
//! ```rust
|
||||
//! use chrono::{DateTime, Duration, TimeZone, Utc};
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
#![cfg(all(Py_3_9, feature = "chrono-tz"))]
|
||||
|
||||
//! Conversions to and from [chrono-tz](https://docs.rs/chrono-tz/)’s `Tz`.
|
||||
//!
|
||||
//! This feature requires at least Python 3.9.
|
||||
//!
|
||||
//! # Setup
|
||||
//!
|
||||
//! To use this feature, add this to your **`Cargo.toml`**:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! chrono-tz = "0.8"
|
||||
#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"chrono-tz\"] }")]
|
||||
//! ```
|
||||
//!
|
||||
//! Note that you must use compatible versions of chrono, chrono-tz and PyO3.
|
||||
//! The required chrono version may vary based on the version of PyO3.
|
||||
//!
|
||||
//! # Example: Convert a `zoneinfo.ZoneInfo` to chrono-tz's `Tz`
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use chrono_tz::Tz;
|
||||
//! use pyo3::{Python, ToPyObject};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! pyo3::prepare_freethreaded_python();
|
||||
//! Python::with_gil(|py| {
|
||||
//! // Convert to Python
|
||||
//! let py_tzinfo = Tz::Europe__Paris.to_object(py);
|
||||
//! // Convert back to Rust
|
||||
//! assert_eq!(py_tzinfo.extract::<Tz>(py).unwrap(), Tz::Europe__Paris);
|
||||
//! });
|
||||
//! }
|
||||
//! ```
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::sync::GILOnceCell;
|
||||
use crate::types::PyType;
|
||||
use crate::{intern, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use chrono_tz::Tz;
|
||||
use std::str::FromStr;
|
||||
|
||||
impl ToPyObject for Tz {
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
static ZONE_INFO: GILOnceCell<Py<PyType>> = GILOnceCell::new();
|
||||
ZONE_INFO
|
||||
.get_or_try_init_type_ref(py, "zoneinfo", "ZoneInfo")
|
||||
.unwrap()
|
||||
.call1((self.name(),))
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPy<PyObject> for Tz {
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromPyObject<'_> for Tz {
|
||||
fn extract(ob: &PyAny) -> PyResult<Tz> {
|
||||
Tz::from_str(ob.getattr(intern!(ob.py(), "key"))?.extract()?)
|
||||
.map_err(|e| PyValueError::new_err(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(windows)))] // Troubles loading timezones on Windows
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_frompyobject() {
|
||||
Python::with_gil(|py| {
|
||||
assert_eq!(
|
||||
new_zoneinfo(py, "Europe/Paris").extract::<Tz>().unwrap(),
|
||||
Tz::Europe__Paris
|
||||
);
|
||||
assert_eq!(new_zoneinfo(py, "UTC").extract::<Tz>().unwrap(), Tz::UTC);
|
||||
assert_eq!(
|
||||
new_zoneinfo(py, "Etc/GMT-5").extract::<Tz>().unwrap(),
|
||||
Tz::Etc__GMTMinus5
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_topyobject() {
|
||||
Python::with_gil(|py| {
|
||||
let assert_eq = |l: PyObject, r: &PyAny| {
|
||||
assert!(l.as_ref(py).eq(r).unwrap());
|
||||
};
|
||||
|
||||
assert_eq(
|
||||
Tz::Europe__Paris.to_object(py),
|
||||
new_zoneinfo(py, "Europe/Paris"),
|
||||
);
|
||||
assert_eq(Tz::UTC.to_object(py), new_zoneinfo(py, "UTC"));
|
||||
assert_eq(
|
||||
Tz::Etc__GMTMinus5.to_object(py),
|
||||
new_zoneinfo(py, "Etc/GMT-5"),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_zoneinfo<'a>(py: Python<'a>, name: &str) -> &'a PyAny {
|
||||
zoneinfo_class(py).call1((name,)).unwrap()
|
||||
}
|
||||
|
||||
fn zoneinfo_class(py: Python<'_>) -> &PyAny {
|
||||
py.import("zoneinfo").unwrap().getattr("ZoneInfo").unwrap()
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub mod anyhow;
|
||||
pub mod chrono;
|
||||
pub mod chrono_tz;
|
||||
pub mod either;
|
||||
pub mod eyre;
|
||||
pub mod hashbrown;
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
//! The following features enable interactions with other crates in the Rust ecosystem:
|
||||
//! - [`anyhow`]: Enables a conversion from [anyhow]’s [`Error`][anyhow_error] type to [`PyErr`].
|
||||
//! - [`chrono`]: Enables a conversion from [chrono]'s structures to the equivalent Python ones.
|
||||
//! - [`chrono-tz`]: Enables a conversion from [chrono-tz]'s `Tz` enum. Requires Python 3.9+.
|
||||
//! - [`either`]: Enables conversions between Python objects and [either]'s [`Either`] type.
|
||||
//! - [`eyre`]: Enables a conversion from [eyre]’s [`Report`] type to [`PyErr`].
|
||||
//! - [`hashbrown`]: Enables conversions between Python objects and [hashbrown]'s [`HashMap`] and
|
||||
|
@ -257,7 +258,9 @@
|
|||
//! [`Deserialize`]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
|
||||
//! [`Serialize`]: https://docs.rs/serde/latest/serde/trait.Serialize.html
|
||||
//! [chrono]: https://docs.rs/chrono/ "Date and Time for Rust."
|
||||
//! [chrono-tz]: https://docs.rs/chrono-tz/ "TimeZone implementations for chrono from the IANA database."
|
||||
//! [`chrono`]: ./chrono/index.html "Documentation about the `chrono` feature."
|
||||
//! [`chrono-tz`]: ./chrono-tz/index.html "Documentation about the `chrono-tz` feature."
|
||||
//! [either]: https://docs.rs/either/ "A type that represents one of two alternatives."
|
||||
//! [`either`]: ./either/index.html "Documentation about the `either` feature."
|
||||
//! [`Either`]: https://docs.rs/either/latest/either/enum.Either.html
|
||||
|
|
Loading…
Reference in New Issue