Skip to main content

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