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