Skip to content

Commit 4c291c7

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

File tree

1 file changed

+102
-4
lines changed

1 file changed

+102
-4
lines changed

crates/iceberg/src/spec/values.rs

Lines changed: 102 additions & 4 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 MICROS_PER_NANO: 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 * MICROS_PER_NANO,
1216+
)),
1217+
(
1218+
PrimitiveLiteral::Int(val),
1219+
PrimitiveType::Date,
1220+
PrimitiveType::TimestamptzNs,
1221+
) => Ok(Datum::timestamptz_nanos(
1222+
*val as i64 * MICROS_PER_DAY * MICROS_PER_NANO,
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 / MICROS_PER_NANO)),
12331258
(
12341259
PrimitiveType::TimestampNs | PrimitiveType::TimestamptzNs,
12351260
PrimitiveType::Timestamptz,
1236-
) => Ok(Datum::timestamptz_micros(val / 1000)),
1261+
) => Ok(Datum::timestamptz_micros(val / MICROS_PER_NANO)),
12371262
(
12381263
PrimitiveType::Timestamp | PrimitiveType::Timestamptz,
12391264
PrimitiveType::TimestampNs,
1240-
) => Ok(Datum::timestamp_nanos(val * 1000)),
1265+
) => Ok(Datum::timestamp_nanos(val * MICROS_PER_NANO)),
12411266
(
12421267
PrimitiveType::Timestamp | PrimitiveType::Timestamptz,
12431268
PrimitiveType::TimestamptzNs,
1244-
) => Ok(Datum::timestamptz_nanos(val * 1000)),
1269+
) => Ok(Datum::timestamptz_nanos(val * MICROS_PER_NANO)),
12451270
_ => Err(Error::new(
12461271
ErrorKind::DataInvalid,
12471272
format!(
@@ -4166,4 +4191,77 @@ mod tests {
41664191

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

0 commit comments

Comments
 (0)