1use crate::ArrowPrimitiveType;
8use crate::timezone::Tz;
9use arrow_schema::{DataType, TimeUnit};
10use chrono::{DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
11use std::mem::ManuallyDrop;
12
13pub const SECONDS_IN_DAY: i64 = 86_400;
15pub const MILLISECONDS: i64 = 1_000;
17pub const MICROSECONDS: i64 = 1_000_000;
19pub const NANOSECONDS: i64 = 1_000_000_000;
21
22pub const MILLISECONDS_IN_DAY: i64 = SECONDS_IN_DAY * MILLISECONDS;
24pub const MICROSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * MICROSECONDS;
26pub const NANOSECONDS_IN_DAY: i64 = SECONDS_IN_DAY * NANOSECONDS;
28
29pub const UNIX_EPOCH_DAY: i64 = 719_163;
40
41#[inline]
43pub fn date32_to_datetime(v: i32) -> Option<NaiveDateTime> {
44 Some(DateTime::from_timestamp(v as i64 * SECONDS_IN_DAY, 0)?.naive_utc())
45}
46
47#[inline]
49pub fn date64_to_datetime(v: i64) -> Option<NaiveDateTime> {
50 let (sec, milli_sec) = split_second(v, MILLISECONDS);
51
52 let datetime = DateTime::from_timestamp(
53 sec,
55 milli_sec * MICROSECONDS as u32,
57 )?;
58 Some(datetime.naive_utc())
59}
60
61#[inline]
63pub fn time32s_to_time(v: i32) -> Option<NaiveTime> {
64 NaiveTime::from_num_seconds_from_midnight_opt(u32::try_from(v).ok()?, 0)
65}
66
67#[inline]
69pub fn time32ms_to_time(v: i32) -> Option<NaiveTime> {
70 let v = v as i64;
71 NaiveTime::from_num_seconds_from_midnight_opt(
72 u32::try_from(v / MILLISECONDS).ok()?,
74 u32::try_from(v % MILLISECONDS * MICROSECONDS).ok()?,
77 )
78}
79
80#[inline]
82pub fn time64us_to_time(v: i64) -> Option<NaiveTime> {
83 NaiveTime::from_num_seconds_from_midnight_opt(
84 u32::try_from(v / MICROSECONDS).ok()?,
86 u32::try_from(v % MICROSECONDS * MILLISECONDS).ok()?,
89 )
90}
91
92#[inline]
94pub fn time64ns_to_time(v: i64) -> Option<NaiveTime> {
95 NaiveTime::from_num_seconds_from_midnight_opt(
96 u32::try_from(v / NANOSECONDS).ok()?,
98 u32::try_from(v % NANOSECONDS).ok()?,
100 )
101}
102
103#[inline]
105pub fn time_to_time32s(v: NaiveTime) -> i32 {
106 v.num_seconds_from_midnight() as i32
107}
108
109#[inline]
111pub fn time_to_time32ms(v: NaiveTime) -> i32 {
112 (v.num_seconds_from_midnight() as i64 * MILLISECONDS
113 + v.nanosecond() as i64 * MILLISECONDS / NANOSECONDS) as i32
114}
115
116#[inline]
118pub fn time_to_time64us(v: NaiveTime) -> i64 {
119 v.num_seconds_from_midnight() as i64 * MICROSECONDS
120 + v.nanosecond() as i64 * MICROSECONDS / NANOSECONDS
121}
122
123#[inline]
125pub fn time_to_time64ns(v: NaiveTime) -> i64 {
126 v.num_seconds_from_midnight() as i64 * NANOSECONDS + v.nanosecond() as i64
127}
128
129#[inline]
131pub fn timestamp_s_to_datetime(v: i64) -> Option<NaiveDateTime> {
132 Some(DateTime::from_timestamp(v, 0)?.naive_utc())
133}
134
135#[inline]
137pub fn timestamp_s_to_date(secs: i64) -> Option<NaiveDateTime> {
138 let days = secs.div_euclid(86_400) + UNIX_EPOCH_DAY;
139 if days < i32::MIN as i64 || days > i32::MAX as i64 {
140 return None;
141 }
142 let date = NaiveDate::from_num_days_from_ce_opt(days as i32)?;
143 Some(date.and_time(NaiveTime::default()).and_utc().naive_utc())
144}
145
146#[inline]
148pub fn timestamp_s_to_time(secs: i64) -> Option<NaiveDateTime> {
149 let secs = secs.rem_euclid(86_400);
150 let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, 0)?;
151 Some(
152 DateTime::<Utc>::from_naive_utc_and_offset(
153 NaiveDateTime::new(NaiveDate::default(), time),
154 Utc,
155 )
156 .naive_utc(),
157 )
158}
159
160#[inline]
162pub fn timestamp_ms_to_datetime(v: i64) -> Option<NaiveDateTime> {
163 let (sec, milli_sec) = split_second(v, MILLISECONDS);
164
165 let datetime = DateTime::from_timestamp(
166 sec,
168 milli_sec * MICROSECONDS as u32,
170 )?;
171 Some(datetime.naive_utc())
172}
173
174#[inline]
176pub fn timestamp_us_to_datetime(v: i64) -> Option<NaiveDateTime> {
177 let (sec, micro_sec) = split_second(v, MICROSECONDS);
178
179 let datetime = DateTime::from_timestamp(
180 sec,
182 micro_sec * MILLISECONDS as u32,
184 )?;
185 Some(datetime.naive_utc())
186}
187
188#[inline]
190pub fn timestamp_ns_to_datetime(v: i64) -> Option<NaiveDateTime> {
191 let (sec, nano_sec) = split_second(v, NANOSECONDS);
192
193 let datetime = DateTime::from_timestamp(
194 sec, nano_sec,
197 )?;
198 Some(datetime.naive_utc())
199}
200
201#[inline]
202pub(crate) fn split_second(v: i64, base: i64) -> (i64, u32) {
203 (v.div_euclid(base), v.rem_euclid(base) as u32)
204}
205
206#[inline]
208pub fn try_duration_s_to_duration(v: i64) -> Option<Duration> {
209 Duration::try_seconds(v)
210}
211
212#[inline]
214pub fn try_duration_ms_to_duration(v: i64) -> Option<Duration> {
215 Duration::try_milliseconds(v)
216}
217
218#[inline]
220pub fn duration_us_to_duration(v: i64) -> Duration {
221 Duration::microseconds(v)
222}
223
224#[inline]
226pub fn duration_ns_to_duration(v: i64) -> Duration {
227 Duration::nanoseconds(v)
228}
229
230pub fn as_datetime<T: ArrowPrimitiveType>(v: i64) -> Option<NaiveDateTime> {
232 as_datetime_with_data_type(&ManuallyDrop::new(T::DATA_TYPE), v)
236}
237
238#[inline]
244pub(crate) fn as_datetime_with_data_type(data_type: &DataType, v: i64) -> Option<NaiveDateTime> {
245 match data_type {
246 DataType::Date32 => date32_to_datetime(v as i32),
247 DataType::Date64 => date64_to_datetime(v),
248 DataType::Time32(_) | DataType::Time64(_) => None,
249 DataType::Timestamp(unit, _) => match unit {
250 TimeUnit::Second => timestamp_s_to_datetime(v),
251 TimeUnit::Millisecond => timestamp_ms_to_datetime(v),
252 TimeUnit::Microsecond => timestamp_us_to_datetime(v),
253 TimeUnit::Nanosecond => timestamp_ns_to_datetime(v),
254 },
255 DataType::Interval(_) => None,
257 _ => None,
258 }
259}
260
261pub fn as_datetime_with_timezone<T: ArrowPrimitiveType>(v: i64, tz: Tz) -> Option<DateTime<Tz>> {
263 as_datetime_with_timezone_and_data_type(&ManuallyDrop::new(T::DATA_TYPE), v, tz)
265}
266
267#[inline]
272pub(crate) fn as_datetime_with_timezone_and_data_type(
273 data_type: &DataType,
274 v: i64,
275 tz: Tz,
276) -> Option<DateTime<Tz>> {
277 let naive = as_datetime_with_data_type(data_type, v)?;
278 Some(Utc.from_utc_datetime(&naive).with_timezone(&tz))
279}
280
281pub fn as_date<T: ArrowPrimitiveType>(v: i64) -> Option<NaiveDate> {
283 as_datetime::<T>(v).map(|datetime| datetime.date())
284}
285
286pub fn as_time<T: ArrowPrimitiveType>(v: i64) -> Option<NaiveTime> {
288 as_time_with_data_type(&ManuallyDrop::new(T::DATA_TYPE), v)
290}
291
292#[inline]
296pub(crate) fn as_time_with_data_type(data_type: &DataType, v: i64) -> Option<NaiveTime> {
297 match data_type {
298 DataType::Time32(unit) => {
299 let v = v as u32;
301 match unit {
302 TimeUnit::Second => time32s_to_time(v as i32),
303 TimeUnit::Millisecond => time32ms_to_time(v as i32),
304 _ => None,
305 }
306 }
307 DataType::Time64(unit) => match unit {
308 TimeUnit::Microsecond => time64us_to_time(v),
309 TimeUnit::Nanosecond => time64ns_to_time(v),
310 _ => None,
311 },
312 DataType::Timestamp(_, _) => {
313 as_datetime_with_data_type(data_type, v).map(|datetime| datetime.time())
314 }
315 DataType::Date32 | DataType::Date64 => NaiveTime::from_hms_opt(0, 0, 0),
316 DataType::Interval(_) => None,
317 _ => None,
318 }
319}
320
321pub fn as_duration<T: ArrowPrimitiveType>(v: i64) -> Option<Duration> {
323 match &*ManuallyDrop::new(T::DATA_TYPE) {
325 DataType::Duration(unit) => match unit {
326 TimeUnit::Second => try_duration_s_to_duration(v),
327 TimeUnit::Millisecond => try_duration_ms_to_duration(v),
328 TimeUnit::Microsecond => Some(duration_us_to_duration(v)),
329 TimeUnit::Nanosecond => Some(duration_ns_to_duration(v)),
330 },
331 _ => None,
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use crate::temporal_conversions::{
338 NANOSECONDS, date64_to_datetime, split_second, time32ms_to_time, time32s_to_time,
339 time64ns_to_time, time64us_to_time, timestamp_ms_to_datetime, timestamp_ns_to_datetime,
340 timestamp_s_to_date, timestamp_s_to_datetime, timestamp_s_to_time,
341 timestamp_us_to_datetime,
342 };
343 use chrono::DateTime;
344
345 #[test]
346 fn test_timestamp_func() {
347 let timestamp = 1234;
348 let datetime = timestamp_s_to_datetime(timestamp).unwrap();
349 let expected_date = datetime.date();
350 let expected_time = datetime.time();
351
352 assert_eq!(
353 timestamp_s_to_date(timestamp).unwrap().date(),
354 expected_date
355 );
356 assert_eq!(
357 timestamp_s_to_time(timestamp).unwrap().time(),
358 expected_time
359 );
360 }
361
362 #[test]
363 fn negative_input_timestamp_ns_to_datetime() {
364 assert_eq!(
365 timestamp_ns_to_datetime(-1),
366 DateTime::from_timestamp(-1, 999_999_999).map(|x| x.naive_utc())
367 );
368
369 assert_eq!(
370 timestamp_ns_to_datetime(-1_000_000_001),
371 DateTime::from_timestamp(-2, 999_999_999).map(|x| x.naive_utc())
372 );
373 }
374
375 #[test]
376 fn negative_input_timestamp_us_to_datetime() {
377 assert_eq!(
378 timestamp_us_to_datetime(-1),
379 DateTime::from_timestamp(-1, 999_999_000).map(|x| x.naive_utc())
380 );
381
382 assert_eq!(
383 timestamp_us_to_datetime(-1_000_001),
384 DateTime::from_timestamp(-2, 999_999_000).map(|x| x.naive_utc())
385 );
386 }
387
388 #[test]
389 fn negative_input_timestamp_ms_to_datetime() {
390 assert_eq!(
391 timestamp_ms_to_datetime(-1),
392 DateTime::from_timestamp(-1, 999_000_000).map(|x| x.naive_utc())
393 );
394
395 assert_eq!(
396 timestamp_ms_to_datetime(-1_001),
397 DateTime::from_timestamp(-2, 999_000_000).map(|x| x.naive_utc())
398 );
399 }
400
401 #[test]
402 fn negative_input_date64_to_datetime() {
403 assert_eq!(
404 date64_to_datetime(-1),
405 DateTime::from_timestamp(-1, 999_000_000).map(|x| x.naive_utc())
406 );
407
408 assert_eq!(
409 date64_to_datetime(-1_001),
410 DateTime::from_timestamp(-2, 999_000_000).map(|x| x.naive_utc())
411 );
412 }
413
414 #[test]
415 fn test_split_seconds() {
416 let (sec, nano_sec) = split_second(100, NANOSECONDS);
417 assert_eq!(sec, 0);
418 assert_eq!(nano_sec, 100);
419
420 let (sec, nano_sec) = split_second(123_000_000_456, NANOSECONDS);
421 assert_eq!(sec, 123);
422 assert_eq!(nano_sec, 456);
423
424 let (sec, nano_sec) = split_second(-1, NANOSECONDS);
425 assert_eq!(sec, -1);
426 assert_eq!(nano_sec, 999_999_999);
427
428 let (sec, nano_sec) = split_second(-123_000_000_001, NANOSECONDS);
429 assert_eq!(sec, -124);
430 assert_eq!(nano_sec, 999_999_999);
431 }
432
433 #[test]
434 fn test_time_conversions_out_of_bounds_no_wrapping() {
435 let wrapped = (1_i64 << 32) + 5;
436 assert_eq!(time64us_to_time(wrapped * 1_000_000), None);
437 assert_eq!(time64ns_to_time(wrapped * 1_000_000_000), None);
438
439 assert_eq!(time32s_to_time(86_400), None);
440 assert_eq!(time32s_to_time(-1), None);
441 assert_eq!(time32ms_to_time(86_400_000), None);
442 assert_eq!(time32ms_to_time(-1), None);
443 assert_eq!(time64us_to_time(86_400_000_000), None);
444 assert_eq!(time64us_to_time(-1), None);
445 assert_eq!(time64ns_to_time(86_400_000_000_000), None);
446 assert_eq!(time64ns_to_time(-1), None);
447 }
448}