From 61353d2cc593f40734ed53f6d409bf64a07b9f03 Mon Sep 17 00:00:00 2001 From: Jay Zhan Date: Thu, 21 Nov 2024 10:37:01 +0800 Subject: [PATCH] Split `timestamp_s_to_datetime` to `date` and `time` to avoid unnecessary computation (#6755) * split timestamp func Signed-off-by: jayzhan211 * timestamp func Signed-off-by: jayzhan211 * rm unused constant Signed-off-by: Jay Zhan --------- Signed-off-by: jayzhan211 Signed-off-by: Jay Zhan --- arrow-array/src/temporal_conversions.rs | 57 ++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/arrow-array/src/temporal_conversions.rs b/arrow-array/src/temporal_conversions.rs index 8d238b3a196c..23f950d55048 100644 --- a/arrow-array/src/temporal_conversions.rs +++ b/arrow-array/src/temporal_conversions.rs @@ -37,8 +37,18 @@ pub const MILLISECONDS_IN_DAY: i64 = SECONDS_IN_DAY * MILLISECONDS; pub const MICROSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * MICROSECONDS; /// Number of nanoseconds in a day pub const NANOSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * NANOSECONDS; -/// Number of days between 0001-01-01 and 1970-01-01 -pub const EPOCH_DAYS_FROM_CE: i32 = 719_163; + +/// Constant from chrono crate +/// +/// Number of days between Januari 1, 1970 and December 31, 1 BCE which we define to be day 0. +/// 4 full leap year cycles until December 31, 1600 4 * 146097 = 584388 +/// 1 day until January 1, 1601 1 +/// 369 years until Januari 1, 1970 369 * 365 = 134685 +/// of which floor(369 / 4) are leap years floor(369 / 4) = 92 +/// except for 1700, 1800 and 1900 -3 + +/// -------- +/// 719163 +pub const UNIX_EPOCH_DAY: i64 = 719_163; /// converts a `i32` representing a `date32` to [`NaiveDateTime`] #[inline] @@ -134,6 +144,31 @@ pub fn timestamp_s_to_datetime(v: i64) -> Option { Some(DateTime::from_timestamp(v, 0)?.naive_utc()) } +/// Similar to timestamp_s_to_datetime but only compute `date` +#[inline] +pub fn timestamp_s_to_date(secs: i64) -> Option { + let days = secs.div_euclid(86_400) + UNIX_EPOCH_DAY; + if days < i32::MIN as i64 || days > i32::MAX as i64 { + return None; + } + let date = NaiveDate::from_num_days_from_ce_opt(days as i32)?; + Some(date.and_time(NaiveTime::default()).and_utc().naive_utc()) +} + +/// Similar to timestamp_s_to_datetime but only compute `time` +#[inline] +pub fn timestamp_s_to_time(secs: i64) -> Option { + let secs = secs.rem_euclid(86_400); + let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, 0)?; + Some( + DateTime::::from_naive_utc_and_offset( + NaiveDateTime::new(NaiveDate::default(), time), + Utc, + ) + .naive_utc(), + ) +} + /// converts a `i64` representing a `timestamp(ms)` to [`NaiveDateTime`] #[inline] pub fn timestamp_ms_to_datetime(v: i64) -> Option { @@ -274,10 +309,28 @@ pub fn as_duration(v: i64) -> Option { mod tests { use crate::temporal_conversions::{ date64_to_datetime, split_second, timestamp_ms_to_datetime, timestamp_ns_to_datetime, + timestamp_s_to_date, timestamp_s_to_datetime, timestamp_s_to_time, timestamp_us_to_datetime, NANOSECONDS, }; use chrono::DateTime; + #[test] + fn test_timestamp_func() { + let timestamp = 1234; + let datetime = timestamp_s_to_datetime(timestamp).unwrap(); + let expected_date = datetime.date(); + let expected_time = datetime.time(); + + assert_eq!( + timestamp_s_to_date(timestamp).unwrap().date(), + expected_date + ); + assert_eq!( + timestamp_s_to_time(timestamp).unwrap().time(), + expected_time + ); + } + #[test] fn negative_input_timestamp_ns_to_datetime() { assert_eq!(