Skip to main content

arrow_arith/
numeric.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Defines numeric arithmetic kernels on [`PrimitiveArray`], such as [`add`]
19
20use std::cmp::Ordering;
21use std::fmt::Formatter;
22use std::sync::Arc;
23
24use arrow_array::cast::AsArray;
25use arrow_array::temporal_conversions::{NANOSECONDS, SECONDS_IN_DAY};
26use arrow_array::timezone::Tz;
27use arrow_array::types::*;
28use arrow_array::*;
29use arrow_buffer::{ArrowNativeType, IntervalDayTime, IntervalMonthDayNano};
30use arrow_schema::{ArrowError, DataType, IntervalUnit, TimeUnit};
31use num_traits::ToPrimitive;
32
33use crate::arity::{binary, try_binary};
34
35/// Perform `lhs + rhs`, returning an error on overflow
36pub fn add(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
37    arithmetic_op(Op::Add, lhs, rhs)
38}
39
40/// Perform `lhs + rhs`, wrapping on overflow for [`DataType::is_integer`]
41pub fn add_wrapping(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
42    arithmetic_op(Op::AddWrapping, lhs, rhs)
43}
44
45/// Perform `lhs - rhs`, returning an error on overflow
46pub fn sub(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
47    arithmetic_op(Op::Sub, lhs, rhs)
48}
49
50/// Perform `lhs - rhs`, wrapping on overflow for [`DataType::is_integer`]
51pub fn sub_wrapping(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
52    arithmetic_op(Op::SubWrapping, lhs, rhs)
53}
54
55/// Perform `lhs * rhs`, returning an error on overflow
56pub fn mul(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
57    arithmetic_op(Op::Mul, lhs, rhs)
58}
59
60/// Perform `lhs * rhs`, wrapping on overflow for [`DataType::is_integer`]
61pub fn mul_wrapping(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
62    arithmetic_op(Op::MulWrapping, lhs, rhs)
63}
64
65/// Perform `lhs / rhs`
66///
67/// Overflow or division by zero will result in an error, with exception to
68/// floating point numbers, which instead follow the IEEE 754 rules
69pub fn div(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
70    arithmetic_op(Op::Div, lhs, rhs)
71}
72
73/// Perform `lhs % rhs`
74///
75/// Division by zero will result in an error, with exception to
76/// floating point numbers, which instead follow the IEEE 754 rules
77///
78/// `signed_integer::MIN % -1` will not result in an error but return 0
79pub fn rem(lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
80    arithmetic_op(Op::Rem, lhs, rhs)
81}
82
83macro_rules! neg_checked {
84    ($t:ty, $a:ident) => {{
85        let array = $a
86            .as_primitive::<$t>()
87            .try_unary::<_, $t, _>(|x| x.neg_checked())?;
88        Ok(Arc::new(array))
89    }};
90}
91
92macro_rules! neg_wrapping {
93    ($t:ty, $a:ident) => {{
94        let array = $a.as_primitive::<$t>().unary::<_, $t>(|x| x.neg_wrapping());
95        Ok(Arc::new(array))
96    }};
97}
98
99/// Negates each element of  `array`, returning an error on overflow
100///
101/// Note: negation of unsigned arrays is not supported and will return in an error,
102/// for wrapping unsigned negation consider using [`neg_wrapping`][neg_wrapping()]
103pub fn neg(array: &dyn Array) -> Result<ArrayRef, ArrowError> {
104    use DataType::*;
105    use IntervalUnit::*;
106    use TimeUnit::*;
107
108    match array.data_type() {
109        Int8 => neg_checked!(Int8Type, array),
110        Int16 => neg_checked!(Int16Type, array),
111        Int32 => neg_checked!(Int32Type, array),
112        Int64 => neg_checked!(Int64Type, array),
113        Float16 => neg_wrapping!(Float16Type, array),
114        Float32 => neg_wrapping!(Float32Type, array),
115        Float64 => neg_wrapping!(Float64Type, array),
116        Decimal32(p, s) => {
117            let a = array
118                .as_primitive::<Decimal32Type>()
119                .try_unary::<_, Decimal32Type, _>(|x| x.neg_checked())?;
120
121            Ok(Arc::new(a.with_precision_and_scale(*p, *s)?))
122        }
123        Decimal64(p, s) => {
124            let a = array
125                .as_primitive::<Decimal64Type>()
126                .try_unary::<_, Decimal64Type, _>(|x| x.neg_checked())?;
127
128            Ok(Arc::new(a.with_precision_and_scale(*p, *s)?))
129        }
130        Decimal128(p, s) => {
131            let a = array
132                .as_primitive::<Decimal128Type>()
133                .try_unary::<_, Decimal128Type, _>(|x| x.neg_checked())?;
134
135            Ok(Arc::new(a.with_precision_and_scale(*p, *s)?))
136        }
137        Decimal256(p, s) => {
138            let a = array
139                .as_primitive::<Decimal256Type>()
140                .try_unary::<_, Decimal256Type, _>(|x| x.neg_checked())?;
141
142            Ok(Arc::new(a.with_precision_and_scale(*p, *s)?))
143        }
144        Duration(Second) => neg_checked!(DurationSecondType, array),
145        Duration(Millisecond) => neg_checked!(DurationMillisecondType, array),
146        Duration(Microsecond) => neg_checked!(DurationMicrosecondType, array),
147        Duration(Nanosecond) => neg_checked!(DurationNanosecondType, array),
148        Interval(YearMonth) => neg_checked!(IntervalYearMonthType, array),
149        Interval(DayTime) => {
150            let a = array
151                .as_primitive::<IntervalDayTimeType>()
152                .try_unary::<_, IntervalDayTimeType, ArrowError>(|x| {
153                    let (days, ms) = IntervalDayTimeType::to_parts(x);
154                    Ok(IntervalDayTimeType::make_value(
155                        days.neg_checked()?,
156                        ms.neg_checked()?,
157                    ))
158                })?;
159            Ok(Arc::new(a))
160        }
161        Interval(MonthDayNano) => {
162            let a = array
163                .as_primitive::<IntervalMonthDayNanoType>()
164                .try_unary::<_, IntervalMonthDayNanoType, ArrowError>(|x| {
165                    let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(x);
166                    Ok(IntervalMonthDayNanoType::make_value(
167                        months.neg_checked()?,
168                        days.neg_checked()?,
169                        nanos.neg_checked()?,
170                    ))
171                })?;
172            Ok(Arc::new(a))
173        }
174        t => Err(ArrowError::InvalidArgumentError(format!(
175            "Invalid arithmetic operation: !{t}"
176        ))),
177    }
178}
179
180/// Negates each element of  `array`, wrapping on overflow for [`DataType::is_integer`]
181pub fn neg_wrapping(array: &dyn Array) -> Result<ArrayRef, ArrowError> {
182    downcast_integer! {
183        array.data_type() => (neg_wrapping, array),
184        _ => neg(array),
185    }
186}
187
188/// An enumeration of arithmetic operations
189///
190/// This allows sharing the type dispatch logic across the various kernels
191#[derive(Debug, Copy, Clone)]
192enum Op {
193    AddWrapping,
194    Add,
195    SubWrapping,
196    Sub,
197    MulWrapping,
198    Mul,
199    Div,
200    Rem,
201}
202
203impl std::fmt::Display for Op {
204    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
205        match self {
206            Op::AddWrapping | Op::Add => write!(f, "+"),
207            Op::SubWrapping | Op::Sub => write!(f, "-"),
208            Op::MulWrapping | Op::Mul => write!(f, "*"),
209            Op::Div => write!(f, "/"),
210            Op::Rem => write!(f, "%"),
211        }
212    }
213}
214
215impl Op {
216    fn commutative(&self) -> bool {
217        matches!(
218            self,
219            Self::Add | Self::AddWrapping | Self::Mul | Self::MulWrapping
220        )
221    }
222}
223
224/// Dispatch the given `op` to the appropriate specialized kernel
225fn arithmetic_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, ArrowError> {
226    use DataType::*;
227    use IntervalUnit::*;
228    use TimeUnit::*;
229
230    macro_rules! integer_helper {
231        ($t:ty, $op:ident, $l:ident, $l_scalar:ident, $r:ident, $r_scalar:ident) => {
232            integer_op::<$t>($op, $l, $l_scalar, $r, $r_scalar)
233        };
234    }
235
236    let (l, l_scalar) = lhs.get();
237    let (r, r_scalar) = rhs.get();
238    downcast_integer! {
239        l.data_type(), r.data_type() => (integer_helper, op, l, l_scalar, r, r_scalar),
240        (Float16, Float16) => float_op::<Float16Type>(op, l, l_scalar, r, r_scalar),
241        (Float32, Float32) => float_op::<Float32Type>(op, l, l_scalar, r, r_scalar),
242        (Float64, Float64) => float_op::<Float64Type>(op, l, l_scalar, r, r_scalar),
243        (Timestamp(Second, _), _) => timestamp_op::<TimestampSecondType>(op, l, l_scalar, r, r_scalar),
244        (Timestamp(Millisecond, _), _) => timestamp_op::<TimestampMillisecondType>(op, l, l_scalar, r, r_scalar),
245        (Timestamp(Microsecond, _), _) => timestamp_op::<TimestampMicrosecondType>(op, l, l_scalar, r, r_scalar),
246        (Timestamp(Nanosecond, _), _) => timestamp_op::<TimestampNanosecondType>(op, l, l_scalar, r, r_scalar),
247        (Duration(Second), Duration(Second)) => duration_op::<DurationSecondType>(op, l, l_scalar, r, r_scalar),
248        (Duration(Millisecond), Duration(Millisecond)) => duration_op::<DurationMillisecondType>(op, l, l_scalar, r, r_scalar),
249        (Duration(Microsecond), Duration(Microsecond)) => duration_op::<DurationMicrosecondType>(op, l, l_scalar, r, r_scalar),
250        (Duration(Nanosecond), Duration(Nanosecond)) => duration_op::<DurationNanosecondType>(op, l, l_scalar, r, r_scalar),
251        (Interval(YearMonth), Interval(YearMonth) | Int64) => interval_op::<IntervalYearMonthType>(op, l, l_scalar, r, r_scalar),
252        (Interval(DayTime), Interval(DayTime) | Int64) => interval_op::<IntervalDayTimeType>(op, l, l_scalar, r, r_scalar),
253        (Interval(MonthDayNano), Interval(MonthDayNano) | Int64) => interval_op::<IntervalMonthDayNanoType>(op, l, l_scalar, r, r_scalar),
254        (Interval(MonthDayNano), Float64) => interval_f64_op(op, l, l_scalar, r, r_scalar),
255        (Date32, _) => date_op::<Date32Type>(op, l, l_scalar, r, r_scalar),
256        (Date64, _) => date_op::<Date64Type>(op, l, l_scalar, r, r_scalar),
257        (Decimal32(_, _), Decimal32(_, _)) => decimal_op::<Decimal32Type>(op, l, l_scalar, r, r_scalar),
258        (Decimal64(_, _), Decimal64(_, _)) => decimal_op::<Decimal64Type>(op, l, l_scalar, r, r_scalar),
259        (Decimal128(_, _), Decimal128(_, _)) => decimal_op::<Decimal128Type>(op, l, l_scalar, r, r_scalar),
260        (Decimal256(_, _), Decimal256(_, _)) => decimal_op::<Decimal256Type>(op, l, l_scalar, r, r_scalar),
261        (l_t, r_t) => match (l_t, r_t) {
262            (Duration(_) | Interval(_), Date32 | Date64 | Timestamp(_, _)) if op.commutative() => {
263                arithmetic_op(op, rhs, lhs)
264            }
265            (Int64, Interval(_)) | (Float64, Interval(MonthDayNano))
266                if matches!(op, Op::Mul) =>
267            {
268                arithmetic_op(op, rhs, lhs)
269            }
270            _ => Err(ArrowError::InvalidArgumentError(
271              format!("Invalid arithmetic operation: {l_t} {op} {r_t}")
272            ))
273        }
274    }
275}
276
277/// Perform an infallible binary operation on potentially scalar inputs
278macro_rules! op {
279    ($l:ident, $l_s:expr, $r:ident, $r_s:expr, $op:expr) => {
280        match ($l_s, $r_s) {
281            (true, true) | (false, false) => binary($l, $r, |$l, $r| $op)?,
282            (true, false) => match ($l.null_count() == 0).then(|| $l.value(0)) {
283                None => PrimitiveArray::new_null($r.len()),
284                Some($l) => $r.unary(|$r| $op),
285            },
286            (false, true) => match ($r.null_count() == 0).then(|| $r.value(0)) {
287                None => PrimitiveArray::new_null($l.len()),
288                Some($r) => $l.unary(|$l| $op),
289            },
290        }
291    };
292}
293
294/// Same as `op` but with a type hint for the returned array
295macro_rules! op_ref {
296    ($t:ty, $l:ident, $l_s:expr, $r:ident, $r_s:expr, $op:expr) => {{
297        let array: PrimitiveArray<$t> = op!($l, $l_s, $r, $r_s, $op);
298        Arc::new(array)
299    }};
300}
301
302/// Perform a fallible binary operation on potentially scalar inputs
303macro_rules! try_op {
304    ($l:ident, $l_s:expr, $r:ident, $r_s:expr, $op:expr) => {
305        match ($l_s, $r_s) {
306            (true, true) | (false, false) => try_binary($l, $r, |$l, $r| $op)?,
307            (true, false) => match ($l.null_count() == 0).then(|| $l.value(0)) {
308                None => PrimitiveArray::new_null($r.len()),
309                Some($l) => $r.try_unary(|$r| $op)?,
310            },
311            (false, true) => match ($r.null_count() == 0).then(|| $r.value(0)) {
312                None => PrimitiveArray::new_null($l.len()),
313                Some($r) => $l.try_unary(|$l| $op)?,
314            },
315        }
316    };
317}
318
319/// Same as `try_op` but with a type hint for the returned array
320macro_rules! try_op_ref {
321    ($t:ty, $l:ident, $l_s:expr, $r:ident, $r_s:expr, $op:expr) => {{
322        let array: PrimitiveArray<$t> = try_op!($l, $l_s, $r, $r_s, $op);
323        Arc::new(array)
324    }};
325}
326
327/// Perform an arithmetic operation on integers
328fn integer_op<T: ArrowPrimitiveType>(
329    op: Op,
330    l: &dyn Array,
331    l_s: bool,
332    r: &dyn Array,
333    r_s: bool,
334) -> Result<ArrayRef, ArrowError> {
335    let l = l.as_primitive::<T>();
336    let r = r.as_primitive::<T>();
337    let array: PrimitiveArray<T> = match op {
338        Op::AddWrapping => op!(l, l_s, r, r_s, l.add_wrapping(r)),
339        Op::Add => try_op!(l, l_s, r, r_s, l.add_checked(r)),
340        Op::SubWrapping => op!(l, l_s, r, r_s, l.sub_wrapping(r)),
341        Op::Sub => try_op!(l, l_s, r, r_s, l.sub_checked(r)),
342        Op::MulWrapping => op!(l, l_s, r, r_s, l.mul_wrapping(r)),
343        Op::Mul => try_op!(l, l_s, r, r_s, l.mul_checked(r)),
344        Op::Div => try_op!(l, l_s, r, r_s, l.div_checked(r)),
345        Op::Rem => try_op!(l, l_s, r, r_s, {
346            if r.is_zero() {
347                Err(ArrowError::DivideByZero)
348            } else {
349                Ok(l.mod_wrapping(r))
350            }
351        }),
352    };
353    Ok(Arc::new(array))
354}
355
356/// Perform an arithmetic operation on floats
357fn float_op<T: ArrowPrimitiveType>(
358    op: Op,
359    l: &dyn Array,
360    l_s: bool,
361    r: &dyn Array,
362    r_s: bool,
363) -> Result<ArrayRef, ArrowError> {
364    let l = l.as_primitive::<T>();
365    let r = r.as_primitive::<T>();
366    let array: PrimitiveArray<T> = match op {
367        Op::AddWrapping | Op::Add => op!(l, l_s, r, r_s, l.add_wrapping(r)),
368        Op::SubWrapping | Op::Sub => op!(l, l_s, r, r_s, l.sub_wrapping(r)),
369        Op::MulWrapping | Op::Mul => op!(l, l_s, r, r_s, l.mul_wrapping(r)),
370        Op::Div => op!(l, l_s, r, r_s, l.div_wrapping(r)),
371        Op::Rem => op!(l, l_s, r, r_s, l.mod_wrapping(r)),
372    };
373    Ok(Arc::new(array))
374}
375
376/// Arithmetic trait for timestamp arrays
377trait TimestampOp: ArrowTimestampType {
378    type Duration: ArrowPrimitiveType<Native = i64>;
379
380    fn add_year_month(timestamp: i64, delta: i32, tz: Tz) -> Option<i64>;
381    fn add_day_time(timestamp: i64, delta: IntervalDayTime, tz: Tz) -> Option<i64>;
382    fn add_month_day_nano(timestamp: i64, delta: IntervalMonthDayNano, tz: Tz) -> Option<i64>;
383
384    fn sub_year_month(timestamp: i64, delta: i32, tz: Tz) -> Option<i64>;
385    fn sub_day_time(timestamp: i64, delta: IntervalDayTime, tz: Tz) -> Option<i64>;
386    fn sub_month_day_nano(timestamp: i64, delta: IntervalMonthDayNano, tz: Tz) -> Option<i64>;
387}
388
389macro_rules! timestamp {
390    ($t:ty, $d:ty) => {
391        impl TimestampOp for $t {
392            type Duration = $d;
393
394            fn add_year_month(left: i64, right: i32, tz: Tz) -> Option<i64> {
395                Self::add_year_months(left, right, tz)
396            }
397
398            fn add_day_time(left: i64, right: IntervalDayTime, tz: Tz) -> Option<i64> {
399                Self::add_day_time(left, right, tz)
400            }
401
402            fn add_month_day_nano(left: i64, right: IntervalMonthDayNano, tz: Tz) -> Option<i64> {
403                Self::add_month_day_nano(left, right, tz)
404            }
405
406            fn sub_year_month(left: i64, right: i32, tz: Tz) -> Option<i64> {
407                Self::subtract_year_months(left, right, tz)
408            }
409
410            fn sub_day_time(left: i64, right: IntervalDayTime, tz: Tz) -> Option<i64> {
411                Self::subtract_day_time(left, right, tz)
412            }
413
414            fn sub_month_day_nano(left: i64, right: IntervalMonthDayNano, tz: Tz) -> Option<i64> {
415                Self::subtract_month_day_nano(left, right, tz)
416            }
417        }
418    };
419}
420timestamp!(TimestampSecondType, DurationSecondType);
421timestamp!(TimestampMillisecondType, DurationMillisecondType);
422timestamp!(TimestampMicrosecondType, DurationMicrosecondType);
423timestamp!(TimestampNanosecondType, DurationNanosecondType);
424
425/// Perform arithmetic operation on a timestamp array
426fn timestamp_op<T: TimestampOp>(
427    op: Op,
428    l: &dyn Array,
429    l_s: bool,
430    r: &dyn Array,
431    r_s: bool,
432) -> Result<ArrayRef, ArrowError> {
433    use DataType::*;
434    use IntervalUnit::*;
435
436    let l = l.as_primitive::<T>();
437    let l_tz: Tz = l.timezone().unwrap_or("+00:00").parse()?;
438
439    let array: PrimitiveArray<T> = match (op, r.data_type()) {
440        (Op::Sub | Op::SubWrapping, Timestamp(unit, _)) if unit == &T::UNIT => {
441            let r = r.as_primitive::<T>();
442            return Ok(try_op_ref!(T::Duration, l, l_s, r, r_s, l.sub_checked(r)));
443        }
444
445        (Op::Add | Op::AddWrapping, Duration(unit)) if unit == &T::UNIT => {
446            let r = r.as_primitive::<T::Duration>();
447            try_op!(l, l_s, r, r_s, l.add_checked(r))
448        }
449        (Op::Sub | Op::SubWrapping, Duration(unit)) if unit == &T::UNIT => {
450            let r = r.as_primitive::<T::Duration>();
451            try_op!(l, l_s, r, r_s, l.sub_checked(r))
452        }
453
454        (Op::Add | Op::AddWrapping, Interval(YearMonth)) => {
455            let r = r.as_primitive::<IntervalYearMonthType>();
456            try_op!(
457                l,
458                l_s,
459                r,
460                r_s,
461                T::add_year_month(l, r, l_tz).ok_or(ArrowError::ComputeError(
462                    "Timestamp out of range".to_string()
463                ))
464            )
465        }
466        (Op::Sub | Op::SubWrapping, Interval(YearMonth)) => {
467            let r = r.as_primitive::<IntervalYearMonthType>();
468            try_op!(
469                l,
470                l_s,
471                r,
472                r_s,
473                T::sub_year_month(l, r, l_tz).ok_or(ArrowError::ComputeError(
474                    "Timestamp out of range".to_string()
475                ))
476            )
477        }
478
479        (Op::Add | Op::AddWrapping, Interval(DayTime)) => {
480            let r = r.as_primitive::<IntervalDayTimeType>();
481            try_op!(
482                l,
483                l_s,
484                r,
485                r_s,
486                T::add_day_time(l, r, l_tz).ok_or(ArrowError::ComputeError(
487                    "Timestamp out of range".to_string()
488                ))
489            )
490        }
491        (Op::Sub | Op::SubWrapping, Interval(DayTime)) => {
492            let r = r.as_primitive::<IntervalDayTimeType>();
493            try_op!(
494                l,
495                l_s,
496                r,
497                r_s,
498                T::sub_day_time(l, r, l_tz).ok_or(ArrowError::ComputeError(
499                    "Timestamp out of range".to_string()
500                ))
501            )
502        }
503
504        (Op::Add | Op::AddWrapping, Interval(MonthDayNano)) => {
505            let r = r.as_primitive::<IntervalMonthDayNanoType>();
506            try_op!(
507                l,
508                l_s,
509                r,
510                r_s,
511                T::add_month_day_nano(l, r, l_tz).ok_or(ArrowError::ComputeError(
512                    "Timestamp out of range".to_string()
513                ))
514            )
515        }
516        (Op::Sub | Op::SubWrapping, Interval(MonthDayNano)) => {
517            let r = r.as_primitive::<IntervalMonthDayNanoType>();
518            try_op!(
519                l,
520                l_s,
521                r,
522                r_s,
523                T::sub_month_day_nano(l, r, l_tz).ok_or(ArrowError::ComputeError(
524                    "Timestamp out of range".to_string()
525                ))
526            )
527        }
528        _ => {
529            return Err(ArrowError::InvalidArgumentError(format!(
530                "Invalid timestamp arithmetic operation: {} {op} {}",
531                l.data_type(),
532                r.data_type()
533            )));
534        }
535    };
536    Ok(Arc::new(array.with_timezone_opt(l.timezone())))
537}
538
539/// Arithmetic trait for date arrays
540trait DateOp: ArrowTemporalType {
541    fn add_year_month(timestamp: Self::Native, delta: i32) -> Result<Self::Native, ArrowError>;
542    fn add_day_time(
543        timestamp: Self::Native,
544        delta: IntervalDayTime,
545    ) -> Result<Self::Native, ArrowError>;
546    fn add_month_day_nano(
547        timestamp: Self::Native,
548        delta: IntervalMonthDayNano,
549    ) -> Result<Self::Native, ArrowError>;
550
551    fn sub_year_month(timestamp: Self::Native, delta: i32) -> Result<Self::Native, ArrowError>;
552    fn sub_day_time(
553        timestamp: Self::Native,
554        delta: IntervalDayTime,
555    ) -> Result<Self::Native, ArrowError>;
556    fn sub_month_day_nano(
557        timestamp: Self::Native,
558        delta: IntervalMonthDayNano,
559    ) -> Result<Self::Native, ArrowError>;
560}
561
562macro_rules! date {
563    ($t:ty) => {
564        impl DateOp for $t {
565            fn add_year_month(left: Self::Native, right: i32) -> Result<Self::Native, ArrowError> {
566                Self::add_year_months_opt(left, right).ok_or_else(|| {
567                    ArrowError::ComputeError(format!(
568                        "Date arithmetic overflow: {left} + {right} months"
569                    ))
570                })
571            }
572
573            fn add_day_time(
574                left: Self::Native,
575                right: IntervalDayTime,
576            ) -> Result<Self::Native, ArrowError> {
577                Self::add_day_time_opt(left, right).ok_or_else(|| {
578                    ArrowError::ComputeError(format!(
579                        "Date arithmetic overflow: {left} + {right:?}"
580                    ))
581                })
582            }
583
584            fn add_month_day_nano(
585                left: Self::Native,
586                right: IntervalMonthDayNano,
587            ) -> Result<Self::Native, ArrowError> {
588                Self::add_month_day_nano_opt(left, right).ok_or_else(|| {
589                    ArrowError::ComputeError(format!(
590                        "Date arithmetic overflow: {left} + {right:?}"
591                    ))
592                })
593            }
594
595            fn sub_year_month(left: Self::Native, right: i32) -> Result<Self::Native, ArrowError> {
596                Self::subtract_year_months_opt(left, right).ok_or_else(|| {
597                    ArrowError::ComputeError(format!(
598                        "Date arithmetic overflow: {left} - {right} months"
599                    ))
600                })
601            }
602
603            fn sub_day_time(
604                left: Self::Native,
605                right: IntervalDayTime,
606            ) -> Result<Self::Native, ArrowError> {
607                Self::subtract_day_time_opt(left, right).ok_or_else(|| {
608                    ArrowError::ComputeError(format!(
609                        "Date arithmetic overflow: {left} - {right:?}"
610                    ))
611                })
612            }
613
614            fn sub_month_day_nano(
615                left: Self::Native,
616                right: IntervalMonthDayNano,
617            ) -> Result<Self::Native, ArrowError> {
618                Self::subtract_month_day_nano_opt(left, right).ok_or_else(|| {
619                    ArrowError::ComputeError(format!(
620                        "Date arithmetic overflow: {left} - {right:?}"
621                    ))
622                })
623            }
624        }
625    };
626}
627
628date!(Date32Type);
629date!(Date64Type);
630
631/// Arithmetic trait for interval arrays
632trait IntervalOp: ArrowPrimitiveType {
633    fn add(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError>;
634    fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError>;
635    fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError>;
636}
637
638fn mul_i32_i64(left: i32, right: i64) -> Result<i32, ArrowError> {
639    let value = i64::from(left).mul_checked(right)?;
640    i32::try_from(value).map_err(|_| {
641        ArrowError::ArithmeticOverflow(format!("Overflow happened on: {left} * {right}"))
642    })
643}
644
645impl IntervalOp for IntervalYearMonthType {
646    fn add(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
647        left.add_checked(right)
648    }
649
650    fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
651        left.sub_checked(right)
652    }
653
654    fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
655        mul_i32_i64(left, right)
656    }
657}
658
659impl IntervalOp for IntervalDayTimeType {
660    fn add(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
661        let (l_days, l_ms) = Self::to_parts(left);
662        let (r_days, r_ms) = Self::to_parts(right);
663        let days = l_days.add_checked(r_days)?;
664        let ms = l_ms.add_checked(r_ms)?;
665        Ok(Self::make_value(days, ms))
666    }
667
668    fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
669        let (l_days, l_ms) = Self::to_parts(left);
670        let (r_days, r_ms) = Self::to_parts(right);
671        let days = l_days.sub_checked(r_days)?;
672        let ms = l_ms.sub_checked(r_ms)?;
673        Ok(Self::make_value(days, ms))
674    }
675
676    fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
677        let (days, ms) = Self::to_parts(left);
678        Ok(Self::make_value(
679            mul_i32_i64(days, right)?,
680            mul_i32_i64(ms, right)?,
681        ))
682    }
683}
684
685impl IntervalOp for IntervalMonthDayNanoType {
686    fn add(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
687        let (l_months, l_days, l_nanos) = Self::to_parts(left);
688        let (r_months, r_days, r_nanos) = Self::to_parts(right);
689        let months = l_months.add_checked(r_months)?;
690        let days = l_days.add_checked(r_days)?;
691        let nanos = l_nanos.add_checked(r_nanos)?;
692        Ok(Self::make_value(months, days, nanos))
693    }
694
695    fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
696        let (l_months, l_days, l_nanos) = Self::to_parts(left);
697        let (r_months, r_days, r_nanos) = Self::to_parts(right);
698        let months = l_months.sub_checked(r_months)?;
699        let days = l_days.sub_checked(r_days)?;
700        let nanos = l_nanos.sub_checked(r_nanos)?;
701        Ok(Self::make_value(months, days, nanos))
702    }
703
704    fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
705        let (months, days, nanos) = Self::to_parts(left);
706        Ok(Self::make_value(
707            mul_i32_i64(months, right)?,
708            mul_i32_i64(days, right)?,
709            nanos.mul_checked(right)?,
710        ))
711    }
712}
713
714fn interval_mul_op<T: IntervalOp>(
715    interval: &dyn Array,
716    interval_scalar: bool,
717    factor: &dyn Array,
718    factor_scalar: bool,
719) -> Result<ArrayRef, ArrowError> {
720    let interval = interval.as_primitive::<T>();
721    let factor = factor.as_primitive::<Int64Type>();
722    Ok(try_op_ref!(
723        T,
724        interval,
725        interval_scalar,
726        factor,
727        factor_scalar,
728        T::mul_i64(interval, factor)
729    ))
730}
731
732/// Multiplies an `IntervalMonthDayNano` by an `f64`, mirroring DuckDB's
733/// `interval_t` layout of months, days, and a sub-day component (nanoseconds in
734/// Arrow, microseconds in DuckDB).
735/// <https://github.com/duckdb/duckdb/blob/21aca0424f1faf78b593b1e6fbfdd4846624c987/src/include/duckdb/common/types/interval.hpp#L24-L27>
736///
737/// Algorithm:
738///
739/// 1. use checked integer multiplication when `factor` fits in `i64` (early return).
740/// 2. multiply months and days separately
741/// 3. cascade remainders: convert fractional months to days using 30
742///    days per month, then fractional days to a sub-day value using 24 hours
743///    per day.
744/// 4. combine the cascaded remainder with the scaled input nanoseconds and round ties-to-even at nanosecond precision.
745/// 5. return an overflow error if any output component is out of
746///    range.
747fn interval_mul_f64(
748    interval: IntervalMonthDayNano,
749    factor: f64,
750) -> Result<IntervalMonthDayNano, ArrowError> {
751    const DAYS_PER_MONTH: f64 = 30.;
752    const NANOS_PER_SECOND: f64 = NANOSECONDS as f64;
753    const SECONDS_PER_DAY: f64 = SECONDS_IN_DAY as f64;
754
755    // Keep integral factors exact instead of round-tripping i64 nanoseconds through f64.
756    if factor.fract() == 0.
757        && let Some(factor) = ToPrimitive::to_i64(&factor)
758    {
759        return IntervalMonthDayNanoType::mul_i64(interval, factor);
760    }
761
762    // Based on DuckDB's INTERVAL * DOUBLE implementation, which is referenced from PostgreSQL's interval_mul:
763    // https://github.com/duckdb/duckdb/blob/21aca0424f1faf78b593b1e6fbfdd4846624c987/src/function/scalar/operator/multiply.cpp#L48-L123
764    // PostgreSQL's interval_mul:
765    // https://github.com/postgres/postgres/blob/78758d37306cd89ab060f00cb06f249018d5b8da/src/backend/utils/adt/timestamp.c#L3627-L3744
766    let overflow =
767        |component| ArrowError::ArithmeticOverflow(format!("Overflow in interval {component}"));
768    let timestamp_round =
769        |value: f64| (value * NANOS_PER_SECOND).round_ties_even() / NANOS_PER_SECOND;
770
771    let months_product = f64::from(interval.months) * factor;
772    if !months_product.is_finite()
773        || months_product < f64::from(i32::MIN)
774        || months_product > f64::from(i32::MAX)
775    {
776        return Err(overflow("months"));
777    }
778    let months = months_product.to_i32().ok_or_else(|| overflow("months"))?;
779
780    let days_product = f64::from(interval.days) * factor;
781    if !days_product.is_finite()
782        || days_product < f64::from(i32::MIN)
783        || days_product > f64::from(i32::MAX)
784    {
785        return Err(overflow("days"));
786    }
787    let mut days = days_product.to_i32().ok_or_else(|| overflow("days"))?;
788
789    let month_remainder = timestamp_round(months_product.fract() * DAYS_PER_MONTH);
790    let month_remainder_days = month_remainder
791        .to_i32()
792        .ok_or_else(|| overflow("month remainder"))?;
793    let mut seconds_remainder =
794        timestamp_round((days_product.fract() + month_remainder.fract()) * SECONDS_PER_DAY);
795
796    if seconds_remainder.abs() >= SECONDS_PER_DAY {
797        let remainder_days = (seconds_remainder / SECONDS_PER_DAY)
798            .to_i32()
799            .ok_or_else(|| overflow("day remainder"))?;
800        days = days
801            .checked_add(remainder_days)
802            .ok_or_else(|| overflow("days"))?;
803        seconds_remainder -= f64::from(remainder_days) * SECONDS_PER_DAY;
804    }
805    days = days
806        .checked_add(month_remainder_days)
807        .ok_or_else(|| overflow("days"))?;
808
809    let nanoseconds = ((interval.nanoseconds as f64) * factor
810        + seconds_remainder * NANOS_PER_SECOND)
811        .round_ties_even();
812    let nanoseconds = ToPrimitive::to_i64(&nanoseconds).ok_or_else(|| {
813        ArrowError::ArithmeticOverflow(format!("Overflow in interval nanoseconds: {nanoseconds}"))
814    })?;
815
816    Ok(IntervalMonthDayNano::new(months, days, nanoseconds))
817}
818
819fn interval_f64_op(
820    op: Op,
821    interval: &dyn Array,
822    interval_scalar: bool,
823    factor: &dyn Array,
824    factor_scalar: bool,
825) -> Result<ArrayRef, ArrowError> {
826    let interval = interval.as_primitive::<IntervalMonthDayNanoType>();
827    let factor = factor.as_primitive::<Float64Type>();
828    Ok(try_op_ref!(
829        IntervalMonthDayNanoType,
830        interval,
831        interval_scalar,
832        factor,
833        factor_scalar,
834        {
835            match op {
836                Op::Mul => interval_mul_f64(interval, factor),
837                Op::Div if factor == 0. => Err(ArrowError::DivideByZero),
838                // DuckDB defines interval division as multiplication by the reciprocal:
839                // https://github.com/duckdb/duckdb/blob/21aca0424f1faf78b593b1e6fbfdd4846624c987/src/function/scalar/operator/arithmetic.cpp#L1102-L1110
840                Op::Div => interval_mul_f64(interval, 1. / factor),
841                _ => Err(ArrowError::InvalidArgumentError(format!(
842                    "Invalid interval arithmetic operation: Interval(MonthDayNano) {op} Float64"
843                ))),
844            }
845        }
846    ))
847}
848
849/// Perform arithmetic operation on an interval array
850fn interval_op<T: IntervalOp>(
851    op: Op,
852    l: &dyn Array,
853    l_s: bool,
854    r: &dyn Array,
855    r_s: bool,
856) -> Result<ArrayRef, ArrowError> {
857    match (op, r.data_type()) {
858        (Op::Add | Op::AddWrapping, data_type) if data_type == l.data_type() => {
859            let l = l.as_primitive::<T>();
860            let r = r.as_primitive::<T>();
861            Ok(try_op_ref!(T, l, l_s, r, r_s, T::add(l, r)))
862        }
863        (Op::Sub | Op::SubWrapping, data_type) if data_type == l.data_type() => {
864            let l = l.as_primitive::<T>();
865            let r = r.as_primitive::<T>();
866            Ok(try_op_ref!(T, l, l_s, r, r_s, T::sub(l, r)))
867        }
868        (Op::Mul, DataType::Int64) => interval_mul_op::<T>(l, l_s, r, r_s),
869        _ => Err(ArrowError::InvalidArgumentError(format!(
870            "Invalid interval arithmetic operation: {} {op} {}",
871            l.data_type(),
872            r.data_type()
873        ))),
874    }
875}
876
877fn duration_op<T: ArrowPrimitiveType>(
878    op: Op,
879    l: &dyn Array,
880    l_s: bool,
881    r: &dyn Array,
882    r_s: bool,
883) -> Result<ArrayRef, ArrowError> {
884    let l = l.as_primitive::<T>();
885    let r = r.as_primitive::<T>();
886    match op {
887        Op::Add | Op::AddWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, l.add_checked(r))),
888        Op::Sub | Op::SubWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, l.sub_checked(r))),
889        _ => Err(ArrowError::InvalidArgumentError(format!(
890            "Invalid duration arithmetic operation: {} {op} {}",
891            l.data_type(),
892            r.data_type()
893        ))),
894    }
895}
896
897/// Perform arithmetic operation on a date array
898fn date_op<T: DateOp>(
899    op: Op,
900    l: &dyn Array,
901    l_s: bool,
902    r: &dyn Array,
903    r_s: bool,
904) -> Result<ArrayRef, ArrowError> {
905    use DataType::*;
906    use IntervalUnit::*;
907
908    const NUM_SECONDS_IN_DAY: i64 = 60 * 60 * 24;
909
910    let r_t = r.data_type();
911    match (T::DATA_TYPE, op, r_t) {
912        (Date32, Op::Sub | Op::SubWrapping, Date32) => {
913            let l = l.as_primitive::<Date32Type>();
914            let r = r.as_primitive::<Date32Type>();
915            return Ok(op_ref!(
916                DurationSecondType,
917                l,
918                l_s,
919                r,
920                r_s,
921                ((l as i64) - (r as i64)) * NUM_SECONDS_IN_DAY
922            ));
923        }
924        (Date64, Op::Sub | Op::SubWrapping, Date64) => {
925            let l = l.as_primitive::<Date64Type>();
926            let r = r.as_primitive::<Date64Type>();
927            let result = try_op_ref!(DurationMillisecondType, l, l_s, r, r_s, l.sub_checked(r));
928            return Ok(result);
929        }
930        _ => {}
931    }
932
933    let l = l.as_primitive::<T>();
934    match (op, r_t) {
935        (Op::Add | Op::AddWrapping, Interval(YearMonth)) => {
936            let r = r.as_primitive::<IntervalYearMonthType>();
937            Ok(try_op_ref!(T, l, l_s, r, r_s, T::add_year_month(l, r)))
938        }
939        (Op::Sub | Op::SubWrapping, Interval(YearMonth)) => {
940            let r = r.as_primitive::<IntervalYearMonthType>();
941            Ok(try_op_ref!(T, l, l_s, r, r_s, T::sub_year_month(l, r)))
942        }
943
944        (Op::Add | Op::AddWrapping, Interval(DayTime)) => {
945            let r = r.as_primitive::<IntervalDayTimeType>();
946            Ok(try_op_ref!(T, l, l_s, r, r_s, T::add_day_time(l, r)))
947        }
948        (Op::Sub | Op::SubWrapping, Interval(DayTime)) => {
949            let r = r.as_primitive::<IntervalDayTimeType>();
950            Ok(try_op_ref!(T, l, l_s, r, r_s, T::sub_day_time(l, r)))
951        }
952
953        (Op::Add | Op::AddWrapping, Interval(MonthDayNano)) => {
954            let r = r.as_primitive::<IntervalMonthDayNanoType>();
955            Ok(try_op_ref!(T, l, l_s, r, r_s, T::add_month_day_nano(l, r)))
956        }
957        (Op::Sub | Op::SubWrapping, Interval(MonthDayNano)) => {
958            let r = r.as_primitive::<IntervalMonthDayNanoType>();
959            Ok(try_op_ref!(T, l, l_s, r, r_s, T::sub_month_day_nano(l, r)))
960        }
961
962        _ => Err(ArrowError::InvalidArgumentError(format!(
963            "Invalid date arithmetic operation: {} {op} {}",
964            l.data_type(),
965            r.data_type()
966        ))),
967    }
968}
969
970/// Perform arithmetic operation on decimal arrays
971fn decimal_op<T: DecimalType>(
972    op: Op,
973    l: &dyn Array,
974    l_s: bool,
975    r: &dyn Array,
976    r_s: bool,
977) -> Result<ArrayRef, ArrowError> {
978    let l = l.as_primitive::<T>();
979    let r = r.as_primitive::<T>();
980
981    let (p1, s1, p2, s2) = match (l.data_type(), r.data_type()) {
982        (DataType::Decimal32(p1, s1), DataType::Decimal32(p2, s2)) => (p1, s1, p2, s2),
983        (DataType::Decimal64(p1, s1), DataType::Decimal64(p2, s2)) => (p1, s1, p2, s2),
984        (DataType::Decimal128(p1, s1), DataType::Decimal128(p2, s2)) => (p1, s1, p2, s2),
985        (DataType::Decimal256(p1, s1), DataType::Decimal256(p2, s2)) => (p1, s1, p2, s2),
986        _ => unreachable!(),
987    };
988
989    // Follow the Hive decimal arithmetic rules
990    // https://cwiki.apache.org/confluence/download/attachments/27362075/Hive_Decimal_Precision_Scale_Support.pdf
991    let array: PrimitiveArray<T> = match op {
992        Op::Add | Op::AddWrapping | Op::Sub | Op::SubWrapping => {
993            // max(s1, s2)
994            let result_scale = *s1.max(s2);
995
996            // max(s1, s2) + max(p1-s1, p2-s2) + 1
997            let result_precision =
998                (result_scale.saturating_add((*p1 as i8 - s1).max(*p2 as i8 - s2)) as u8)
999                    .saturating_add(1)
1000                    .min(T::MAX_PRECISION);
1001
1002            let l_mul = T::Native::usize_as(10).pow_checked((result_scale - s1) as _)?;
1003            let r_mul = T::Native::usize_as(10).pow_checked((result_scale - s2) as _)?;
1004
1005            match op {
1006                // Equal scales make both decimal multipliers one.
1007                Op::Add | Op::AddWrapping if s1 == s2 => {
1008                    try_op!(l, l_s, r, r_s, l.add_checked(r))
1009                }
1010                Op::Sub | Op::SubWrapping if s1 == s2 => {
1011                    try_op!(l, l_s, r, r_s, l.sub_checked(r))
1012                }
1013                Op::Add | Op::AddWrapping => {
1014                    try_op!(
1015                        l,
1016                        l_s,
1017                        r,
1018                        r_s,
1019                        l.mul_checked(l_mul)?.add_checked(r.mul_checked(r_mul)?)
1020                    )
1021                }
1022                Op::Sub | Op::SubWrapping => {
1023                    try_op!(
1024                        l,
1025                        l_s,
1026                        r,
1027                        r_s,
1028                        l.mul_checked(l_mul)?.sub_checked(r.mul_checked(r_mul)?)
1029                    )
1030                }
1031                _ => unreachable!(),
1032            }
1033            .with_precision_and_scale(result_precision, result_scale)?
1034        }
1035        Op::Mul | Op::MulWrapping => {
1036            let result_precision = p1.saturating_add(p2 + 1).min(T::MAX_PRECISION);
1037            let result_scale = s1.saturating_add(*s2);
1038            if result_scale > T::MAX_SCALE {
1039                // SQL standard says that if the resulting scale of a multiply operation goes
1040                // beyond the maximum, rounding is not acceptable and thus an error occurs
1041                return Err(ArrowError::InvalidArgumentError(format!(
1042                    "Output scale of {} {op} {} would exceed max scale of {}",
1043                    l.data_type(),
1044                    r.data_type(),
1045                    T::MAX_SCALE
1046                )));
1047            }
1048
1049            try_op!(l, l_s, r, r_s, l.mul_checked(r))
1050                .with_precision_and_scale(result_precision, result_scale)?
1051        }
1052
1053        Op::Div => {
1054            // Follow postgres and MySQL adding a fixed scale increment of 4
1055            // s1 + 4
1056            let result_scale = s1.saturating_add(4).min(T::MAX_SCALE);
1057            let mul_pow = result_scale - s1 + s2;
1058
1059            // p1 - s1 + s2 + result_scale
1060            let result_precision = (mul_pow.saturating_add(*p1 as i8) as u8).min(T::MAX_PRECISION);
1061
1062            let (l_mul, r_mul) = match mul_pow.cmp(&0) {
1063                Ordering::Greater => (
1064                    T::Native::usize_as(10).pow_checked(mul_pow as _)?,
1065                    T::Native::ONE,
1066                ),
1067                Ordering::Equal => (T::Native::ONE, T::Native::ONE),
1068                Ordering::Less => (
1069                    T::Native::ONE,
1070                    T::Native::usize_as(10).pow_checked(mul_pow.neg_wrapping() as _)?,
1071                ),
1072            };
1073
1074            try_op!(
1075                l,
1076                l_s,
1077                r,
1078                r_s,
1079                l.mul_checked(l_mul)?.div_checked(r.mul_checked(r_mul)?)
1080            )
1081            .with_precision_and_scale(result_precision, result_scale)?
1082        }
1083
1084        Op::Rem => {
1085            // max(s1, s2)
1086            let result_scale = *s1.max(s2);
1087            // min(p1-s1, p2 -s2) + max( s1,s2 )
1088            let result_precision =
1089                (result_scale.saturating_add((*p1 as i8 - s1).min(*p2 as i8 - s2)) as u8)
1090                    .min(T::MAX_PRECISION);
1091
1092            let l_mul = T::Native::usize_as(10).pow_wrapping((result_scale - s1) as _);
1093            let r_mul = T::Native::usize_as(10).pow_wrapping((result_scale - s2) as _);
1094
1095            try_op!(
1096                l,
1097                l_s,
1098                r,
1099                r_s,
1100                l.mul_checked(l_mul)?.mod_checked(r.mul_checked(r_mul)?)
1101            )
1102            .with_precision_and_scale(result_precision, result_scale)?
1103        }
1104    };
1105
1106    Ok(Arc::new(array))
1107}
1108
1109#[cfg(test)]
1110mod tests {
1111    use super::*;
1112    use arrow_array::temporal_conversions::{as_date, as_datetime};
1113    use arrow_buffer::{ScalarBuffer, i256};
1114    use chrono::{DateTime, NaiveDate};
1115
1116    // The valid date range of NaiveDate is from January 1, -262143 to December 31, 262142 (Gregorian calendar).
1117    const MAX_VALID_DATE: NaiveDate = NaiveDate::from_ymd_opt(262142, 12, 31).unwrap();
1118    const MIN_VALID_DATE: NaiveDate = NaiveDate::from_ymd_opt(-262143, 1, 1).unwrap();
1119    const MAX_VALID_MILLIS: i64 = date_to_millis(MAX_VALID_DATE);
1120    const MIN_VALID_MILLIS: i64 = date_to_millis(MIN_VALID_DATE);
1121    const MAX_VALID_DAYS: i32 = date_to_days(MAX_VALID_DATE);
1122    const MIN_VALID_DAYS: i32 = date_to_days(MIN_VALID_DATE);
1123    const EPOCH: NaiveDate = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
1124    const YEAR_2000: NaiveDate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
1125
1126    const fn date_to_millis(date: NaiveDate) -> i64 {
1127        date.signed_duration_since(EPOCH).num_milliseconds()
1128    }
1129
1130    const fn date_to_days(date: NaiveDate) -> i32 {
1131        date.signed_duration_since(EPOCH).num_days() as i32
1132    }
1133
1134    fn test_neg_primitive<T: ArrowPrimitiveType>(
1135        input: &[T::Native],
1136        out: Result<&[T::Native], &str>,
1137    ) {
1138        let a = PrimitiveArray::<T>::new(ScalarBuffer::from(input.to_vec()), None);
1139        match out {
1140            Ok(expected) => {
1141                let result = neg(&a).unwrap();
1142                assert_eq!(result.as_primitive::<T>().values(), expected);
1143            }
1144            Err(e) => {
1145                let err = neg(&a).unwrap_err().to_string();
1146                assert_eq!(e, err);
1147            }
1148        }
1149    }
1150
1151    #[test]
1152    fn test_neg() {
1153        let input = &[1, -5, 2, 693, 3929];
1154        let output = &[-1, 5, -2, -693, -3929];
1155        test_neg_primitive::<Int32Type>(input, Ok(output));
1156
1157        let input = &[1, -5, 2, 693, 3929];
1158        let output = &[-1, 5, -2, -693, -3929];
1159        test_neg_primitive::<Int64Type>(input, Ok(output));
1160        test_neg_primitive::<DurationSecondType>(input, Ok(output));
1161        test_neg_primitive::<DurationMillisecondType>(input, Ok(output));
1162        test_neg_primitive::<DurationMicrosecondType>(input, Ok(output));
1163        test_neg_primitive::<DurationNanosecondType>(input, Ok(output));
1164
1165        let input = &[f32::MAX, f32::MIN, f32::INFINITY, 1.3, 0.5];
1166        let output = &[f32::MIN, f32::MAX, f32::NEG_INFINITY, -1.3, -0.5];
1167        test_neg_primitive::<Float32Type>(input, Ok(output));
1168
1169        test_neg_primitive::<Int32Type>(
1170            &[i32::MIN],
1171            Err("Arithmetic overflow: Overflow happened on: - -2147483648"),
1172        );
1173        test_neg_primitive::<Int64Type>(
1174            &[i64::MIN],
1175            Err("Arithmetic overflow: Overflow happened on: - -9223372036854775808"),
1176        );
1177        test_neg_primitive::<DurationSecondType>(
1178            &[i64::MIN],
1179            Err("Arithmetic overflow: Overflow happened on: - -9223372036854775808"),
1180        );
1181
1182        let r = neg_wrapping(&Int32Array::from(vec![i32::MIN])).unwrap();
1183        assert_eq!(r.as_primitive::<Int32Type>().value(0), i32::MIN);
1184
1185        let r = neg_wrapping(&Int64Array::from(vec![i64::MIN])).unwrap();
1186        assert_eq!(r.as_primitive::<Int64Type>().value(0), i64::MIN);
1187
1188        let err = neg_wrapping(&DurationSecondArray::from(vec![i64::MIN]))
1189            .unwrap_err()
1190            .to_string();
1191
1192        assert_eq!(
1193            err,
1194            "Arithmetic overflow: Overflow happened on: - -9223372036854775808"
1195        );
1196
1197        let a = Decimal32Array::from(vec![1, 3, -44, 2, 4])
1198            .with_precision_and_scale(9, 6)
1199            .unwrap();
1200
1201        let r = neg(&a).unwrap();
1202        assert_eq!(r.data_type(), a.data_type());
1203        assert_eq!(
1204            r.as_primitive::<Decimal32Type>().values(),
1205            &[-1, -3, 44, -2, -4]
1206        );
1207
1208        let a = Decimal64Array::from(vec![1, 3, -44, 2, 4])
1209            .with_precision_and_scale(9, 6)
1210            .unwrap();
1211
1212        let r = neg(&a).unwrap();
1213        assert_eq!(r.data_type(), a.data_type());
1214        assert_eq!(
1215            r.as_primitive::<Decimal64Type>().values(),
1216            &[-1, -3, 44, -2, -4]
1217        );
1218
1219        let a = Decimal128Array::from(vec![1, 3, -44, 2, 4])
1220            .with_precision_and_scale(9, 6)
1221            .unwrap();
1222
1223        let r = neg(&a).unwrap();
1224        assert_eq!(r.data_type(), a.data_type());
1225        assert_eq!(
1226            r.as_primitive::<Decimal128Type>().values(),
1227            &[-1, -3, 44, -2, -4]
1228        );
1229
1230        let a = Decimal256Array::from(vec![
1231            i256::from_i128(342),
1232            i256::from_i128(-4949),
1233            i256::from_i128(3),
1234        ])
1235        .with_precision_and_scale(9, 6)
1236        .unwrap();
1237
1238        let r = neg(&a).unwrap();
1239        assert_eq!(r.data_type(), a.data_type());
1240        assert_eq!(
1241            r.as_primitive::<Decimal256Type>().values(),
1242            &[
1243                i256::from_i128(-342),
1244                i256::from_i128(4949),
1245                i256::from_i128(-3),
1246            ]
1247        );
1248
1249        let a = IntervalYearMonthArray::from(vec![
1250            IntervalYearMonthType::make_value(2, 4),
1251            IntervalYearMonthType::make_value(2, -4),
1252            IntervalYearMonthType::make_value(-3, -5),
1253        ]);
1254        let r = neg(&a).unwrap();
1255        assert_eq!(
1256            r.as_primitive::<IntervalYearMonthType>().values(),
1257            &[
1258                IntervalYearMonthType::make_value(-2, -4),
1259                IntervalYearMonthType::make_value(-2, 4),
1260                IntervalYearMonthType::make_value(3, 5),
1261            ]
1262        );
1263
1264        let a = IntervalDayTimeArray::from(vec![
1265            IntervalDayTimeType::make_value(2, 4),
1266            IntervalDayTimeType::make_value(2, -4),
1267            IntervalDayTimeType::make_value(-3, -5),
1268        ]);
1269        let r = neg(&a).unwrap();
1270        assert_eq!(
1271            r.as_primitive::<IntervalDayTimeType>().values(),
1272            &[
1273                IntervalDayTimeType::make_value(-2, -4),
1274                IntervalDayTimeType::make_value(-2, 4),
1275                IntervalDayTimeType::make_value(3, 5),
1276            ]
1277        );
1278
1279        let a = IntervalMonthDayNanoArray::from(vec![
1280            IntervalMonthDayNanoType::make_value(2, 4, 5953394),
1281            IntervalMonthDayNanoType::make_value(2, -4, -45839),
1282            IntervalMonthDayNanoType::make_value(-3, -5, 6944),
1283        ]);
1284        let r = neg(&a).unwrap();
1285        assert_eq!(
1286            r.as_primitive::<IntervalMonthDayNanoType>().values(),
1287            &[
1288                IntervalMonthDayNanoType::make_value(-2, -4, -5953394),
1289                IntervalMonthDayNanoType::make_value(-2, 4, 45839),
1290                IntervalMonthDayNanoType::make_value(3, 5, -6944),
1291            ]
1292        );
1293    }
1294
1295    #[test]
1296    fn test_integer() {
1297        let a = Int32Array::from(vec![4, 3, 5, -6, 100]);
1298        let b = Int32Array::from(vec![6, 2, 5, -7, 3]);
1299        let result = add(&a, &b).unwrap();
1300        assert_eq!(
1301            result.as_ref(),
1302            &Int32Array::from(vec![10, 5, 10, -13, 103])
1303        );
1304        let result = sub(&a, &b).unwrap();
1305        assert_eq!(result.as_ref(), &Int32Array::from(vec![-2, 1, 0, 1, 97]));
1306        let result = div(&a, &b).unwrap();
1307        assert_eq!(result.as_ref(), &Int32Array::from(vec![0, 1, 1, 0, 33]));
1308        let result = mul(&a, &b).unwrap();
1309        assert_eq!(result.as_ref(), &Int32Array::from(vec![24, 6, 25, 42, 300]));
1310        let result = rem(&a, &b).unwrap();
1311        assert_eq!(result.as_ref(), &Int32Array::from(vec![4, 1, 0, -6, 1]));
1312
1313        let a = Int8Array::from(vec![Some(2), None, Some(45)]);
1314        let b = Int8Array::from(vec![Some(5), Some(3), None]);
1315        let result = add(&a, &b).unwrap();
1316        assert_eq!(result.as_ref(), &Int8Array::from(vec![Some(7), None, None]));
1317
1318        let a = UInt8Array::from(vec![56, 5, 3]);
1319        let b = UInt8Array::from(vec![200, 2, 5]);
1320        let err = add(&a, &b).unwrap_err().to_string();
1321        assert_eq!(err, "Arithmetic overflow: Overflow happened on: 56 + 200");
1322        let result = add_wrapping(&a, &b).unwrap();
1323        assert_eq!(result.as_ref(), &UInt8Array::from(vec![0, 7, 8]));
1324
1325        let a = UInt8Array::from(vec![34, 5, 3]);
1326        let b = UInt8Array::from(vec![200, 2, 5]);
1327        let err = sub(&a, &b).unwrap_err().to_string();
1328        assert_eq!(err, "Arithmetic overflow: Overflow happened on: 34 - 200");
1329        let result = sub_wrapping(&a, &b).unwrap();
1330        assert_eq!(result.as_ref(), &UInt8Array::from(vec![90, 3, 254]));
1331
1332        let a = UInt8Array::from(vec![34, 5, 3]);
1333        let b = UInt8Array::from(vec![200, 2, 5]);
1334        let err = mul(&a, &b).unwrap_err().to_string();
1335        assert_eq!(err, "Arithmetic overflow: Overflow happened on: 34 * 200");
1336        let result = mul_wrapping(&a, &b).unwrap();
1337        assert_eq!(result.as_ref(), &UInt8Array::from(vec![144, 10, 15]));
1338
1339        let a = Int16Array::from(vec![i16::MIN]);
1340        let b = Int16Array::from(vec![-1]);
1341        let err = div(&a, &b).unwrap_err().to_string();
1342        assert_eq!(
1343            err,
1344            "Arithmetic overflow: Overflow happened on: -32768 / -1"
1345        );
1346
1347        let a = Int16Array::from(vec![i16::MIN]);
1348        let b = Int16Array::from(vec![-1]);
1349        let result = rem(&a, &b).unwrap();
1350        assert_eq!(result.as_ref(), &Int16Array::from(vec![0]));
1351
1352        let a = Int16Array::from(vec![21]);
1353        let b = Int16Array::from(vec![0]);
1354        let err = div(&a, &b).unwrap_err().to_string();
1355        assert_eq!(err, "Divide by zero error");
1356
1357        let a = Int16Array::from(vec![21]);
1358        let b = Int16Array::from(vec![0]);
1359        let err = rem(&a, &b).unwrap_err().to_string();
1360        assert_eq!(err, "Divide by zero error");
1361    }
1362
1363    #[test]
1364    fn test_float() {
1365        let a = Float32Array::from(vec![1., f32::MAX, 6., -4., -1., 0.]);
1366        let b = Float32Array::from(vec![1., f32::MAX, f32::MAX, -3., 45., 0.]);
1367        let result = add(&a, &b).unwrap();
1368        assert_eq!(
1369            result.as_ref(),
1370            &Float32Array::from(vec![2., f32::INFINITY, f32::MAX, -7., 44.0, 0.])
1371        );
1372
1373        let result = sub(&a, &b).unwrap();
1374        assert_eq!(
1375            result.as_ref(),
1376            &Float32Array::from(vec![0., 0., f32::MIN, -1., -46., 0.])
1377        );
1378
1379        let result = mul(&a, &b).unwrap();
1380        assert_eq!(
1381            result.as_ref(),
1382            &Float32Array::from(vec![1., f32::INFINITY, f32::INFINITY, 12., -45., 0.])
1383        );
1384
1385        let result = div(&a, &b).unwrap();
1386        let r = result.as_primitive::<Float32Type>();
1387        assert_eq!(r.value(0), 1.);
1388        assert_eq!(r.value(1), 1.);
1389        assert!(r.value(2) < f32::EPSILON);
1390        assert_eq!(r.value(3), -4. / -3.);
1391        assert!(r.value(5).is_nan());
1392
1393        let result = rem(&a, &b).unwrap();
1394        let r = result.as_primitive::<Float32Type>();
1395        assert_eq!(&r.values()[..5], &[0., 0., 6., -1., -1.]);
1396        assert!(r.value(5).is_nan());
1397    }
1398
1399    #[test]
1400    fn test_decimal() {
1401        // 0.015 7.842 -0.577 0.334 -0.078 0.003
1402        let a = Decimal128Array::from(vec![15, 0, -577, 334, -78, 3])
1403            .with_precision_and_scale(12, 3)
1404            .unwrap();
1405
1406        // 5.4 0 -35.6 0.3 0.6 7.45
1407        let b = Decimal128Array::from(vec![54, 34, -356, 3, 6, 745])
1408            .with_precision_and_scale(12, 1)
1409            .unwrap();
1410
1411        let result = add(&a, &b).unwrap();
1412        assert_eq!(result.data_type(), &DataType::Decimal128(15, 3));
1413        assert_eq!(
1414            result.as_primitive::<Decimal128Type>().values(),
1415            &[5415, 3400, -36177, 634, 522, 74503]
1416        );
1417
1418        let result = sub(&a, &b).unwrap();
1419        assert_eq!(result.data_type(), &DataType::Decimal128(15, 3));
1420        assert_eq!(
1421            result.as_primitive::<Decimal128Type>().values(),
1422            &[-5385, -3400, 35023, 34, -678, -74497]
1423        );
1424
1425        let result = mul(&a, &b).unwrap();
1426        assert_eq!(result.data_type(), &DataType::Decimal128(25, 4));
1427        assert_eq!(
1428            result.as_primitive::<Decimal128Type>().values(),
1429            &[810, 0, 205412, 1002, -468, 2235]
1430        );
1431
1432        let result = div(&a, &b).unwrap();
1433        assert_eq!(result.data_type(), &DataType::Decimal128(17, 7));
1434        assert_eq!(
1435            result.as_primitive::<Decimal128Type>().values(),
1436            &[27777, 0, 162078, 11133333, -1300000, 402]
1437        );
1438
1439        let result = rem(&a, &b).unwrap();
1440        assert_eq!(result.data_type(), &DataType::Decimal128(12, 3));
1441        assert_eq!(
1442            result.as_primitive::<Decimal128Type>().values(),
1443            &[15, 0, -577, 34, -78, 3]
1444        );
1445
1446        let a = Decimal128Array::from(vec![1])
1447            .with_precision_and_scale(3, 3)
1448            .unwrap();
1449        let b = Decimal128Array::from(vec![1])
1450            .with_precision_and_scale(37, 37)
1451            .unwrap();
1452        let err = mul(&a, &b).unwrap_err().to_string();
1453        assert_eq!(
1454            err,
1455            "Invalid argument error: Output scale of Decimal128(3, 3) * Decimal128(37, 37) would exceed max scale of 38"
1456        );
1457
1458        let a = Decimal128Array::from(vec![1])
1459            .with_precision_and_scale(3, -2)
1460            .unwrap();
1461        let err = add(&a, &b).unwrap_err().to_string();
1462        assert_eq!(err, "Arithmetic overflow: Overflow happened on: 10 ^ 39");
1463
1464        let a = Decimal128Array::from(vec![10])
1465            .with_precision_and_scale(3, -1)
1466            .unwrap();
1467        let err = add(&a, &b).unwrap_err().to_string();
1468        assert_eq!(
1469            err,
1470            "Arithmetic overflow: Overflow happened on: 10 * 100000000000000000000000000000000000000"
1471        );
1472
1473        let b = Decimal128Array::from(vec![0])
1474            .with_precision_and_scale(1, 1)
1475            .unwrap();
1476        let err = div(&a, &b).unwrap_err().to_string();
1477        assert_eq!(err, "Divide by zero error");
1478        let err = rem(&a, &b).unwrap_err().to_string();
1479        assert_eq!(err, "Divide by zero error");
1480    }
1481
1482    #[test]
1483    fn test_decimal256_same_scale_add_sub() {
1484        let lhs = Decimal256Array::from(vec![
1485            Some(i256::from_parts(u128::MAX, 0)),
1486            Some(i256::MINUS_ONE),
1487            None,
1488        ])
1489        .with_precision_and_scale(70, 2)
1490        .unwrap();
1491        let rhs = Decimal256Array::from(vec![Some(i256::ONE), Some(i256::ONE), Some(i256::MAX)])
1492            .with_precision_and_scale(70, 2)
1493            .unwrap();
1494
1495        let expected =
1496            Decimal256Array::from(vec![Some(i256::from_parts(0, 1)), Some(i256::ZERO), None])
1497                .with_precision_and_scale(71, 2)
1498                .unwrap();
1499        for operation in [add, add_wrapping] {
1500            let result = operation(&lhs, &rhs).unwrap();
1501            assert_eq!(result.as_primitive::<Decimal256Type>(), &expected);
1502        }
1503
1504        let expected = Decimal256Array::from(vec![
1505            Some(i256::from_parts(u128::MAX - 1, 0)),
1506            Some(i256::from_i128(-2)),
1507            None,
1508        ])
1509        .with_precision_and_scale(71, 2)
1510        .unwrap();
1511        for operation in [sub, sub_wrapping] {
1512            let result = operation(&lhs, &rhs).unwrap();
1513            assert_eq!(result.as_primitive::<Decimal256Type>(), &expected);
1514        }
1515
1516        let lhs = Decimal256Array::from(vec![i256::MAX])
1517            .with_precision_and_scale(76, 0)
1518            .unwrap();
1519        let rhs = Decimal256Array::from(vec![i256::ONE])
1520            .with_precision_and_scale(76, 0)
1521            .unwrap();
1522        for operation in [add, add_wrapping] {
1523            assert_eq!(
1524                operation(&lhs, &rhs).unwrap_err().to_string(),
1525                format!(
1526                    "Arithmetic overflow: Overflow happened on: {:?} + {:?}",
1527                    i256::MAX,
1528                    i256::ONE
1529                )
1530            );
1531        }
1532
1533        let lhs = Decimal256Array::from(vec![i256::MIN])
1534            .with_precision_and_scale(76, 0)
1535            .unwrap();
1536        for operation in [sub, sub_wrapping] {
1537            assert_eq!(
1538                operation(&lhs, &rhs).unwrap_err().to_string(),
1539                format!(
1540                    "Arithmetic overflow: Overflow happened on: {:?} - {:?}",
1541                    i256::MIN,
1542                    i256::ONE
1543                )
1544            );
1545        }
1546    }
1547
1548    fn test_timestamp_impl<T: TimestampOp>() {
1549        let a = PrimitiveArray::<T>::new(vec![2000000, 434030324, 53943340].into(), None);
1550        let b = PrimitiveArray::<T>::new(vec![329593, 59349, 694994].into(), None);
1551
1552        let result = sub(&a, &b).unwrap();
1553        assert_eq!(
1554            result.as_primitive::<T::Duration>().values(),
1555            &[1670407, 433970975, 53248346]
1556        );
1557
1558        let r2 = add(&b, &result.as_ref()).unwrap();
1559        assert_eq!(r2.as_ref(), &a);
1560
1561        let r3 = add(&result.as_ref(), &b).unwrap();
1562        assert_eq!(r3.as_ref(), &a);
1563
1564        let format_array = |x: &dyn Array| -> Vec<String> {
1565            x.as_primitive::<T>()
1566                .values()
1567                .into_iter()
1568                .map(|x| as_datetime::<T>(*x).unwrap().to_string())
1569                .collect()
1570        };
1571
1572        let values = vec![
1573            "1970-01-01T00:00:00Z",
1574            "2010-04-01T04:00:20Z",
1575            "1960-01-30T04:23:20Z",
1576        ]
1577        .into_iter()
1578        .map(|x| {
1579            T::from_naive_datetime(DateTime::parse_from_rfc3339(x).unwrap().naive_utc(), None)
1580                .unwrap()
1581        })
1582        .collect();
1583
1584        let a = PrimitiveArray::<T>::new(values, None);
1585        let b = IntervalYearMonthArray::from(vec![
1586            IntervalYearMonthType::make_value(5, 34),
1587            IntervalYearMonthType::make_value(-2, 4),
1588            IntervalYearMonthType::make_value(7, -4),
1589        ]);
1590        let r4 = add(&a, &b).unwrap();
1591        assert_eq!(
1592            &format_array(r4.as_ref()),
1593            &[
1594                "1977-11-01 00:00:00".to_string(),
1595                "2008-08-01 04:00:20".to_string(),
1596                "1966-09-30 04:23:20".to_string()
1597            ]
1598        );
1599
1600        let r5 = sub(&r4, &b).unwrap();
1601        assert_eq!(r5.as_ref(), &a);
1602
1603        let b = IntervalDayTimeArray::from(vec![
1604            IntervalDayTimeType::make_value(5, 454000),
1605            IntervalDayTimeType::make_value(-34, 0),
1606            IntervalDayTimeType::make_value(7, -4000),
1607        ]);
1608        let r6 = add(&a, &b).unwrap();
1609        assert_eq!(
1610            &format_array(r6.as_ref()),
1611            &[
1612                "1970-01-06 00:07:34".to_string(),
1613                "2010-02-26 04:00:20".to_string(),
1614                "1960-02-06 04:23:16".to_string()
1615            ]
1616        );
1617
1618        let r7 = sub(&r6, &b).unwrap();
1619        assert_eq!(r7.as_ref(), &a);
1620
1621        let b = IntervalMonthDayNanoArray::from(vec![
1622            IntervalMonthDayNanoType::make_value(344, 34, -43_000_000_000),
1623            IntervalMonthDayNanoType::make_value(-593, -33, 13_000_000_000),
1624            IntervalMonthDayNanoType::make_value(5, 2, 493_000_000_000),
1625        ]);
1626        let r8 = add(&a, &b).unwrap();
1627        assert_eq!(
1628            &format_array(r8.as_ref()),
1629            &[
1630                "1998-10-04 23:59:17".to_string(),
1631                "1960-09-29 04:00:33".to_string(),
1632                "1960-07-02 04:31:33".to_string()
1633            ]
1634        );
1635
1636        let r9 = sub(&r8, &b).unwrap();
1637        // Note: subtraction is not the inverse of addition for intervals
1638        assert_eq!(
1639            &format_array(r9.as_ref()),
1640            &[
1641                "1970-01-02 00:00:00".to_string(),
1642                "2010-04-02 04:00:20".to_string(),
1643                "1960-01-31 04:23:20".to_string()
1644            ]
1645        );
1646    }
1647
1648    #[test]
1649    fn test_timestamp() {
1650        test_timestamp_impl::<TimestampSecondType>();
1651        test_timestamp_impl::<TimestampMillisecondType>();
1652        test_timestamp_impl::<TimestampMicrosecondType>();
1653        test_timestamp_impl::<TimestampNanosecondType>();
1654    }
1655
1656    #[test]
1657    fn test_interval() {
1658        let a = IntervalYearMonthArray::from(vec![
1659            IntervalYearMonthType::make_value(32, 4),
1660            IntervalYearMonthType::make_value(32, 4),
1661        ]);
1662        let b = IntervalYearMonthArray::from(vec![
1663            IntervalYearMonthType::make_value(-4, 6),
1664            IntervalYearMonthType::make_value(-3, 23),
1665        ]);
1666        let result = add(&a, &b).unwrap();
1667        assert_eq!(
1668            result.as_ref(),
1669            &IntervalYearMonthArray::from(vec![
1670                IntervalYearMonthType::make_value(28, 10),
1671                IntervalYearMonthType::make_value(29, 27)
1672            ])
1673        );
1674        let result = sub(&a, &b).unwrap();
1675        assert_eq!(
1676            result.as_ref(),
1677            &IntervalYearMonthArray::from(vec![
1678                IntervalYearMonthType::make_value(36, -2),
1679                IntervalYearMonthType::make_value(35, -19)
1680            ])
1681        );
1682
1683        let a = IntervalDayTimeArray::from(vec![
1684            IntervalDayTimeType::make_value(32, 4),
1685            IntervalDayTimeType::make_value(32, 4),
1686        ]);
1687        let b = IntervalDayTimeArray::from(vec![
1688            IntervalDayTimeType::make_value(-4, 6),
1689            IntervalDayTimeType::make_value(-3, 23),
1690        ]);
1691        let result = add(&a, &b).unwrap();
1692        assert_eq!(
1693            result.as_ref(),
1694            &IntervalDayTimeArray::from(vec![
1695                IntervalDayTimeType::make_value(28, 10),
1696                IntervalDayTimeType::make_value(29, 27)
1697            ])
1698        );
1699        let result = sub(&a, &b).unwrap();
1700        assert_eq!(
1701            result.as_ref(),
1702            &IntervalDayTimeArray::from(vec![
1703                IntervalDayTimeType::make_value(36, -2),
1704                IntervalDayTimeType::make_value(35, -19)
1705            ])
1706        );
1707        let a = IntervalMonthDayNanoArray::from(vec![
1708            IntervalMonthDayNanoType::make_value(32, 4, 4000000000000),
1709            IntervalMonthDayNanoType::make_value(32, 4, 45463000000000000),
1710        ]);
1711        let b = IntervalMonthDayNanoArray::from(vec![
1712            IntervalMonthDayNanoType::make_value(-4, 6, 46000000000000),
1713            IntervalMonthDayNanoType::make_value(-3, 23, 3564000000000000),
1714        ]);
1715        let result = add(&a, &b).unwrap();
1716        assert_eq!(
1717            result.as_ref(),
1718            &IntervalMonthDayNanoArray::from(vec![
1719                IntervalMonthDayNanoType::make_value(28, 10, 50000000000000),
1720                IntervalMonthDayNanoType::make_value(29, 27, 49027000000000000)
1721            ])
1722        );
1723        let result = sub(&a, &b).unwrap();
1724        assert_eq!(
1725            result.as_ref(),
1726            &IntervalMonthDayNanoArray::from(vec![
1727                IntervalMonthDayNanoType::make_value(36, -2, -42000000000000),
1728                IntervalMonthDayNanoType::make_value(35, -19, 41899000000000000)
1729            ])
1730        );
1731        let a = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNano::MAX]);
1732        let b = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNano::ONE]);
1733        let err = add(&a, &b).unwrap_err().to_string();
1734        assert_eq!(
1735            err,
1736            "Arithmetic overflow: Overflow happened on: 2147483647 + 1"
1737        );
1738    }
1739
1740    #[test]
1741    fn test_interval_mul_i64() {
1742        let interval = IntervalYearMonthArray::from(vec![16, 5, 0]);
1743        let factor = Int64Array::from(vec![3, -2, i64::MAX]);
1744        let expected = IntervalYearMonthArray::from(vec![48, -10, 0]);
1745        assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1746        assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1747
1748        let interval = IntervalDayTimeArray::from(vec![
1749            Some(IntervalDayTimeType::make_value(10, 2 * 60 * 60 * 1000)),
1750            None,
1751        ]);
1752        let factor = Int64Array::new_scalar(3);
1753        let expected = IntervalDayTimeArray::from(vec![
1754            Some(IntervalDayTimeType::make_value(30, 6 * 60 * 60 * 1000)),
1755            None,
1756        ]);
1757        assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1758        assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1759
1760        let null_factor = Scalar::new(Int64Array::new_null(1));
1761        let expected = IntervalDayTimeArray::new_null(interval.len());
1762        assert_eq!(mul(&interval, &null_factor).unwrap().as_ref(), &expected);
1763        assert_eq!(mul(&null_factor, &interval).unwrap().as_ref(), &expected);
1764
1765        let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1766            12,
1767            15,
1768            5_000_000_000,
1769        ));
1770        let factor = Int64Array::from(vec![2, 0, -1]);
1771        let expected = IntervalMonthDayNanoArray::from(vec![
1772            IntervalMonthDayNanoType::make_value(24, 30, 10_000_000_000),
1773            IntervalMonthDayNanoType::make_value(0, 0, 0),
1774            IntervalMonthDayNanoType::make_value(-12, -15, -5_000_000_000),
1775        ]);
1776        assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1777        assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1778
1779        let float_factor = Float64Array::new_scalar(2.);
1780        assert!(mul_wrapping(&float_factor, &interval).is_err());
1781        assert!(mul_wrapping(&factor, &interval).is_err());
1782    }
1783
1784    #[test]
1785    fn test_interval_mul_div_f64() {
1786        const HOUR_NANOS: i64 = 3_600_000_000_000;
1787        const MINUTE_NANOS: i64 = 60_000_000_000;
1788
1789        // Adapted from DuckDB's interval multiplication tests:
1790        // https://github.com/duckdb/duckdb/blob/21aca0424f1faf78b593b1e6fbfdd4846624c987/test/sql/function/interval/test_interval_muldiv.test#L1-L99
1791        // DuckDB's cases come from PostgreSQL's interval regression tests:
1792        // https://github.com/postgres/postgres/blob/78758d37306cd89ab060f00cb06f249018d5b8da/src/test/regress/sql/interval.sql#L118-L164
1793        let interval = IntervalMonthDayNanoArray::from(vec![
1794            IntervalMonthDayNanoType::make_value(41, 12, 360 * HOUR_NANOS),
1795            IntervalMonthDayNanoType::make_value(-41, -12, 360 * HOUR_NANOS),
1796            IntervalMonthDayNanoType::make_value(1, 1, 0),
1797            IntervalMonthDayNanoType::make_value(0, 0, 1),
1798            IntervalMonthDayNanoType::make_value(0, 0, 3),
1799            IntervalMonthDayNanoType::make_value(0, 0, -1),
1800            IntervalMonthDayNanoType::make_value(0, 0, -3),
1801        ]);
1802        let factor = Float64Array::from(vec![0.3, 0.3, 1.5, 0.5, 0.5, 0.5, 0.5]);
1803        let expected = IntervalMonthDayNanoArray::from(vec![
1804            IntervalMonthDayNanoType::make_value(12, 12, 122 * HOUR_NANOS + 24 * MINUTE_NANOS),
1805            IntervalMonthDayNanoType::make_value(-12, -12, 93 * HOUR_NANOS + 36 * MINUTE_NANOS),
1806            IntervalMonthDayNanoType::make_value(1, 16, 12 * HOUR_NANOS),
1807            IntervalMonthDayNanoType::make_value(0, 0, 0),
1808            IntervalMonthDayNanoType::make_value(0, 0, 2),
1809            IntervalMonthDayNanoType::make_value(0, 0, 0),
1810            IntervalMonthDayNanoType::make_value(0, 0, -2),
1811        ]);
1812        assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1813        assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1814
1815        let interval = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNanoType::make_value(
1816            9,
1817            -27,
1818            45_296 * NANOSECONDS,
1819        )]);
1820        let factor = Float64Array::new_scalar(0.3);
1821        let expected = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNanoType::make_value(
1822            2,
1823            13,
1824            4_948_800_000_000,
1825        )]);
1826        assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1827
1828        let interval = IntervalMonthDayNanoArray::from(vec![
1829            IntervalMonthDayNanoType::make_value(0, 1, 0),
1830            IntervalMonthDayNanoType::make_value(4, 0, 0),
1831            IntervalMonthDayNanoType::make_value(1, 1, 0),
1832            IntervalMonthDayNanoType::make_value(0, 0, (1_i64 << 53) - 1),
1833            IntervalMonthDayNanoType::make_value(0, 0, i64::MAX),
1834            IntervalMonthDayNanoType::make_value(1, 1, 1),
1835            IntervalMonthDayNanoType::make_value(1, 1, 1),
1836            IntervalMonthDayNanoType::make_value(1, 0, 0),
1837        ]);
1838        let factor = Float64Array::from(vec![
1839            3.,
1840            5.,
1841            2.,
1842            0.7,
1843            1.,
1844            f64::INFINITY,
1845            f64::NEG_INFINITY,
1846            -2.,
1847        ]);
1848        let expected = IntervalMonthDayNanoArray::from(vec![
1849            IntervalMonthDayNanoType::make_value(0, 0, 8 * HOUR_NANOS),
1850            IntervalMonthDayNanoType::make_value(0, 24, 0),
1851            IntervalMonthDayNanoType::make_value(0, 15, 12 * HOUR_NANOS),
1852            IntervalMonthDayNanoType::make_value(0, 0, 12_867_427_506_772_844),
1853            IntervalMonthDayNanoType::make_value(0, 0, i64::MAX),
1854            IntervalMonthDayNanoType::make_value(0, 0, 0),
1855            IntervalMonthDayNanoType::make_value(0, 0, 0),
1856            IntervalMonthDayNanoType::make_value(0, -15, 0),
1857        ]);
1858        assert_eq!(div(&interval, &factor).unwrap().as_ref(), &expected);
1859
1860        let null_factor = Scalar::new(Float64Array::new_null(1));
1861        assert_eq!(
1862            mul(&interval, &null_factor).unwrap().as_ref(),
1863            &IntervalMonthDayNanoArray::new_null(interval.len())
1864        );
1865    }
1866
1867    #[test]
1868    fn test_interval_mul_div_f64_errors() {
1869        let factor = Float64Array::new_scalar(2.);
1870        let year_month = IntervalYearMonthArray::new_scalar(1);
1871        let day_time = IntervalDayTimeArray::new_scalar(IntervalDayTime::new(1, 1));
1872        for interval in [&year_month as &dyn Datum, &day_time] {
1873            assert!(mul(interval, &factor).is_err());
1874            assert!(mul(&factor, interval).is_err());
1875            assert!(div(interval, &factor).is_err());
1876        }
1877
1878        let interval =
1879            IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(1, 1, 1));
1880
1881        assert!(matches!(
1882            add(&interval, &factor),
1883            Err(ArrowError::InvalidArgumentError(_))
1884        ));
1885
1886        let zero = Float64Array::new_scalar(-0.);
1887        assert!(matches!(
1888            div(&interval, &zero),
1889            Err(ArrowError::DivideByZero)
1890        ));
1891
1892        assert!(div(&factor, &interval).is_err());
1893
1894        for factor in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
1895            let factor = Float64Array::new_scalar(factor);
1896            assert!(matches!(
1897                mul(&interval, &factor),
1898                Err(ArrowError::ArithmeticOverflow(_))
1899            ));
1900        }
1901
1902        let nan = Float64Array::new_scalar(f64::NAN);
1903        assert!(matches!(
1904            div(&interval, &nan),
1905            Err(ArrowError::ArithmeticOverflow(_))
1906        ));
1907
1908        let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1909            i32::MAX,
1910            0,
1911            0,
1912        ));
1913        assert!(matches!(
1914            mul(&interval, &factor),
1915            Err(ArrowError::ArithmeticOverflow(_))
1916        ));
1917
1918        let factor = Float64Array::new_scalar(1.5);
1919        let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1920            0,
1921            0,
1922            i64::MAX,
1923        ));
1924        assert!(matches!(
1925            mul(&interval, &factor),
1926            Err(ArrowError::ArithmeticOverflow(_))
1927        ));
1928
1929        let factor = Float64Array::new_scalar(1.000_000_000_4);
1930        let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1931            i32::MIN,
1932            0,
1933            0,
1934        ));
1935        assert!(matches!(
1936            mul(&interval, &factor),
1937            Err(ArrowError::ArithmeticOverflow(_))
1938        ));
1939
1940        let factor = Float64Array::new_scalar(0.999_999_999);
1941        let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1942            1,
1943            i32::MAX,
1944            0,
1945        ));
1946        assert!(matches!(
1947            mul(&interval, &factor),
1948            Err(ArrowError::ArithmeticOverflow(_))
1949        ));
1950    }
1951
1952    #[test]
1953    fn test_interval_mul_i64_overflow() {
1954        let interval = IntervalYearMonthArray::from(vec![i32::MAX]);
1955        let factor = Int64Array::from(vec![2]);
1956        assert_eq!(
1957            mul(&interval, &factor).unwrap_err().to_string(),
1958            "Arithmetic overflow: Overflow happened on: 2147483647 * 2"
1959        );
1960
1961        let interval = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNanoType::make_value(
1962            0,
1963            0,
1964            i64::MAX,
1965        )]);
1966        assert_eq!(
1967            mul(&interval, &factor).unwrap_err().to_string(),
1968            "Arithmetic overflow: Overflow happened on: 9223372036854775807 * 2"
1969        );
1970    }
1971
1972    fn test_duration_impl<T: ArrowPrimitiveType<Native = i64>>() {
1973        let a = PrimitiveArray::<T>::new(vec![1000, 4394, -3944].into(), None);
1974        let b = PrimitiveArray::<T>::new(vec![4, -5, -243].into(), None);
1975
1976        let result = add(&a, &b).unwrap();
1977        assert_eq!(result.as_primitive::<T>().values(), &[1004, 4389, -4187]);
1978        let result = sub(&a, &b).unwrap();
1979        assert_eq!(result.as_primitive::<T>().values(), &[996, 4399, -3701]);
1980
1981        let err = mul(&a, &b).unwrap_err().to_string();
1982        assert!(
1983            err.contains("Invalid duration arithmetic operation"),
1984            "{err}"
1985        );
1986
1987        let err = div(&a, &b).unwrap_err().to_string();
1988        assert!(
1989            err.contains("Invalid duration arithmetic operation"),
1990            "{err}"
1991        );
1992
1993        let err = rem(&a, &b).unwrap_err().to_string();
1994        assert!(
1995            err.contains("Invalid duration arithmetic operation"),
1996            "{err}"
1997        );
1998
1999        let a = PrimitiveArray::<T>::new(vec![i64::MAX].into(), None);
2000        let b = PrimitiveArray::<T>::new(vec![1].into(), None);
2001        let err = add(&a, &b).unwrap_err().to_string();
2002        assert_eq!(
2003            err,
2004            "Arithmetic overflow: Overflow happened on: 9223372036854775807 + 1"
2005        );
2006    }
2007
2008    #[test]
2009    fn test_duration() {
2010        test_duration_impl::<DurationSecondType>();
2011        test_duration_impl::<DurationMillisecondType>();
2012        test_duration_impl::<DurationMicrosecondType>();
2013        test_duration_impl::<DurationNanosecondType>();
2014    }
2015
2016    fn test_date_impl<T: ArrowPrimitiveType, F>(f: F)
2017    where
2018        F: Fn(NaiveDate) -> T::Native,
2019        T::Native: TryInto<i64>,
2020    {
2021        let a = PrimitiveArray::<T>::new(
2022            vec![
2023                f(NaiveDate::from_ymd_opt(1979, 1, 30).unwrap()),
2024                f(NaiveDate::from_ymd_opt(2010, 4, 3).unwrap()),
2025                f(NaiveDate::from_ymd_opt(2008, 2, 29).unwrap()),
2026            ]
2027            .into(),
2028            None,
2029        );
2030
2031        let b = IntervalYearMonthArray::from(vec![
2032            IntervalYearMonthType::make_value(34, 2),
2033            IntervalYearMonthType::make_value(3, -3),
2034            IntervalYearMonthType::make_value(-12, 4),
2035        ]);
2036
2037        let format_array = |x: &dyn Array| -> Vec<String> {
2038            x.as_primitive::<T>()
2039                .values()
2040                .into_iter()
2041                .map(|x| {
2042                    as_date::<T>((*x).try_into().ok().unwrap())
2043                        .unwrap()
2044                        .to_string()
2045                })
2046                .collect()
2047        };
2048
2049        let result = add(&a, &b).unwrap();
2050        assert_eq!(
2051            &format_array(result.as_ref()),
2052            &[
2053                "2013-03-30".to_string(),
2054                "2013-01-03".to_string(),
2055                "1996-06-29".to_string(),
2056            ]
2057        );
2058        let result = sub(&result, &b).unwrap();
2059        assert_eq!(result.as_ref(), &a);
2060
2061        let b = IntervalDayTimeArray::from(vec![
2062            IntervalDayTimeType::make_value(34, 2),
2063            IntervalDayTimeType::make_value(3, -3),
2064            IntervalDayTimeType::make_value(-12, 4),
2065        ]);
2066
2067        let result = add(&a, &b).unwrap();
2068        assert_eq!(
2069            &format_array(result.as_ref()),
2070            &[
2071                "1979-03-05".to_string(),
2072                "2010-04-06".to_string(),
2073                "2008-02-17".to_string(),
2074            ]
2075        );
2076        let result = sub(&result, &b).unwrap();
2077        assert_eq!(result.as_ref(), &a);
2078
2079        let b = IntervalMonthDayNanoArray::from(vec![
2080            IntervalMonthDayNanoType::make_value(34, 2, -34353534),
2081            IntervalMonthDayNanoType::make_value(3, -3, 2443),
2082            IntervalMonthDayNanoType::make_value(-12, 4, 2323242423232),
2083        ]);
2084
2085        let result = add(&a, &b).unwrap();
2086        assert_eq!(
2087            &format_array(result.as_ref()),
2088            &[
2089                "1981-12-02".to_string(),
2090                "2010-06-30".to_string(),
2091                "2007-03-04".to_string(),
2092            ]
2093        );
2094        let result = sub(&result, &b).unwrap();
2095        assert_eq!(
2096            &format_array(result.as_ref()),
2097            &[
2098                "1979-01-31".to_string(),
2099                "2010-04-02".to_string(),
2100                "2008-02-29".to_string(),
2101            ]
2102        );
2103    }
2104
2105    #[test]
2106    fn test_date() {
2107        test_date_impl::<Date32Type, _>(Date32Type::from_naive_date);
2108        test_date_impl::<Date64Type, _>(Date64Type::from_naive_date);
2109
2110        let a = Date32Array::from(vec![i32::MIN, i32::MAX, 23, 7684]);
2111        let b = Date32Array::from(vec![i32::MIN, i32::MIN, -2, 45]);
2112        let result = sub(&a, &b).unwrap();
2113        assert_eq!(
2114            result.as_primitive::<DurationSecondType>().values(),
2115            &[0, 371085174288000, 2160000, 660009600]
2116        );
2117
2118        let a = Date64Array::from(vec![4343, 76676, 3434]);
2119        let b = Date64Array::from(vec![3, -5, 5]);
2120        let result = sub(&a, &b).unwrap();
2121        assert_eq!(
2122            result.as_primitive::<DurationMillisecondType>().values(),
2123            &[4340, 76681, 3429]
2124        );
2125
2126        let a = Date64Array::from(vec![i64::MAX]);
2127        let b = Date64Array::from(vec![-1]);
2128        let err = sub(&a, &b).unwrap_err().to_string();
2129        assert_eq!(
2130            err,
2131            "Arithmetic overflow: Overflow happened on: 9223372036854775807 - -1"
2132        );
2133    }
2134
2135    #[test]
2136    fn test_date32_to_naive_date_opt_boundaries() {
2137        assert_eq!(MAX_VALID_DAYS, 95026236);
2138        assert_eq!(MIN_VALID_DAYS, -96465292);
2139
2140        // Valid boundary dates work
2141        assert!(Date32Type::to_naive_date_opt(MAX_VALID_DAYS).is_some());
2142        assert!(Date32Type::to_naive_date_opt(MIN_VALID_DAYS).is_some());
2143
2144        // Beyond boundaries fail
2145        assert!(Date32Type::to_naive_date_opt(MAX_VALID_DAYS + 1).is_none());
2146        assert!(Date32Type::to_naive_date_opt(MIN_VALID_DAYS - 1).is_none());
2147
2148        // Extreme values fail
2149        assert!(Date32Type::to_naive_date_opt(i32::MAX).is_none());
2150        assert!(Date32Type::to_naive_date_opt(i32::MIN).is_none());
2151
2152        // Common values work
2153        assert!(Date32Type::to_naive_date_opt(0).is_some());
2154        assert!(Date32Type::to_naive_date_opt(date_to_days(YEAR_2000)).is_some());
2155    }
2156
2157    #[test]
2158    fn test_date64_to_naive_date_opt_boundaries() {
2159        const MS_PER_DAY: i64 = 24 * 60 * 60 * 1000;
2160
2161        // Verify boundary millisecond values
2162        assert_eq!(MAX_VALID_MILLIS, 8210266790400000i64);
2163        assert_eq!(MIN_VALID_MILLIS, -8334601228800000i64);
2164
2165        // Valid boundary dates work
2166        assert!(Date64Type::to_naive_date_opt(MAX_VALID_MILLIS).is_some());
2167        assert!(Date64Type::to_naive_date_opt(MIN_VALID_MILLIS).is_some());
2168
2169        // Beyond boundaries fail
2170        assert!(Date64Type::to_naive_date_opt(MAX_VALID_MILLIS + MS_PER_DAY).is_none());
2171        assert!(Date64Type::to_naive_date_opt(MIN_VALID_MILLIS - MS_PER_DAY).is_none());
2172
2173        // Extreme values fail
2174        assert!(Date64Type::to_naive_date_opt(i64::MAX).is_none());
2175        assert!(Date64Type::to_naive_date_opt(i64::MIN).is_none());
2176
2177        // Common values work
2178        assert!(Date64Type::to_naive_date_opt(0).is_some());
2179        assert!(Date64Type::to_naive_date_opt(date_to_millis(YEAR_2000)).is_some());
2180    }
2181
2182    macro_rules! test_year_month_ops {
2183        ($type:ty, $date_fn:expr) => {{
2184            let date = $date_fn(YEAR_2000);
2185
2186            // Normal operations succeed
2187            assert!(
2188                <$type>::add_year_months_opt(date, 120).is_some(),
2189                "add_year_months: normal add"
2190            );
2191            assert!(
2192                <$type>::add_year_months_opt(date, 0).is_some(),
2193                "add_year_months: zero interval"
2194            );
2195            assert!(
2196                <$type>::subtract_year_months_opt(date, 120).is_some(),
2197                "subtract_year_months: normal subtract"
2198            );
2199            assert!(
2200                <$type>::subtract_year_months_opt(date, 0).is_some(),
2201                "subtract_year_months: zero interval"
2202            );
2203
2204            // Large but valid years work
2205            let large_year = $date_fn(NaiveDate::from_ymd_opt(5000, 1, 1).unwrap());
2206            let neg_year = $date_fn(NaiveDate::from_ymd_opt(-5000, 12, 31).unwrap());
2207            assert!(
2208                <$type>::add_year_months_opt(large_year, 12).is_some(),
2209                "add_year_months: large year"
2210            );
2211            assert!(
2212                <$type>::add_year_months_opt(neg_year, -12).is_some(),
2213                "add_year_months: negative year"
2214            );
2215            assert!(
2216                <$type>::subtract_year_months_opt(large_year, 12).is_some(),
2217                "subtract_year_months: large year"
2218            );
2219            assert!(
2220                <$type>::subtract_year_months_opt(neg_year, -12).is_some(),
2221                "subtract_year_months: negative year"
2222            );
2223
2224            // Overflow handling
2225            assert!(
2226                <$type>::subtract_year_months_opt($date_fn(MIN_VALID_DATE), 1).is_none(),
2227                "subtract_year_months: overflow days from min"
2228            );
2229            assert!(
2230                <$type>::subtract_year_months_opt($date_fn(MAX_VALID_DATE), -1).is_none(),
2231                "subtract_year_months: overflow neg days from max"
2232            );
2233            assert!(
2234                <$type>::add_year_months_opt($date_fn(MAX_VALID_DATE), 1).is_none(),
2235                "add_year_months: overflow days"
2236            );
2237            assert!(
2238                <$type>::add_year_months_opt($date_fn(MIN_VALID_DATE), -1).is_none(),
2239                "add_year_months: overflow neg days"
2240            );
2241        }};
2242    }
2243
2244    #[test]
2245    fn test_date_year_month_operations() {
2246        test_year_month_ops!(Date32Type, date_to_days);
2247        test_year_month_ops!(Date64Type, date_to_millis);
2248    }
2249
2250    macro_rules! test_day_time_ops {
2251        ($type:ty, $date_fn:expr) => {{
2252            let date = $date_fn(YEAR_2000);
2253
2254            // Moderate intervals succeed
2255            assert!(
2256                <$type>::add_day_time_opt(date, IntervalDayTime::new(30, 0)).is_some(),
2257                "add_day_time: +30 days"
2258            );
2259            assert!(
2260                <$type>::add_day_time_opt(date, IntervalDayTime::new(-30, 0)).is_some(),
2261                "add_day_time: -30 days"
2262            );
2263            assert!(
2264                <$type>::add_day_time_opt(date, IntervalDayTime::new(1000, 12345)).is_some(),
2265                "add_day_time: normal"
2266            );
2267            assert!(
2268                <$type>::subtract_day_time_opt(date, IntervalDayTime::new(30, 0)).is_some(),
2269                "subtract_day_time: +30 days"
2270            );
2271            assert!(
2272                <$type>::subtract_day_time_opt(date, IntervalDayTime::new(-30, 0)).is_some(),
2273                "subtract_day_time: -30 days"
2274            );
2275            assert!(
2276                <$type>::subtract_day_time_opt(date, IntervalDayTime::new(1000, 12345)).is_some(),
2277                "subtract_day_time: normal"
2278            );
2279
2280            // Overflow handling - subtract
2281            assert!(
2282                <$type>::subtract_day_time_opt(
2283                    $date_fn(MIN_VALID_DATE),
2284                    IntervalDayTime::new(1, 0)
2285                )
2286                .is_none(),
2287                "subtract_day_time: overflow days from min"
2288            );
2289            assert!(
2290                <$type>::subtract_day_time_opt(
2291                    $date_fn(MAX_VALID_DATE),
2292                    IntervalDayTime::new(-1, 0)
2293                )
2294                .is_none(),
2295                "subtract_day_time: overflow neg days from max"
2296            );
2297
2298            // Overflow handling - add
2299            assert!(
2300                <$type>::add_day_time_opt($date_fn(MAX_VALID_DATE), IntervalDayTime::new(1, 0))
2301                    .is_none(),
2302                "add_day_time: overflow days"
2303            );
2304            assert!(
2305                <$type>::add_day_time_opt($date_fn(MIN_VALID_DATE), IntervalDayTime::new(-1, 0))
2306                    .is_none(),
2307                "add_day_time: overflow neg days"
2308            );
2309
2310            // Extreme intervals fail
2311            assert!(
2312                <$type>::add_day_time_opt(
2313                    $date_fn(EPOCH),
2314                    IntervalDayTime::new(i32::MAX, i32::MAX)
2315                )
2316                .is_none(),
2317                "add_day_time: max interval"
2318            );
2319            assert!(
2320                <$type>::add_day_time_opt(
2321                    $date_fn(EPOCH),
2322                    IntervalDayTime::new(i32::MIN, i32::MIN)
2323                )
2324                .is_none(),
2325                "add_day_time: min interval"
2326            );
2327            assert!(
2328                <$type>::subtract_day_time_opt(
2329                    $date_fn(EPOCH),
2330                    IntervalDayTime::new(i32::MAX, i32::MAX)
2331                )
2332                .is_none(),
2333                "subtract_day_time: max interval"
2334            );
2335            assert!(
2336                <$type>::subtract_day_time_opt(
2337                    $date_fn(EPOCH),
2338                    IntervalDayTime::new(i32::MIN, i32::MIN)
2339                )
2340                .is_none(),
2341                "subtract_day_time: min interval"
2342            );
2343        }};
2344    }
2345
2346    #[test]
2347    fn test_date_day_time_operations() {
2348        test_day_time_ops!(Date32Type, date_to_days);
2349        test_day_time_ops!(Date64Type, date_to_millis);
2350    }
2351
2352    macro_rules! test_month_day_nano_ops {
2353        ($type:ty, $date_fn:expr) => {{
2354            let date = $date_fn(YEAR_2000);
2355            let zero = IntervalMonthDayNano::new(0, 0, 0);
2356
2357            // Normal operations succeed
2358            assert!(
2359                <$type>::add_month_day_nano_opt(date, IntervalMonthDayNano::new(1, 30, 0))
2360                    .is_some(),
2361                "add_month_day_nano: +1mo +30d"
2362            );
2363            assert!(
2364                <$type>::add_month_day_nano_opt(date, IntervalMonthDayNano::new(-1, -30, 0))
2365                    .is_some(),
2366                "add_month_day_nano: -1mo -30d"
2367            );
2368            assert!(
2369                <$type>::add_month_day_nano_opt(date, zero).is_some(),
2370                "add_month_day_nano: zero interval"
2371            );
2372            assert!(
2373                <$type>::add_month_day_nano_opt(
2374                    date,
2375                    IntervalMonthDayNano::new(2, 10, 123_456_789_000)
2376                )
2377                .is_some(),
2378                "add_month_day_nano: normal"
2379            );
2380            assert!(
2381                <$type>::subtract_month_day_nano_opt(date, IntervalMonthDayNano::new(1, 30, 0))
2382                    .is_some(),
2383                "subtract_month_day_nano: +1mo +30d"
2384            );
2385            assert!(
2386                <$type>::subtract_month_day_nano_opt(date, IntervalMonthDayNano::new(-1, -30, 0))
2387                    .is_some(),
2388                "subtract_month_day_nano: -1mo -30d"
2389            );
2390            assert!(
2391                <$type>::subtract_month_day_nano_opt(date, zero).is_some(),
2392                "subtract_month_day_nano: zero interval"
2393            );
2394            assert!(
2395                <$type>::subtract_month_day_nano_opt(
2396                    date,
2397                    IntervalMonthDayNano::new(2, 10, 123_456_789_000)
2398                )
2399                .is_some(),
2400                "subtract_month_day_nano: normal"
2401            );
2402
2403            // Overflow handling - subtract
2404            assert!(
2405                <$type>::subtract_month_day_nano_opt(
2406                    $date_fn(MIN_VALID_DATE),
2407                    IntervalMonthDayNano::new(0, 1, 0)
2408                )
2409                .is_none(),
2410                "subtract_month_day_nano: overflow days from min"
2411            );
2412            assert!(
2413                <$type>::subtract_month_day_nano_opt(
2414                    $date_fn(MAX_VALID_DATE),
2415                    IntervalMonthDayNano::new(0, -1, 0)
2416                )
2417                .is_none(),
2418                "subtract_month_day_nano: overflow neg days from max"
2419            );
2420
2421            // Overflow handling - add
2422            assert!(
2423                <$type>::add_month_day_nano_opt(
2424                    $date_fn(MAX_VALID_DATE),
2425                    IntervalMonthDayNano::new(0, 1, 0)
2426                )
2427                .is_none(),
2428                "add_month_day_nano: overflow days"
2429            );
2430            assert!(
2431                <$type>::add_month_day_nano_opt(
2432                    $date_fn(MIN_VALID_DATE),
2433                    IntervalMonthDayNano::new(0, -1, 0)
2434                )
2435                .is_none(),
2436                "add_month_day_nano: overflow neg days"
2437            );
2438
2439            // Nanosecond precision works
2440            assert!(
2441                <$type>::add_month_day_nano_opt(date, IntervalMonthDayNano::new(0, 0, 999_999_999))
2442                    .is_some(),
2443                "add_month_day_nano: nanos"
2444            );
2445            assert!(
2446                <$type>::subtract_month_day_nano_opt(
2447                    date,
2448                    IntervalMonthDayNano::new(0, 0, 999_999_999)
2449                )
2450                .is_some(),
2451                "subtract_month_day_nano: nanos"
2452            );
2453            // 1 day in nanos
2454            assert!(
2455                <$type>::add_month_day_nano_opt(
2456                    date,
2457                    IntervalMonthDayNano::new(0, 0, 86_400_000_000_000)
2458                )
2459                .is_some(),
2460                "add_month_day_nano: 1 day nanos"
2461            );
2462            assert!(
2463                <$type>::subtract_month_day_nano_opt(
2464                    date,
2465                    IntervalMonthDayNano::new(0, 0, 86_400_000_000_000)
2466                )
2467                .is_some(),
2468                "subtract_month_day_nano: 1 day nanos"
2469            );
2470        }};
2471    }
2472
2473    #[test]
2474    fn test_date_month_day_nano_operations() {
2475        test_month_day_nano_ops!(Date32Type, date_to_days);
2476        test_month_day_nano_ops!(Date64Type, date_to_millis);
2477    }
2478}