common_time/
interval.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::hash::Hash;
16
17use arrow::datatypes::IntervalUnit as ArrowIntervalUnit;
18use serde::{Deserialize, Serialize};
19
20#[derive(
21    Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
22)]
23pub enum IntervalUnit {
24    /// Indicates the number of elapsed whole months, stored as 4-byte integers.
25    YearMonth,
26    /// Indicates the number of elapsed days and milliseconds,
27    /// stored as 2 contiguous 32-bit integers (days, milliseconds) (8-bytes in total).
28    DayTime,
29    /// A triple of the number of elapsed months, days, and nanoseconds.
30    /// The values are stored contiguously in 16 byte blocks. Months and
31    /// days are encoded as 32 bit integers and nanoseconds is encoded as a
32    /// 64 bit integer. All integers are signed. Each field is independent
33    /// (e.g. there is no constraint that nanoseconds have the same sign
34    /// as days or that the quantity of nanoseconds represents less
35    /// than a day's worth of time).
36    #[default]
37    MonthDayNano,
38}
39
40impl From<&ArrowIntervalUnit> for IntervalUnit {
41    fn from(unit: &ArrowIntervalUnit) -> Self {
42        match unit {
43            ArrowIntervalUnit::YearMonth => IntervalUnit::YearMonth,
44            ArrowIntervalUnit::DayTime => IntervalUnit::DayTime,
45            ArrowIntervalUnit::MonthDayNano => IntervalUnit::MonthDayNano,
46        }
47    }
48}
49
50impl From<ArrowIntervalUnit> for IntervalUnit {
51    fn from(unit: ArrowIntervalUnit) -> Self {
52        (&unit).into()
53    }
54}
55
56// The `Value` type requires Serialize, Deserialize.
57#[derive(
58    Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
59)]
60#[repr(C)]
61pub struct IntervalYearMonth {
62    /// Number of months
63    pub months: i32,
64}
65
66impl IntervalYearMonth {
67    pub fn new(months: i32) -> Self {
68        Self { months }
69    }
70
71    pub fn to_i32(&self) -> i32 {
72        self.months
73    }
74
75    pub fn from_i32(months: i32) -> Self {
76        Self { months }
77    }
78
79    pub fn negative(&self) -> Self {
80        Self::new(-self.months)
81    }
82
83    pub fn to_iso8601_string(&self) -> String {
84        IntervalFormat::from(*self).to_iso8601_string()
85    }
86}
87
88impl From<IntervalYearMonth> for IntervalFormat {
89    fn from(interval: IntervalYearMonth) -> Self {
90        IntervalFormat {
91            years: interval.months / 12,
92            months: interval.months % 12,
93            ..Default::default()
94        }
95    }
96}
97
98impl From<i32> for IntervalYearMonth {
99    fn from(v: i32) -> Self {
100        Self::from_i32(v)
101    }
102}
103
104impl From<IntervalYearMonth> for i32 {
105    fn from(v: IntervalYearMonth) -> Self {
106        v.to_i32()
107    }
108}
109
110impl From<IntervalYearMonth> for serde_json::Value {
111    fn from(v: IntervalYearMonth) -> Self {
112        serde_json::Value::from(v.to_i32())
113    }
114}
115
116#[derive(
117    Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
118)]
119#[repr(C)]
120pub struct IntervalDayTime {
121    /// Number of days
122    pub days: i32,
123    /// Number of milliseconds
124    pub milliseconds: i32,
125}
126
127impl IntervalDayTime {
128    /// The additive identity i.e. `0`.
129    pub const ZERO: Self = Self::new(0, 0);
130
131    /// The multiplicative inverse, i.e. `-1`.
132    pub const MINUS_ONE: Self = Self::new(-1, -1);
133
134    /// The maximum value that can be represented
135    pub const MAX: Self = Self::new(i32::MAX, i32::MAX);
136
137    /// The minimum value that can be represented
138    pub const MIN: Self = Self::new(i32::MIN, i32::MIN);
139
140    pub const fn new(days: i32, milliseconds: i32) -> Self {
141        Self { days, milliseconds }
142    }
143
144    pub fn to_i64(&self) -> i64 {
145        let d = (self.days as u64 & u32::MAX as u64) << 32;
146        let m = self.milliseconds as u64 & u32::MAX as u64;
147        (d | m) as i64
148    }
149
150    pub fn from_i64(value: i64) -> Self {
151        let days = (value >> 32) as i32;
152        let milliseconds = value as i32;
153        Self { days, milliseconds }
154    }
155
156    pub fn negative(&self) -> Self {
157        Self::new(-self.days, -self.milliseconds)
158    }
159
160    pub fn to_iso8601_string(&self) -> String {
161        IntervalFormat::from(*self).to_iso8601_string()
162    }
163
164    pub fn as_millis(&self) -> i64 {
165        self.days as i64 * MS_PER_DAY + self.milliseconds as i64
166    }
167}
168
169impl From<i64> for IntervalDayTime {
170    fn from(v: i64) -> Self {
171        Self::from_i64(v)
172    }
173}
174
175impl From<IntervalDayTime> for i64 {
176    fn from(v: IntervalDayTime) -> Self {
177        v.to_i64()
178    }
179}
180
181impl From<IntervalDayTime> for serde_json::Value {
182    fn from(v: IntervalDayTime) -> Self {
183        serde_json::Value::from(v.to_i64())
184    }
185}
186
187impl From<arrow::datatypes::IntervalDayTime> for IntervalDayTime {
188    fn from(value: arrow::datatypes::IntervalDayTime) -> Self {
189        Self {
190            days: value.days,
191            milliseconds: value.milliseconds,
192        }
193    }
194}
195
196impl From<IntervalDayTime> for arrow::datatypes::IntervalDayTime {
197    fn from(value: IntervalDayTime) -> Self {
198        Self {
199            days: value.days,
200            milliseconds: value.milliseconds,
201        }
202    }
203}
204
205// Millisecond convert to other time unit
206pub const MS_PER_SEC: i64 = 1_000;
207pub const MS_PER_MINUTE: i64 = 60 * MS_PER_SEC;
208pub const MS_PER_HOUR: i64 = 60 * MS_PER_MINUTE;
209pub const MS_PER_DAY: i64 = 24 * MS_PER_HOUR;
210pub const NANOS_PER_MILLI: i64 = 1_000_000;
211
212impl From<IntervalDayTime> for IntervalFormat {
213    fn from(interval: IntervalDayTime) -> Self {
214        IntervalFormat {
215            days: interval.days,
216            hours: interval.milliseconds as i64 / MS_PER_HOUR,
217            minutes: (interval.milliseconds as i64 % MS_PER_HOUR) / MS_PER_MINUTE,
218            seconds: (interval.milliseconds as i64 % MS_PER_MINUTE) / MS_PER_SEC,
219            microseconds: (interval.milliseconds as i64 % MS_PER_SEC) * MS_PER_SEC,
220            ..Default::default()
221        }
222    }
223}
224
225#[derive(
226    Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
227)]
228#[repr(C)]
229pub struct IntervalMonthDayNano {
230    /// Number of months
231    pub months: i32,
232    /// Number of days
233    pub days: i32,
234    /// Number of nanoseconds
235    pub nanoseconds: i64,
236}
237
238impl IntervalMonthDayNano {
239    /// The additive identity i.e. `0`.
240    pub const ZERO: Self = Self::new(0, 0, 0);
241
242    /// The multiplicative inverse, i.e. `-1`.
243    pub const MINUS_ONE: Self = Self::new(-1, -1, -1);
244
245    /// The maximum value that can be represented
246    pub const MAX: Self = Self::new(i32::MAX, i32::MAX, i64::MAX);
247
248    /// The minimum value that can be represented
249    pub const MIN: Self = Self::new(i32::MIN, i32::MIN, i64::MIN);
250
251    pub const fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
252        Self {
253            months,
254            days,
255            nanoseconds,
256        }
257    }
258
259    pub fn to_i128(&self) -> i128 {
260        let m = (self.months as u128 & u32::MAX as u128) << 96;
261        let d = (self.days as u128 & u32::MAX as u128) << 64;
262        let n = self.nanoseconds as u128 & u64::MAX as u128;
263        (m | d | n) as i128
264    }
265
266    pub fn from_i128(value: i128) -> Self {
267        let months = (value >> 96) as i32;
268        let days = (value >> 64) as i32;
269        let nanoseconds = value as i64;
270        Self {
271            months,
272            days,
273            nanoseconds,
274        }
275    }
276
277    pub fn negative(&self) -> Self {
278        Self::new(-self.months, -self.days, -self.nanoseconds)
279    }
280
281    pub fn to_iso8601_string(&self) -> String {
282        IntervalFormat::from(*self).to_iso8601_string()
283    }
284}
285
286impl From<i128> for IntervalMonthDayNano {
287    fn from(v: i128) -> Self {
288        Self::from_i128(v)
289    }
290}
291
292impl From<IntervalMonthDayNano> for i128 {
293    fn from(v: IntervalMonthDayNano) -> Self {
294        v.to_i128()
295    }
296}
297
298impl From<IntervalMonthDayNano> for serde_json::Value {
299    fn from(v: IntervalMonthDayNano) -> Self {
300        serde_json::Value::from(v.to_i128().to_string())
301    }
302}
303
304impl From<arrow::datatypes::IntervalMonthDayNano> for IntervalMonthDayNano {
305    fn from(value: arrow::datatypes::IntervalMonthDayNano) -> Self {
306        Self {
307            months: value.months,
308            days: value.days,
309            nanoseconds: value.nanoseconds,
310        }
311    }
312}
313
314impl From<IntervalMonthDayNano> for arrow::datatypes::IntervalMonthDayNano {
315    fn from(value: IntervalMonthDayNano) -> Self {
316        Self {
317            months: value.months,
318            days: value.days,
319            nanoseconds: value.nanoseconds,
320        }
321    }
322}
323
324// Nanosecond convert to other time unit
325pub const NS_PER_SEC: i64 = 1_000_000_000;
326pub const NS_PER_MINUTE: i64 = 60 * NS_PER_SEC;
327pub const NS_PER_HOUR: i64 = 60 * NS_PER_MINUTE;
328pub const NS_PER_DAY: i64 = 24 * NS_PER_HOUR;
329
330impl From<IntervalMonthDayNano> for IntervalFormat {
331    fn from(interval: IntervalMonthDayNano) -> Self {
332        IntervalFormat {
333            years: interval.months / 12,
334            months: interval.months % 12,
335            days: interval.days,
336            hours: interval.nanoseconds / NS_PER_HOUR,
337            minutes: (interval.nanoseconds % NS_PER_HOUR) / NS_PER_MINUTE,
338            seconds: (interval.nanoseconds % NS_PER_MINUTE) / NS_PER_SEC,
339            microseconds: (interval.nanoseconds % NS_PER_SEC) / 1_000,
340        }
341    }
342}
343
344pub fn interval_year_month_to_month_day_nano(interval: IntervalYearMonth) -> IntervalMonthDayNano {
345    IntervalMonthDayNano {
346        months: interval.months,
347        days: 0,
348        nanoseconds: 0,
349    }
350}
351
352pub fn interval_day_time_to_month_day_nano(interval: IntervalDayTime) -> IntervalMonthDayNano {
353    IntervalMonthDayNano {
354        months: 0,
355        days: interval.days,
356        nanoseconds: interval.milliseconds as i64 * NANOS_PER_MILLI,
357    }
358}
359
360/// <https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-OUTPUT>
361/// support postgres format, iso8601 format and sql standard format
362#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
363pub struct IntervalFormat {
364    pub years: i32,
365    pub months: i32,
366    pub days: i32,
367    pub hours: i64,
368    pub minutes: i64,
369    pub seconds: i64,
370    pub microseconds: i64,
371}
372
373impl IntervalFormat {
374    /// All the field in the interval is 0
375    pub fn is_zero(&self) -> bool {
376        self.years == 0
377            && self.months == 0
378            && self.days == 0
379            && self.hours == 0
380            && self.minutes == 0
381            && self.seconds == 0
382            && self.microseconds == 0
383    }
384
385    /// Determine if year or month exist
386    pub fn has_year_month(&self) -> bool {
387        self.years != 0 || self.months != 0
388    }
389
390    /// Determine if day exists
391    pub fn has_day(&self) -> bool {
392        self.days != 0
393    }
394
395    /// Determine time part(includes hours, minutes, seconds, microseconds) is positive
396    pub fn has_time_part_positive(&self) -> bool {
397        self.hours > 0 || self.minutes > 0 || self.seconds > 0 || self.microseconds > 0
398    }
399
400    // time part means hours, minutes, seconds, microseconds
401    pub fn has_time_part(&self) -> bool {
402        self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.microseconds != 0
403    }
404
405    /// Convert IntervalFormat to iso8601 format string
406    /// ISO pattern - PnYnMnDTnHnMnS
407    /// for example: P1Y2M3DT4H5M6.789S
408    pub fn to_iso8601_string(&self) -> String {
409        if self.is_zero() {
410            return "PT0S".to_string();
411        }
412        let fract_str = match self.microseconds {
413            0 => String::default(),
414            _ => format!(".{:06}", self.microseconds)
415                .trim_end_matches('0')
416                .to_string(),
417        };
418        format!(
419            "P{}Y{}M{}DT{}H{}M{}{}S",
420            self.years, self.months, self.days, self.hours, self.minutes, self.seconds, fract_str
421        )
422    }
423
424    /// Convert IntervalFormat to sql standard format string
425    /// SQL standard pattern `- [years - months] [days] [hours:minutes:seconds[.fractional seconds]]`
426    /// for example: 1-2 3:4:5.678
427    pub fn to_sql_standard_string(self) -> String {
428        if self.is_zero() {
429            "0".to_string()
430        } else if !self.has_time_part() && !self.has_day() {
431            get_year_month(self.months, self.years, true)
432        } else if !self.has_time_part() && !self.has_year_month() {
433            format!("{} 0:00:00", self.days)
434        } else if !self.has_year_month() && !self.has_day() {
435            get_time_part(
436                self.hours,
437                self.minutes,
438                self.seconds,
439                self.microseconds,
440                self.has_time_part_positive(),
441                true,
442            )
443        } else {
444            let year_month = get_year_month(self.months, self.years, false);
445            let time_interval = get_time_part(
446                self.hours,
447                self.minutes,
448                self.seconds,
449                self.microseconds,
450                self.has_time_part_positive(),
451                false,
452            );
453            format!("{} {:+} {}", year_month, self.days, time_interval)
454        }
455    }
456
457    /// Convert IntervalFormat to postgres format string
458    /// postgres pattern `- [years - months] [days] [hours[:minutes[:seconds[.fractional seconds]]]]`
459    /// for example: -1 year -2 mons +3 days -04:05:06
460    pub fn to_postgres_string(&self) -> String {
461        if self.is_zero() {
462            return "00:00:00".to_string();
463        }
464        let mut result = String::default();
465        if self.has_year_month() {
466            if self.years != 0 {
467                result.push_str(&format!("{} year ", self.years));
468            }
469            if self.months != 0 {
470                result.push_str(&format!("{} mons ", self.months));
471            }
472        }
473        if self.has_day() {
474            result.push_str(&format!("{} days ", self.days));
475        }
476        result.push_str(&self.get_postgres_time_part());
477        result.trim().to_string()
478    }
479
480    /// get postgres time part(include hours, minutes, seconds, microseconds)
481    fn get_postgres_time_part(&self) -> String {
482        let mut time_part = String::default();
483        if self.has_time_part() {
484            let sign = if !self.has_time_part_positive() {
485                "-"
486            } else {
487                ""
488            };
489            let hours = Self::padding_i64(self.hours);
490            time_part.push_str(&format!(
491                "{}{}:{}:{}",
492                sign,
493                hours,
494                Self::padding_i64(self.minutes),
495                Self::padding_i64(self.seconds),
496            ));
497            if self.microseconds != 0 {
498                time_part.push_str(&format!(".{:06}", self.microseconds.unsigned_abs()))
499            }
500        }
501        time_part
502    }
503
504    /// padding i64 to string with 2 digits
505    fn padding_i64(val: i64) -> String {
506        let num = if val < 0 {
507            val.unsigned_abs()
508        } else {
509            val as u64
510        };
511        format!("{:02}", num)
512    }
513}
514
515/// get year month string
516fn get_year_month(mons: i32, years: i32, is_only_year_month: bool) -> String {
517    let months = mons.unsigned_abs();
518    if years == 0 || is_only_year_month {
519        format!("{}-{}", years, months)
520    } else {
521        format!("{:+}-{}", years, months)
522    }
523}
524
525/// get time part string
526fn get_time_part(
527    hours: i64,
528    mins: i64,
529    secs: i64,
530    micros: i64,
531    is_time_part_positive: bool,
532    is_only_time: bool,
533) -> String {
534    let mut interval = String::default();
535    if is_time_part_positive && is_only_time {
536        interval.push_str(&format!("{}:{:02}:{:02}", hours, mins, secs));
537    } else {
538        let minutes = mins.unsigned_abs();
539        let seconds = secs.unsigned_abs();
540        interval.push_str(&format!("{:+}:{:02}:{:02}", hours, minutes, seconds));
541    }
542    if micros != 0 {
543        let microseconds = format!(".{:06}", micros.unsigned_abs());
544        interval.push_str(&microseconds);
545    }
546    interval
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[test]
554    fn test_from_year_month() {
555        let interval = IntervalYearMonth::new(1);
556        assert_eq!(interval.months, 1);
557    }
558
559    #[test]
560    fn test_from_date_time() {
561        let interval = IntervalDayTime::new(1, 2);
562        assert_eq!(interval.days, 1);
563        assert_eq!(interval.milliseconds, 2);
564    }
565
566    #[test]
567    fn test_from_month_day_nano() {
568        let interval = IntervalMonthDayNano::new(1, 2, 3);
569        assert_eq!(interval.months, 1);
570        assert_eq!(interval.days, 2);
571        assert_eq!(interval.nanoseconds, 3);
572    }
573
574    #[test]
575    fn test_interval_i128_convert() {
576        let test_interval_eq = |month, day, nano| {
577            let interval = IntervalMonthDayNano::new(month, day, nano);
578            let interval_i128 = interval.to_i128();
579            let interval2 = IntervalMonthDayNano::from_i128(interval_i128);
580            assert_eq!(interval, interval2);
581        };
582
583        test_interval_eq(1, 2, 3);
584        test_interval_eq(1, -2, 3);
585        test_interval_eq(1, -2, -3);
586        test_interval_eq(-1, -2, -3);
587        test_interval_eq(i32::MAX, i32::MAX, i64::MAX);
588        test_interval_eq(i32::MIN, i32::MAX, i64::MAX);
589        test_interval_eq(i32::MAX, i32::MIN, i64::MAX);
590        test_interval_eq(i32::MAX, i32::MAX, i64::MIN);
591        test_interval_eq(i32::MIN, i32::MIN, i64::MAX);
592        test_interval_eq(i32::MAX, i32::MIN, i64::MIN);
593        test_interval_eq(i32::MIN, i32::MAX, i64::MIN);
594        test_interval_eq(i32::MIN, i32::MIN, i64::MIN);
595
596        let interval = IntervalMonthDayNano::from_i128(1);
597        assert_eq!(interval, IntervalMonthDayNano::new(0, 0, 1));
598        assert_eq!(1, IntervalMonthDayNano::new(0, 0, 1).to_i128());
599    }
600
601    #[test]
602    fn test_interval_i64_convert() {
603        let interval = IntervalDayTime::from_i64(1);
604        assert_eq!(interval, IntervalDayTime::new(0, 1));
605        assert_eq!(1, IntervalDayTime::new(0, 1).to_i64());
606    }
607
608    #[test]
609    fn test_convert_interval_format() {
610        let interval = IntervalMonthDayNano {
611            months: 14,
612            days: 160,
613            nanoseconds: 1000000,
614        };
615        let interval_format = IntervalFormat::from(interval);
616        assert_eq!(interval_format.years, 1);
617        assert_eq!(interval_format.months, 2);
618        assert_eq!(interval_format.days, 160);
619        assert_eq!(interval_format.hours, 0);
620        assert_eq!(interval_format.minutes, 0);
621        assert_eq!(interval_format.seconds, 0);
622        assert_eq!(interval_format.microseconds, 1000);
623    }
624
625    #[test]
626    fn test_to_iso8601_string() {
627        // Test interval zero
628        let interval = IntervalMonthDayNano::new(0, 0, 0);
629        assert_eq!(interval.to_iso8601_string(), "PT0S");
630
631        let interval = IntervalMonthDayNano::new(1, 1, 1);
632        assert_eq!(interval.to_iso8601_string(), "P0Y1M1DT0H0M0S");
633
634        let interval = IntervalMonthDayNano::new(14, 31, 10000000000);
635        assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT0H0M10S");
636
637        let interval = IntervalMonthDayNano::new(14, 31, 23210200000000);
638        assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT6H26M50.2S");
639    }
640
641    #[test]
642    fn test_to_postgres_string() {
643        // Test interval zero
644        let interval = IntervalMonthDayNano::new(0, 0, 0);
645        assert_eq!(
646            IntervalFormat::from(interval).to_postgres_string(),
647            "00:00:00"
648        );
649
650        let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
651        assert_eq!(
652            IntervalFormat::from(interval).to_postgres_string(),
653            "1 year 11 mons 100 days 06:26:50.200000"
654        );
655    }
656
657    #[test]
658    fn test_to_sql_standard_string() {
659        // Test zero interval
660        let interval = IntervalMonthDayNano::new(0, 0, 0);
661        assert_eq!(IntervalFormat::from(interval).to_sql_standard_string(), "0");
662
663        let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
664        assert_eq!(
665            IntervalFormat::from(interval).to_sql_standard_string(),
666            "+1-11 +100 +6:26:50.200000"
667        );
668
669        // Test interval without year, month, day
670        let interval = IntervalMonthDayNano::new(0, 0, 23210200000000);
671        assert_eq!(
672            IntervalFormat::from(interval).to_sql_standard_string(),
673            "6:26:50.200000"
674        );
675    }
676
677    #[test]
678    fn test_from_arrow_interval_unit() {
679        let unit = ArrowIntervalUnit::YearMonth;
680        assert_eq!(IntervalUnit::from(unit), IntervalUnit::YearMonth);
681
682        let unit = ArrowIntervalUnit::DayTime;
683        assert_eq!(IntervalUnit::from(unit), IntervalUnit::DayTime);
684
685        let unit = ArrowIntervalUnit::MonthDayNano;
686        assert_eq!(IntervalUnit::from(unit), IntervalUnit::MonthDayNano);
687    }
688}