Skip to content

Commit a9ed950

Browse files
committed
fix: handle converting Datum::date to timestamp
This helps with predicate expressions such as `date > DATE_TRUNC('day', ts)`.
1 parent 9a68eea commit a9ed950

File tree

1 file changed

+102
-5
lines changed

1 file changed

+102
-5
lines changed

crates/iceberg/src/spec/values.rs

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ const INT_MIN: i32 = -2147483648;
6060
const LONG_MAX: i64 = 9223372036854775807;
6161
const LONG_MIN: i64 = -9223372036854775808;
6262

63+
const MICROS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000;
64+
const NANOS_PER_MICRO: i64 = 1000;
65+
6366
/// Values present in iceberg type
6467
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash, Eq)]
6568
pub enum PrimitiveLiteral {
@@ -1196,6 +1199,28 @@ impl Datum {
11961199
(PrimitiveLiteral::Int(val), _, PrimitiveType::Int) => Ok(Datum::int(*val)),
11971200
(PrimitiveLiteral::Int(val), _, PrimitiveType::Date) => Ok(Datum::date(*val)),
11981201
(PrimitiveLiteral::Int(val), _, PrimitiveType::Long) => Ok(Datum::long(*val)),
1202+
(PrimitiveLiteral::Int(val), PrimitiveType::Date, PrimitiveType::Timestamp) => {
1203+
Ok(Datum::timestamp_micros(*val as i64 * MICROS_PER_DAY))
1204+
}
1205+
(
1206+
PrimitiveLiteral::Int(val),
1207+
PrimitiveType::Date,
1208+
PrimitiveType::Timestamptz,
1209+
) => Ok(Datum::timestamptz_micros(*val as i64 * MICROS_PER_DAY)),
1210+
(
1211+
PrimitiveLiteral::Int(val),
1212+
PrimitiveType::Date,
1213+
PrimitiveType::TimestampNs,
1214+
) => Ok(Datum::timestamp_nanos(
1215+
*val as i64 * MICROS_PER_DAY * NANOS_PER_MICRO,
1216+
)),
1217+
(
1218+
PrimitiveLiteral::Int(val),
1219+
PrimitiveType::Date,
1220+
PrimitiveType::TimestamptzNs,
1221+
) => Ok(Datum::timestamptz_nanos(
1222+
*val as i64 * MICROS_PER_DAY * NANOS_PER_MICRO,
1223+
)),
11991224
(PrimitiveLiteral::Long(val), _, PrimitiveType::Int) => {
12001225
Ok(Datum::i64_to_i32(*val))
12011226
}
@@ -1229,19 +1254,19 @@ impl Datum {
12291254
(
12301255
PrimitiveType::TimestampNs | PrimitiveType::TimestamptzNs,
12311256
PrimitiveType::Timestamp,
1232-
) => Ok(Datum::timestamp_micros(val / 1000)),
1257+
) => Ok(Datum::timestamp_micros(val / NANOS_PER_MICRO)),
12331258
(
12341259
PrimitiveType::TimestampNs | PrimitiveType::TimestamptzNs,
12351260
PrimitiveType::Timestamptz,
1236-
) => Ok(Datum::timestamptz_micros(val / 1000)),
1261+
) => Ok(Datum::timestamptz_micros(val / NANOS_PER_MICRO)),
12371262
(
12381263
PrimitiveType::Timestamp | PrimitiveType::Timestamptz,
12391264
PrimitiveType::TimestampNs,
1240-
) => Ok(Datum::timestamp_nanos(val * 1000)),
1265+
) => Ok(Datum::timestamp_nanos(val * NANOS_PER_MICRO)),
12411266
(
12421267
PrimitiveType::Timestamp | PrimitiveType::Timestamptz,
12431268
PrimitiveType::TimestamptzNs,
1244-
) => Ok(Datum::timestamptz_nanos(val * 1000)),
1269+
) => Ok(Datum::timestamptz_nanos(val * NANOS_PER_MICRO)),
12451270
_ => Err(Error::new(
12461271
ErrorKind::DataInvalid,
12471272
format!(
@@ -3990,7 +4015,6 @@ mod tests {
39904015
assert_eq!(double_sorted, double_expected);
39914016
}
39924017

3993-
// Tests for timestamp nanosecond conversions
39944018
#[test]
39954019
fn test_datum_timestamp_nanos_convert_to_timestamp_micros() {
39964020
let datum = Datum::timestamp_nanos(12345000);
@@ -4166,4 +4190,77 @@ mod tests {
41664190

41674191
assert_eq!(result, expected);
41684192
}
4193+
4194+
#[test]
4195+
fn test_datum_date_convert_to_timestamp() {
4196+
let datum = Datum::date(1); // 1970-01-02
4197+
4198+
let result = datum.to(&Primitive(PrimitiveType::Timestamp)).unwrap();
4199+
4200+
let expected = Datum::timestamp_from_datetime(
4201+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z")
4202+
.unwrap()
4203+
.naive_utc(),
4204+
);
4205+
4206+
assert_eq!(result, expected);
4207+
}
4208+
4209+
#[test]
4210+
fn test_datum_date_convert_to_timestamptz() {
4211+
let datum = Datum::date(1);
4212+
4213+
let result = datum.to(&Primitive(PrimitiveType::Timestamptz)).unwrap();
4214+
4215+
let expected = Datum::timestamptz_from_str("1970-01-02T00:00:00Z").unwrap();
4216+
4217+
assert_eq!(result, expected);
4218+
}
4219+
4220+
#[test]
4221+
fn test_datum_date_convert_to_timestamp_nanos() {
4222+
let datum = Datum::date(1);
4223+
4224+
let result = datum.to(&Primitive(PrimitiveType::TimestampNs)).unwrap();
4225+
4226+
let expected = Datum::timestamp_from_datetime(
4227+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z")
4228+
.unwrap()
4229+
.naive_utc(),
4230+
)
4231+
.to(&Primitive(PrimitiveType::TimestampNs))
4232+
.unwrap();
4233+
4234+
assert_eq!(result, expected);
4235+
}
4236+
4237+
#[test]
4238+
fn test_datum_date_convert_to_timestamptz_nanos() {
4239+
let datum = Datum::date(1);
4240+
4241+
let result = datum.to(&Primitive(PrimitiveType::TimestamptzNs)).unwrap();
4242+
4243+
let expected = Datum::timestamptz_from_datetime(
4244+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z").unwrap(),
4245+
)
4246+
.to(&Primitive(PrimitiveType::TimestamptzNs))
4247+
.unwrap();
4248+
4249+
assert_eq!(result, expected);
4250+
}
4251+
4252+
#[test]
4253+
fn test_datum_date_negative_convert_to_timestamp() {
4254+
let datum = Datum::date(-1);
4255+
4256+
let result = datum.to(&Primitive(PrimitiveType::Timestamp)).unwrap();
4257+
4258+
let expected = Datum::timestamp_from_datetime(
4259+
DateTime::parse_from_rfc3339("1969-12-31T00:00:00Z")
4260+
.unwrap()
4261+
.naive_utc(),
4262+
);
4263+
4264+
assert_eq!(result, expected);
4265+
}
41694266
}

0 commit comments

Comments
 (0)