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