Open grantslatton opened 1 year ago
@Psykopear @pickfire Do you have the bandwidth to look into this?
FWIW I wound up implementing this in my own code like:
pub fn get_canonical_tz_py_obj_kwargs(tz: Tz) -> Py<PyDict> {
lazy_static! {
static ref TIMEZONES: HashMap<Tz, Py<PyDict>> = {
let code = r#"
import zoneinfo
all_zones = zoneinfo.available_timezones()
zone_dict = {}
for zone_name in all_zones:
zone = zoneinfo.ZoneInfo(zone_name)
zone_dict[zone_name] = zone
"#.trim();
Python::with_gil(|py| {
let globals = PyDict::new(py);
py.run(code, Some(globals), None).unwrap();
let zone_dict = globals.get_item("zone_dict").unwrap().extract::<HashMap<String, Py<PyAny>>>().unwrap();
zone_dict
.into_iter()
.map(|(mut name, zone)| {
// Weird idiosyncratic "factory reset" type zone that python has but rust does not
if name == "Factory" {
name = "UTC".to_string();
}
let kwargs = [("tzinfo", zone)].into_py_dict(py);
(Tz::from_str(&name).unwrap(), kwargs.into_py(py))
})
.collect()
})
};
}
TIMEZONES.get(&tz).unwrap().clone()
}
and then to convert the DateTime<Tz>
to PyAny
:
let kwargs = get_canonical_tz_py_obj_kwargs(dt.timezone());
let datetime = dt.naive_local();
let datetime = datetime.into_py(py);
let result = datetime.call_method(py, "replace", (), Some(kwargs.as_ref(py))).unwrap();
You can probably think of something more pythonic or efficient here, but if not, feel free to steal from this
The
IntoPy
implementation forchrono::DateTime
is implemented generically for anyT: TimeZone
by converting everything to fixed offsets. This means it loses theTz
information in a timezone likeAmerica/Los Angeles
and just converts it toUTC-8
which is a lossy conversion (due to daylight savings, etc).PyO3 should probably provide two different implements, one for
DateTime<FixedOffset>
and another forDateTime<chrono_tz::Tz>
that creates a pythonZoneInfo
object.This is technically a bug since this should be a lossless conversion but I have filed it under feature request since it doesn't match the bug template very well.