Conversation
Spark resolves spark.sql.session.timeZone with ZoneId.of(id, ZoneId.SHORT_IDS), which accepts IDs such as Z, +8, +08:00:00, GMT+8 and PST. Native code parses the timezone with arrow's Tz, which accepts only IANA names and +HH, +HHMM or +HH:MM offsets, so with those IDs casts, hour, year and df.show() failed at execution time. Add CometTimeZone, which normalizes the timezone every serde passes to native code: fixed offsets become +HH:MM, zero offsets become UTC, and short IDs become their region. An offset with seconds cannot be written for native code, so those expressions are reported as unsupported and go through the codegen dispatcher or fall back. Closes apache#6329.
sunchao
left a comment
There was a problem hiding this comment.
Summary
- Prior state and problem: Raw Spark session timezone IDs such as
GMT+8,Z, andPSTcould fail native parsing. - Design approach: Centralize normalization in
CometTimeZone, using Spark’sgetZoneIdand Java’snormalized(). - Correctness / compatibility analysis: Checked Spark sources across 3.4.3, 3.5.9, 4.0.4, 4.1.3, and 4.2.0. Normalization preserves timezone rules, including DST. Reviewed unsupported-offset guards and existing fallback paths.
- Key design decisions: Reuse Spark’s resolver, preserve region rules, and reject fixed offsets containing nonzero seconds. The helper adds planning-time work without adding per-row normalization or a new dependency.
- Implementation sketch: Update cast, temporal, JSON/CSV, pretty-string, and native-scan serialization. Add normalization and SQL regression tests.
- Behavioral changes worth calling out: Accepted aliases reach native code in parseable form. Fixed-zero aliases also qualify for existing UTC execution paths.
- Suggested improvements: None at P1/P2. No introduced P1/P2 issues found within this review.
Reviewed the full 11-file diff from e1d2c11729c2fc60a5def4e87bb17e5b28df2a29 to c2aa0b427c20bb6396da025e3396051c52fce050. The PR is not a draft. Routed skills: review-comet-pr and review-comet-expression-pr. Existing discussion collections were empty.
Exact-head CI: Linux builds, lint, Rust tests, scans, execution, shuffle, and TPC-H/TPC-DS passed. The expression job passed 1,554 tests, including both new SQL fixtures and the normalization test, with one canceled and 12 ignored tests.
Local validation: Compiled the production helper in an isolated Spark 4.1.3 probe with minimal support-level test types. Checked 788 timezone IDs and verified all 88 rewritten IDs against Arrow 59.3.0 at eight historical, DST-boundary, and future instants. Unsupported-offset checks passed.
Validation limits: No full local Comet build or end-to-end suite was run. Spark’s own SQL suites, additional runtime profiles, macOS, and Iceberg suites were skipped in exact-head CI. Project files remain unchanged.
Which issue does this PR close?
Closes #6329.
Rationale for this change
Comet passed the session timezone to native code as the raw string stamped on each expression. Native code parses that string with arrow's
Tz::from_str, which only accepts:+HH,+HHMMor+HH:MMSpark resolves the ID with
ZoneId.of(id, ZoneId.SHORT_IDS), which also accepts:Z+8and+08:00:00GMT+8andUTC+08:00PSTandISTThe
spark.sql.session.timeZonedocumentation listsZand(+|-)HH:mm:ssexplicitly. With any of these IDs, most timestamp expressions failed at execution time withParser error: Invalid timezone "GMT+8". That coveredCAST(ts AS STRING/DATE),hour,year, string- and date-to-timestamp casts,unix_timestampof a date, casts betweenTIMESTAMPandTIMESTAMP_NTZ, anddf.show().GMT+8in particular is a common production setting.What changes are included in this PR?
CometTimeZone.nativeIdnormalizes the stamped timezone into an ID native code parses. It uses Spark's ownDateTimeUtils.getZoneId, thenZoneId.normalized():+HH:MMUTCUTC, since Spark only leaves it unset on casts that don't use one (the measurement is in [EPIC] Timezone handling bugs #6335)+05:45:30, can't be written for native code.nativeIdreturnsNonefor it, and the serde reports the expression as unsupported. The cast then goes through the codegen dispatcher, and expressions without a dispatcher path fall back.Cast,hour,minute,second,unix_timestamp,date_trunc,from_unixtime,to_json,from_json,to_csv,ToPrettyString(both shims) and the native Parquet scan's session timezone. These were thegetOrElse("UTC")sites that Review use of hard-coded UTC assumptions #2730 asked about.date_truncanddate_formatdecide "is this session UTC" through the same helper.GMT,Zand+00:00sessions now take their native UTC paths as well.CometDaysis left alone, because fix: count the days partition transform in UTC #6348 moves it to UTC.How are these changes tested?
CometTemporalExpressionSuitefor the normalization.session_timezone_ids.sql, which runs casts, field extraction,unix_timestamp,TIMESTAMP_NTZcasts,date_truncanddate_formatunderGMT+8,UTC+08:00,+8,-08,+08:00:00,Z,PSTandIST.session_timezone_ids_unsupported.sql, which covers+05:45:30.main's serdes,session_timezone_ids.sqlfails for every timezone except-08, which arrow already parses. The unsupported file fails too.spotlessandscalastylechecks included:CometTemporalExpressionSuiteexpressions/datetime/SQL file testCometJsonExpressionSuiteCometCsvExpressionSuiteCometNativeCastSuitetests pass on Spark 4.1.ToPrettyStringshim.