Skip to content

Commit

Permalink
Fix day timestamp micro (apache#312)
Browse files Browse the repository at this point in the history
* basic fix

* change to Result<i32>

* use try_unary
  • Loading branch information
marvinlanhenke authored Apr 1, 2024
1 parent 6e5a871 commit d57d91b
Showing 1 changed file with 34 additions and 9 deletions.
43 changes: 34 additions & 9 deletions crates/iceberg/src/transform/temporal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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>()
Expand All @@ -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,
Expand Down Expand Up @@ -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));
}

Expand Down

0 comments on commit d57d91b

Please sign in to comment.