1use std::{
2 hash::Hash,
3 ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6#[derive(Default, Debug, Clone, Copy)]
7pub struct Interval {
8 pub months: i64,
9 pub days: i64,
10 pub nanos: i128,
11}
12
13impl Interval {
14 pub const DAYS_IN_MONTH: f64 = 30.0;
15 pub const DAYS_IN_MONTH_AVG: f64 = 30.436875;
16 pub const SECS_IN_DAY: i64 = 60 * 60 * 24;
17 pub const NANOS_IN_SEC: i128 = 1_000_000_000;
18 pub const NANOS_IN_DAY: i128 = Self::SECS_IN_DAY as i128 * Self::NANOS_IN_SEC;
19
20 pub fn new(months: i64, days: i64, nanos: i128) -> Self {
21 Self {
22 months,
23 days,
24 nanos,
25 }
26 }
27
28 pub const fn from_duration(duration: &std::time::Duration) -> Self {
29 Self {
30 months: 0,
31 days: 0,
32 nanos: duration.as_nanos() as i128,
33 }
34 }
35
36 pub const fn from_nanos(value: i128) -> Self {
37 Self {
38 months: 0,
39 days: (value / Self::NANOS_IN_DAY) as _,
40 nanos: (value % Self::NANOS_IN_DAY),
41 }
42 }
43
44 pub const fn from_micros(value: i128) -> Self {
45 const MICROS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000_000) as _;
46 Self {
47 months: 0,
48 days: (value / MICROS_IN_DAY) as _,
49 nanos: (value % MICROS_IN_DAY) * 1_000,
50 }
51 }
52
53 pub const fn from_millis(value: i128) -> Self {
54 const MILLIS_IN_DAY: i128 = (Interval::SECS_IN_DAY * 1_000) as _;
55 Self {
56 months: 0,
57 days: (value / MILLIS_IN_DAY) as _,
58 nanos: ((value % MILLIS_IN_DAY) * 1_000_000),
59 }
60 }
61
62 pub const fn from_secs(value: i64) -> Self {
63 Self {
64 months: 0,
65 days: (value / Self::SECS_IN_DAY) as _,
66 nanos: ((value % Self::SECS_IN_DAY) * 1_000_000_000) as _,
67 }
68 }
69
70 pub const fn from_mins(value: i64) -> Self {
71 const MINS_IN_DAYS: i64 = 60 * 24;
72 Self {
73 months: 0,
74 days: (value / MINS_IN_DAYS),
75 nanos: ((value % MINS_IN_DAYS) * 60 * 1_000_000_000) as _,
76 }
77 }
78
79 pub const fn from_days(value: i64) -> Self {
80 Self {
81 months: 0,
82 days: value,
83 nanos: 0,
84 }
85 }
86
87 pub const fn from_weeks(value: i64) -> Self {
88 Self {
89 months: 0,
90 days: value * 7,
91 nanos: 0,
92 }
93 }
94
95 pub const fn from_months(value: i64) -> Self {
96 Self {
97 months: value,
98 days: 0,
99 nanos: 0,
100 }
101 }
102
103 pub const fn from_years(value: i64) -> Self {
104 Self {
105 months: value * 12,
106 days: 0,
107 nanos: 0,
108 }
109 }
110
111 pub const fn is_zero(&self) -> bool {
112 self.months == 0 && self.days == 0 && self.nanos == 0
113 }
114
115 pub const fn as_duration(&self, days_in_month: f64) -> std::time::Duration {
116 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;
119 let secs = (nanos / Interval::NANOS_IN_SEC) as u64;
120 let nanos = (nanos % Interval::NANOS_IN_SEC) as u32;
121 std::time::Duration::new(secs, nanos)
122 }
123}
124
125impl PartialEq for Interval {
126 fn eq(&self, other: &Self) -> bool {
127 self.months == other.months
128 && self.days as i128 * Interval::NANOS_IN_DAY + self.nanos
129 == other.days as i128 * Interval::NANOS_IN_DAY + other.nanos
130 }
131}
132
133impl Eq for Interval {}
134
135impl Hash for Interval {
136 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
137 self.months.hash(state);
138 (self.days as i128 * Interval::NANOS_IN_DAY + self.nanos).hash(state);
139 }
140}
141
142macro_rules! sum_intervals {
143 ($lhs:ident $op:tt $rhs:ident) => {{
144 let days_total = $lhs.days as i128 $op $rhs.days as i128
145 + $lhs.nanos / Interval::NANOS_IN_DAY $op $rhs.nanos / Interval::NANOS_IN_DAY;
146 let days = days_total.clamp(i64::MIN as _, i64::MAX as _);
147 let mut nanos = $lhs.nanos % Interval::NANOS_IN_DAY + $rhs.nanos % Interval::NANOS_IN_DAY;
148 if days != days_total {
149 nanos += (days_total - days) * Interval::NANOS_IN_DAY;
150 }
151 Interval {
152 months: $lhs.months $op $rhs.months,
153 days: days as _,
154 nanos,
155 }
156 }};
157}
158
159impl Add for Interval {
160 type Output = Interval;
161
162 fn add(self, rhs: Self) -> Self {
163 sum_intervals!(self + rhs)
164 }
165}
166
167impl AddAssign for Interval {
168 fn add_assign(&mut self, rhs: Self) {
169 *self = sum_intervals!(self + rhs);
170 }
171}
172
173impl Sub for Interval {
174 type Output = Interval;
175
176 fn sub(self, rhs: Self) -> Self::Output {
177 sum_intervals!(self - rhs)
178 }
179}
180
181impl SubAssign for Interval {
182 fn sub_assign(&mut self, rhs: Self) {
183 *self = sum_intervals!(self - rhs);
184 }
185}
186
187impl From<std::time::Duration> for Interval {
188 fn from(value: std::time::Duration) -> Self {
189 Self {
190 months: 0,
191 days: value.as_secs() as i64 / Interval::SECS_IN_DAY,
192 nanos: (value.as_secs() as i64 % Interval::SECS_IN_DAY) as i128
193 * Interval::NANOS_IN_SEC
194 + value.subsec_nanos() as i128,
195 }
196 }
197}
198
199impl From<Interval> for std::time::Duration {
200 fn from(value: Interval) -> Self {
201 value.as_duration(Interval::DAYS_IN_MONTH)
202 }
203}
204
205impl From<time::Duration> for Interval {
206 fn from(value: time::Duration) -> Self {
207 let seconds = value.whole_seconds();
208 Self {
209 months: 0,
210 days: seconds / Interval::SECS_IN_DAY,
211 nanos: ((seconds % Interval::SECS_IN_DAY) * Interval::NANOS_IN_SEC as i64
212 + value.subsec_nanoseconds() as i64) as i128,
213 }
214 }
215}
216
217impl From<Interval> for time::Duration {
218 fn from(value: Interval) -> Self {
219 let seconds = ((value.days + value.months * Interval::DAYS_IN_MONTH as i64)
220 * Interval::SECS_IN_DAY) as i128
221 + value.nanos * Interval::NANOS_IN_SEC;
222 let nanos = (value.nanos % Interval::NANOS_IN_SEC) as i32;
223 time::Duration::new(seconds as i64, nanos)
224 }
225}