You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by gi...@apache.org on 2026/09/25 21:39:19 UTC
(iceberg-rust) branch main updated: fix(spec): format pre-epoch timestamptz values (#2892)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new 9a2111a91 fix(spec): format pre-epoch timestamptz values (#2892)
9a2111a91 is described below
commit 9a2111a914e9b17fdc039700186056ec14fae606
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Fri Sep 25 21:32:09 2026 +0000
fix(spec): format pre-epoch timestamptz values (#2892)
* fix(spec): format pre-epoch timestamptz values
* fix(spec): strengthen pre-epoch timestamptz regression
* style: format temporal conversion
* test(spec): match chrono fractional timestamp display
* test(spec): match trimmed timestamp fraction
---
crates/iceberg/src/spec/values/temporal.rs | 12 +++++------
crates/iceberg/src/spec/values/tests.rs | 32 ++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/crates/iceberg/src/spec/values/temporal.rs b/crates/iceberg/src/spec/values/temporal.rs
index 80aadeb65..76b2c897e 100644
--- a/crates/iceberg/src/spec/values/temporal.rs
+++ b/crates/iceberg/src/spec/values/temporal.rs
@@ -98,15 +98,15 @@ pub(crate) mod timestamptz {
}
pub(crate) fn microseconds_to_datetimetz(micros: i64) -> DateTime<Utc> {
- let (secs, rem) = (micros / 1_000_000, micros % 1_000_000);
-
- DateTime::from_timestamp(secs, rem as u32 * 1_000).unwrap()
+ // Returns None only outside chrono's range (~year -262144 to +262142);
+ // unreachable for any realistic Iceberg timestamp.
+ DateTime::from_timestamp_micros(micros).expect(
+ "timestamp micros outside chrono's representable range (~year -262144..+262142)",
+ )
}
pub(crate) fn nanoseconds_to_datetimetz(nanos: i64) -> DateTime<Utc> {
- let (secs, rem) = (nanos / 1_000_000_000, nanos % 1_000_000_000);
-
- DateTime::from_timestamp(secs, rem as u32).unwrap()
+ DateTime::from_timestamp_nanos(nanos)
}
/// Nanoseconds since the Unix epoch, or `None` if outside the representable `i64` range
diff --git a/crates/iceberg/src/spec/values/tests.rs b/crates/iceberg/src/spec/values/tests.rs
index 97b2f6b32..ff51f318e 100644
--- a/crates/iceberg/src/spec/values/tests.rs
+++ b/crates/iceberg/src/spec/values/tests.rs
@@ -1124,6 +1124,38 @@ fn test_parse_timestamptz() {
);
}
+#[test]
+fn test_pre_epoch_timestamptz_formatting() {
+ assert_eq!(
+ Datum::timestamptz_micros(-1).to_string(),
+ "1969-12-31 23:59:59.999999 UTC"
+ );
+ assert_eq!(
+ Datum::timestamptz_nanos(-1).to_string(),
+ "1969-12-31 23:59:59.999999999 UTC"
+ );
+ assert_eq!(
+ Datum::timestamptz_micros(-1_500_000).to_string(),
+ "1969-12-31 23:59:58.500 UTC"
+ );
+
+ check_json_serde(
+ r#""1969-12-31T23:59:59.999999+00:00""#,
+ Literal::long(-1),
+ &Primitive(PrimitiveType::Timestamptz),
+ );
+ check_json_serde(
+ r#""1969-12-31T23:59:58.500+00:00""#,
+ Literal::long(-1_500_000),
+ &Primitive(PrimitiveType::Timestamptz),
+ );
+ check_json_serde(
+ r#""1969-12-31T23:59:59.999999999+00:00""#,
+ Literal::long(-1),
+ &Primitive(PrimitiveType::TimestamptzNs),
+ );
+}
+
#[test]
fn test_datum_ser_deser() {
let test_fn = |datum: Datum| {