1use std::ops::Add;
7use std::ops::AddAssign;
8use std::ops::Div;
9use std::ops::Mul;
10use std::ops::Sub;
11use std::ops::SubAssign;
12
13use crate::DateTime;
14
15#[cfg(feature = "macros")]
16#[doc(hidden)]
17pub mod __private_api {
18 pub use datetime_rs_macros::nanoseconds;
19}
20
21#[macro_export]
36#[cfg(feature = "macros")]
37#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
38macro_rules! time_interval {
39 ($($interval:tt)+) => { const {
40 $crate::interval::TimeInterval::from_nanoseconds(
41 $crate::interval::__private_api::nanoseconds!($($interval)*)
42 )
43 }}
44}
45
46#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
62pub struct TimeInterval {
63 seconds: i64,
64 nanos: u32,
65}
66
67impl TimeInterval {
68 pub const fn new(seconds: i64, nanos: u32) -> Self {
70 Self { seconds, nanos }
71 }
72
73 pub const fn from_milliseconds(millis: i64) -> Self {
75 Self::new(millis.div_euclid(1_000), millis.rem_euclid(1_000) as u32 * 1_000_000)
76 }
77
78 pub const fn from_microseconds(micros: i64) -> Self {
80 Self::new(micros.div_euclid(1_000_000), micros.rem_euclid(1_000_000) as u32 * 1_000)
81 }
82
83 pub const fn from_nanoseconds(nanos: i128) -> Self {
85 Self::new(nanos.div_euclid(1_000_000_000) as i64, nanos.rem_euclid(1_000_000_000) as u32)
86 }
87
88 pub const fn seconds(&self) -> i64 {
94 self.seconds
95 }
96
97 pub const fn nanoseconds(&self) -> u32 {
103 self.nanos
104 }
105
106 pub const fn as_milliseconds(&self) -> i64 {
108 self.seconds * 1_000 + (self.nanos / 1_000_000) as i64
109 }
110
111 pub const fn as_microseconds(&self) -> i64 {
113 self.seconds * 1_000_000 + (self.nanos / 1_000) as i64
114 }
115
116 pub const fn as_nanoseconds(&self) -> i128 {
118 self.seconds as i128 * 1_000_000_000 + self.nanos as i128
119 }
120}
121
122impl Add<TimeInterval> for DateTime {
123 type Output = DateTime;
124
125 fn add(self, rhs: TimeInterval) -> Self::Output {
126 let seconds = self.seconds + rhs.seconds + ((self.nanos + rhs.nanos) / 1_000_000_000) as i64;
127 let nanos = (self.nanos + rhs.nanos) % 1_000_000_000;
128 Self {
129 seconds,
130 nanos,
131 #[cfg(feature = "tz")]
132 tz: self.tz,
133 }
134 }
135}
136
137impl AddAssign<TimeInterval> for DateTime {
138 fn add_assign(&mut self, rhs: TimeInterval) {
139 self.seconds += rhs.seconds;
140 self.nanos += rhs.nanos;
141 while self.nanos >= 1_000_000_000 {
142 self.seconds += 1;
143 self.nanos -= 1_000_000_000;
144 }
145 }
146}
147
148impl Sub<TimeInterval> for DateTime {
149 type Output = DateTime;
150
151 #[allow(clippy::suspicious_arithmetic_impl)]
152 fn sub(self, rhs: TimeInterval) -> Self::Output {
153 let mut seconds = self.seconds - rhs.seconds;
154 let nanos = self.nanos.checked_sub(rhs.nanos).unwrap_or_else(|| {
155 seconds -= 1;
156 self.nanos + 1_000_000_000 - rhs.nanos
157 });
158 Self {
159 seconds,
160 nanos,
161 #[cfg(feature = "tz")]
162 tz: self.tz,
163 }
164 }
165}
166
167impl SubAssign<TimeInterval> for DateTime {
168 fn sub_assign(&mut self, rhs: TimeInterval) {
169 self.seconds -= rhs.seconds;
170 match self.nanos >= rhs.nanos {
171 true => self.nanos -= rhs.nanos,
172 false => {
173 self.nanos += 1_000_000_000;
174 self.seconds -= 1;
175 self.nanos -= rhs.nanos;
176 },
177 }
178 }
179}
180
181impl Sub for DateTime {
182 type Output = TimeInterval;
183
184 #[allow(clippy::suspicious_arithmetic_impl)]
185 fn sub(self, rhs: Self) -> Self::Output {
186 let mut seconds = self.seconds - rhs.seconds;
187 let nanos = self.nanos.checked_sub(rhs.nanos).unwrap_or_else(|| {
188 seconds -= 1;
189 self.nanos + 1_000_000_000 - rhs.nanos
190 });
191 TimeInterval { seconds, nanos }
192 }
193}
194
195impl<I: Into<i128>> Mul<I> for TimeInterval {
196 type Output = Self;
197
198 fn mul(self, rhs: I) -> Self::Output {
199 Self::from_nanoseconds(self.as_nanoseconds() * rhs.into())
200 }
201}
202
203impl<I: Into<i128>> Div<I> for TimeInterval {
204 type Output = Self;
205
206 fn div(self, rhs: I) -> Self::Output {
207 Self::from_nanoseconds(self.as_nanoseconds() / rhs.into())
208 }
209}
210
211impl Div for TimeInterval {
212 type Output = f64;
213
214 fn div(self, rhs: Self) -> Self::Output {
215 self.as_nanoseconds() as f64 / rhs.as_nanoseconds() as f64
216 }
217}
218
219#[cfg(feature = "syn")]
220mod syn {
221 use datetime_rs_codegen::Delta;
222 use syn::Result;
223 use syn::parse::Parse;
224 use syn::parse::ParseStream;
225
226 use super::TimeInterval;
227
228 #[cfg_attr(docsrs, doc(cfg(feature = "syn")))]
229 impl Parse for TimeInterval {
230 fn parse(input: ParseStream) -> Result<Self> {
231 let delta = Delta::parse(input)?;
232 Ok(Self::new(delta.seconds(), delta.nanos()))
233 }
234 }
235
236 #[cfg(test)]
237 mod tests {
238 use assert2::check;
239 use quote::quote;
240
241 use super::*;
242
243 #[test]
244 fn test_parse() -> Result<()> {
245 check!(syn::parse2::<TimeInterval>(quote! { 15m 30s })? == time_interval!(15m 30s));
246 check!(syn::parse2::<TimeInterval>(quote! { -1h 15m })? == time_interval!(-1h 15m));
247 Ok(())
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use assert2::check;
255
256 use super::*;
257 use crate::DateTime;
258 use crate::datetime;
259 use crate::time_interval;
260
261 #[test]
262 fn test_interval_macro() {
263 check!(time_interval!(1d) == TimeInterval::new(86_400, 0));
264 check!(time_interval!(2h) == TimeInterval::new(7_200, 0));
265 check!(time_interval!(1m) == TimeInterval::new(60, 0));
266 check!(time_interval!(1h30m) == TimeInterval::new(5_400, 0));
267 check!(time_interval!(1h 30m) == TimeInterval::new(5_400, 0));
268 check!(time_interval!(-1h 30m) == TimeInterval::new(-5_400, 0));
269 check!(time_interval!(1m 30s) == TimeInterval::new(90, 0));
270 check!(time_interval!(-1m 20s) == TimeInterval::new(-80, 0));
271 check!(time_interval!(-20s) == TimeInterval::new(-20, 0));
272 check!(time_interval!(-1.25s) == TimeInterval::new(-2, 750_000_000));
273 check!(time_interval!(1m 1.5s) == TimeInterval::new(61, 500_000_000));
274 check!(time_interval!(-0.5s) == TimeInterval::new(-1, 500_000_000));
275 check!(time_interval!(0.5s) == TimeInterval::new(0, 500_000_000));
276 }
277
278 #[test]
279 fn test_from_fractionals() {
280 for (millis, secs, nanos) in [(2_400, 2, 400_000_000), (-2_400, -3, 600_000_000)] {
281 let interval = TimeInterval::from_milliseconds(millis);
282 check!(interval.seconds() == secs);
283 check!(interval.nanoseconds() == nanos);
284 }
285 for (micros, secs, nanos) in [(2_400_000, 2, 400_000_000), (-2_400_000, -3, 600_000_000)] {
286 let interval = TimeInterval::from_microseconds(micros);
287 check!(interval.seconds() == secs);
288 check!(interval.nanoseconds() == nanos);
289 }
290 for (ns, s, n) in [(2_400_000_000, 2, 400_000_000), (-2_400_000_000, -3, 600_000_000)] {
291 let interval = TimeInterval::from_nanoseconds(ns);
292 check!(interval.seconds() == s);
293 check!(interval.nanoseconds() == n);
294 }
295 }
296
297 #[test]
298 fn test_add() {
299 check!(
300 datetime! { 2012-04-21 11:00:00 } + TimeInterval::new(3600, 0)
301 == datetime! { 2012-04-21 12:00:00 }
302 );
303 check!(
304 datetime! { 2012-04-21 11:00:00 } + TimeInterval::new(1800, 0)
305 == datetime! { 2012-04-21 11:30:00 }
306 );
307 check!(
308 datetime! { 2012-04-21 11:00:00 } + TimeInterval::new(0, 500_000_000)
309 == DateTime::ymd(2012, 4, 21).hms(11, 0, 0).nanos(500_000_000).build()
310 );
311 let incr = datetime! { 2012-04-21 11:00:00 }
312 + TimeInterval::new(0, 500_000_000)
313 + TimeInterval::new(0, 500_000_000);
314 check!(incr.seconds % 10 == 1);
315 check!(incr.nanos == 0);
316 }
317
318 #[test]
319 fn test_add_assign() {
320 let mut dt = datetime! { 2012-04-21 11:00:00 };
321 dt += TimeInterval::new(3600, 0);
322 check!(dt == datetime! { 2012-04-21 12:00:00 });
323 dt += TimeInterval::new(0, 750_000_000);
324 dt += TimeInterval::new(0, 250_000_000);
325 check!(dt == datetime! { 2012-04-21 12:00:01 });
326 }
327
328 #[test]
329 fn test_sub() {
330 check!(
331 datetime! { 2012-04-21 11:00:00 } - TimeInterval::new(3600, 0)
332 == datetime! { 2012-04-21 10:00:00 }
333 );
334 check!(
335 datetime! { 2012-04-21 11:00:00 } - TimeInterval::new(0, 500_000_000)
336 == DateTime::ymd(2012, 4, 21).hms(10, 59, 59).nanos(500_000_000).build()
337 );
338 }
339
340 #[test]
341 fn test_sub_assign() {
342 let mut dt = datetime! { 2012-04-21 11:00:00 };
343 dt -= TimeInterval::new(3600, 0);
344 check!(dt == datetime! { 2012-04-21 10:00:00 });
345 dt -= TimeInterval::new(0, 750_000_000);
346 dt -= TimeInterval::new(0, 350_000_000);
347 dt -= TimeInterval::new(0, 900_000_000);
348 check!(dt == datetime! { 2012-04-21 09:59:58 });
349 }
350
351 #[test]
352 fn test_sub_dt() {
353 check!(
354 datetime! { 2012-04-21 11:00:00 } - datetime! { 2012-04-21 10:00:00 }
355 == TimeInterval::new(3600, 0)
356 );
357 check!(
358 datetime! { 2012-04-21 11:00:00 } - datetime! { 2012-04-21 12:00:00 }
359 == TimeInterval::new(-3600, 0)
360 );
361 }
362
363 #[test]
364 fn test_mul_int() {
365 let interval = TimeInterval::new(3, 500_000_000) * 3;
366 check!(interval.seconds() == 10);
367 check!(interval.nanoseconds() == 500_000_000);
368 }
369
370 #[test]
371 fn test_div_int() {
372 let interval = TimeInterval::new(4, 500_000_000) / 3;
373 check!(interval.seconds() == 1);
374 check!(interval.nanoseconds() == 500_000_000);
375 }
376
377 #[test]
378 fn test_div() {
379 check!(TimeInterval::new(3600, 0) / TimeInterval::new(1800, 0) == 2.0);
380 check!(TimeInterval::new(-1800, 0) / TimeInterval::new(-3600, 0) == 0.5);
381 check!(TimeInterval::new(-1800, 0) / TimeInterval::new(3600, 0) == -0.5);
382 check!(TimeInterval::new(0, 3600) / TimeInterval::new(0, 1800) == 2.0);
383 }
384
385 #[test]
386 fn test_as() {
387 let dur = TimeInterval::new(5, 0);
388 check!(dur.as_milliseconds() == 5_000);
389 check!(dur.as_microseconds() == 5_000_000);
390 check!(dur.as_nanoseconds() == 5_000_000_000);
391 }
392}