## Coverage Report

#### Created: 2025-11-17 14:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/arrow-rs/arrow-array/src/delta.rs

    Line
    Count
    Source
    1
    // MIT License
    2
    //
    3
    // Copyright (c) 2020-2022 Oliver Margetts
    4
    //
    5
    // Permission is hereby granted, free of charge, to any person obtaining a copy
    6
    // of this software and associated documentation files (the "Software"), to deal
    7
    // in the Software without restriction, including without limitation the rights
    8
    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    9
    // copies of the Software, and to permit persons to whom the Software is
    10
    // furnished to do so, subject to the following conditions:
    11
    //
    12
    // The above copyright notice and this permission notice shall be included in all
    13
    // copies or substantial portions of the Software.
    14
    //
    15
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16
    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17
    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18
    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19
    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20
    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21
    // SOFTWARE.
    22
    23
    // Copied from chronoutil crate
    24
    25
    //! Contains utility functions for shifting Date objects.
    26
    use chrono::{DateTime, Datelike, Days, Months, TimeZone};
    27
    use std::cmp::Ordering;
    28
    29
    /// Shift a date by the given number of months.
    30
    0
    pub(crate) fn shift_months<D>(date: D, months: i32) -> D
    31
    0
    where
    32
    0
        D: Datelike + std::ops::Add<Months, Output = D> + std::ops::Sub<Months, Output = D>,
    33
    {
    34
    0
        match months.cmp(&0) {
    35
    0
            Ordering::Equal => date,
    36
    0
            Ordering::Greater => date + Months::new(months as u32),
    37
    0
            Ordering::Less => date - Months::new(months.unsigned_abs()),
    38
        }
    39
    0
    }
    40
    41
    /// Add the given number of months to the given datetime.
    42
    ///
    43
    /// Returns `None` when it will result in overflow.
    44
    0
    pub(crate) fn add_months_datetime<Tz: TimeZone>(
    45
    0
        dt: DateTime<Tz>,
    46
    0
        months: i32,
    47
    0
    ) -> Option<DateTime<Tz>> {
    48
    0
        match months.cmp(&0) {
    49
    0
            Ordering::Equal => Some(dt),
    50
    0
            Ordering::Greater => dt.checked_add_months(Months::new(months as u32)),
    51
    0
            Ordering::Less => dt.checked_sub_months(Months::new(months.unsigned_abs())),
    52
        }
    53
    0
    }
    54
    55
    /// Add the given number of days to the given datetime.
    56
    ///
    57
    /// Returns `None` when it will result in overflow.
    58
    0
    pub(crate) fn add_days_datetime<Tz: TimeZone>(dt: DateTime<Tz>, days: i32) -> Option<DateTime<Tz>> {
    59
    0
        match days.cmp(&0) {
    60
    0
            Ordering::Equal => Some(dt),
    61
    0
            Ordering::Greater => dt.checked_add_days(Days::new(days as u64)),
    62
    0
            Ordering::Less => dt.checked_sub_days(Days::new(days.unsigned_abs() as u64)),
    63
        }
    64
    0
    }
    65
    66
    /// Substract the given number of months to the given datetime.
    67
    ///
    68
    /// Returns `None` when it will result in overflow.
    69
    0
    pub(crate) fn sub_months_datetime<Tz: TimeZone>(
    70
    0
        dt: DateTime<Tz>,
    71
    0
        months: i32,
    72
    0
    ) -> Option<DateTime<Tz>> {
    73
    0
        match months.cmp(&0) {
    74
    0
            Ordering::Equal => Some(dt),
    75
    0
            Ordering::Greater => dt.checked_sub_months(Months::new(months as u32)),
    76
    0
            Ordering::Less => dt.checked_add_months(Months::new(months.unsigned_abs())),
    77
        }
    78
    0
    }
    79
    80
    /// Substract the given number of days to the given datetime.
    81
    ///
    82
    /// Returns `None` when it will result in overflow.
    83
    0
    pub(crate) fn sub_days_datetime<Tz: TimeZone>(dt: DateTime<Tz>, days: i32) -> Option<DateTime<Tz>> {
    84
    0
        match days.cmp(&0) {
    85
    0
            Ordering::Equal => Some(dt),
    86
    0
            Ordering::Greater => dt.checked_sub_days(Days::new(days as u64)),
    87
    0
            Ordering::Less => dt.checked_add_days(Days::new(days.unsigned_abs() as u64)),
    88
        }
    89
    0
    }
    90
    91
    #[cfg(test)]
    92
    mod tests {
    93
    94
        use chrono::naive::{NaiveDate, NaiveDateTime, NaiveTime};
    95
    96
        use super::*;
    97
    98
        #[test]
    99
        fn test_shift_months() {
    100
            let base = NaiveDate::from_ymd_opt(2020, 1, 31).unwrap();
    101
    102
            assert_eq!(
    103
                shift_months(base, 0),
    104
                NaiveDate::from_ymd_opt(2020, 1, 31).unwrap()
    105
            );
    106
            assert_eq!(
    107
                shift_months(base, 1),
    108
                NaiveDate::from_ymd_opt(2020, 2, 29).unwrap()
    109
            );
    110
            assert_eq!(
    111
                shift_months(base, 2),
    112
                NaiveDate::from_ymd_opt(2020, 3, 31).unwrap()
    113
            );
    114
            assert_eq!(
    115
                shift_months(base, 3),
    116
                NaiveDate::from_ymd_opt(2020, 4, 30).unwrap()
    117
            );
    118
            assert_eq!(
    119
                shift_months(base, 4),
    120
                NaiveDate::from_ymd_opt(2020, 5, 31).unwrap()
    121
            );
    122
            assert_eq!(
    123
                shift_months(base, 5),
    124
                NaiveDate::from_ymd_opt(2020, 6, 30).unwrap()
    125
            );
    126
            assert_eq!(
    127
                shift_months(base, 6),
    128
                NaiveDate::from_ymd_opt(2020, 7, 31).unwrap()
    129
            );
    130
            assert_eq!(
    131
                shift_months(base, 7),
    132
                NaiveDate::from_ymd_opt(2020, 8, 31).unwrap()
    133
            );
    134
            assert_eq!(
    135
                shift_months(base, 8),
    136
                NaiveDate::from_ymd_opt(2020, 9, 30).unwrap()
    137
            );
    138
            assert_eq!(
    139
                shift_months(base, 9),
    140
                NaiveDate::from_ymd_opt(2020, 10, 31).unwrap()
    141
            );
    142
            assert_eq!(
    143
                shift_months(base, 10),
    144
                NaiveDate::from_ymd_opt(2020, 11, 30).unwrap()
    145
            );
    146
            assert_eq!(
    147
                shift_months(base, 11),
    148
                NaiveDate::from_ymd_opt(2020, 12, 31).unwrap()
    149
            );
    150
            assert_eq!(
    151
                shift_months(base, 12),
    152
                NaiveDate::from_ymd_opt(2021, 1, 31).unwrap()
    153
            );
    154
            assert_eq!(
    155
                shift_months(base, 13),
    156
                NaiveDate::from_ymd_opt(2021, 2, 28).unwrap()
    157
            );
    158
    159
            assert_eq!(
    160
                shift_months(base, -1),
    161
                NaiveDate::from_ymd_opt(2019, 12, 31).unwrap()
    162
            );
    163
            assert_eq!(
    164
                shift_months(base, -2),
    165
                NaiveDate::from_ymd_opt(2019, 11, 30).unwrap()
    166
            );
    167
            assert_eq!(
    168
                shift_months(base, -3),
    169
                NaiveDate::from_ymd_opt(2019, 10, 31).unwrap()
    170
            );
    171
            assert_eq!(
    172
                shift_months(base, -4),
    173
                NaiveDate::from_ymd_opt(2019, 9, 30).unwrap()
    174
            );
    175
            assert_eq!(
    176
                shift_months(base, -5),
    177
                NaiveDate::from_ymd_opt(2019, 8, 31).unwrap()
    178
            );
    179
            assert_eq!(
    180
                shift_months(base, -6),
    181
                NaiveDate::from_ymd_opt(2019, 7, 31).unwrap()
    182
            );
    183
            assert_eq!(
    184
                shift_months(base, -7),
    185
                NaiveDate::from_ymd_opt(2019, 6, 30).unwrap()
    186
            );
    187
            assert_eq!(
    188
                shift_months(base, -8),
    189
                NaiveDate::from_ymd_opt(2019, 5, 31).unwrap()
    190
            );
    191
            assert_eq!(
    192
                shift_months(base, -9),
    193
                NaiveDate::from_ymd_opt(2019, 4, 30).unwrap()
    194
            );
    195
            assert_eq!(
    196
                shift_months(base, -10),
    197
                NaiveDate::from_ymd_opt(2019, 3, 31).unwrap()
    198
            );
    199
            assert_eq!(
    200
                shift_months(base, -11),
    201
                NaiveDate::from_ymd_opt(2019, 2, 28).unwrap()
    202
            );
    203
            assert_eq!(
    204
                shift_months(base, -12),
    205
                NaiveDate::from_ymd_opt(2019, 1, 31).unwrap()
    206
            );
    207
            assert_eq!(
    208
                shift_months(base, -13),
    209
                NaiveDate::from_ymd_opt(2018, 12, 31).unwrap()
    210
            );
    211
    212
            assert_eq!(
    213
                shift_months(base, 1265),
    214
                NaiveDate::from_ymd_opt(2125, 6, 30).unwrap()
    215
            );
    216
        }
    217
    218
        #[test]
    219
        fn test_shift_months_with_overflow() {
    220
            let base = NaiveDate::from_ymd_opt(2020, 12, 31).unwrap();
    221
    222
            assert_eq!(shift_months(base, 0), base);
    223
            assert_eq!(
    224
                shift_months(base, 1),
    225
                NaiveDate::from_ymd_opt(2021, 1, 31).unwrap()
    226
            );
    227
            assert_eq!(
    228
                shift_months(base, 2),
    229
                NaiveDate::from_ymd_opt(2021, 2, 28).unwrap()
    230
            );
    231
            assert_eq!(
    232
                shift_months(base, 12),
    233
                NaiveDate::from_ymd_opt(2021, 12, 31).unwrap()
    234
            );
    235
            assert_eq!(
    236
                shift_months(base, 18),
    237
                NaiveDate::from_ymd_opt(2022, 6, 30).unwrap()
    238
            );
    239
    240
            assert_eq!(
    241
                shift_months(base, -1),
    242
                NaiveDate::from_ymd_opt(2020, 11, 30).unwrap()
    243
            );
    244
            assert_eq!(
    245
                shift_months(base, -2),
    246
                NaiveDate::from_ymd_opt(2020, 10, 31).unwrap()
    247
            );
    248
            assert_eq!(
    249
                shift_months(base, -10),
    250
                NaiveDate::from_ymd_opt(2020, 2, 29).unwrap()
    251
            );
    252
            assert_eq!(
    253
                shift_months(base, -12),
    254
                NaiveDate::from_ymd_opt(2019, 12, 31).unwrap()
    255
            );
    256
            assert_eq!(
    257
                shift_months(base, -18),
    258
                NaiveDate::from_ymd_opt(2019, 6, 30).unwrap()
    259
            );
    260
        }
    261
    262
        #[test]
    263
        fn test_shift_months_datetime() {
    264
            let date = NaiveDate::from_ymd_opt(2020, 1, 31).unwrap();
    265
            let o_clock = NaiveTime::from_hms_opt(1, 2, 3).unwrap();
    266
    267
            let base = NaiveDateTime::new(date, o_clock);
    268
    269
            assert_eq!(
    270
                shift_months(base, 0).date(),
    271
                NaiveDate::from_ymd_opt(2020, 1, 31).unwrap()
    272
            );
    273
            assert_eq!(
    274
                shift_months(base, 1).date(),
    275
                NaiveDate::from_ymd_opt(2020, 2, 29).unwrap()
    276
            );
    277
            assert_eq!(
    278
                shift_months(base, 2).date(),
    279
                NaiveDate::from_ymd_opt(2020, 3, 31).unwrap()
    280
            );
    281
            assert_eq!(shift_months(base, 0).time(), o_clock);
    282
            assert_eq!(shift_months(base, 1).time(), o_clock);
    283
            assert_eq!(shift_months(base, 2).time(), o_clock);
    284
        }
    285
    }