Skip to main content

reifydb_value/value/container/
temporal_array.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{
5	slice,
6	sync::{Arc, LazyLock},
7};
8
9use arrow_array::{
10	Date32Array, IntervalMonthDayNanoArray, PrimitiveArray, Time64NanosecondArray, TimestampNanosecondArray,
11};
12use arrow_buffer::{IntervalMonthDayNano, ScalarBuffer};
13use arrow_schema::{DataType, TimeUnit};
14
15use crate::value::{Value, date::Date, datetime::DateTime, duration::Duration, is::IsTemporal, time::Time};
16
17pub const DATETIME_TIMEZONE: &str = "+00:00";
18
19static TIMEZONE: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from(DATETIME_TIMEZONE));
20
21static DATETIME_DATA_TYPE: LazyLock<DataType> =
22	LazyLock::new(|| DataType::Timestamp(TimeUnit::Nanosecond, Some(datetime_timezone())));
23
24pub fn datetime_timezone() -> Arc<str> {
25	TIMEZONE.clone()
26}
27
28pub fn datetime_data_type() -> DataType {
29	DATETIME_DATA_TYPE.clone()
30}
31
32pub fn dates(array: &Date32Array) -> &[Date] {
33	let values: &[i32] = array.values();
34	// SAFETY: Date is repr(transparent) over i32 with no niche, so the cast keeps the bounds and lifetime.
35	unsafe { slice::from_raw_parts(values.as_ptr().cast::<Date>(), values.len()) }
36}
37
38pub fn datetimes(array: &TimestampNanosecondArray) -> &[DateTime] {
39	let values: &[i64] = array.values();
40	// SAFETY: DateTime is repr(transparent) over i64 with no niche, so the cast keeps the bounds and lifetime.
41	unsafe { slice::from_raw_parts(values.as_ptr().cast::<DateTime>(), values.len()) }
42}
43
44pub fn times(array: &Time64NanosecondArray) -> &[Time] {
45	let values: &[i64] = array.values();
46	// SAFETY: Time is repr(transparent) over u64, which has the size and alignment of i64 and no niche.
47	unsafe { slice::from_raw_parts(values.as_ptr().cast::<Time>(), values.len()) }
48}
49
50pub fn durations(array: &IntervalMonthDayNanoArray) -> &[Duration] {
51	let values: &[IntervalMonthDayNano] = array.values();
52	// SAFETY: Duration and IntervalMonthDayNano are both repr(C) {i32, i32, i64} with no niche.
53	unsafe { slice::from_raw_parts(values.as_ptr().cast::<Duration>(), values.len()) }
54}
55
56pub fn date_to_native(value: Date) -> i32 {
57	value.to_days_since_epoch()
58}
59
60pub fn datetime_to_native(value: DateTime) -> i64 {
61	value.to_nanos()
62}
63
64pub fn time_to_native(value: Time) -> i64 {
65	value.to_nanos_since_midnight() as i64
66}
67
68pub fn duration_to_native(value: Duration) -> IntervalMonthDayNano {
69	IntervalMonthDayNano::new(value.get_months(), value.get_days(), value.get_nanos())
70}
71
72pub fn date_array(values: impl IntoIterator<Item = Date>) -> Date32Array {
73	let natives: Vec<i32> = values.into_iter().map(date_to_native).collect();
74	PrimitiveArray::new(ScalarBuffer::from(natives), None)
75}
76
77pub fn datetime_array(values: impl IntoIterator<Item = DateTime>) -> TimestampNanosecondArray {
78	let natives: Vec<i64> = values.into_iter().map(datetime_to_native).collect();
79	PrimitiveArray::new(ScalarBuffer::from(natives), None).with_timezone(datetime_timezone())
80}
81
82pub fn time_array(values: impl IntoIterator<Item = Time>) -> Time64NanosecondArray {
83	let natives: Vec<i64> = values.into_iter().map(time_to_native).collect();
84	PrimitiveArray::new(ScalarBuffer::from(natives), None)
85}
86
87pub fn duration_array(values: impl IntoIterator<Item = Duration>) -> IntervalMonthDayNanoArray {
88	let natives: Vec<IntervalMonthDayNano> = values.into_iter().map(duration_to_native).collect();
89	PrimitiveArray::new(ScalarBuffer::from(natives), None)
90}
91
92pub fn get_value<T: IsTemporal + Copy>(values: &[T], index: usize) -> Value {
93	if index < values.len() {
94		values[index].to_value()
95	} else {
96		Value::none()
97	}
98}
99
100pub fn as_string<T: IsTemporal>(values: &[T], index: usize) -> String {
101	if index < values.len() {
102		values[index].to_string()
103	} else {
104		"none".to_string()
105	}
106}