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

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


The following commit(s) were added to refs/heads/main by this push:
     new 989044cd0f refactor: streamline date64 tests (#9165)
989044cd0f is described below

commit 989044cd0fbb8e43834180f2a720dc3f7cb56e75
Author: cht42 <[email protected]>
AuthorDate: Thu Jan 15 02:23:12 2026 +0400

    refactor: streamline date64 tests (#9165)
    
    # Which issue does this PR close?
    
    - Closes #N/A (internal refactoring - no issue)
    
    # Rationale for this change
    
    Noticed while writing tests in #9144, that the current tests could be
    re-written to be easier to read/re-use.
    
    ⚠️ FIY, I used claude to refactor those tests, I read the changes and we
    are keeping the same test cases.
    
    The Date64 boundary tests in `arrow-arith/src/numeric.rs` had
    significant code duplication. Each test function for Date64 operations
    (`to_naive_date_opt`, `add_year_months_opt`, `subtract_year_months_opt`,
    `add_day_time_opt`, `subtract_day_time_opt`, `add_month_day_nano_opt`,
    `subtract_month_day_nano_opt`) repeated similar setup code and boundary
    checks, making the test suite harder to maintain and extend.
    
    # What changes are included in this PR?
    
    This PR refactors the Date64 boundary tests by:
    
    1. **Introducing shared constants** for commonly used values:
    - `MAX_VALID_DATE`, `MIN_VALID_DATE`, `EPOCH` - NaiveDate constants for
    chrono's valid date range
    
    2. **Adding utility functions** to reduce repetition:
    - `date_to_millis(year, month, day)` - converts a date to milliseconds
    from epoch
    - `max_valid_millis()`, `min_valid_millis()`, `year_2000_millis()` -
    common millisecond values
    
    3. **Consolidating similar test patterns** into parameterized helper
    functions:
    - `test_year_month_op()` - tests `add_year_months_opt` and
    `subtract_year_months_opt`
    - `test_day_time_op()` - tests `add_day_time_opt` and
    `subtract_day_time_opt`
    - `test_month_day_nano_op()` - tests `add_month_day_nano_opt` and
    `subtract_month_day_nano_opt`
    
    4. **Reducing 8 separate test functions to 4** while maintaining the
    same test coverage
    
    Net result: **-297 lines** (163 added, 460 removed) with equivalent
    functionality.
    
    # Are these changes tested?
    
    Yes - this is a refactoring of existing tests. The same boundary
    conditions and edge cases are still tested, just organized more
    efficiently. Running `cargo test` confirms all tests pass.
    
    # Are there any user-facing changes?
    
    No. This is an internal test refactoring with no changes to public APIs
    or functionality.
    
    ---------
    
    Co-authored-by: Andrew Lamb <[email protected]>
---
 arrow-arith/src/numeric.rs | 636 ++++++++++++---------------------------------
 1 file changed, 161 insertions(+), 475 deletions(-)

diff --git a/arrow-arith/src/numeric.rs b/arrow-arith/src/numeric.rs
index 022a3bb641..6c6d4c8999 100644
--- a/arrow-arith/src/numeric.rs
+++ b/arrow-arith/src/numeric.rs
@@ -944,6 +944,23 @@ mod tests {
     use arrow_buffer::{ScalarBuffer, i256};
     use chrono::{DateTime, NaiveDate};
 
+    // The valid date range of NaiveDate is from January 1, -262143 to 
December 31, 262142 (Gregorian calendar).
+    const MAX_VALID_DATE: NaiveDate = NaiveDate::from_ymd_opt(262142, 12, 
31).unwrap();
+    const MIN_VALID_DATE: NaiveDate = NaiveDate::from_ymd_opt(-262143, 1, 
1).unwrap();
+    const MAX_VALID_MILLIS: i64 = MAX_VALID_DATE
+        .signed_duration_since(EPOCH)
+        .num_milliseconds();
+    const MIN_VALID_MILLIS: i64 = MIN_VALID_DATE
+        .signed_duration_since(EPOCH)
+        .num_milliseconds();
+    const EPOCH: NaiveDate = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
+    const YEAR_2000_MILLIS: i64 = date_to_millis(2000, 1, 1);
+
+    const fn date_to_millis(year: i32, month: u32, day: u32) -> i64 {
+        let date = NaiveDate::from_ymd_opt(year, month, day).unwrap();
+        date.signed_duration_since(EPOCH).num_milliseconds()
+    }
+
     fn test_neg_primitive<T: ArrowPrimitiveType>(
         input: &[T::Native],
         out: Result<&[T::Native], &str>,
@@ -1645,534 +1662,203 @@ mod tests {
     }
 
     #[test]
-    fn test_date64_to_naive_date_opt_boundary_values() {
+    fn test_date64_to_naive_date_opt_boundaries() {
         use arrow_array::types::Date64Type;
 
-        // Date64Type::to_naive_date_opt has boundaries determined by 
NaiveDate's supported range.
-        // The valid date range is from January 1, -262143 to December 31, 
262142 (Gregorian calendar).
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
-        let ms_per_day = 24 * 60 * 60 * 1000i64;
+        const MS_PER_DAY: i64 = 24 * 60 * 60 * 1000;
 
-        // Define the boundary dates using NaiveDate::from_ymd_opt
-        let max_valid_date = NaiveDate::from_ymd_opt(262142, 12, 31).unwrap();
-        let min_valid_date = NaiveDate::from_ymd_opt(-262143, 1, 1).unwrap();
-
-        // Calculate their millisecond values from epoch
-        let max_valid_millis = (max_valid_date - epoch).num_milliseconds();
-        let min_valid_millis = (min_valid_date - epoch).num_milliseconds();
-
-        // Verify these match the expected boundaries in milliseconds
-        assert_eq!(
-            max_valid_millis, 8210266790400000i64,
-            "December 31, 262142 should be 8210266790400000 ms from epoch"
-        );
-        assert_eq!(
-            min_valid_millis, -8334601228800000i64,
-            "January 1, -262143 should be -8334601228800000 ms from epoch"
-        );
+        // Verify boundary millisecond values
+        assert_eq!(MAX_VALID_MILLIS, 8210266790400000i64);
+        assert_eq!(MIN_VALID_MILLIS, -8334601228800000i64);
 
-        // Test that the boundary dates work
-        assert!(
-            Date64Type::to_naive_date_opt(max_valid_millis).is_some(),
-            "December 31, 262142 should return Some"
-        );
-        assert!(
-            Date64Type::to_naive_date_opt(min_valid_millis).is_some(),
-            "January 1, -262143 should return Some"
-        );
+        // Valid boundary dates work
+        assert!(Date64Type::to_naive_date_opt(MAX_VALID_MILLIS).is_some());
+        assert!(Date64Type::to_naive_date_opt(MIN_VALID_MILLIS).is_some());
 
-        // Test that one day beyond the boundaries fails
-        assert!(
-            Date64Type::to_naive_date_opt(max_valid_millis + 
ms_per_day).is_none(),
-            "January 1, 262143 should return None"
-        );
-        assert!(
-            Date64Type::to_naive_date_opt(min_valid_millis - 
ms_per_day).is_none(),
-            "December 31, -262144 should return None"
-        );
+        // Beyond boundaries fail
+        assert!(Date64Type::to_naive_date_opt(MAX_VALID_MILLIS + 
MS_PER_DAY).is_none());
+        assert!(Date64Type::to_naive_date_opt(MIN_VALID_MILLIS - 
MS_PER_DAY).is_none());
 
-        // Test some values well within the valid range
-        assert!(
-            Date64Type::to_naive_date_opt(0).is_some(),
-            "Epoch (1970-01-01) should return Some"
-        );
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-        assert!(
-            Date64Type::to_naive_date_opt(year_2000_millis).is_some(),
-            "Year 2000 should return Some"
-        );
+        // Extreme values fail
+        assert!(Date64Type::to_naive_date_opt(i64::MAX).is_none());
+        assert!(Date64Type::to_naive_date_opt(i64::MIN).is_none());
 
-        // Test extreme values that definitely fail due to Duration constraints
-        assert!(
-            Date64Type::to_naive_date_opt(i64::MAX).is_none(),
-            "i64::MAX should return None"
-        );
-        assert!(
-            Date64Type::to_naive_date_opt(i64::MIN).is_none(),
-            "i64::MIN should return None"
-        );
+        // Common values work
+        assert!(Date64Type::to_naive_date_opt(0).is_some());
+        assert!(Date64Type::to_naive_date_opt(YEAR_2000_MILLIS).is_some());
     }
 
-    #[test]
-    fn test_date64_add_year_months_opt_boundary_values() {
-        use arrow_array::types::Date64Type;
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-        assert!(
-            Date64Type::add_year_months_opt(year_2000_millis, 120).is_some(),
-            "Adding 10 years to year 2000 should succeed"
-        );
-
-        // Test with moderate years that are within chrono's safe range
-        let large_year = NaiveDate::from_ymd_opt(5000, 1, 1).unwrap();
-        let large_year_millis = (large_year - epoch).num_milliseconds();
-        assert!(
-            Date64Type::add_year_months_opt(large_year_millis, 12).is_some(),
-            "Adding 12 months to year 5000 should succeed"
-        );
-
-        let neg_year = NaiveDate::from_ymd_opt(-5000, 12, 31).unwrap();
-        let neg_year_millis = (neg_year - epoch).num_milliseconds();
-        assert!(
-            Date64Type::add_year_months_opt(neg_year_millis, -12).is_some(),
-            "Subtracting 12 months from year -5000 should succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::add_year_months_opt(i64::MAX, 1).is_none(),
-            "Adding months to i64::MAX should fail"
-        );
+    fn test_year_month_op<F>(op: F, op_name: &str)
+    where
+        F: Fn(i64, i32) -> Option<i64>,
+    {
+        // Normal operations succeed
+        assert!(op(YEAR_2000_MILLIS, 120).is_some(), "{op_name}: normal add");
         assert!(
-            Date64Type::add_year_months_opt(i64::MIN, -1).is_none(),
-            "Subtracting months from i64::MIN should fail"
+            op(YEAR_2000_MILLIS, 0).is_some(),
+            "{op_name}: zero interval"
         );
 
-        // Test edge case: adding zero should always work for valid dates
-        assert!(
-            Date64Type::add_year_months_opt(year_2000_millis, 0).is_some(),
-            "Adding zero months should always succeed for valid dates"
-        );
+        // Large but valid years work
+        let large_year = date_to_millis(5000, 1, 1);
+        let neg_year = date_to_millis(-5000, 12, 31);
+        assert!(op(large_year, 12).is_some(), "{op_name}: large year");
+        assert!(op(neg_year, -12).is_some(), "{op_name}: negative year");
     }
 
     #[test]
-    fn test_date64_add_day_time_opt_boundary_values() {
+    fn test_date64_year_month_operations() {
         use arrow_array::types::Date64Type;
-        use arrow_buffer::IntervalDayTime;
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
 
-        // Test with a date far from the boundary but still testing the 
function
-        let near_max_date = NaiveDate::from_ymd_opt(200000, 12, 1).unwrap();
-        let near_max_millis = (near_max_date - epoch).num_milliseconds();
-
-        // Adding 30 days should succeed
-        let interval_30_days = IntervalDayTime::new(30, 0);
-        assert!(
-            Date64Type::add_day_time_opt(near_max_millis, 
interval_30_days).is_some(),
-            "Adding 30 days to large year should succeed"
-        );
-
-        // Adding a very large number of days should fail
-        let interval_large_days = IntervalDayTime::new(100000000, 0);
-        assert!(
-            Date64Type::add_day_time_opt(near_max_millis, 
interval_large_days).is_none(),
-            "Adding 100M days to large year should fail"
-        );
-
-        // Test with a date far from the boundary in the negative direction
-        let near_min_date = NaiveDate::from_ymd_opt(-200000, 2, 1).unwrap();
-        let near_min_millis = (near_min_date - epoch).num_milliseconds();
-
-        // Subtracting 30 days should succeed
-        let interval_minus_30_days = IntervalDayTime::new(-30, 0);
-        assert!(
-            Date64Type::add_day_time_opt(near_min_millis, 
interval_minus_30_days).is_some(),
-            "Subtracting 30 days from large negative year should succeed"
-        );
-
-        // Subtracting a very large number of days should fail
-        let interval_minus_large_days = IntervalDayTime::new(-100000000, 0);
-        assert!(
-            Date64Type::add_day_time_opt(near_min_millis, 
interval_minus_large_days).is_none(),
-            "Subtracting 100M days from large negative year should fail"
-        );
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-        let interval_1000_days = IntervalDayTime::new(1000, 12345);
-        assert!(
-            Date64Type::add_day_time_opt(year_2000_millis, 
interval_1000_days).is_some(),
-            "Adding 1000 days and time to year 2000 should succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        let interval_one_day = IntervalDayTime::new(1, 0);
-        assert!(
-            Date64Type::add_day_time_opt(i64::MAX, interval_one_day).is_none(),
-            "Adding interval to i64::MAX should fail"
-        );
-        assert!(
-            Date64Type::add_day_time_opt(i64::MIN, IntervalDayTime::new(-1, 
0)).is_none(),
-            "Subtracting interval from i64::MIN should fail"
-        );
-
-        // Test with extreme interval values
-        let max_interval = IntervalDayTime::new(i32::MAX, i32::MAX);
-        assert!(
-            Date64Type::add_day_time_opt(0, max_interval).is_none(),
-            "Adding extreme interval should fail"
-        );
-
-        let min_interval = IntervalDayTime::new(i32::MIN, i32::MIN);
-        assert!(
-            Date64Type::add_day_time_opt(0, min_interval).is_none(),
-            "Adding extreme negative interval should fail"
-        );
-
-        // Test millisecond overflow within a day
-        let large_ms_interval = IntervalDayTime::new(0, i32::MAX);
-        assert!(
-            Date64Type::add_day_time_opt(year_2000_millis, 
large_ms_interval).is_some(),
-            "Adding large milliseconds within valid range should succeed"
-        );
+        test_year_month_op(Date64Type::add_year_months_opt, "add_year_months");
+        test_year_month_op(Date64Type::subtract_year_months_opt, 
"subtract_year_months");
     }
 
-    #[test]
-    fn test_date64_add_month_day_nano_opt_boundary_values() {
-        use arrow_array::types::Date64Type;
-        use arrow_buffer::IntervalMonthDayNano;
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
-
-        // Test with a large year that is still within chrono's safe range
-        let near_max_date = NaiveDate::from_ymd_opt(5000, 11, 1).unwrap();
-        let near_max_millis = (near_max_date - epoch).num_milliseconds();
-
-        // Adding 1 month and 30 days should succeed
-        let interval_safe = IntervalMonthDayNano::new(1, 30, 0);
-        assert!(
-            Date64Type::add_month_day_nano_opt(near_max_millis, 
interval_safe).is_some(),
-            "Adding 1 month 30 days to large year should succeed"
-        );
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-
-        // Test edge case: adding zero should always work for valid dates
-        let zero_interval = IntervalMonthDayNano::new(0, 0, 0);
-        assert!(
-            Date64Type::add_month_day_nano_opt(year_2000_millis, 
zero_interval).is_some(),
-            "Adding zero interval should always succeed for valid dates"
-        );
-
-        // Test with a negative year that is still within chrono's safe range
-        let near_min_date = NaiveDate::from_ymd_opt(-5000, 2, 28).unwrap();
-        let near_min_millis = (near_min_date - epoch).num_milliseconds();
-
-        // Subtracting 1 month and 30 days should succeed
-        let interval_safe_neg = IntervalMonthDayNano::new(-1, -30, 0);
-        assert!(
-            Date64Type::add_month_day_nano_opt(near_min_millis, 
interval_safe_neg).is_some(),
-            "Subtracting 1 month 30 days from large negative year should 
succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::add_month_day_nano_opt(i64::MAX, 
IntervalMonthDayNano::new(1, 0, 0))
-                .is_none(),
-            "Adding interval to i64::MAX should fail"
-        );
-
-        let interval_normal = IntervalMonthDayNano::new(2, 10, 
123_456_789_000);
-        assert!(
-            Date64Type::add_month_day_nano_opt(year_2000_millis, 
interval_normal).is_some(),
-            "Adding 2 months, 10 days, and nanos to year 2000 should succeed"
-        );
+    fn test_day_time_op<F>(op: F, op_name: &str, is_subtract: bool)
+    where
+        F: Fn(i64, IntervalDayTime) -> Option<i64>,
+    {
+        // Moderate intervals succeed
+        assert!(
+            op(YEAR_2000_MILLIS, IntervalDayTime::new(30, 0)).is_some(),
+            "{op_name}: +30 days"
+        );
+        assert!(
+            op(YEAR_2000_MILLIS, IntervalDayTime::new(-30, 0)).is_some(),
+            "{op_name}: -30 days"
+        );
+        assert!(
+            op(YEAR_2000_MILLIS, IntervalDayTime::new(1000, 12345)).is_some(),
+            "{op_name}: normal"
+        );
+
+        // Overflow handling
+        if is_subtract {
+            assert!(
+                op(MIN_VALID_MILLIS, IntervalDayTime::new(1, 0)).is_none(),
+                "{op_name}: overflow days from min"
+            );
+            assert!(
+                op(MAX_VALID_MILLIS, IntervalDayTime::new(-1, 0)).is_none(),
+                "{op_name}: overflow neg days from max"
+            );
+        } else {
+            assert!(
+                op(MAX_VALID_MILLIS, IntervalDayTime::new(1, 0)).is_none(),
+                "{op_name}: overflow days"
+            );
+            assert!(
+                op(MIN_VALID_MILLIS, IntervalDayTime::new(-1, 0)).is_none(),
+                "{op_name}: overflow neg days"
+            );
+        }
 
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::add_month_day_nano_opt(i64::MAX, 
IntervalMonthDayNano::new(1, 0, 0))
-                .is_none(),
-            "Adding interval to i64::MAX should fail"
-        );
+        // Extreme intervals fail
         assert!(
-            Date64Type::add_month_day_nano_opt(i64::MIN, 
IntervalMonthDayNano::new(-1, 0, 0))
-                .is_none(),
-            "Subtracting interval from i64::MIN should fail"
+            op(0, IntervalDayTime::new(i32::MAX, i32::MAX)).is_none(),
+            "{op_name}: max interval"
         );
-
-        // Test with invalid timestamp input (the _opt function should handle 
these gracefully)
-
-        // Test nanosecond precision (should not affect boundary since it's < 
1ms)
-        let nano_interval = IntervalMonthDayNano::new(0, 0, 999_999_999);
         assert!(
-            Date64Type::add_month_day_nano_opt(year_2000_millis, 
nano_interval).is_some(),
-            "Adding nanoseconds within valid range should succeed"
+            op(0, IntervalDayTime::new(i32::MIN, i32::MIN)).is_none(),
+            "{op_name}: min interval"
         );
 
-        // Test large nanosecond values that convert to milliseconds
-        let large_nano_interval = IntervalMonthDayNano::new(0, 0, 
86_400_000_000_000); // 1 day in nanos
+        // Large ms within valid range succeed
         assert!(
-            Date64Type::add_month_day_nano_opt(year_2000_millis, 
large_nano_interval).is_some(),
-            "Adding 1 day worth of nanoseconds should succeed"
+            op(YEAR_2000_MILLIS, IntervalDayTime::new(0, i32::MAX)).is_some(),
+            "{op_name}: large ms"
         );
     }
 
     #[test]
-    fn test_date64_subtract_year_months_opt_boundary_values() {
+    fn test_date64_day_time_operations() {
         use arrow_array::types::Date64Type;
 
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
-
-        // Test with a negative year that is still within chrono's safe range
-        let near_min_date = NaiveDate::from_ymd_opt(-5000, 12, 31).unwrap();
-        let near_min_millis = (near_min_date - epoch).num_milliseconds();
-
-        // Subtracting 12 months should succeed
-        assert!(
-            Date64Type::subtract_year_months_opt(near_min_millis, 
12).is_some(),
-            "Subtracting 12 months from year -5000 should succeed"
-        );
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-
-        // Test edge case: subtracting zero should always work for valid dates
-        assert!(
-            Date64Type::subtract_year_months_opt(year_2000_millis, 
0).is_some(),
-            "Subtracting zero months should always succeed for valid dates"
-        );
-
-        // Test with a large year that is still within chrono's safe range
-        let near_max_date = NaiveDate::from_ymd_opt(5000, 1, 1).unwrap();
-        let near_max_millis = (near_max_date - epoch).num_milliseconds();
-
-        // Adding 12 months (subtracting negative) should succeed
-        assert!(
-            Date64Type::subtract_year_months_opt(near_max_millis, 
-12).is_some(),
-            "Adding 12 months to year 5000 should succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::subtract_year_months_opt(i64::MAX, -1).is_none(),
-            "Adding months to i64::MAX should fail"
-        );
-
-        assert!(
-            Date64Type::subtract_year_months_opt(year_2000_millis, 
12).is_some(),
-            "Subtracting 1 year from year 2000 should succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::subtract_year_months_opt(i64::MAX, -1).is_none(),
-            "Adding months to i64::MAX should fail"
-        );
-        assert!(
-            Date64Type::subtract_year_months_opt(i64::MIN, 1).is_none(),
-            "Subtracting months from i64::MIN should fail"
-        );
-
-        // Test edge case: subtracting zero should always work for valid dates
-        let valid_date = NaiveDate::from_ymd_opt(2020, 6, 15).unwrap();
-        let valid_millis = (valid_date - epoch).num_milliseconds();
-        assert!(
-            Date64Type::subtract_year_months_opt(valid_millis, 0).is_some(),
-            "Subtracting zero months should always succeed for valid dates"
-        );
+        test_day_time_op(Date64Type::add_day_time_opt, "add_day_time", false);
+        test_day_time_op(Date64Type::subtract_day_time_opt, 
"subtract_day_time", true);
     }
 
-    #[test]
-    fn test_date64_subtract_day_time_opt_boundary_values() {
-        use arrow_array::types::Date64Type;
-        use arrow_buffer::IntervalDayTime;
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
-
-        // Test with a date far from the boundary in the negative direction
-        let near_min_date = NaiveDate::from_ymd_opt(-200000, 2, 1).unwrap();
-        let near_min_millis = (near_min_date - epoch).num_milliseconds();
-
-        // Subtracting 30 days should succeed
-        let interval_30_days = IntervalDayTime::new(30, 0);
-        assert!(
-            Date64Type::subtract_day_time_opt(near_min_millis, 
interval_30_days).is_some(),
-            "Subtracting 30 days from large negative year should succeed"
-        );
-
-        // Subtracting a very large number of days should fail
-        let interval_large_days = IntervalDayTime::new(100000000, 0);
-        assert!(
-            Date64Type::subtract_day_time_opt(near_min_millis, 
interval_large_days).is_none(),
-            "Subtracting 100M days from large negative year should fail"
-        );
-
-        // Test with a date far from the boundary but still testing the 
function
-        let near_max_date = NaiveDate::from_ymd_opt(200000, 12, 1).unwrap();
-        let near_max_millis = (near_max_date - epoch).num_milliseconds();
-
-        // Adding 30 days (subtracting negative) should succeed
-        let interval_minus_30_days = IntervalDayTime::new(-30, 0);
-        assert!(
-            Date64Type::subtract_day_time_opt(near_max_millis, 
interval_minus_30_days).is_some(),
-            "Adding 30 days to large year should succeed"
-        );
-
-        // Adding a very large number of days should fail
-        let interval_minus_large_days = IntervalDayTime::new(-100000000, 0);
-        assert!(
-            Date64Type::subtract_day_time_opt(near_max_millis, 
interval_minus_large_days).is_none(),
-            "Adding 100M days to large year should fail"
-        );
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-        let interval_1000_days = IntervalDayTime::new(1000, 12345);
-        assert!(
-            Date64Type::subtract_day_time_opt(year_2000_millis, 
interval_1000_days).is_some(),
-            "Subtracting 1000 days and time from year 2000 should succeed"
-        );
+    fn test_month_day_nano_op<F>(op: F, op_name: &str, is_subtract: bool)
+    where
+        F: Fn(i64, IntervalMonthDayNano) -> Option<i64>,
+    {
+        let zero = IntervalMonthDayNano::new(0, 0, 0);
 
-        // Test with extreme input values that would cause overflow
-        let interval_one_day = IntervalDayTime::new(1, 0);
         assert!(
-            Date64Type::subtract_day_time_opt(i64::MIN, 
interval_one_day).is_none(),
-            "Subtracting interval from i64::MIN should fail"
+            op(YEAR_2000_MILLIS, IntervalMonthDayNano::new(1, 30, 
0)).is_some(),
+            "{op_name}: +1mo +30d"
         );
         assert!(
-            Date64Type::subtract_day_time_opt(i64::MAX, 
IntervalDayTime::new(-1, 0)).is_none(),
-            "Adding interval to i64::MAX should fail"
+            op(YEAR_2000_MILLIS, IntervalMonthDayNano::new(-1, -30, 
0)).is_some(),
+            "{op_name}: -1mo -30d"
         );
-
-        // Test with extreme interval values
-        let max_interval = IntervalDayTime::new(i32::MAX, i32::MAX);
         assert!(
-            Date64Type::subtract_day_time_opt(0, max_interval).is_none(),
-            "Subtracting extreme interval should fail"
+            op(YEAR_2000_MILLIS, zero).is_some(),
+            "{op_name}: zero interval"
         );
-
-        let min_interval = IntervalDayTime::new(i32::MIN, i32::MIN);
         assert!(
-            Date64Type::subtract_day_time_opt(0, min_interval).is_none(),
-            "Subtracting extreme negative interval should fail"
-        );
+            op(
+                YEAR_2000_MILLIS,
+                IntervalMonthDayNano::new(2, 10, 123_456_789_000)
+            )
+            .is_some(),
+            "{op_name}: normal"
+        );
+
+        // overflow handling
+        if is_subtract {
+            assert!(
+                op(MIN_VALID_MILLIS, IntervalMonthDayNano::new(0, 1, 
0)).is_none(),
+                "{op_name}: overflow days from min"
+            );
+            assert!(
+                op(MAX_VALID_MILLIS, IntervalMonthDayNano::new(0, -1, 
0)).is_none(),
+                "{op_name}: overflow neg days from max"
+            );
+        } else {
+            assert!(
+                op(MAX_VALID_MILLIS, IntervalMonthDayNano::new(0, 1, 
0)).is_none(),
+                "{op_name}: overflow days"
+            );
+            assert!(
+                op(MIN_VALID_MILLIS, IntervalMonthDayNano::new(0, -1, 
0)).is_none(),
+                "{op_name}: overflow neg days"
+            );
+        }
 
-        // Test millisecond precision
-        let large_ms_interval = IntervalDayTime::new(0, i32::MAX);
+        // Nanosecond precision works
         assert!(
-            Date64Type::subtract_day_time_opt(year_2000_millis, 
large_ms_interval).is_some(),
-            "Subtracting large milliseconds within valid range should succeed"
+            op(
+                YEAR_2000_MILLIS,
+                IntervalMonthDayNano::new(0, 0, 999_999_999)
+            )
+            .is_some(),
+            "{op_name}: nanos"
         );
-
-        // Test edge case: subtracting zero should always work for valid dates
-        let zero_interval = IntervalDayTime::new(0, 0);
-        let valid_date = NaiveDate::from_ymd_opt(2020, 6, 15).unwrap();
-        let valid_millis = (valid_date - epoch).num_milliseconds();
+        // 1 day in nanos
         assert!(
-            Date64Type::subtract_day_time_opt(valid_millis, 
zero_interval).is_some(),
-            "Subtracting zero interval should always succeed for valid dates"
+            op(
+                YEAR_2000_MILLIS,
+                IntervalMonthDayNano::new(0, 0, 86_400_000_000_000)
+            )
+            .is_some(),
+            "{op_name}: 1 day nanos"
         );
     }
 
     #[test]
-    fn test_date64_subtract_month_day_nano_opt_boundary_values() {
+    fn test_date64_month_day_nano_operations() {
         use arrow_array::types::Date64Type;
-        use arrow_buffer::IntervalMonthDayNano;
-
-        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
 
-        // Test with a negative year that is still within chrono's safe range
-        let near_min_date = NaiveDate::from_ymd_opt(-5000, 2, 28).unwrap();
-        let near_min_millis = (near_min_date - epoch).num_milliseconds();
-
-        // Subtracting 1 month and 30 days should succeed
-        let interval_safe = IntervalMonthDayNano::new(1, 30, 0);
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(near_min_millis, 
interval_safe).is_some(),
-            "Subtracting 1 month 30 days from large negative year should 
succeed"
-        );
-
-        // Test normal case within valid range
-        let year_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
-        let year_2000_millis = (year_2000 - epoch).num_milliseconds();
-
-        // Test edge case: subtracting zero should always work for valid dates
-        let zero_interval = IntervalMonthDayNano::new(0, 0, 0);
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(year_2000_millis, 
zero_interval).is_some(),
-            "Subtracting zero interval should always succeed for valid dates"
+        test_month_day_nano_op(
+            Date64Type::add_month_day_nano_opt,
+            "add_month_day_nano",
+            false,
         );
-
-        // Test with a large year that is still within chrono's safe range
-        let near_max_date = NaiveDate::from_ymd_opt(5000, 11, 1).unwrap();
-        let near_max_millis = (near_max_date - epoch).num_milliseconds();
-
-        // Adding 1 month and 30 days (subtracting negative) should succeed
-        let interval_safe_neg = IntervalMonthDayNano::new(-1, -30, 0);
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(near_max_millis, 
interval_safe_neg).is_some(),
-            "Adding 1 month 30 days to large year should succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(i64::MIN, 
IntervalMonthDayNano::new(1, 0, 0))
-                .is_none(),
-            "Subtracting interval from i64::MIN should fail"
-        );
-
-        let interval_normal = IntervalMonthDayNano::new(2, 10, 
123_456_789_000);
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(year_2000_millis, 
interval_normal).is_some(),
-            "Subtracting 2 months, 10 days, and nanos from year 2000 should 
succeed"
-        );
-
-        // Test with extreme input values that would cause overflow
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(i64::MIN, 
IntervalMonthDayNano::new(1, 0, 0))
-                .is_none(),
-            "Subtracting interval from i64::MIN should fail"
-        );
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(i64::MAX, 
IntervalMonthDayNano::new(-1, 0, 0))
-                .is_none(),
-            "Adding interval to i64::MAX should fail"
-        );
-
-        // Test nanosecond precision (should not affect boundary since it's < 
1ms)
-        let nano_interval = IntervalMonthDayNano::new(0, 0, 999_999_999);
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(year_2000_millis, 
nano_interval).is_some(),
-            "Subtracting nanoseconds within valid range should succeed"
-        );
-
-        // Test large nanosecond values that convert to milliseconds
-        let large_nano_interval = IntervalMonthDayNano::new(0, 0, 
86_400_000_000_000); // 1 day in nanos
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(year_2000_millis, 
large_nano_interval)
-                .is_some(),
-            "Subtracting 1 day worth of nanoseconds should succeed"
-        );
-
-        // Test edge case: subtracting zero should always work for valid dates
-        let zero_interval = IntervalMonthDayNano::new(0, 0, 0);
-        let valid_date = NaiveDate::from_ymd_opt(2020, 6, 15).unwrap();
-        let valid_millis = (valid_date - epoch).num_milliseconds();
-        assert!(
-            Date64Type::subtract_month_day_nano_opt(valid_millis, 
zero_interval).is_some(),
-            "Subtracting zero interval should always succeed for valid dates"
+        test_month_day_nano_op(
+            Date64Type::subtract_month_day_nano_opt,
+            "subtract_month_day_nano",
+            true,
         );
     }
 }

Reply via email to