1use crate::Result;
2use anyhow::anyhow;
3use std::{
4 hash::Hash,
5 ops::{Add, AddAssign, Neg, Sub, SubAssign},
6};
7
8#[derive(PartialEq, Eq, Clone, Copy, Debug)]
12pub enum IntervalUnit {
13 Year,
14 Month,
15 Day,
16 Hour,
17 Minute,
18 Second,
19 Microsecond,
20 Nanosecond,
21}
22
23impl IntervalUnit {
24 pub fn from_bitmask(mask: u8) -> Result<IntervalUnit> {
25 Ok(match mask {
26 1 => IntervalUnit::Nanosecond,
27 2 => IntervalUnit::Microsecond,
28 4 => IntervalUnit::Second,
29 8 => IntervalUnit::Minute,
30 16 => IntervalUnit::Hour,
31 32 => IntervalUnit::Day,
32 64 => IntervalUnit::Month,
33 128 => IntervalUnit::Year,
34 _ => return Err(anyhow!("Invalid mask, it must be a single bit on")),
35 })
36 }
37}
38
39#[derive(Default, Debug, Clone, Copy)]
43pub struct Interval {
44 pub months: i64,
45 pub days: i64,
46 pub nanos: i128,
47}
48
49impl Interval {
50 pub const ZERO: Interval = Interval {
51 months: 0,
52 days: 0,
53 nanos: 0,
54 };
55
56 pub const DAYS_IN_MONTH: f64 = 30.0;
57 pub const DAYS_IN_MONTH_AVG: f64 = 30.436875;
58 pub const SECS_IN_DAY: i64 = 60 * 60 * 24;
59 pub const NANOS_IN_SEC: i128 = 1_000_000_000;
60 pub const NANOS_IN_HOUR: i128 = Self::NANOS_IN_SEC * 3600;
61 pub const NANOS_IN_DAY: i128 = Self::SECS_IN_DAY as i128 * Self::NANOS_IN_SEC;
62 pub const MICROS_IN_DAY: i128 = Self::SECS_IN_DAY as i128 * 1_000_000;
63
64 pub const fn new(months: i64, days: i64, nanos: i128) -> Self {
65 Self {
66 months,
67 days,
68 nanos,
69 }
70 }
71
72 pub const fn from_duration(duration: &std::time::Duration) -> Self {
73 Self {
74 months: 0,
75 days: 0,
76 nanos: duration.as_nanos() as i128,
77 }
78 }
79
80 pub const fn from_nanos(value: i128) -> Self {
81 Self {
82 months: 0,
83 days: (value / Self::NANOS_IN_DAY) as _,
84 nanos: (value % Self::NANOS_IN_DAY),
85 }
86 }
87
88 pub const fn from_micros(value: i128) -> Self {
89 const MICROS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000_000) as _;
90 Self {
91 months: 0,
92 days: (value / MICROS_IN_DAY) as _,
93 nanos: (value % MICROS_IN_DAY) * 1_000,
94 }
95 }
96
97 pub const fn from_millis(value: i128) -> Self {
98 const MILLIS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000) as _;
99 Self {
100 months: 0,
101 days: (value / MILLIS_IN_DAY) as _,
102 nanos: ((value % MILLIS_IN_DAY) * 1_000_000),
103 }
104 }
105
106 pub const fn from_secs(value: i64) -> Self {
107 Self {
108 months: 0,
109 days: (value / Self::SECS_IN_DAY) as _,
110 nanos: ((value % Self::SECS_IN_DAY) * 1_000_000_000) as _,
111 }
112 }
113
114 pub const fn from_mins(value: i64) -> Self {
115 const MINS_IN_DAYS: i64 = 60 * 24;
116 let days = value / MINS_IN_DAYS;
117 let nanos = (value % MINS_IN_DAYS) * Interval::NANOS_IN_SEC as i64 * 60;
118 Self {
119 months: 0,
120 days,
121 nanos: nanos as _,
122 }
123 }
124
125 pub const fn from_hours(value: i64) -> Self {
126 Self {
127 months: 0,
128 days: (value / 24),
129 nanos: ((value % 24) * Interval::NANOS_IN_HOUR as i64) as _,
130 }
131 }
132
133 pub const fn from_days(value: i64) -> Self {
134 Self {
135 months: 0,
136 days: value,
137 nanos: 0,
138 }
139 }
140
141 pub const fn from_weeks(value: i64) -> Self {
142 Self {
143 months: 0,
144 days: value * 7,
145 nanos: 0,
146 }
147 }
148
149 pub const fn from_months(value: i64) -> Self {
150 Self {
151 months: value,
152 days: 0,
153 nanos: 0,
154 }
155 }
156
157 pub const fn from_years(value: i64) -> Self {
158 Self {
159 months: value * 12,
160 days: 0,
161 nanos: 0,
162 }
163 }
164
165 pub const fn as_hmsns(&self) -> (i128, u8, u8, u32) {
167 let mut nanos = self.nanos;
168 let mut hours = self.nanos / Self::NANOS_IN_HOUR;
169 nanos %= Self::NANOS_IN_HOUR;
170 hours += ((self.months * 30 + self.days) * 24) as i128;
171 if nanos < 0 {
172 nanos = -nanos;
173 }
174 let m = nanos / (60 * Self::NANOS_IN_SEC);
175 nanos %= 60 * Self::NANOS_IN_SEC;
176 let s = nanos / Self::NANOS_IN_SEC;
177 nanos %= Self::NANOS_IN_SEC;
178 (hours, m as _, s as _, nanos as _)
179 }
180
181 pub const fn is_zero(&self) -> bool {
182 self.months == 0 && self.days == 0 && self.nanos == 0
183 }
184
185 pub const fn as_duration(&self, days_in_month: f64) -> std::time::Duration {
189 let nanos = (self.months as f64) * days_in_month * (Interval::NANOS_IN_DAY as f64); let nanos = nanos as i128 + self.days as i128 * Interval::NANOS_IN_DAY; let nanos = nanos + self.nanos as i128;
192 let secs = (nanos / Interval::NANOS_IN_SEC) as u64;
193 let nanos = (nanos % Interval::NANOS_IN_SEC) as u32;
194 std::time::Duration::new(secs, nanos)
195 }
196
197 pub const fn units_and_factors(&self) -> &[(IntervalUnit, i128)] {
198 static UNITS: &[(IntervalUnit, i128)] = &[
199 (IntervalUnit::Year, Interval::NANOS_IN_DAY * 30 * 12),
200 (IntervalUnit::Month, Interval::NANOS_IN_DAY * 30),
201 (IntervalUnit::Day, Interval::NANOS_IN_DAY),
202 (IntervalUnit::Hour, Interval::NANOS_IN_SEC * 3600),
203 (IntervalUnit::Minute, Interval::NANOS_IN_SEC * 60),
204 (IntervalUnit::Second, Interval::NANOS_IN_SEC),
205 (IntervalUnit::Microsecond, 1_000),
206 (IntervalUnit::Nanosecond, 1),
207 ];
208 UNITS
209 }
210
211 pub fn units_mask(&self) -> u8 {
213 let mut mask = 0_u8;
214 if self.months != 0 {
215 if self.months % 12 == 0 {
216 mask |= 1 << 7;
217 } else if self.months != 0 {
218 mask |= 1 << 6;
219 }
220 }
221 let nanos = self.nanos + self.days as i128 * Interval::NANOS_IN_DAY;
222 if nanos != 0 {
223 for (i, &(_, factor)) in self.units_and_factors().iter().skip(2).enumerate() {
224 if nanos % factor == 0 {
225 let shift = 5 - i; mask |= 1 << shift;
227 break;
228 }
229 }
230 }
231 mask
232 }
233
234 pub fn unit_value(&self, unit: IntervalUnit) -> i128 {
236 if unit == IntervalUnit::Year {
237 self.months as i128 / 12
238 } else if unit == IntervalUnit::Month {
239 self.months as i128
240 } else {
241 let factor = *self
242 .units_and_factors()
243 .iter()
244 .find_map(|(u, k)| if *u == unit { Some(k) } else { None })
245 .expect("The unit must be present");
246 (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos) / factor
247 }
248 }
249}
250
251impl PartialEq for Interval {
252 fn eq(&self, other: &Self) -> bool {
253 self.months == other.months
254 && self.days as i128 * Interval::NANOS_IN_DAY + self.nanos
255 == other.days as i128 * Interval::NANOS_IN_DAY + other.nanos
256 }
257}
258
259impl Eq for Interval {}
260
261impl Hash for Interval {
262 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
263 self.months.hash(state);
264 (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos).hash(state);
265 }
266}
267
268macro_rules! sum_intervals {
269 ($lhs:ident $op:tt $rhs:ident) => {{
270 let days_total = $lhs.days as i128 $op $rhs.days as i128
271 + $lhs.nanos / Interval::NANOS_IN_DAY $op $rhs.nanos / Interval::NANOS_IN_DAY;
272 let days = days_total.clamp(i64::MIN as _, i64::MAX as _);
273 let mut nanos = $lhs.nanos % Interval::NANOS_IN_DAY $op $rhs.nanos % Interval::NANOS_IN_DAY;
274 if days != days_total {
275 nanos += (days_total - days) * Interval::NANOS_IN_DAY;
276 }
277 Interval {
278 months: $lhs.months $op $rhs.months,
279 days: days as _,
280 nanos,
281 }
282 }};
283}
284
285impl Add for Interval {
286 type Output = Interval;
287 fn add(self, rhs: Self) -> Self {
288 sum_intervals!(self + rhs)
289 }
290}
291
292impl AddAssign for Interval {
293 fn add_assign(&mut self, rhs: Self) {
294 *self = sum_intervals!(self + rhs);
295 }
296}
297
298impl Sub for Interval {
299 type Output = Interval;
300 fn sub(self, rhs: Self) -> Self::Output {
301 sum_intervals!(self - rhs)
302 }
303}
304
305impl SubAssign for Interval {
306 fn sub_assign(&mut self, rhs: Self) {
307 *self = sum_intervals!(self - rhs);
308 }
309}
310
311impl Neg for Interval {
312 type Output = Interval;
313 fn neg(self) -> Self::Output {
314 Self::default() - self
315 }
316}
317
318impl From<std::time::Duration> for Interval {
319 fn from(value: std::time::Duration) -> Self {
320 Self {
321 months: 0,
322 days: value.as_secs() as i64 / Interval::SECS_IN_DAY,
323 nanos: (value.as_secs() as i64 % Interval::SECS_IN_DAY) as i128
324 * Interval::NANOS_IN_SEC
325 + value.subsec_nanos() as i128,
326 }
327 }
328}
329
330impl From<Interval> for std::time::Duration {
331 fn from(value: Interval) -> Self {
332 value.as_duration(Interval::DAYS_IN_MONTH)
333 }
334}
335
336impl From<time::Duration> for Interval {
337 fn from(value: time::Duration) -> Self {
338 let seconds = value.whole_seconds();
339 Self {
340 months: 0,
341 days: seconds / Interval::SECS_IN_DAY,
342 nanos: ((seconds % Interval::SECS_IN_DAY) * Interval::NANOS_IN_SEC as i64
343 + value.subsec_nanoseconds() as i64) as i128,
344 }
345 }
346}
347
348impl From<Interval> for time::Duration {
349 fn from(value: Interval) -> Self {
350 let seconds = ((value.days + value.months * Interval::DAYS_IN_MONTH as i64)
351 * Interval::SECS_IN_DAY) as i128
352 + value.nanos / Interval::NANOS_IN_SEC;
353 let nanos = (value.nanos % Interval::NANOS_IN_SEC) as i32;
354 time::Duration::new(seconds as i64, nanos)
355 }
356}