1use arrow_array::{cast::*, temporal_conversions::*, timezone::Tz, types::*, *};
9use arrow_buffer::IntervalMonthDayNano;
10use arrow_schema::{ArrowError, DataType, TimeUnit};
11use chrono::{NaiveTime, Offset};
12use std::sync::Arc;
13
14use num_traits::AsPrimitive;
15
16use crate::local_time::resolve_local_offset;
17
18use super::{CastOptions, time_unit_multiple, units_per_day};
19
20pub(crate) fn cast_interval_year_month_to_interval_month_day_nano(
22 array: &dyn Array,
23 _cast_options: &CastOptions,
24) -> Result<ArrayRef, ArrowError> {
25 let array = array.as_primitive::<IntervalYearMonthType>();
26
27 Ok(Arc::new(array.unary::<_, IntervalMonthDayNanoType>(|v| {
28 let months = IntervalYearMonthType::to_months(v);
29 IntervalMonthDayNanoType::make_value(months, 0, 0)
30 })))
31}
32
33pub(crate) fn cast_interval_day_time_to_interval_month_day_nano(
35 array: &dyn Array,
36 _cast_options: &CastOptions,
37) -> Result<ArrayRef, ArrowError> {
38 let array = array.as_primitive::<IntervalDayTimeType>();
39 let mul = 1_000_000;
40
41 Ok(Arc::new(array.unary::<_, IntervalMonthDayNanoType>(|v| {
42 let (days, ms) = IntervalDayTimeType::to_parts(v);
43 IntervalMonthDayNanoType::make_value(0, days, ms as i64 * mul)
44 })))
45}
46
47pub(crate) fn cast_month_day_nano_to_duration<D: ArrowTemporalType<Native = i64>>(
49 array: &dyn Array,
50 cast_options: &CastOptions,
51) -> Result<ArrayRef, ArrowError> {
52 let array = array.as_primitive::<IntervalMonthDayNanoType>();
53 let scale = match D::DATA_TYPE {
54 DataType::Duration(TimeUnit::Second) => 1_000_000_000,
55 DataType::Duration(TimeUnit::Millisecond) => 1_000_000,
56 DataType::Duration(TimeUnit::Microsecond) => 1_000,
57 DataType::Duration(TimeUnit::Nanosecond) => 1,
58 _ => unreachable!(),
59 };
60
61 if cast_options.safe {
62 let iter = array.iter().map(|v| {
63 let v = v?;
64 (v.days == 0 && v.months == 0).then_some(v.nanoseconds / scale)
65 });
66 Ok(Arc::new(unsafe {
67 PrimitiveArray::<D>::from_trusted_len_iter(iter)
68 }))
69 } else {
70 let vec = array
71 .iter()
72 .map(|v| {
73 v.map(|v| match v.days == 0 && v.months == 0 {
74 true => Ok((v.nanoseconds) / scale),
75 _ => Err(ArrowError::ComputeError(
76 "Cannot convert interval containing non-zero months or days to duration"
77 .to_string(),
78 )),
79 })
80 .transpose()
81 })
82 .collect::<Result<Vec<_>, _>>()?;
83 Ok(Arc::new(unsafe {
84 PrimitiveArray::<D>::from_trusted_len_iter(vec.iter())
85 }))
86 }
87}
88
89pub(crate) fn cast_duration_to_interval<D: ArrowTemporalType<Native = i64>>(
91 array: &dyn Array,
92 cast_options: &CastOptions,
93) -> Result<ArrayRef, ArrowError> {
94 let array = array.as_primitive::<D>();
95
96 let scale = match array.data_type() {
97 DataType::Duration(TimeUnit::Second) => 1_000_000_000,
98 DataType::Duration(TimeUnit::Millisecond) => 1_000_000,
99 DataType::Duration(TimeUnit::Microsecond) => 1_000,
100 DataType::Duration(TimeUnit::Nanosecond) => 1,
101 _ => unreachable!(),
102 };
103
104 if cast_options.safe {
105 let iter = array.iter().map(|v| {
106 v?.checked_mul(scale)
107 .map(|v| IntervalMonthDayNano::new(0, 0, v))
108 });
109 Ok(Arc::new(unsafe {
110 PrimitiveArray::<IntervalMonthDayNanoType>::from_trusted_len_iter(iter)
111 }))
112 } else {
113 let vec = array
114 .iter()
115 .map(|v| {
116 v.map(|v| {
117 if let Ok(v) = v.mul_checked(scale) {
118 Ok(IntervalMonthDayNano::new(0, 0, v))
119 } else {
120 Err(ArrowError::ComputeError(format!(
121 "Cannot cast to {:?}. Overflowing on {:?}",
122 IntervalMonthDayNanoType::DATA_TYPE,
123 v
124 )))
125 }
126 })
127 .transpose()
128 })
129 .collect::<Result<Vec<_>, _>>()?;
130 Ok(Arc::new(unsafe {
131 PrimitiveArray::<IntervalMonthDayNanoType>::from_trusted_len_iter(vec.iter())
132 }))
133 }
134}
135
136pub(crate) fn make_timestamp_array(
137 array: &PrimitiveArray<Int64Type>,
138 unit: TimeUnit,
139 tz: Option<Arc<str>>,
140) -> ArrayRef {
141 match unit {
142 TimeUnit::Second => Arc::new(
143 array
144 .reinterpret_cast::<TimestampSecondType>()
145 .with_timezone_opt(tz),
146 ),
147 TimeUnit::Millisecond => Arc::new(
148 array
149 .reinterpret_cast::<TimestampMillisecondType>()
150 .with_timezone_opt(tz),
151 ),
152 TimeUnit::Microsecond => Arc::new(
153 array
154 .reinterpret_cast::<TimestampMicrosecondType>()
155 .with_timezone_opt(tz),
156 ),
157 TimeUnit::Nanosecond => Arc::new(
158 array
159 .reinterpret_cast::<TimestampNanosecondType>()
160 .with_timezone_opt(tz),
161 ),
162 }
163}
164
165pub(crate) fn make_duration_array(array: &PrimitiveArray<Int64Type>, unit: TimeUnit) -> ArrayRef {
166 match unit {
167 TimeUnit::Second => Arc::new(array.reinterpret_cast::<DurationSecondType>()),
168 TimeUnit::Millisecond => Arc::new(array.reinterpret_cast::<DurationMillisecondType>()),
169 TimeUnit::Microsecond => Arc::new(array.reinterpret_cast::<DurationMicrosecondType>()),
170 TimeUnit::Nanosecond => Arc::new(array.reinterpret_cast::<DurationNanosecondType>()),
171 }
172}
173
174pub(crate) fn cast_timestamp_to_time<T: ArrowTimestampType>(
175 array: &dyn Array,
176 to_type: &DataType,
177 cast_options: &CastOptions,
178) -> Result<ArrayRef, ArrowError> {
179 let array = array.as_primitive::<T>();
180 match to_type {
181 DataType::Time32(TimeUnit::Second) => {
182 timestamp_to_time::<T, Time32SecondType>(array, cast_options)
183 }
184 DataType::Time32(TimeUnit::Millisecond) => {
185 timestamp_to_time::<T, Time32MillisecondType>(array, cast_options)
186 }
187 DataType::Time64(TimeUnit::Microsecond) => {
188 timestamp_to_time::<T, Time64MicrosecondType>(array, cast_options)
189 }
190 DataType::Time64(TimeUnit::Nanosecond) => {
191 timestamp_to_time::<T, Time64NanosecondType>(array, cast_options)
192 }
193 _ => Err(ArrowError::InvalidArgumentError(format!(
194 "Casting from {} to {to_type} not supported",
195 array.data_type()
196 ))),
197 }
198}
199
200trait TimeType: ArrowTemporalType {
201 const UNIT_MULTIPLE: i64;
203
204 fn from_naive_time(time: NaiveTime) -> Self::Native;
205}
206
207impl TimeType for Time32SecondType {
208 const UNIT_MULTIPLE: i64 = 1;
209
210 fn from_naive_time(time: NaiveTime) -> Self::Native {
211 time_to_time32s(time)
212 }
213}
214
215impl TimeType for Time32MillisecondType {
216 const UNIT_MULTIPLE: i64 = MILLISECONDS;
217
218 fn from_naive_time(time: NaiveTime) -> Self::Native {
219 time_to_time32ms(time)
220 }
221}
222
223impl TimeType for Time64MicrosecondType {
224 const UNIT_MULTIPLE: i64 = MICROSECONDS;
225
226 fn from_naive_time(time: NaiveTime) -> Self::Native {
227 time_to_time64us(time)
228 }
229}
230
231impl TimeType for Time64NanosecondType {
232 const UNIT_MULTIPLE: i64 = NANOSECONDS;
233
234 fn from_naive_time(time: NaiveTime) -> Self::Native {
235 time_to_time64ns(time)
236 }
237}
238
239fn timestamp_to_time<T, O>(
240 array: &PrimitiveArray<T>,
241 cast_options: &CastOptions,
242) -> Result<ArrayRef, ArrowError>
243where
244 T: ArrowTimestampType,
245 O: TimeType,
246 i64: AsPrimitive<O::Native>,
247{
248 let array = match array.timezone() {
250 Some(tz) => {
251 let tz: Tz = tz.parse()?;
252 let time = |v: i64| {
253 as_datetime_with_timezone::<T>(v, tz).map(|d| O::from_naive_time(d.time()))
254 };
255 if cast_options.safe {
256 array.unary_opt::<_, O>(time)
257 } else {
258 array.try_unary::<_, O, _>(|v| {
259 time(v).ok_or_else(|| {
260 ArrowError::CastError(format!(
261 "Failed to create naive time with {} {}",
262 std::any::type_name::<T>(),
263 v
264 ))
265 })
266 })?
267 }
268 }
269 None => array.unary::<_, O>(|v| {
270 let from = time_unit_multiple(&T::UNIT);
274 let time = v.rem_euclid(SECONDS_IN_DAY * from);
275 let time = if from >= O::UNIT_MULTIPLE {
276 time / (from / O::UNIT_MULTIPLE)
277 } else {
278 time * (O::UNIT_MULTIPLE / from)
279 };
280 time.as_()
281 }),
282 };
283 Ok(Arc::new(array))
284}
285
286pub(crate) fn timestamp_to_date32<T: ArrowTimestampType>(
287 array: &PrimitiveArray<T>,
288 cast_options: &CastOptions,
289) -> Result<ArrayRef, ArrowError> {
290 let err = |x: i64| {
291 ArrowError::CastError(format!(
292 "Cannot convert {} {x} to Date32",
293 std::any::type_name::<T>()
294 ))
295 };
296
297 let array: Date32Array = match array.timezone() {
298 Some(tz) => {
299 let tz: Tz = tz.parse()?;
300 let date = |x: i64| {
301 as_datetime_with_timezone::<T>(x, tz)
302 .map(|d| Date32Type::from_naive_date(d.date_naive()))
303 };
304 if cast_options.safe {
305 array.unary_opt(date)
306 } else {
307 array.try_unary(|x| date(x).ok_or_else(|| err(x)))?
308 }
309 }
310 None => {
311 let days = |x: i64| x.div_euclid(units_per_day::<T>());
314 let all_in_range = match T::UNIT {
315 TimeUnit::Microsecond | TimeUnit::Nanosecond => true,
317 _ => {
319 let day = units_per_day::<T>();
320 let (lo, hi) = (i32::MIN as i64 * day, (i32::MAX as i64 + 1) * day);
321 array
322 .values()
323 .iter()
324 .fold(true, |ok, &x| ok & (lo <= x) & (x < hi))
325 }
326 };
327 if all_in_range {
328 array.unary(|x| days(x) as i32)
329 } else if cast_options.safe {
330 array.unary_opt(|x| i32::try_from(days(x)).ok())
331 } else {
332 array.try_unary(|x| i32::try_from(days(x)).map_err(|_| err(x)))?
333 }
334 }
335 };
336 Ok(Arc::new(array))
337}
338
339pub(crate) fn timestamp_to_date64<T: ArrowTimestampType>(
346 array: &PrimitiveArray<T>,
347 cast_options: &CastOptions,
348) -> Result<ArrayRef, ArrowError> {
349 let err = |x: i64| {
350 ArrowError::CastError(format!(
351 "Cannot convert {} {x} to Date64",
352 std::any::type_name::<T>()
353 ))
354 };
355
356 let array: Date64Array = match array.timezone() {
357 Some(tz) => {
358 let tz: Tz = tz.parse()?;
359 let date = |x: i64| {
361 as_datetime_with_timezone::<T>(x, tz).map(|d| {
362 Date32Type::from_naive_date(d.date_naive()) as i64 * MILLISECONDS_IN_DAY
363 })
364 };
365 if cast_options.safe {
366 array.unary_opt(date)
367 } else {
368 array.try_unary(|x| date(x).ok_or_else(|| err(x)))?
369 }
370 }
371 None => {
372 let date = |x: i64| {
375 x.div_euclid(units_per_day::<T>())
376 .checked_mul(MILLISECONDS_IN_DAY)
377 };
378 if cast_options.safe {
379 array.unary_opt(date)
380 } else {
381 array.try_unary(|x| date(x).ok_or_else(|| err(x)))?
382 }
383 }
384 };
385 Ok(Arc::new(array))
386}
387
388pub(crate) fn adjust_timestamp_to_timezone<T: ArrowTimestampType>(
389 array: PrimitiveArray<Int64Type>,
390 to_tz: &Tz,
391 cast_options: &CastOptions,
392) -> Result<PrimitiveArray<Int64Type>, ArrowError> {
393 let adjust = |o| {
394 let local = as_datetime::<T>(o)?;
395 let offset = resolve_local_offset(to_tz, &local)?.fix();
396 T::from_naive_datetime(local - offset, None)
397 };
398 let adjusted = if cast_options.safe {
399 array.unary_opt::<_, Int64Type>(adjust)
400 } else {
401 array.try_unary::<_, Int64Type, _>(|o| {
402 adjust(o).ok_or_else(|| {
403 ArrowError::CastError("Cannot cast timezone to different timezone".to_string())
404 })
405 })?
406 };
407 Ok(adjusted)
408}