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]

Reply via email to