tea_time/impls/
impl_ops.rs

1use std::ops::{Add, Div, Mul, Neg, Sub};
2
3use chrono::{DateTime as CrDateTime, Months, Utc};
4
5use crate::{DateTime, Time, TimeDelta, TimeUnitTrait};
6
7// TODO: improve performance for time operation
8
9impl<U: TimeUnitTrait> Add<TimeDelta> for DateTime<U>
10where
11    Self: From<CrDateTime<Utc>> + TryInto<CrDateTime<Utc>>,
12{
13    type Output = DateTime<U>;
14    fn add(self, rhs: TimeDelta) -> Self::Output {
15        if self.is_not_nat() && rhs.is_not_nat() {
16            let dt = self.as_cr().unwrap();
17            let out = if rhs.months != 0 {
18                if rhs.months > 0 {
19                    dt + Months::new(rhs.months as u32)
20                } else {
21                    dt - Months::new((-rhs.months) as u32)
22                }
23            } else {
24                dt
25            };
26            (out + rhs.inner).into()
27        } else {
28            DateTime::nat()
29        }
30    }
31}
32
33impl<U: TimeUnitTrait> Sub<TimeDelta> for DateTime<U>
34where
35    Self: From<CrDateTime<Utc>> + TryInto<CrDateTime<Utc>>,
36{
37    type Output = DateTime<U>;
38    fn sub(self, rhs: TimeDelta) -> Self::Output {
39        if self.is_not_nat() && rhs.is_not_nat() {
40            let dt = self.as_cr().unwrap();
41            let out = if rhs.months != 0 {
42                if rhs.months > 0 {
43                    dt - Months::new(rhs.months as u32)
44                } else {
45                    dt + Months::new((-rhs.months) as u32)
46                }
47            } else {
48                dt
49            };
50            (out - rhs.inner).into()
51        } else {
52            DateTime::nat()
53        }
54    }
55}
56
57impl<U: TimeUnitTrait> Sub<DateTime<U>> for DateTime<U>
58where
59    Self: From<CrDateTime<Utc>> + TryInto<CrDateTime<Utc>>,
60{
61    type Output = TimeDelta;
62    fn sub(self, rhs: DateTime<U>) -> Self::Output {
63        // TODO: improve performance
64        // this can be done by implement unit conversion
65        if self.is_not_nat() && rhs.is_not_nat() {
66            let dt1 = self.as_cr().unwrap();
67            let dt2 = rhs.as_cr().unwrap();
68            let duration = dt1 - dt2;
69            TimeDelta {
70                months: 0,
71                inner: duration,
72            }
73            // let r_year = dt2.year();
74            // let years = dt1.year() - r_year;
75            // let months = dt1.month() as i32 - dt2.month() as i32;
76            // let duration =
77            //     dt1.with_year(r_year).expect(&format!("{dt1} with {r_year}")).with_month(1).expect(&format!("{dt1} with month1")) - dt2.with_month(1).expect(&format!("{dt2} with month1"));
78            // TimeDelta {
79            //     months: 12 * years + months,
80            //     inner: duration,
81            // }
82        } else {
83            TimeDelta::nat()
84        }
85    }
86}
87
88impl Neg for TimeDelta {
89    type Output = TimeDelta;
90
91    #[inline]
92    fn neg(self) -> TimeDelta {
93        if self.is_not_nat() {
94            Self {
95                months: -self.months,
96                inner: -self.inner,
97            }
98        } else {
99            self
100        }
101    }
102}
103
104impl Add for TimeDelta {
105    type Output = TimeDelta;
106    #[inline]
107    fn add(self, rhs: TimeDelta) -> TimeDelta {
108        if self.is_not_nat() & rhs.is_not_nat() {
109            Self {
110                months: self.months + rhs.months,
111                inner: self.inner + rhs.inner,
112            }
113        } else {
114            TimeDelta::nat()
115        }
116    }
117}
118
119impl Sub for TimeDelta {
120    type Output = TimeDelta;
121    #[inline]
122    fn sub(self, rhs: TimeDelta) -> TimeDelta {
123        if self.is_not_nat() & rhs.is_not_nat() {
124            Self {
125                months: self.months - rhs.months,
126                inner: self.inner - rhs.inner,
127            }
128        } else {
129            TimeDelta::nat()
130        }
131    }
132}
133
134impl Mul<i32> for TimeDelta {
135    type Output = TimeDelta;
136    #[inline]
137    fn mul(self, rhs: i32) -> Self {
138        if self.is_not_nat() {
139            Self {
140                months: self.months * rhs,
141                inner: self.inner * rhs,
142            }
143        } else {
144            TimeDelta::nat()
145        }
146    }
147}
148
149impl Div<TimeDelta> for TimeDelta {
150    type Output = i32;
151
152    fn div(self, rhs: TimeDelta) -> Self::Output {
153        if self.is_not_nat() & rhs.is_not_nat() {
154            // may not as expected
155            let inner_div =
156                self.inner.num_nanoseconds().unwrap() / rhs.inner.num_nanoseconds().unwrap();
157            if self.months == 0 || rhs.months == 0 {
158                return inner_div as i32;
159            }
160            let month_div = self.months / rhs.months;
161            if month_div == inner_div as i32 {
162                month_div
163            } else {
164                panic!("not support div TimeDelta when month div and time div is not equal")
165            }
166        } else {
167            panic!("not support div TimeDelta when one of them is nat")
168        }
169    }
170}
171
172impl Add<TimeDelta> for Time {
173    type Output = Time;
174    fn add(self, rhs: TimeDelta) -> Self::Output {
175        if rhs.is_not_nat() {
176            if rhs.months != 0 {
177                panic!("not support add TimeDelta with months");
178            }
179            if let Some(nanos) = rhs.inner.num_nanoseconds() {
180                let nanos = self.0 + nanos;
181                return Time(nanos);
182            }
183        }
184        Time::nat()
185    }
186}
187
188impl Sub<TimeDelta> for Time {
189    type Output = Time;
190    fn sub(self, rhs: TimeDelta) -> Self::Output {
191        if rhs.is_not_nat() {
192            if rhs.months != 0 {
193                panic!("not support sub TimeDelta with months");
194            }
195            if let Some(nanos) = rhs.inner.num_nanoseconds() {
196                let nanos = self.0 - nanos;
197                return Time(nanos);
198            }
199        }
200        Time::nat()
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207    use crate::TimeDelta;
208
209    #[test]
210    fn test_time_add_timedelta() {
211        let time = Time::from_hms(12, 0, 0);
212        let delta = TimeDelta::parse("1h30m").unwrap();
213        let result = time + delta;
214        assert_eq!(result, Time::from_hms(13, 30, 0));
215    }
216
217    #[test]
218    fn test_time_sub_timedelta() {
219        let time = Time::from_hms(12, 0, 0);
220        let delta = TimeDelta::parse("1h30m").unwrap();
221        let result = time - delta;
222        assert_eq!(result, Time::from_hms(10, 30, 0));
223    }
224
225    #[test]
226    #[should_panic]
227    fn test_time_add_timedelta_with_months() {
228        let time = Time::from_hms(12, 0, 0);
229        let delta = TimeDelta::parse("1mo1h30m").unwrap();
230        let _ = time + delta;
231    }
232
233    #[test]
234    #[should_panic]
235    fn test_time_sub_timedelta_with_months() {
236        let time = Time::from_hms(12, 0, 0);
237        let delta = TimeDelta::parse("1mo1h30m").unwrap();
238        let _ = time - delta;
239    }
240
241    #[test]
242    fn test_time_add_nat_timedelta() {
243        let time = Time::from_hms(12, 0, 0);
244        let nat_delta = TimeDelta::nat();
245        let result = time + nat_delta;
246        assert_eq!(result, Time::nat());
247    }
248
249    #[test]
250    fn test_time_sub_nat_timedelta() {
251        let time = Time::from_hms(12, 0, 0);
252        let nat_delta = TimeDelta::nat();
253        let result = time - nat_delta;
254        assert_eq!(result, Time::nat());
255    }
256}