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));
}