tank_core/
interval.rs

1use std::{
2    hash::Hash,
3    ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6#[derive(Default, Debug, Clone, Copy)]
7pub struct Interval {
8    pub months: i64,
9    pub days: i64,
10    pub nanos: i128,
11}
12
13impl Interval {
14    pub const DAYS_IN_MONTH: f64 = 30.0;
15    pub const DAYS_IN_MONTH_AVG: f64 = 30.436875;
16    pub const SECS_IN_DAY: i64 = 60 * 60 * 24;
17    pub const NANOS_IN_SEC: i128 = 1_000_000_000;
18    pub const NANOS_IN_DAY: i128 = Self::SECS_IN_DAY as i128 * Self::NANOS_IN_SEC;
19
20    pub fn new(months: i64, days: i64, nanos: i128) -> Self {
21        Self {
22            months,
23            days,
24            nanos,
25        }
26    }
27
28    pub const fn from_duration(duration: &std::time::Duration) -> Self {
29        Self {
30            months: 0,
31            days: 0,
32            nanos: duration.as_nanos() as i128,
33        }
34    }
35
36    pub const fn from_nanos(value: i128) -> Self {
37        Self {
38            months: 0,
39            days: (value / Self::NANOS_IN_DAY) as _,
40            nanos: (value % Self::NANOS_IN_DAY),
41        }
42    }
43
44    pub const fn from_micros(value: i128) -> Self {
45        const MICROS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000_000) as _;
46        Self {
47            months: 0,
48            days: (value / MICROS_IN_DAY) as _,
49            nanos: (value % MICROS_IN_DAY) * 1_000,
50        }
51    }
52
53    pub const fn from_millis(value: i128) -> Self {
54        const MILLIS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000) as _;
55        Self {
56            months: 0,
57            days: (value / MILLIS_IN_DAY) as _,
58            nanos: ((value % MILLIS_IN_DAY) * 1_000_000),
59        }
60    }
61
62    pub const fn from_secs(value: i64) -> Self {
63        Self {
64            months: 0,
65            days: (value / Self::SECS_IN_DAY) as _,
66            nanos: ((value % Self::SECS_IN_DAY) * 1_000_000_000) as _,
67        }
68    }
69
70    pub const fn from_mins(value: i64) -> Self {
71        const MINS_IN_DAYS: i64 = 60 * 24;
72        Self {
73            months: 0,
74            days: (value / MINS_IN_DAYS),
75            nanos: ((value % MINS_IN_DAYS) * 60 * 1_000_000_000) as _,
76        }
77    }
78
79    pub const fn from_days(value: i64) -> Self {
80        Self {
81            months: 0,
82            days: value,
83            nanos: 0,
84        }
85    }
86
87    pub const fn from_weeks(value: i64) -> Self {
88        Self {
89            months: 0,
90            days: value * 7,
91            nanos: 0,
92        }
93    }
94
95    pub const fn from_months(value: i64) -> Self {
96        Self {
97            months: value,
98            days: 0,
99            nanos: 0,
100        }
101    }
102
103    pub const fn from_years(value: i64) -> Self {
104        Self {
105            months: value * 12,
106            days: 0,
107            nanos: 0,
108        }
109    }
110
111    pub const fn is_zero(&self) -> bool {
112        self.months == 0 && self.days == 0 && self.nanos == 0
113    }
114
115    pub const fn as_duration(&self, days_in_month: f64) -> std::time::Duration {
116        let nanos = (self.months as f64) * days_in_month * (Interval::NANOS_IN_DAY as f64); // months
117        let nanos = nanos as i128 + self.days as i128 * Interval::NANOS_IN_DAY; // days
118        let nanos = nanos + self.nanos as i128;
119        let secs = (nanos / Interval::NANOS_IN_SEC) as u64;
120        let nanos = (nanos % Interval::NANOS_IN_SEC) as u32;
121        std::time::Duration::new(secs, nanos)
122    }
123}
124
125impl PartialEq for Interval {
126    fn eq(&self, other: &Self) -> bool {
127        self.months == other.months
128            && self.days as i128 * Interval::NANOS_IN_DAY + self.nanos
129                == other.days as i128 * Interval::NANOS_IN_DAY + other.nanos
130    }
131}
132
133impl Eq for Interval {}
134
135impl Hash for Interval {
136    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
137        self.months.hash(state);
138        (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos).hash(state);
139    }
140}
141
142macro_rules! sum_intervals {
143    ($lhs:ident $op:tt $rhs:ident) => {{
144        let days_total = $lhs.days as i128 $op $rhs.days as i128
145            + $lhs.nanos / Interval::NANOS_IN_DAY $op $rhs.nanos / Interval::NANOS_IN_DAY;
146        let days = days_total.clamp(i64::MIN as _, i64::MAX as _);
147        let mut nanos = $lhs.nanos % Interval::NANOS_IN_DAY + $rhs.nanos % Interval::NANOS_IN_DAY;
148        if days != days_total {
149            nanos += (days_total - days) * Interval::NANOS_IN_DAY;
150        }
151        Interval {
152            months: $lhs.months $op $rhs.months,
153            days: days as _,
154            nanos,
155        }
156    }};
157}
158
159impl Add for Interval {
160    type Output = Interval;
161
162    fn add(self, rhs: Self) -> Self {
163        sum_intervals!(self + rhs)
164    }
165}
166
167impl AddAssign for Interval {
168    fn add_assign(&mut self, rhs: Self) {
169        *self = sum_intervals!(self + rhs);
170    }
171}
172
173impl Sub for Interval {
174    type Output = Interval;
175
176    fn sub(self, rhs: Self) -> Self::Output {
177        sum_intervals!(self - rhs)
178    }
179}
180
181impl SubAssign for Interval {
182    fn sub_assign(&mut self, rhs: Self) {
183        *self = sum_intervals!(self - rhs);
184    }
185}
186
187impl From<std::time::Duration> for Interval {
188    fn from(value: std::time::Duration) -> Self {
189        Self {
190            months: 0,
191            days: value.as_secs() as i64 / Interval::SECS_IN_DAY,
192            nanos: (value.as_secs() as i64 % Interval::SECS_IN_DAY) as i128
193                * Interval::NANOS_IN_SEC
194                + value.subsec_nanos() as i128,
195        }
196    }
197}
198
199impl From<Interval> for std::time::Duration {
200    fn from(value: Interval) -> Self {
201        value.as_duration(Interval::DAYS_IN_MONTH)
202    }
203}
204
205impl From<time::Duration> for Interval {
206    fn from(value: time::Duration) -> Self {
207        let seconds = value.whole_seconds();
208        Self {
209            months: 0,
210            days: seconds / Interval::SECS_IN_DAY,
211            nanos: ((seconds % Interval::SECS_IN_DAY) * Interval::NANOS_IN_SEC as i64
212                + value.subsec_nanoseconds() as i64) as i128,
213        }
214    }
215}
216
217impl From<Interval> for time::Duration {
218    fn from(value: Interval) -> Self {
219        let seconds = ((value.days + value.months * Interval::DAYS_IN_MONTH as i64)
220            * Interval::SECS_IN_DAY) as i128
221            + value.nanos * Interval::NANOS_IN_SEC;
222        let nanos = (value.nanos % Interval::NANOS_IN_SEC) as i32;
223        time::Duration::new(seconds as i64, nanos)
224    }
225}