Coverage Report

Created: 2025-08-26 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/andrewlamb/Software/arrow-rs/arrow-buffer/src/interval.rs
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
use crate::arith::derive_arith;
19
use std::ops::Neg;
20
21
/// Value of an IntervalMonthDayNano array
22
///
23
///  ## Representation
24
///
25
/// This type is stored as a single 128 bit integer, interpreted as three
26
/// different signed integral fields:
27
///
28
/// 1. The number of months (32 bits)
29
/// 2. The number days (32 bits)
30
/// 2. The number of nanoseconds (64 bits).
31
///
32
/// Nanoseconds does not allow for leap seconds.
33
///
34
/// Each field is independent (e.g. there is no constraint that the quantity of
35
/// nanoseconds represents less than a day's worth of time).
36
///
37
/// ```text
38
/// ┌───────────────┬─────────────┬─────────────────────────────┐
39
/// │     Months    │     Days    │            Nanos            │
40
/// │   (32 bits)   │  (32 bits)  │          (64 bits)          │
41
/// └───────────────┴─────────────┴─────────────────────────────┘
42
/// 0            32             64                           128 bit offset
43
/// ```
44
/// Please see the [Arrow Spec](https://github.com/apache/arrow/blob/081b4022fe6f659d8765efc82b3f4787c5039e3c/format/Schema.fbs#L409-L415) for more details
45
///
46
///## Note on Comparing and Ordering for Calendar Types
47
///
48
/// Values of `IntervalMonthDayNano` are compared using their binary
49
/// representation, which can lead to surprising results.
50
///
51
/// Spans of time measured in calendar units are not fixed in absolute size (e.g.
52
/// number of seconds) which makes defining comparisons and ordering non trivial.
53
/// For example `1 month` is 28 days for February but `1 month` is 31 days
54
/// in December.
55
///
56
/// This makes the seemingly simple operation of comparing two intervals
57
/// complicated in practice. For example is `1 month` more or less than `30
58
/// days`? The answer depends on what month you are talking about.
59
///
60
/// This crate defines comparisons for calendar types using their binary
61
/// representation which is fast and efficient, but leads
62
/// to potentially surprising results.
63
///
64
/// For example a
65
/// `IntervalMonthDayNano` of `1 month` will compare as **greater** than a
66
/// `IntervalMonthDayNano` of `100 days` because the binary representation of `1 month`
67
/// is larger than the binary representation of 100 days.
68
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
69
#[repr(C)]
70
pub struct IntervalMonthDayNano {
71
    /// Number of months
72
    pub months: i32,
73
    /// Number of days
74
    pub days: i32,
75
    /// Number of nanoseconds
76
    pub nanoseconds: i64,
77
}
78
79
impl IntervalMonthDayNano {
80
    /// The additive identity i.e. `0`.
81
    pub const ZERO: Self = Self::new(0, 0, 0);
82
83
    /// The multiplicative identity, i.e. `1`.
84
    pub const ONE: Self = Self::new(1, 1, 1);
85
86
    /// The multiplicative inverse, i.e. `-1`.
87
    pub const MINUS_ONE: Self = Self::new(-1, -1, -1);
88
89
    /// The maximum value that can be represented
90
    pub const MAX: Self = Self::new(i32::MAX, i32::MAX, i64::MAX);
91
92
    /// The minimum value that can be represented
93
    pub const MIN: Self = Self::new(i32::MIN, i32::MIN, i64::MIN);
94
95
    /// Create a new [`IntervalMonthDayNano`]
96
    #[inline]
97
6
    pub const fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
98
6
        Self {
99
6
            months,
100
6
            days,
101
6
            nanoseconds,
102
6
        }
103
6
    }
104
105
    /// Computes the absolute value
106
    #[inline]
107
    pub fn wrapping_abs(self) -> Self {
108
        Self {
109
            months: self.months.wrapping_abs(),
110
            days: self.days.wrapping_abs(),
111
            nanoseconds: self.nanoseconds.wrapping_abs(),
112
        }
113
    }
114
115
    /// Computes the absolute value
116
    #[inline]
117
    pub fn checked_abs(self) -> Option<Self> {
118
        Some(Self {
119
            months: self.months.checked_abs()?,
120
            days: self.days.checked_abs()?,
121
            nanoseconds: self.nanoseconds.checked_abs()?,
122
        })
123
    }
124
125
    /// Negates the value
126
    #[inline]
127
    pub fn wrapping_neg(self) -> Self {
128
        Self {
129
            months: self.months.wrapping_neg(),
130
            days: self.days.wrapping_neg(),
131
            nanoseconds: self.nanoseconds.wrapping_neg(),
132
        }
133
    }
134
135
    /// Negates the value
136
    #[inline]
137
0
    pub fn checked_neg(self) -> Option<Self> {
138
        Some(Self {
139
0
            months: self.months.checked_neg()?,
140
0
            days: self.days.checked_neg()?,
141
0
            nanoseconds: self.nanoseconds.checked_neg()?,
142
        })
143
0
    }
144
145
    /// Performs wrapping addition
146
    #[inline]
147
    pub fn wrapping_add(self, other: Self) -> Self {
148
        Self {
149
            months: self.months.wrapping_add(other.months),
150
            days: self.days.wrapping_add(other.days),
151
            nanoseconds: self.nanoseconds.wrapping_add(other.nanoseconds),
152
        }
153
    }
154
155
    /// Performs checked addition
156
    #[inline]
157
0
    pub fn checked_add(self, other: Self) -> Option<Self> {
158
        Some(Self {
159
0
            months: self.months.checked_add(other.months)?,
160
0
            days: self.days.checked_add(other.days)?,
161
0
            nanoseconds: self.nanoseconds.checked_add(other.nanoseconds)?,
162
        })
163
0
    }
164
165
    /// Performs wrapping subtraction
166
    #[inline]
167
    pub fn wrapping_sub(self, other: Self) -> Self {
168
        Self {
169
            months: self.months.wrapping_sub(other.months),
170
            days: self.days.wrapping_sub(other.days),
171
            nanoseconds: self.nanoseconds.wrapping_sub(other.nanoseconds),
172
        }
173
    }
174
175
    /// Performs checked subtraction
176
    #[inline]
177
0
    pub fn checked_sub(self, other: Self) -> Option<Self> {
178
        Some(Self {
179
0
            months: self.months.checked_sub(other.months)?,
180
0
            days: self.days.checked_sub(other.days)?,
181
0
            nanoseconds: self.nanoseconds.checked_sub(other.nanoseconds)?,
182
        })
183
0
    }
184
185
    /// Performs wrapping multiplication
186
    #[inline]
187
    pub fn wrapping_mul(self, other: Self) -> Self {
188
        Self {
189
            months: self.months.wrapping_mul(other.months),
190
            days: self.days.wrapping_mul(other.days),
191
            nanoseconds: self.nanoseconds.wrapping_mul(other.nanoseconds),
192
        }
193
    }
194
195
    /// Performs checked multiplication
196
0
    pub fn checked_mul(self, other: Self) -> Option<Self> {
197
        Some(Self {
198
0
            months: self.months.checked_mul(other.months)?,
199
0
            days: self.days.checked_mul(other.days)?,
200
0
            nanoseconds: self.nanoseconds.checked_mul(other.nanoseconds)?,
201
        })
202
0
    }
203
204
    /// Performs wrapping division
205
    #[inline]
206
    pub fn wrapping_div(self, other: Self) -> Self {
207
        Self {
208
            months: self.months.wrapping_div(other.months),
209
            days: self.days.wrapping_div(other.days),
210
            nanoseconds: self.nanoseconds.wrapping_div(other.nanoseconds),
211
        }
212
    }
213
214
    /// Performs checked division
215
0
    pub fn checked_div(self, other: Self) -> Option<Self> {
216
        Some(Self {
217
0
            months: self.months.checked_div(other.months)?,
218
0
            days: self.days.checked_div(other.days)?,
219
0
            nanoseconds: self.nanoseconds.checked_div(other.nanoseconds)?,
220
        })
221
0
    }
222
223
    /// Performs wrapping remainder
224
    #[inline]
225
    pub fn wrapping_rem(self, other: Self) -> Self {
226
        Self {
227
            months: self.months.wrapping_rem(other.months),
228
            days: self.days.wrapping_rem(other.days),
229
            nanoseconds: self.nanoseconds.wrapping_rem(other.nanoseconds),
230
        }
231
    }
232
233
    /// Performs checked remainder
234
0
    pub fn checked_rem(self, other: Self) -> Option<Self> {
235
        Some(Self {
236
0
            months: self.months.checked_rem(other.months)?,
237
0
            days: self.days.checked_rem(other.days)?,
238
0
            nanoseconds: self.nanoseconds.checked_rem(other.nanoseconds)?,
239
        })
240
0
    }
241
242
    /// Performs wrapping exponentiation
243
    #[inline]
244
    pub fn wrapping_pow(self, exp: u32) -> Self {
245
        Self {
246
            months: self.months.wrapping_pow(exp),
247
            days: self.days.wrapping_pow(exp),
248
            nanoseconds: self.nanoseconds.wrapping_pow(exp),
249
        }
250
    }
251
252
    /// Performs checked exponentiation
253
    #[inline]
254
    pub fn checked_pow(self, exp: u32) -> Option<Self> {
255
        Some(Self {
256
            months: self.months.checked_pow(exp)?,
257
            days: self.days.checked_pow(exp)?,
258
            nanoseconds: self.nanoseconds.checked_pow(exp)?,
259
        })
260
    }
261
}
262
263
impl Neg for IntervalMonthDayNano {
264
    type Output = Self;
265
266
    #[cfg(debug_assertions)]
267
0
    fn neg(self) -> Self::Output {
268
0
        self.checked_neg().expect("IntervalMonthDayNano overflow")
269
0
    }
270
271
    #[cfg(not(debug_assertions))]
272
    fn neg(self) -> Self::Output {
273
        self.wrapping_neg()
274
    }
275
}
276
277
derive_arith!(
278
    IntervalMonthDayNano,
279
    Add,
280
    AddAssign,
281
    add,
282
    add_assign,
283
    wrapping_add,
284
    checked_add
285
);
286
derive_arith!(
287
    IntervalMonthDayNano,
288
    Sub,
289
    SubAssign,
290
    sub,
291
    sub_assign,
292
    wrapping_sub,
293
    checked_sub
294
);
295
derive_arith!(
296
    IntervalMonthDayNano,
297
    Mul,
298
    MulAssign,
299
    mul,
300
    mul_assign,
301
    wrapping_mul,
302
    checked_mul
303
);
304
derive_arith!(
305
    IntervalMonthDayNano,
306
    Div,
307
    DivAssign,
308
    div,
309
    div_assign,
310
    wrapping_div,
311
    checked_div
312
);
313
derive_arith!(
314
    IntervalMonthDayNano,
315
    Rem,
316
    RemAssign,
317
    rem,
318
    rem_assign,
319
    wrapping_rem,
320
    checked_rem
321
);
322
323
/// Value of an IntervalDayTime array
324
///
325
/// ## Representation
326
///
327
/// This type is stored as a single 64 bit integer, interpreted as two i32
328
/// fields:
329
///
330
/// 1. the number of elapsed days
331
/// 2. The number of milliseconds (no leap seconds),
332
///
333
/// ```text
334
/// ┌──────────────┬──────────────┐
335
/// │     Days     │ Milliseconds │
336
/// │  (32 bits)   │  (32 bits)   │
337
/// └──────────────┴──────────────┘
338
/// 0              31            63 bit offset
339
/// ```
340
///
341
/// Please see the [Arrow Spec](https://github.com/apache/arrow/blob/081b4022fe6f659d8765efc82b3f4787c5039e3c/format/Schema.fbs#L406-L408) for more details
342
///
343
/// ## Note on Comparing and Ordering for Calendar Types
344
///
345
/// Values of `IntervalDayTime` are compared using their binary representation,
346
/// which can lead to surprising results. Please see the description of ordering on
347
/// [`IntervalMonthDayNano`] for more details
348
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
349
#[repr(C)]
350
pub struct IntervalDayTime {
351
    /// Number of days
352
    pub days: i32,
353
    /// Number of milliseconds
354
    pub milliseconds: i32,
355
}
356
357
impl IntervalDayTime {
358
    /// The additive identity i.e. `0`.
359
    pub const ZERO: Self = Self::new(0, 0);
360
361
    /// The multiplicative identity, i.e. `1`.
362
    pub const ONE: Self = Self::new(1, 1);
363
364
    /// The multiplicative inverse, i.e. `-1`.
365
    pub const MINUS_ONE: Self = Self::new(-1, -1);
366
367
    /// The maximum value that can be represented
368
    pub const MAX: Self = Self::new(i32::MAX, i32::MAX);
369
370
    /// The minimum value that can be represented
371
    pub const MIN: Self = Self::new(i32::MIN, i32::MIN);
372
373
    /// Create a new [`IntervalDayTime`]
374
    #[inline]
375
0
    pub const fn new(days: i32, milliseconds: i32) -> Self {
376
0
        Self { days, milliseconds }
377
0
    }
378
379
    /// Computes the absolute value
380
    #[inline]
381
    pub fn wrapping_abs(self) -> Self {
382
        Self {
383
            days: self.days.wrapping_abs(),
384
            milliseconds: self.milliseconds.wrapping_abs(),
385
        }
386
    }
387
388
    /// Computes the absolute value
389
    #[inline]
390
    pub fn checked_abs(self) -> Option<Self> {
391
        Some(Self {
392
            days: self.days.checked_abs()?,
393
            milliseconds: self.milliseconds.checked_abs()?,
394
        })
395
    }
396
397
    /// Negates the value
398
    #[inline]
399
    pub fn wrapping_neg(self) -> Self {
400
        Self {
401
            days: self.days.wrapping_neg(),
402
            milliseconds: self.milliseconds.wrapping_neg(),
403
        }
404
    }
405
406
    /// Negates the value
407
    #[inline]
408
0
    pub fn checked_neg(self) -> Option<Self> {
409
        Some(Self {
410
0
            days: self.days.checked_neg()?,
411
0
            milliseconds: self.milliseconds.checked_neg()?,
412
        })
413
0
    }
414
415
    /// Performs wrapping addition
416
    #[inline]
417
    pub fn wrapping_add(self, other: Self) -> Self {
418
        Self {
419
            days: self.days.wrapping_add(other.days),
420
            milliseconds: self.milliseconds.wrapping_add(other.milliseconds),
421
        }
422
    }
423
424
    /// Performs checked addition
425
    #[inline]
426
0
    pub fn checked_add(self, other: Self) -> Option<Self> {
427
        Some(Self {
428
0
            days: self.days.checked_add(other.days)?,
429
0
            milliseconds: self.milliseconds.checked_add(other.milliseconds)?,
430
        })
431
0
    }
432
433
    /// Performs wrapping subtraction
434
    #[inline]
435
    pub fn wrapping_sub(self, other: Self) -> Self {
436
        Self {
437
            days: self.days.wrapping_sub(other.days),
438
            milliseconds: self.milliseconds.wrapping_sub(other.milliseconds),
439
        }
440
    }
441
442
    /// Performs checked subtraction
443
    #[inline]
444
0
    pub fn checked_sub(self, other: Self) -> Option<Self> {
445
        Some(Self {
446
0
            days: self.days.checked_sub(other.days)?,
447
0
            milliseconds: self.milliseconds.checked_sub(other.milliseconds)?,
448
        })
449
0
    }
450
451
    /// Performs wrapping multiplication
452
    #[inline]
453
    pub fn wrapping_mul(self, other: Self) -> Self {
454
        Self {
455
            days: self.days.wrapping_mul(other.days),
456
            milliseconds: self.milliseconds.wrapping_mul(other.milliseconds),
457
        }
458
    }
459
460
    /// Performs checked multiplication
461
0
    pub fn checked_mul(self, other: Self) -> Option<Self> {
462
        Some(Self {
463
0
            days: self.days.checked_mul(other.days)?,
464
0
            milliseconds: self.milliseconds.checked_mul(other.milliseconds)?,
465
        })
466
0
    }
467
468
    /// Performs wrapping division
469
    #[inline]
470
    pub fn wrapping_div(self, other: Self) -> Self {
471
        Self {
472
            days: self.days.wrapping_div(other.days),
473
            milliseconds: self.milliseconds.wrapping_div(other.milliseconds),
474
        }
475
    }
476
477
    /// Performs checked division
478
0
    pub fn checked_div(self, other: Self) -> Option<Self> {
479
        Some(Self {
480
0
            days: self.days.checked_div(other.days)?,
481
0
            milliseconds: self.milliseconds.checked_div(other.milliseconds)?,
482
        })
483
0
    }
484
485
    /// Performs wrapping remainder
486
    #[inline]
487
    pub fn wrapping_rem(self, other: Self) -> Self {
488
        Self {
489
            days: self.days.wrapping_rem(other.days),
490
            milliseconds: self.milliseconds.wrapping_rem(other.milliseconds),
491
        }
492
    }
493
494
    /// Performs checked remainder
495
0
    pub fn checked_rem(self, other: Self) -> Option<Self> {
496
        Some(Self {
497
0
            days: self.days.checked_rem(other.days)?,
498
0
            milliseconds: self.milliseconds.checked_rem(other.milliseconds)?,
499
        })
500
0
    }
501
502
    /// Performs wrapping exponentiation
503
    #[inline]
504
    pub fn wrapping_pow(self, exp: u32) -> Self {
505
        Self {
506
            days: self.days.wrapping_pow(exp),
507
            milliseconds: self.milliseconds.wrapping_pow(exp),
508
        }
509
    }
510
511
    /// Performs checked exponentiation
512
    #[inline]
513
    pub fn checked_pow(self, exp: u32) -> Option<Self> {
514
        Some(Self {
515
            days: self.days.checked_pow(exp)?,
516
            milliseconds: self.milliseconds.checked_pow(exp)?,
517
        })
518
    }
519
}
520
521
impl Neg for IntervalDayTime {
522
    type Output = Self;
523
524
    #[cfg(debug_assertions)]
525
0
    fn neg(self) -> Self::Output {
526
0
        self.checked_neg().expect("IntervalDayMillisecond overflow")
527
0
    }
528
529
    #[cfg(not(debug_assertions))]
530
    fn neg(self) -> Self::Output {
531
        self.wrapping_neg()
532
    }
533
}
534
535
derive_arith!(
536
    IntervalDayTime,
537
    Add,
538
    AddAssign,
539
    add,
540
    add_assign,
541
    wrapping_add,
542
    checked_add
543
);
544
derive_arith!(
545
    IntervalDayTime,
546
    Sub,
547
    SubAssign,
548
    sub,
549
    sub_assign,
550
    wrapping_sub,
551
    checked_sub
552
);
553
derive_arith!(
554
    IntervalDayTime,
555
    Mul,
556
    MulAssign,
557
    mul,
558
    mul_assign,
559
    wrapping_mul,
560
    checked_mul
561
);
562
derive_arith!(
563
    IntervalDayTime,
564
    Div,
565
    DivAssign,
566
    div,
567
    div_assign,
568
    wrapping_div,
569
    checked_div
570
);
571
derive_arith!(
572
    IntervalDayTime,
573
    Rem,
574
    RemAssign,
575
    rem,
576
    rem_assign,
577
    wrapping_rem,
578
    checked_rem
579
);