Skip to content

Commit 2a1f575

Browse files
committed
fix: handle converting Datum::date to timestamp and vice versa
This helps with predicate expressions such as `date > DATE_TRUNC('day', ts)`.
1 parent 5ce0ba3 commit 2a1f575

File tree

1 file changed

+160
-4
lines changed

1 file changed

+160
-4
lines changed

crates/iceberg/src/spec/values.rs

Lines changed: 160 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 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,29 @@ 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)),
1270+
(
1271+
PrimitiveType::Timestamp | PrimitiveType::Timestamptz,
1272+
PrimitiveType::Date,
1273+
) => Ok(Datum::date((val / MICROS_PER_DAY) as i32)),
1274+
(
1275+
PrimitiveType::TimestampNs | PrimitiveType::TimestamptzNs,
1276+
PrimitiveType::Date,
1277+
) => Ok(Datum::date(
1278+
(val / (MICROS_PER_DAY * NANOS_PER_MICRO)) as i32,
1279+
)),
12451280
_ => Err(Error::new(
12461281
ErrorKind::DataInvalid,
12471282
format!(
@@ -4176,4 +4211,125 @@ mod tests {
41764211

41774212
assert_eq!(result, expected);
41784213
}
4214+
4215+
#[test]
4216+
fn test_datum_date_convert_to_timestamp() {
4217+
let datum = Datum::date(1); // 1970-01-02
4218+
4219+
let result = datum.to(&Primitive(PrimitiveType::Timestamp)).unwrap();
4220+
4221+
let expected = Datum::timestamp_from_datetime(
4222+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z")
4223+
.unwrap()
4224+
.naive_utc(),
4225+
);
4226+
4227+
assert_eq!(result, expected);
4228+
}
4229+
4230+
#[test]
4231+
fn test_datum_date_convert_to_timestamptz() {
4232+
let datum = Datum::date(1);
4233+
4234+
let result = datum.to(&Primitive(PrimitiveType::Timestamptz)).unwrap();
4235+
4236+
let expected = Datum::timestamptz_from_str("1970-01-02T00:00:00Z").unwrap();
4237+
4238+
assert_eq!(result, expected);
4239+
}
4240+
4241+
#[test]
4242+
fn test_datum_date_convert_to_timestamp_nanos() {
4243+
let datum = Datum::date(1);
4244+
4245+
let result = datum.to(&Primitive(PrimitiveType::TimestampNs)).unwrap();
4246+
4247+
let expected = Datum::timestamp_from_datetime(
4248+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z")
4249+
.unwrap()
4250+
.naive_utc(),
4251+
)
4252+
.to(&Primitive(PrimitiveType::TimestampNs))
4253+
.unwrap();
4254+
4255+
assert_eq!(result, expected);
4256+
}
4257+
4258+
#[test]
4259+
fn test_datum_date_convert_to_timestamptz_nanos() {
4260+
let datum = Datum::date(1);
4261+
4262+
let result = datum.to(&Primitive(PrimitiveType::TimestamptzNs)).unwrap();
4263+
4264+
let expected = Datum::timestamptz_from_datetime(
4265+
DateTime::parse_from_rfc3339("1970-01-02T00:00:00Z").unwrap(),
4266+
)
4267+
.to(&Primitive(PrimitiveType::TimestamptzNs))
4268+
.unwrap();
4269+
4270+
assert_eq!(result, expected);
4271+
}
4272+
4273+
#[test]
4274+
fn test_datum_date_negative_convert_to_timestamp() {
4275+
let datum = Datum::date(-1);
4276+
4277+
let result = datum.to(&Primitive(PrimitiveType::Timestamp)).unwrap();
4278+
4279+
let expected = Datum::timestamp_from_datetime(
4280+
DateTime::parse_from_rfc3339("1969-12-31T00:00:00Z")
4281+
.unwrap()
4282+
.naive_utc(),
4283+
);
4284+
4285+
assert_eq!(result, expected);
4286+
}
4287+
4288+
#[test]
4289+
fn test_datum_timestamp_convert_to_date() {
4290+
let micros = 24 * 60 * 60 * 1_000_000; // 1 day in microseconds
4291+
let datum = Datum::timestamp_micros(micros);
4292+
4293+
let result = datum.to(&Primitive(PrimitiveType::Date)).unwrap();
4294+
4295+
let expected = Datum::date(1);
4296+
4297+
assert_eq!(result, expected);
4298+
}
4299+
4300+
#[test]
4301+
fn test_datum_timestamptz_convert_to_date() {
4302+
let micros = 24 * 60 * 60 * 1_000_000; // 1 day in microseconds
4303+
let datum = Datum::timestamptz_micros(micros);
4304+
4305+
let result = datum.to(&Primitive(PrimitiveType::Date)).unwrap();
4306+
4307+
let expected = Datum::date(1);
4308+
4309+
assert_eq!(result, expected);
4310+
}
4311+
4312+
#[test]
4313+
fn test_datum_timestamp_nanos_convert_to_date() {
4314+
let nanos = 24 * 60 * 60 * 1_000_000_000; // 1 day in nanoseconds
4315+
let datum = Datum::timestamp_nanos(nanos);
4316+
4317+
let result = datum.to(&Primitive(PrimitiveType::Date)).unwrap();
4318+
4319+
let expected = Datum::date(1);
4320+
4321+
assert_eq!(result, expected);
4322+
}
4323+
4324+
#[test]
4325+
fn test_datum_timestamptz_nanos_convert_to_date() {
4326+
let nanos = 24 * 60 * 60 * 1_000_000_000; // 1 day in nanoseconds
4327+
let datum = Datum::timestamptz_nanos(nanos);
4328+
4329+
let result = datum.to(&Primitive(PrimitiveType::Date)).unwrap();
4330+
4331+
let expected = Datum::date(1);
4332+
4333+
assert_eq!(result, expected);
4334+
}
41794335
}

0 commit comments

Comments
 (0)