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 let m = nanos / (60 * Self::NANOS_IN_SEC);
171 nanos %= 60 * Self::NANOS_IN_SEC;
172 let s = nanos / Self::NANOS_IN_SEC;
173 nanos %= Self::NANOS_IN_SEC;
174 (hours, m as _, s as _, nanos as _)
175 }
176
177 pub const fn is_zero(&self) -> bool {
178 self.months == 0 && self.days == 0 && self.nanos == 0
179 }
180
181 pub const fn as_duration(&self, days_in_month: f64) -> std::time::Duration {
185 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;
188 let secs = (nanos / Interval::NANOS_IN_SEC) as u64;
189 let nanos = (nanos % Interval::NANOS_IN_SEC) as u32;
190 std::time::Duration::new(secs, nanos)
191 }
192
193 pub const fn units_and_factors(&self) -> &[(IntervalUnit, i128)] {
194 static UNITS: &[(IntervalUnit, i128)] = &[
195 (IntervalUnit::Year, Interval::NANOS_IN_DAY * 30 * 12),
196 (IntervalUnit::Month, Interval::NANOS_IN_DAY * 30),
197 (IntervalUnit::Day, Interval::NANOS_IN_DAY),
198 (IntervalUnit::Hour, Interval::NANOS_IN_SEC * 3600),
199 (IntervalUnit::Minute, Interval::NANOS_IN_SEC * 60),
200 (IntervalUnit::Second, Interval::NANOS_IN_SEC),
201 (IntervalUnit::Microsecond, 1_000),
202 (IntervalUnit::Nanosecond, 1),
203 ];
204 UNITS
205 }
206
207 pub fn units_mask(&self) -> u8 {
209 let mut mask = 0_u8;
210 if self.months != 0 {
211 if self.months % 12 == 0 {
212 mask |= 1 << 7;
213 } else if self.months != 0 {
214 mask |= 1 << 6;
215 }
216 }
217 let nanos = self.nanos + self.days as i128 * Interval::NANOS_IN_DAY;
218 if nanos != 0 {
219 for (i, &(_, factor)) in self.units_and_factors().iter().skip(2).enumerate() {
220 if nanos % factor == 0 {
221 let shift = 5 - i; mask |= 1 << shift;
223 break;
224 }
225 }
226 }
227 mask
228 }
229
230 pub fn unit_value(&self, unit: IntervalUnit) -> i128 {
232 if unit == IntervalUnit::Year {
233 self.months as i128 / 12
234 } else if unit == IntervalUnit::Month {
235 self.months as i128
236 } else {
237 let factor = *self
238 .units_and_factors()
239 .iter()
240 .find_map(|(u, k)| if *u == unit { Some(k) } else { None })
241 .expect("The unit must be present");
242 (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos) / factor
243 }
244 }
245}
246
247impl PartialEq for Interval {
248 fn eq(&self, other: &Self) -> bool {
249 self.months == other.months
250 && self.days as i128 * Interval::NANOS_IN_DAY + self.nanos
251 == other.days as i128 * Interval::NANOS_IN_DAY + other.nanos
252 }
253}
254
255impl Eq for Interval {}
256
257impl Hash for Interval {
258 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
259 self.months.hash(state);
260 (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos).hash(state);
261 }
262}
263
264macro_rules! sum_intervals {
265 ($lhs:ident $op:tt $rhs:ident) => {{
266 let days_total = $lhs.days as i128 $op $rhs.days as i128
267 + $lhs.nanos / Interval::NANOS_IN_DAY $op $rhs.nanos / Interval::NANOS_IN_DAY;
268 let days = days_total.clamp(i64::MIN as _, i64::MAX as _);
269 let mut nanos = $lhs.nanos % Interval::NANOS_IN_DAY $op $rhs.nanos % Interval::NANOS_IN_DAY;
270 if days != days_total {
271 nanos += (days_total - days) * Interval::NANOS_IN_DAY;
272 }
273 Interval {
274 months: $lhs.months $op $rhs.months,
275 days: days as _,
276 nanos,
277 }
278 }};
279}
280
281impl Add for Interval {
282 type Output = Interval;
283 fn add(self, rhs: Self) -> Self {
284 sum_intervals!(self + rhs)
285 }
286}
287
288impl AddAssign for Interval {
289 fn add_assign(&mut self, rhs: Self) {
290 *self = sum_intervals!(self + rhs);
291 }
292}
293
294impl Sub for Interval {
295 type Output = Interval;
296 fn sub(self, rhs: Self) -> Self::Output {
297 sum_intervals!(self - rhs)
298 }
299}
300
301impl SubAssign for Interval {
302 fn sub_assign(&mut self, rhs: Self) {
303 *self = sum_intervals!(self - rhs);
304 }
305}
306
307impl Neg for Interval {
308 type Output = Interval;
309 fn neg(self) -> Self::Output {
310 Self::default() - self
311 }
312}
313
314impl From<std::time::Duration> for Interval {
315 fn from(value: std::time::Duration) -> Self {
316 Self {
317 months: 0,
318 days: value.as_secs() as i64 / Interval::SECS_IN_DAY,
319 nanos: (value.as_secs() as i64 % Interval::SECS_IN_DAY) as i128
320 * Interval::NANOS_IN_SEC
321 + value.subsec_nanos() as i128,
322 }
323 }
324}
325
326impl From<Interval> for std::time::Duration {
327 fn from(value: Interval) -> Self {
328 value.as_duration(Interval::DAYS_IN_MONTH)
329 }
330}
331
332impl From<time::Duration> for Interval {
333 fn from(value: time::Duration) -> Self {
334 let seconds = value.whole_seconds();
335 Self {
336 months: 0,
337 days: seconds / Interval::SECS_IN_DAY,
338 nanos: ((seconds % Interval::SECS_IN_DAY) * Interval::NANOS_IN_SEC as i64
339 + value.subsec_nanoseconds() as i64) as i128,
340 }
341 }
342}
343
344impl From<Interval> for time::Duration {
345 fn from(value: Interval) -> Self {
346 let seconds = ((value.days + value.months * Interval::DAYS_IN_MONTH as i64)
347 * Interval::SECS_IN_DAY) as i128
348 + value.nanos / Interval::NANOS_IN_SEC;
349 let nanos = (value.nanos % Interval::NANOS_IN_SEC) as i32;
350 time::Duration::new(seconds as i64, nanos)
351 }
352}