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