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