1use arrow_array::{cast::*, temporal_conversions::*, timezone::Tz, types::*, *};
22use arrow_buffer::IntervalMonthDayNano;
23use arrow_schema::{ArrowError, DataType, TimeUnit};
24use chrono::{FixedOffset, LocalResult, NaiveDateTime, NaiveTime, Offset, TimeDelta, TimeZone};
25use std::sync::Arc;
26
27use num_traits::AsPrimitive;
28
29use super::{CastOptions, time_unit_multiple, units_per_day};
30
31pub(crate) fn cast_interval_year_month_to_interval_month_day_nano(
33 array: &dyn Array,
34 _cast_options: &CastOptions,
35) -> Result<ArrayRef, ArrowError> {
36 let array = array.as_primitive::<IntervalYearMonthType>();
37
38 Ok(Arc::new(array.unary::<_, IntervalMonthDayNanoType>(|v| {
39 let months = IntervalYearMonthType::to_months(v);
40 IntervalMonthDayNanoType::make_value(months, 0, 0)
41 })))
42}
43
44pub(crate) fn cast_interval_day_time_to_interval_month_day_nano(
46 array: &dyn Array,
47 _cast_options: &CastOptions,
48) -> Result<ArrayRef, ArrowError> {
49 let array = array.as_primitive::<IntervalDayTimeType>();
50 let mul = 1_000_000;
51
52 Ok(Arc::new(array.unary::<_, IntervalMonthDayNanoType>(|v| {
53 let (days, ms) = IntervalDayTimeType::to_parts(v);
54 IntervalMonthDayNanoType::make_value(0, days, ms as i64 * mul)
55 })))
56}
57
58pub(crate) fn cast_month_day_nano_to_duration<D: ArrowTemporalType<Native = i64>>(
60 array: &dyn Array,
61 cast_options: &CastOptions,
62) -> Result<ArrayRef, ArrowError> {
63 let array = array.as_primitive::<IntervalMonthDayNanoType>();
64 let scale = match D::DATA_TYPE {
65 DataType::Duration(TimeUnit::Second) => 1_000_000_000,
66 DataType::Duration(TimeUnit::Millisecond) => 1_000_000,
67 DataType::Duration(TimeUnit::Microsecond) => 1_000,
68 DataType::Duration(TimeUnit::Nanosecond) => 1,
69 _ => unreachable!(),
70 };
71
72 if cast_options.safe {
73 let iter = array.iter().map(|v| {
74 let v = v?;
75 (v.days == 0 && v.months == 0).then_some(v.nanoseconds / scale)
76 });
77 Ok(Arc::new(unsafe {
78 PrimitiveArray::<D>::from_trusted_len_iter(iter)
79 }))
80 } else {
81 let vec = array
82 .iter()
83 .map(|v| {
84 v.map(|v| match v.days == 0 && v.months == 0 {
85 true => Ok((v.nanoseconds) / scale),
86 _ => Err(ArrowError::ComputeError(
87 "Cannot convert interval containing non-zero months or days to duration"
88 .to_string(),
89 )),
90 })
91 .transpose()
92 })
93 .collect::<Result<Vec<_>, _>>()?;
94 Ok(Arc::new(unsafe {
95 PrimitiveArray::<D>::from_trusted_len_iter(vec.iter())
96 }))
97 }
98}
99
100pub(crate) fn cast_duration_to_interval<D: ArrowTemporalType<Native = i64>>(
102 array: &dyn Array,
103 cast_options: &CastOptions,
104) -> Result<ArrayRef, ArrowError> {
105 let array = array
106 .as_any()
107 .downcast_ref::<PrimitiveArray<D>>()
108 .ok_or_else(|| {
109 ArrowError::ComputeError(
110 "Internal Error: Cannot cast duration to DurationArray of expected type"
111 .to_string(),
112 )
113 })?;
114
115 let scale = match array.data_type() {
116 DataType::Duration(TimeUnit::Second) => 1_000_000_000,
117 DataType::Duration(TimeUnit::Millisecond) => 1_000_000,
118 DataType::Duration(TimeUnit::Microsecond) => 1_000,
119 DataType::Duration(TimeUnit::Nanosecond) => 1,
120 _ => unreachable!(),
121 };
122
123 if cast_options.safe {
124 let iter = array.iter().map(|v| {
125 v?.checked_mul(scale)
126 .map(|v| IntervalMonthDayNano::new(0, 0, v))
127 });
128 Ok(Arc::new(unsafe {
129 PrimitiveArray::<IntervalMonthDayNanoType>::from_trusted_len_iter(iter)
130 }))
131 } else {
132 let vec = array
133 .iter()
134 .map(|v| {
135 v.map(|v| {
136 if let Ok(v) = v.mul_checked(scale) {
137 Ok(IntervalMonthDayNano::new(0, 0, v))
138 } else {
139 Err(ArrowError::ComputeError(format!(
140 "Cannot cast to {:?}. Overflowing on {:?}",
141 IntervalMonthDayNanoType::DATA_TYPE,
142 v
143 )))
144 }
145 })
146 .transpose()
147 })
148 .collect::<Result<Vec<_>, _>>()?;
149 Ok(Arc::new(unsafe {
150 PrimitiveArray::<IntervalMonthDayNanoType>::from_trusted_len_iter(vec.iter())
151 }))
152 }
153}
154
155pub(crate) fn make_timestamp_array(
156 array: &PrimitiveArray<Int64Type>,
157 unit: TimeUnit,
158 tz: Option<Arc<str>>,
159) -> ArrayRef {
160 match unit {
161 TimeUnit::Second => Arc::new(
162 array
163 .reinterpret_cast::<TimestampSecondType>()
164 .with_timezone_opt(tz),
165 ),
166 TimeUnit::Millisecond => Arc::new(
167 array
168 .reinterpret_cast::<TimestampMillisecondType>()
169 .with_timezone_opt(tz),
170 ),
171 TimeUnit::Microsecond => Arc::new(
172 array
173 .reinterpret_cast::<TimestampMicrosecondType>()
174 .with_timezone_opt(tz),
175 ),
176 TimeUnit::Nanosecond => Arc::new(
177 array
178 .reinterpret_cast::<TimestampNanosecondType>()
179 .with_timezone_opt(tz),
180 ),
181 }
182}
183
184pub(crate) fn make_duration_array(array: &PrimitiveArray<Int64Type>, unit: TimeUnit) -> ArrayRef {
185 match unit {
186 TimeUnit::Second => Arc::new(array.reinterpret_cast::<DurationSecondType>()),
187 TimeUnit::Millisecond => Arc::new(array.reinterpret_cast::<DurationMillisecondType>()),
188 TimeUnit::Microsecond => Arc::new(array.reinterpret_cast::<DurationMicrosecondType>()),
189 TimeUnit::Nanosecond => Arc::new(array.reinterpret_cast::<DurationNanosecondType>()),
190 }
191}
192
193pub(crate) fn cast_timestamp_to_time<T: ArrowTimestampType>(
194 array: &dyn Array,
195 to_type: &DataType,
196 cast_options: &CastOptions,
197) -> Result<ArrayRef, ArrowError> {
198 let array = array.as_primitive::<T>();
199 match to_type {
200 DataType::Time32(TimeUnit::Second) => {
201 timestamp_to_time::<T, Time32SecondType>(array, cast_options)
202 }
203 DataType::Time32(TimeUnit::Millisecond) => {
204 timestamp_to_time::<T, Time32MillisecondType>(array, cast_options)
205 }
206 DataType::Time64(TimeUnit::Microsecond) => {
207 timestamp_to_time::<T, Time64MicrosecondType>(array, cast_options)
208 }
209 DataType::Time64(TimeUnit::Nanosecond) => {
210 timestamp_to_time::<T, Time64NanosecondType>(array, cast_options)
211 }
212 _ => Err(ArrowError::CastError(format!(
213 "Casting from {} to {to_type} not supported",
214 array.data_type()
215 ))),
216 }
217}
218
219trait TimeType: ArrowTemporalType {
220 const UNIT_MULTIPLE: i64;
222
223 fn from_naive_time(time: NaiveTime) -> Self::Native;
224}
225
226impl TimeType for Time32SecondType {
227 const UNIT_MULTIPLE: i64 = 1;
228
229 fn from_naive_time(time: NaiveTime) -> Self::Native {
230 time_to_time32s(time)
231 }
232}
233
234impl TimeType for Time32MillisecondType {
235 const UNIT_MULTIPLE: i64 = MILLISECONDS;
236
237 fn from_naive_time(time: NaiveTime) -> Self::Native {
238 time_to_time32ms(time)
239 }
240}
241
242impl TimeType for Time64MicrosecondType {
243 const UNIT_MULTIPLE: i64 = MICROSECONDS;
244
245 fn from_naive_time(time: NaiveTime) -> Self::Native {
246 time_to_time64us(time)
247 }
248}
249
250impl TimeType for Time64NanosecondType {
251 const UNIT_MULTIPLE: i64 = NANOSECONDS;
252
253 fn from_naive_time(time: NaiveTime) -> Self::Native {
254 time_to_time64ns(time)
255 }
256}
257
258fn timestamp_to_time<T, O>(
259 array: &PrimitiveArray<T>,
260 cast_options: &CastOptions,
261) -> Result<ArrayRef, ArrowError>
262where
263 T: ArrowTimestampType,
264 O: TimeType,
265 i64: AsPrimitive<O::Native>,
266{
267 let array = match array.timezone() {
269 Some(tz) => {
270 let tz: Tz = tz.parse()?;
271 let time = |v: i64| {
272 as_datetime_with_timezone::<T>(v, tz).map(|d| O::from_naive_time(d.time()))
273 };
274 if cast_options.safe {
275 array.unary_opt::<_, O>(time)
276 } else {
277 array.try_unary::<_, O, _>(|v| {
278 time(v).ok_or_else(|| {
279 ArrowError::CastError(format!(
280 "Failed to create naive time with {} {}",
281 std::any::type_name::<T>(),
282 v
283 ))
284 })
285 })?
286 }
287 }
288 None => array.unary::<_, O>(|v| {
289 let from = time_unit_multiple(&T::UNIT);
293 let time = v.rem_euclid(SECONDS_IN_DAY * from);
294 let time = if from >= O::UNIT_MULTIPLE {
295 time / (from / O::UNIT_MULTIPLE)
296 } else {
297 time * (O::UNIT_MULTIPLE / from)
298 };
299 time.as_()
300 }),
301 };
302 Ok(Arc::new(array))
303}
304
305pub(crate) fn timestamp_to_date32<T: ArrowTimestampType>(
306 array: &PrimitiveArray<T>,
307 cast_options: &CastOptions,
308) -> Result<ArrayRef, ArrowError> {
309 let err = |x: i64| {
310 ArrowError::CastError(format!(
311 "Cannot convert {} {x} to Date32",
312 std::any::type_name::<T>()
313 ))
314 };
315
316 let array: Date32Array = match array.timezone() {
317 Some(tz) => {
318 let tz: Tz = tz.parse()?;
319 let date = |x: i64| {
320 as_datetime_with_timezone::<T>(x, tz)
321 .map(|d| Date32Type::from_naive_date(d.date_naive()))
322 };
323 if cast_options.safe {
324 array.unary_opt(date)
325 } else {
326 array.try_unary(|x| date(x).ok_or_else(|| err(x)))?
327 }
328 }
329 None => {
330 let days = |x: i64| x.div_euclid(units_per_day::<T>());
333 let all_in_range = match T::UNIT {
334 TimeUnit::Microsecond | TimeUnit::Nanosecond => true,
336 _ => {
338 let day = units_per_day::<T>();
339 let (lo, hi) = (i32::MIN as i64 * day, (i32::MAX as i64 + 1) * day);
340 array
341 .values()
342 .iter()
343 .fold(true, |ok, &x| ok & (lo <= x) & (x < hi))
344 }
345 };
346 if all_in_range {
347 array.unary(|x| days(x) as i32)
348 } else if cast_options.safe {
349 array.unary_opt(|x| i32::try_from(days(x)).ok())
350 } else {
351 array.try_unary(|x| i32::try_from(days(x)).map_err(|_| err(x)))?
352 }
353 }
354 };
355 Ok(Arc::new(array))
356}
357
358fn resolve_local_offset(tz: &Tz, local: &NaiveDateTime) -> Option<FixedOffset> {
386 match tz.offset_from_local_datetime(local) {
387 LocalResult::Single(offset) => Some(offset.fix()),
388 LocalResult::Ambiguous(_earlier, later) => Some(later.fix()),
389 LocalResult::None => {
390 tz.offset_from_local_datetime(&(*local - TimeDelta::hours(24)))
411 .earliest()
412 .map(|offset| offset.fix())
413 }
414 }
415}
416
417pub(crate) fn adjust_timestamp_to_timezone<T: ArrowTimestampType>(
418 array: PrimitiveArray<Int64Type>,
419 to_tz: &Tz,
420 cast_options: &CastOptions,
421) -> Result<PrimitiveArray<Int64Type>, ArrowError> {
422 let adjust = |o| {
423 let local = as_datetime::<T>(o)?;
424 let offset = resolve_local_offset(to_tz, &local)?;
425 T::from_naive_datetime(local - offset, None)
426 };
427 let adjusted = if cast_options.safe {
428 array.unary_opt::<_, Int64Type>(adjust)
429 } else {
430 array.try_unary::<_, Int64Type, _>(|o| {
431 adjust(o).ok_or_else(|| {
432 ArrowError::CastError("Cannot cast timezone to different timezone".to_string())
433 })
434 })?
435 };
436 Ok(adjusted)
437}