viirya commented on code in PR #11482:
URL: https://github.com/apache/arrow-rs/pull/11482#discussion_r4333359800
##########
arrow-array/Cargo.toml:
##########
@@ -55,6 +55,14 @@ arrow-schema = { workspace = true }
arrow-data = { workspace = true }
chrono = { workspace = true }
chrono-tz = { version = "0.10", optional = true }
+# Names are only looked up in the IANA database that `tzdb-bundle-always`
compiles in, much as
+# chrono-tz compiles in its own, and never in the host's zoneinfo files, even
if another crate
+# enables jiff's `tzdb-zoneinfo`. `tz-fat` and `perf-inline` are jiff defaults
that speed up
+# offset lookups.
+jiff = { version = "0.2", default-features = false, features = ["std",
"tzdb-bundle-always", "tz-fat", "perf-inline"], optional = true }
+# jiff does not report which release of the database it bundles, so
`timezone::tzdb_version`
+# reads it from jiff-tzdb, which Cargo resolves to the same copy that jiff uses
+jiff-tzdb = { version = "0.1.8", optional = true }
Review Comment:
This holds today. Both this and jiff 0.2.35 ask for `^0.1.8`, and a fresh
resolve picks a single jiff-tzdb 0.1.9, with `tzdb_version()` reporting 2026e
to match the data. It would only break if jiff moves to a semver-incompatible
jiff-tzdb, as @comphead noted. Building the table from `jiff_tzdb` directly
(see the comment on `named_tz`) would remove that dependency on Cargo's
resolution.
##########
arrow-array/src/timezone.rs:
##########
@@ -48,7 +90,414 @@ fn parse_fixed_offset(tz: &str) -> Option<FixedOffset> {
}
}
-#[cfg(feature = "chrono-tz")]
+#[cfg(feature = "jiff")]
+mod private {
+ use super::*;
+ use chrono::offset::TimeZone;
+ use chrono::{Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime,
Offset, Timelike};
+ use jiff::Timestamp;
+ use jiff::civil::DateTime as JiffDateTime;
+ use jiff::tz::{AmbiguousOffset, TimeZone as JiffTimeZone,
TimeZoneDatabase};
+ use std::collections::HashMap;
+ use std::fmt::Display;
+ use std::str::FromStr;
+ use std::sync::OnceLock;
+
+ /// An [`Offset`] for [`Tz`]
+ #[derive(Debug, Copy, Clone)]
+ pub struct TzOffset {
+ tz: Tz,
+ offset: FixedOffset,
+ }
+
+ impl std::fmt::Display for TzOffset {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.offset.fmt(f)
+ }
+ }
+
+ impl Offset for TzOffset {
+ fn fix(&self) -> FixedOffset {
+ self.offset
+ }
+ }
+
+ /// The IANA database compiled into jiff.
+ ///
+ /// Names are not resolved through `jiff::tz::db()`: if any crate in the
build enables jiff's
+ /// `tzdb-zoneinfo` feature, that reads the host's zoneinfo files, or
`$TZDIR`, instead.
+ fn bundled_db() -> &'static TimeZoneDatabase {
+ static DB: OnceLock<TimeZoneDatabase> = OnceLock::new();
+ DB.get_or_init(TimeZoneDatabase::bundled)
+ }
+
+ /// A named timezone resolved by jiff, together with its name.
+ #[derive(Debug)]
+ struct NamedTz {
+ name: &'static str,
+ tz: JiffTimeZone,
+ }
+
+ /// Resolves a name in the bundled database. Unlike jiff, and like
chrono-tz, the name must
+ /// match exactly, including its case.
+ ///
+ /// [`Tz`] is `Copy` whereas [`JiffTimeZone`] is reference counted, so
`Tz` holds a `'static`
+ /// reference into a table with one slot for each name in the database,
each resolved on first
+ /// use. The table's keys are fixed when it is built, so its memory is
bounded by the database
+ /// and lookups take no lock.
+ fn named_tz(name: &str) -> Result<&'static NamedTz, ArrowError> {
+ type Slots = HashMap<Box<str>, OnceLock<Option<NamedTz>>,
ahash::RandomState>;
+ static SLOTS: OnceLock<Slots> = OnceLock::new();
+ let slots = SLOTS.get_or_init(|| {
+ bundled_db()
+ .available()
+ // A placeholder for an unknown local timezone, which
chrono-tz does not accept
+ .filter(|name| name.as_str() != "Factory")
+ .map(|name| (name.as_str().into(), OnceLock::new()))
+ .collect()
+ });
+ slots
+ .get_key_value(name)
+ .and_then(|(name, slot)| {
+ slot.get_or_init(|| {
+ let tz = bundled_db().get(name).ok()?;
Review Comment:
Re @comphead's point 6: the bundled database's `get` is exactly
`TimeZone::tzif(canonical_name, <jiff_tzdb bytes>)` (jiff
`src/tz/db/bundled/enabled.rs`). So `jiff_tzdb::get(name)` + `TimeZone::tzif`
would produce the same `TimeZone`, fat table included. One difference to keep
if this switches over: before touching TZif data, the bundled `get` maps `UTC`
to `TimeZone::UTC` via `special_time_zone`. A direct construction would turn
`UTC` into a TZif-backed zone, so it should special-case `UTC` the same way.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]