This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b75e8fbb1 Fix timezoned timestamp arithmetic (#4546)
0b75e8fbb1 is described below

commit 0b75e8fbb1f20fb14c4aefae953f535e5be9bdbd
Author: Alexandre Crayssac <[email protected]>
AuthorDate: Wed Jul 26 17:37:29 2023 +0200

    Fix timezoned timestamp arithmetic (#4546)
    
    * Fix arithmetic for timezone-aware timestamp arrays
    
    * Remove debug test from issue
    
    * Update to pass timezone by value instead of reference because it's smaller
    
    * Use as_datetime_with_timezone instead of manual conversion
    
    * Add support for string tz, fix bug and refactor the whole thing
    
    * Use checked arithmetic for months and days and use Self instead of type 
name
    
    * Refactor tests
    
    * Add DST test case
    
    * Add new tz parameter to docstring
    
    * Add checked negations and refactor substraction
    
    * Add tests for interval overflow
    
    * Remove checked negation and use absolute unsigned instead
    
    * Use Option instead of Result for overflow errors
    
    * Move arithmetic functions to TimestampOp trait
    
    * Revert "Move arithmetic functions to TimestampOp trait"
    
    This reverts commit 8ec4be477e71531c6238ccf48a695a6b7e6a8798.
---
 arrow-arith/Cargo.toml     |   1 +
 arrow-arith/src/numeric.rs | 250 ++++++++++++++++--
 arrow-array/src/delta.rs   |  72 +++++-
 arrow-array/src/types.rs   | 626 ++++++++++++++++-----------------------------
 4 files changed, 508 insertions(+), 441 deletions(-)

diff --git a/arrow-arith/Cargo.toml b/arrow-arith/Cargo.toml
index b5ea2e3c43..6da472be66 100644
--- a/arrow-arith/Cargo.toml
+++ b/arrow-arith/Cargo.toml
@@ -46,3 +46,4 @@ num = { version = "0.4", default-features = false, features = 
["std"] }
 
 [features]
 simd = ["arrow-array/simd"]
+chrono-tz = ["arrow-array/chrono-tz"]
diff --git a/arrow-arith/src/numeric.rs b/arrow-arith/src/numeric.rs
index b0bbb75c12..7862fe2f9b 100644
--- a/arrow-arith/src/numeric.rs
+++ b/arrow-arith/src/numeric.rs
@@ -22,6 +22,7 @@ use std::fmt::Formatter;
 use std::sync::Arc;
 
 use arrow_array::cast::AsArray;
+use arrow_array::timezone::Tz;
 use arrow_array::types::*;
 use arrow_array::*;
 use arrow_buffer::ArrowNativeType;
@@ -345,13 +346,13 @@ fn float_op<T: ArrowPrimitiveType>(
 trait TimestampOp: ArrowTimestampType {
     type Duration: ArrowPrimitiveType<Native = i64>;
 
-    fn add_year_month(timestamp: i64, delta: i32) -> Result<i64, ArrowError>;
-    fn add_day_time(timestamp: i64, delta: i64) -> Result<i64, ArrowError>;
-    fn add_month_day_nano(timestamp: i64, delta: i128) -> Result<i64, 
ArrowError>;
+    fn add_year_month(timestamp: i64, delta: i32, tz: Tz) -> Option<i64>;
+    fn add_day_time(timestamp: i64, delta: i64, tz: Tz) -> Option<i64>;
+    fn add_month_day_nano(timestamp: i64, delta: i128, tz: Tz) -> Option<i64>;
 
-    fn sub_year_month(timestamp: i64, delta: i32) -> Result<i64, ArrowError>;
-    fn sub_day_time(timestamp: i64, delta: i64) -> Result<i64, ArrowError>;
-    fn sub_month_day_nano(timestamp: i64, delta: i128) -> Result<i64, 
ArrowError>;
+    fn sub_year_month(timestamp: i64, delta: i32, tz: Tz) -> Option<i64>;
+    fn sub_day_time(timestamp: i64, delta: i64, tz: Tz) -> Option<i64>;
+    fn sub_month_day_nano(timestamp: i64, delta: i128, tz: Tz) -> Option<i64>;
 }
 
 macro_rules! timestamp {
@@ -359,28 +360,28 @@ macro_rules! timestamp {
         impl TimestampOp for $t {
             type Duration = $d;
 
-            fn add_year_month(left: i64, right: i32) -> Result<i64, 
ArrowError> {
-                Self::add_year_months(left, right)
+            fn add_year_month(left: i64, right: i32, tz: Tz) -> Option<i64> {
+                Self::add_year_months(left, right, tz)
             }
 
-            fn add_day_time(left: i64, right: i64) -> Result<i64, ArrowError> {
-                Self::add_day_time(left, right)
+            fn add_day_time(left: i64, right: i64, tz: Tz) -> Option<i64> {
+                Self::add_day_time(left, right, tz)
             }
 
-            fn add_month_day_nano(left: i64, right: i128) -> Result<i64, 
ArrowError> {
-                Self::add_month_day_nano(left, right)
+            fn add_month_day_nano(left: i64, right: i128, tz: Tz) -> 
Option<i64> {
+                Self::add_month_day_nano(left, right, tz)
             }
 
-            fn sub_year_month(left: i64, right: i32) -> Result<i64, 
ArrowError> {
-                Self::subtract_year_months(left, right)
+            fn sub_year_month(left: i64, right: i32, tz: Tz) -> Option<i64> {
+                Self::subtract_year_months(left, right, tz)
             }
 
-            fn sub_day_time(left: i64, right: i64) -> Result<i64, ArrowError> {
-                Self::subtract_day_time(left, right)
+            fn sub_day_time(left: i64, right: i64, tz: Tz) -> Option<i64> {
+                Self::subtract_day_time(left, right, tz)
             }
 
-            fn sub_month_day_nano(left: i64, right: i128) -> Result<i64, 
ArrowError> {
-                Self::subtract_month_day_nano(left, right)
+            fn sub_month_day_nano(left: i64, right: i128, tz: Tz) -> 
Option<i64> {
+                Self::subtract_month_day_nano(left, right, tz)
             }
         }
     };
@@ -401,8 +402,9 @@ fn timestamp_op<T: TimestampOp>(
     use DataType::*;
     use IntervalUnit::*;
 
-    // Note: interval arithmetic should account for timezones (#4457)
     let l = l.as_primitive::<T>();
+    let l_tz: Tz = l.timezone().unwrap_or("+00:00").parse()?;
+
     let array: PrimitiveArray<T> = match (op, r.data_type()) {
         (Op::Sub | Op::SubWrapping, Timestamp(unit, _)) if unit == &T::UNIT => 
{
             let r = r.as_primitive::<T>();
@@ -420,29 +422,77 @@ fn timestamp_op<T: TimestampOp>(
 
         (Op::Add | Op::AddWrapping, Interval(YearMonth)) => {
             let r = r.as_primitive::<IntervalYearMonthType>();
-            try_op!(l, l_s, r, r_s, T::add_year_month(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::add_year_month(l, r, l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
         (Op::Sub | Op::SubWrapping, Interval(YearMonth)) => {
             let r = r.as_primitive::<IntervalYearMonthType>();
-            try_op!(l, l_s, r, r_s, T::sub_year_month(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::sub_year_month(l, r, l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
 
         (Op::Add | Op::AddWrapping, Interval(DayTime)) => {
             let r = r.as_primitive::<IntervalDayTimeType>();
-            try_op!(l, l_s, r, r_s, T::add_day_time(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::add_day_time(l, r, l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
         (Op::Sub | Op::SubWrapping, Interval(DayTime)) => {
             let r = r.as_primitive::<IntervalDayTimeType>();
-            try_op!(l, l_s, r, r_s, T::sub_day_time(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::sub_day_time(l, r, l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
 
         (Op::Add | Op::AddWrapping, Interval(MonthDayNano)) => {
             let r = r.as_primitive::<IntervalMonthDayNanoType>();
-            try_op!(l, l_s, r, r_s, T::add_month_day_nano(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::add_month_day_nano(l, r, 
l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
         (Op::Sub | Op::SubWrapping, Interval(MonthDayNano)) => {
             let r = r.as_primitive::<IntervalMonthDayNanoType>();
-            try_op!(l, l_s, r, r_s, T::sub_month_day_nano(l, r))
+            try_op!(
+                l,
+                l_s,
+                r,
+                r_s,
+                T::sub_month_day_nano(l, r, 
l_tz).ok_or(ArrowError::ComputeError(
+                    "Timestamp out of range".to_string()
+                ))
+            )
         }
         _ => {
             return Err(ArrowError::InvalidArgumentError(format!(
@@ -803,9 +853,11 @@ fn decimal_op<T: DecimalType>(
 #[cfg(test)]
 mod tests {
     use super::*;
-    use arrow_array::temporal_conversions::{as_date, as_datetime};
+    use arrow_array::temporal_conversions::{
+        as_date, as_datetime, as_datetime_with_timezone,
+    };
     use arrow_buffer::{i256, ScalarBuffer};
-    use chrono::{DateTime, NaiveDate};
+    use chrono::{DateTime, NaiveDate, TimeZone};
 
     fn test_neg_primitive<T: ArrowPrimitiveType>(
         input: &[T::Native],
@@ -1472,4 +1524,148 @@ mod tests {
             "Compute error: Overflow happened on: 9223372036854775807 - -1"
         );
     }
+
+    fn test_timestamp_with_timezone_impl<T: TimestampOp>(tz_str: &str) {
+        let tz: Tz = tz_str.parse().unwrap();
+
+        let transform_array = |x: &dyn Array| -> Vec<DateTime<_>> {
+            x.as_primitive::<T>()
+                .values()
+                .into_iter()
+                .map(|x| as_datetime_with_timezone::<T>(*x, tz).unwrap())
+                .collect()
+        };
+
+        let values = vec![
+            tz.with_ymd_and_hms(1970, 1, 28, 23, 0, 0)
+                .unwrap()
+                .naive_utc(),
+            tz.with_ymd_and_hms(1970, 1, 1, 0, 0, 0)
+                .unwrap()
+                .naive_utc(),
+            tz.with_ymd_and_hms(2010, 4, 1, 4, 0, 20)
+                .unwrap()
+                .naive_utc(),
+            tz.with_ymd_and_hms(1960, 1, 30, 4, 23, 20)
+                .unwrap()
+                .naive_utc(),
+            tz.with_ymd_and_hms(2023, 3, 25, 14, 0, 0)
+                .unwrap()
+                .naive_utc(),
+        ]
+        .into_iter()
+        .map(|x| T::make_value(x).unwrap())
+        .collect();
+
+        let a = PrimitiveArray::<T>::new(values, None).with_timezone(tz_str);
+
+        // IntervalYearMonth
+        let b = IntervalYearMonthArray::from(vec![
+            IntervalYearMonthType::make_value(0, 1),
+            IntervalYearMonthType::make_value(5, 34),
+            IntervalYearMonthType::make_value(-2, 4),
+            IntervalYearMonthType::make_value(7, -4),
+            IntervalYearMonthType::make_value(0, 1),
+        ]);
+        let r1 = add(&a, &b).unwrap();
+        assert_eq!(
+            &transform_array(r1.as_ref()),
+            &[
+                tz.with_ymd_and_hms(1970, 2, 28, 23, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(1977, 11, 1, 0, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(2008, 8, 1, 4, 0, 20).unwrap(),
+                tz.with_ymd_and_hms(1966, 9, 30, 4, 23, 20).unwrap(),
+                tz.with_ymd_and_hms(2023, 4, 25, 14, 0, 0).unwrap(),
+            ]
+        );
+
+        let r2 = sub(&r1, &b).unwrap();
+        assert_eq!(r2.as_ref(), &a);
+
+        // IntervalDayTime
+        let b = IntervalDayTimeArray::from(vec![
+            IntervalDayTimeType::make_value(0, 0),
+            IntervalDayTimeType::make_value(5, 454000),
+            IntervalDayTimeType::make_value(-34, 0),
+            IntervalDayTimeType::make_value(7, -4000),
+            IntervalDayTimeType::make_value(1, 0),
+        ]);
+        let r3 = add(&a, &b).unwrap();
+        assert_eq!(
+            &transform_array(r3.as_ref()),
+            &[
+                tz.with_ymd_and_hms(1970, 1, 28, 23, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(1970, 1, 6, 0, 7, 34).unwrap(),
+                tz.with_ymd_and_hms(2010, 2, 26, 4, 0, 20).unwrap(),
+                tz.with_ymd_and_hms(1960, 2, 6, 4, 23, 16).unwrap(),
+                tz.with_ymd_and_hms(2023, 3, 26, 14, 0, 0).unwrap(),
+            ]
+        );
+
+        let r4 = sub(&r3, &b).unwrap();
+        assert_eq!(r4.as_ref(), &a);
+
+        // IntervalMonthDayNano
+        let b = IntervalMonthDayNanoArray::from(vec![
+            IntervalMonthDayNanoType::make_value(1, 0, 0),
+            IntervalMonthDayNanoType::make_value(344, 34, -43_000_000_000),
+            IntervalMonthDayNanoType::make_value(-593, -33, 13_000_000_000),
+            IntervalMonthDayNanoType::make_value(5, 2, 493_000_000_000),
+            IntervalMonthDayNanoType::make_value(1, 0, 0),
+        ]);
+        let r5 = add(&a, &b).unwrap();
+        assert_eq!(
+            &transform_array(r5.as_ref()),
+            &[
+                tz.with_ymd_and_hms(1970, 2, 28, 23, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(1998, 10, 4, 23, 59, 17).unwrap(),
+                tz.with_ymd_and_hms(1960, 9, 29, 4, 0, 33).unwrap(),
+                tz.with_ymd_and_hms(1960, 7, 2, 4, 31, 33).unwrap(),
+                tz.with_ymd_and_hms(2023, 4, 25, 14, 0, 0).unwrap(),
+            ]
+        );
+
+        let r6 = sub(&r5, &b).unwrap();
+        assert_eq!(
+            &transform_array(r6.as_ref()),
+            &[
+                tz.with_ymd_and_hms(1970, 1, 28, 23, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(1970, 1, 2, 0, 0, 0).unwrap(),
+                tz.with_ymd_and_hms(2010, 4, 2, 4, 0, 20).unwrap(),
+                tz.with_ymd_and_hms(1960, 1, 31, 4, 23, 20).unwrap(),
+                tz.with_ymd_and_hms(2023, 3, 25, 14, 0, 0).unwrap(),
+            ]
+        );
+    }
+
+    #[cfg(not(feature = "chrono-tz"))]
+    #[test]
+    fn test_timestamp_with_timezone() {
+        let timezones = ["+00:00", "+01:00", "-01:00", "+03:30"];
+        for timezone in timezones {
+            test_timestamp_with_timezone_impl::<TimestampSecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampMillisecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampMicrosecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampNanosecondType>(timezone);
+        }
+    }
+
+    #[cfg(feature = "chrono-tz")]
+    #[test]
+    fn test_timestamp_with_timezone() {
+        let timezones = [
+            "Europe/Paris",
+            "Europe/London",
+            "Africa/Bamako",
+            "America/Dominica",
+            "Asia/Seoul",
+            "Asia/Shanghai",
+        ];
+        for timezone in timezones {
+            test_timestamp_with_timezone_impl::<TimestampSecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampMillisecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampMicrosecondType>(timezone);
+            
test_timestamp_with_timezone_impl::<TimestampNanosecondType>(timezone);
+        }
+    }
 }
diff --git a/arrow-array/src/delta.rs b/arrow-array/src/delta.rs
index 029168242b..bf9ee5ca68 100644
--- a/arrow-array/src/delta.rs
+++ b/arrow-array/src/delta.rs
@@ -23,22 +23,74 @@
 // Copied from chronoutil crate
 
 //! Contains utility functions for shifting Date objects.
-use chrono::{Datelike, Months};
+use chrono::{DateTime, Datelike, Days, Months, TimeZone};
 use std::cmp::Ordering;
 
 /// Shift a date by the given number of months.
-pub(crate) fn shift_months<
-    D: Datelike
-        + std::ops::Add<chrono::Months, Output = D>
-        + std::ops::Sub<chrono::Months, Output = D>,
->(
-    date: D,
-    months: i32,
-) -> D {
+pub(crate) fn shift_months<D>(date: D, months: i32) -> D
+where
+    D: Datelike + std::ops::Add<Months, Output = D> + std::ops::Sub<Months, 
Output = D>,
+{
     match months.cmp(&0) {
         Ordering::Equal => date,
         Ordering::Greater => date + Months::new(months as u32),
-        Ordering::Less => date - Months::new(-months as u32),
+        Ordering::Less => date - Months::new(months.unsigned_abs()),
+    }
+}
+
+/// Add the given number of months to the given datetime.
+///
+/// Returns `None` when it will result in overflow.
+pub(crate) fn add_months_datetime<Tz: TimeZone>(
+    dt: DateTime<Tz>,
+    months: i32,
+) -> Option<DateTime<Tz>> {
+    match months.cmp(&0) {
+        Ordering::Equal => Some(dt),
+        Ordering::Greater => dt.checked_add_months(Months::new(months as u32)),
+        Ordering::Less => 
dt.checked_sub_months(Months::new(months.unsigned_abs())),
+    }
+}
+
+/// Add the given number of days to the given datetime.
+///
+/// Returns `None` when it will result in overflow.
+pub(crate) fn add_days_datetime<Tz: TimeZone>(
+    dt: DateTime<Tz>,
+    days: i32,
+) -> Option<DateTime<Tz>> {
+    match days.cmp(&0) {
+        Ordering::Equal => Some(dt),
+        Ordering::Greater => dt.checked_add_days(Days::new(days as u64)),
+        Ordering::Less => dt.checked_sub_days(Days::new(days.unsigned_abs() as 
u64)),
+    }
+}
+
+/// Substract the given number of months to the given datetime.
+///
+/// Returns `None` when it will result in overflow.
+pub(crate) fn sub_months_datetime<Tz: TimeZone>(
+    dt: DateTime<Tz>,
+    months: i32,
+) -> Option<DateTime<Tz>> {
+    match months.cmp(&0) {
+        Ordering::Equal => Some(dt),
+        Ordering::Greater => dt.checked_sub_months(Months::new(months as u32)),
+        Ordering::Less => 
dt.checked_add_months(Months::new(months.unsigned_abs())),
+    }
+}
+
+/// Substract the given number of days to the given datetime.
+///
+/// Returns `None` when it will result in overflow.
+pub(crate) fn sub_days_datetime<Tz: TimeZone>(
+    dt: DateTime<Tz>,
+    days: i32,
+) -> Option<DateTime<Tz>> {
+    match days.cmp(&0) {
+        Ordering::Equal => Some(dt),
+        Ordering::Greater => dt.checked_sub_days(Days::new(days as u64)),
+        Ordering::Less => dt.checked_add_days(Days::new(days.unsigned_abs() as 
u64)),
     }
 }
 
diff --git a/arrow-array/src/types.rs b/arrow-array/src/types.rs
index 3d14cff384..769dbf974b 100644
--- a/arrow-array/src/types.rs
+++ b/arrow-array/src/types.rs
@@ -17,7 +17,12 @@
 
 //! Zero-sized types used to parameterize generic array implementations
 
-use crate::delta::shift_months;
+use crate::delta::{
+    add_days_datetime, add_months_datetime, shift_months, sub_days_datetime,
+    sub_months_datetime,
+};
+use crate::temporal_conversions::as_datetime_with_timezone;
+use crate::timezone::Tz;
 use crate::{ArrowNativeTypeOp, OffsetSizeTrait};
 use arrow_buffer::{i256, Buffer, OffsetBuffer};
 use arrow_data::decimal::{validate_decimal256_precision, 
validate_decimal_precision};
@@ -350,158 +355,184 @@ impl ArrowTimestampType for TimestampNanosecondType {
     }
 }
 
+fn add_year_months<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let months = IntervalYearMonthType::to_months(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = add_months_datetime(res, months)?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
+fn add_day_time<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let (days, ms) = IntervalDayTimeType::to_parts(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = add_days_datetime(res, days)?;
+    let res = res.checked_add_signed(Duration::milliseconds(ms as i64))?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
+fn add_month_day_nano<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = add_months_datetime(res, months)?;
+    let res = add_days_datetime(res, days)?;
+    let res = res.checked_add_signed(Duration::nanoseconds(nanos))?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
+fn subtract_year_months<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let months = IntervalYearMonthType::to_months(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = sub_months_datetime(res, months)?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
+fn subtract_day_time<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let (days, ms) = IntervalDayTimeType::to_parts(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = sub_days_datetime(res, days)?;
+    let res = res.checked_sub_signed(Duration::milliseconds(ms as i64))?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
+fn subtract_month_day_nano<T: ArrowTimestampType>(
+    timestamp: <T as ArrowPrimitiveType>::Native,
+    delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
+    tz: Tz,
+) -> Option<<T as ArrowPrimitiveType>::Native> {
+    let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
+    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
+    let res = sub_months_datetime(res, months)?;
+    let res = sub_days_datetime(res, days)?;
+    let res = res.checked_sub_signed(Duration::nanoseconds(nanos))?;
+    let res = res.naive_utc();
+    T::make_value(res)
+}
+
 impl TimestampSecondType {
-    /// Adds the given IntervalYearMonthType to an arrow TimestampSecondType
+    /// Adds the given IntervalYearMonthType to an arrow TimestampSecondType.
+    ///
+    /// Returns `None` when it will result in overflow.
     ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_year_months(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let prior = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-
-        let months = IntervalYearMonthType::to_months(delta);
-        let posterior = shift_months(prior, months);
-        TimestampSecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_year_months::<Self>(timestamp, delta, tz)
     }
 
-    /// Adds the given IntervalDayTimeType to an arrow TimestampSecondType
+    /// Adds the given IntervalDayTimeType to an arrow TimestampSecondType.
+    ///
+    /// Returns `None` when it will result in overflow.
     ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_day_time(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampSecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalMonthDayNanoType to an arrow TimestampSecondType
     ///
+    /// Returns `None` when it will result in overflow.
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_month_day_nano(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, months);
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampSecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_month_day_nano::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalYearMonthType to an arrow 
TimestampSecondType
     ///
+    /// Returns `None` when it will result in overflow.
+    ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_year_months(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let prior = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let months = IntervalYearMonthType::to_months(-delta);
-        let posterior = shift_months(prior, months);
-        TimestampSecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalDayTimeType to an arrow TimestampSecondType
     ///
+    /// Returns `None` when it will result in overflow.
+    ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_day_time(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampSecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalMonthDayNanoType to an arrow 
TimestampSecondType
     ///
+    /// Returns `None` when it will result in overflow.
+    ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_month_day_nano(
-        timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = NaiveDateTime::from_timestamp_opt(timestamp, 
0).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, -months);
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampSecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_month_day_nano::<Self>(timestamp, delta, tz)
     }
 }
 
@@ -512,18 +543,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_year_months(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let prior = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let months = IntervalYearMonthType::to_months(delta);
-        let posterior = shift_months(prior, months);
-        TimestampMicrosecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalDayTimeType to an arrow TimestampMicrosecondType
@@ -532,27 +558,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_day_time(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMicrosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalMonthDayNanoType to an arrow 
TimestampMicrosecondType
@@ -561,28 +573,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_month_day_nano(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, months);
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMicrosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_month_day_nano::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalYearMonthType to an arrow 
TimestampMicrosecondType
@@ -591,18 +588,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_year_months(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let prior = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let months = IntervalYearMonthType::to_months(-delta);
-        let posterior = shift_months(prior, months);
-        TimestampMicrosecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalDayTimeType to an arrow 
TimestampMicrosecondType
@@ -611,27 +603,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_day_time(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMicrosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalMonthDayNanoType to an arrow 
TimestampMicrosecondType
@@ -640,28 +618,13 @@ impl TimestampMicrosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_month_day_nano(
-        timestamp: <TimestampMicrosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, -months);
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMicrosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_month_day_nano::<Self>(timestamp, delta, tz)
     }
 }
 
@@ -672,18 +635,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_year_months(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let prior = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let months = IntervalYearMonthType::to_months(delta);
-        let posterior = shift_months(prior, months);
-        TimestampMillisecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalDayTimeType to an arrow TimestampMillisecondType
@@ -692,27 +650,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_day_time(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMillisecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalMonthDayNanoType to an arrow 
TimestampMillisecondType
@@ -721,28 +665,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_month_day_nano(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, months);
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMillisecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_month_day_nano::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalYearMonthType to an arrow 
TimestampMillisecondType
@@ -751,18 +680,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_year_months(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let prior = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let months = IntervalYearMonthType::to_months(-delta);
-        let posterior = shift_months(prior, months);
-        TimestampMillisecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalDayTimeType to an arrow 
TimestampMillisecondType
@@ -771,27 +695,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_day_time(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMillisecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Subtracts the given IntervalMonthDayNanoType to an arrow 
TimestampMillisecondType
@@ -800,28 +710,13 @@ impl TimestampMillisecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_month_day_nano(
-        timestamp: <TimestampMillisecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, 
ArrowError>
-    {
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = 
NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
-            ArrowError::ComputeError("Timestamp out of range".to_string())
-        })?;
-        let res = shift_months(res, -months);
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampMillisecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_month_day_nano::<Self>(timestamp, delta, tz)
     }
 }
 
@@ -832,19 +727,13 @@ impl TimestampNanosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_year_months(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let prior = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
-        let months = IntervalYearMonthType::to_months(delta);
-        let posterior = shift_months(prior, months);
-        TimestampNanosecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_year_months::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalDayTimeType to an arrow TimestampNanosecondType
@@ -853,28 +742,13 @@ impl TimestampNanosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_day_time(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let res = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampNanosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_day_time::<Self>(timestamp, delta, tz)
     }
 
     /// Adds the given IntervalMonthDayNanoType to an arrow 
TimestampNanosecondType
@@ -883,114 +757,58 @@ impl TimestampNanosecondType {
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn add_month_day_nano(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let res = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        add_month_day_nano::<Self>(timestamp, delta, tz)
+    }
 
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = shift_months(res, months);
-        let res = res
-            .checked_add_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_add_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampNanosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
-    }
-
-    /// Subtracs the given IntervalYearMonthType to an arrow 
TimestampNanosecondType
+    /// Subtracts the given IntervalYearMonthType to an arrow 
TimestampNanosecondType
     ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_year_months(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let prior = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
-        let months = IntervalYearMonthType::to_months(-delta);
-        let posterior = shift_months(prior, months);
-        TimestampNanosecondType::make_value(posterior)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_year_months::<Self>(timestamp, delta, tz)
     }
 
-    /// Subtracs the given IntervalDayTimeType to an arrow 
TimestampNanosecondType
+    /// Subtracts the given IntervalDayTimeType to an arrow 
TimestampNanosecondType
     ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_day_time(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let res = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_day_time::<Self>(timestamp, delta, tz)
+    }
 
-        let (days, ms) = IntervalDayTimeType::to_parts(delta);
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::milliseconds(ms as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampNanosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
-    }
-
-    /// Subtracs the given IntervalMonthDayNanoType to an arrow 
TimestampNanosecondType
+    /// Subtracts the given IntervalMonthDayNanoType to an arrow 
TimestampNanosecondType
     ///
     /// # Arguments
     ///
     /// * `timestamp` - The date on which to perform the operation
     /// * `delta` - The interval to add
+    /// * `tz` - The timezone in which to interpret `timestamp`
     pub fn subtract_month_day_nano(
-        timestamp: <TimestampNanosecondType as ArrowPrimitiveType>::Native,
+        timestamp: <Self as ArrowPrimitiveType>::Native,
         delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
-    ) -> Result<<TimestampNanosecondType as ArrowPrimitiveType>::Native, 
ArrowError> {
-        let seconds = timestamp / 1_000_000_000;
-        let nanos = timestamp % 1_000_000_000;
-        let res = NaiveDateTime::from_timestamp_opt(seconds, nanos as 
u32).ok_or_else(
-            || ArrowError::ComputeError("Timestamp out of range".to_string()),
-        )?;
-
-        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
-        let res = shift_months(res, -months);
-        let res = res
-            .checked_sub_signed(Duration::days(days as i64))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        let res = res
-            .checked_sub_signed(Duration::nanoseconds(nanos))
-            .ok_or_else(|| {
-                ArrowError::ComputeError("Timestamp out of range".to_string())
-            })?;
-        TimestampNanosecondType::make_value(res)
-            .ok_or_else(|| ArrowError::ComputeError("Timestamp out of 
range".to_string()))
+        tz: Tz,
+    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
+        subtract_month_day_nano::<Self>(timestamp, delta, tz)
     }
 }
 

Reply via email to