This is an automated email from the ASF dual-hosted git repository.

liurenjie1024 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 d57d91b  Fix day timestamp micro (#312)
d57d91b is described below

commit d57d91b9a72c516c6665d5faef349f52ebe59585
Author: Marvin Lanhenke <[email protected]>
AuthorDate: Mon Apr 1 09:26:37 2024 +0200

    Fix day timestamp micro (#312)
    
    * basic fix
    
    * change to Result<i32>
    
    * use try_unary
---
 crates/iceberg/src/transform/temporal.rs | 43 +++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/crates/iceberg/src/transform/temporal.rs 
b/crates/iceberg/src/transform/temporal.rs
index 2a79db3..9c6489e 100644
--- a/crates/iceberg/src/transform/temporal.rs
+++ b/crates/iceberg/src/transform/temporal.rs
@@ -24,15 +24,15 @@ use arrow_array::{
     types::Date32Type, Array, ArrayRef, Date32Array, Int32Array, 
TimestampMicrosecondArray,
 };
 use arrow_schema::{DataType, TimeUnit};
-use chrono::{DateTime, Datelike};
+use chrono::{DateTime, Datelike, Duration};
 use std::sync::Arc;
 
 /// Hour in one second.
 const HOUR_PER_SECOND: f64 = 1.0_f64 / 3600.0_f64;
-/// Day in one second.
-const DAY_PER_SECOND: f64 = 1.0_f64 / 24.0_f64 / 3600.0_f64;
 /// Year of unix epoch.
 const UNIX_EPOCH_YEAR: i32 = 1970;
+/// One second in micros.
+const MICROS_PER_SECOND: i64 = 1_000_000;
 
 /// Extract a date or timestamp year, as years from 1970
 #[derive(Debug)]
@@ -163,8 +163,33 @@ pub struct Day;
 
 impl Day {
     #[inline]
-    fn day_timestamp_micro(v: i64) -> i32 {
-        (v as f64 / 1000.0 / 1000.0 * DAY_PER_SECOND) as i32
+    fn day_timestamp_micro(v: i64) -> Result<i32> {
+        let secs = v / MICROS_PER_SECOND;
+
+        let (nanos, offset) = if v >= 0 {
+            let nanos = (v.rem_euclid(MICROS_PER_SECOND) * 1_000) as u32;
+            let offset = 0i64;
+            (nanos, offset)
+        } else {
+            let v = v + 1;
+            let nanos = (v.rem_euclid(MICROS_PER_SECOND) * 1_000) as u32;
+            let offset = 1i64;
+            (nanos, offset)
+        };
+
+        let delta = Duration::new(secs, nanos).ok_or_else(|| {
+            Error::new(
+                ErrorKind::DataInvalid,
+                format!(
+                    "Failed to create 'TimeDelta' from seconds {} and nanos 
{}",
+                    secs, nanos
+                ),
+            )
+        })?;
+
+        let days = (delta.num_days() - offset) as i32;
+
+        Ok(days)
     }
 }
 
@@ -175,7 +200,7 @@ impl TransformFunction for Day {
                 .as_any()
                 .downcast_ref::<TimestampMicrosecondArray>()
                 .unwrap()
-                .unary(|v| -> i32 { Self::day_timestamp_micro(v) }),
+                .try_unary(|v| -> Result<i32> { Self::day_timestamp_micro(v) 
})?,
             DataType::Date32 => input
                 .as_any()
                 .downcast_ref::<Date32Array>()
@@ -197,8 +222,8 @@ impl TransformFunction for Day {
     fn transform_literal(&self, input: &crate::spec::Datum) -> 
Result<Option<crate::spec::Datum>> {
         let val = match input.literal() {
             PrimitiveLiteral::Date(v) => *v,
-            PrimitiveLiteral::Timestamp(v) => Self::day_timestamp_micro(*v),
-            PrimitiveLiteral::TimestampTZ(v) => Self::day_timestamp_micro(*v),
+            PrimitiveLiteral::Timestamp(v) => Self::day_timestamp_micro(*v)?,
+            PrimitiveLiteral::TimestampTZ(v) => Self::day_timestamp_micro(*v)?,
             _ => {
                 return Err(crate::Error::new(
                     crate::ErrorKind::FeatureUnsupported,
@@ -584,7 +609,7 @@ mod test {
 
         // Test TimestampMicrosecond
         test_timestamp_and_tz_transform_using_i64(1512151975038194, &day, 
Datum::int(17501));
-        test_timestamp_and_tz_transform_using_i64(-115200000000, &day, 
Datum::int(-1));
+        test_timestamp_and_tz_transform_using_i64(-115200000000, &day, 
Datum::int(-2));
         test_timestamp_and_tz_transform("2017-12-01 10:30:42.123", &day, 
Datum::int(17501));
     }
 

Reply via email to