1use chrono::{Offset, TimeZone};
2
3use crate::{atomic::Atomic, error};
4
5pub(crate) trait EqWithDefaultOffset: ToDateTimeStamp {
6 fn eq_with_default_offset(&self, other: &Self, default_offset: chrono::FixedOffset) -> bool {
7 let self_date_time_stamp = self.to_date_time_stamp(default_offset);
8 let other_date_time_stamp = other.to_date_time_stamp(default_offset);
9 self_date_time_stamp == other_date_time_stamp
10 }
11}
12
13pub(crate) trait OrdWithDefaultOffset: ToDateTimeStamp {
14 fn cmp_with_default_offset(
15 &self,
16 other: &Self,
17 default_offset: chrono::FixedOffset,
18 ) -> std::cmp::Ordering {
19 let self_date_time_stamp = self.to_date_time_stamp(default_offset);
20 let other_date_time_stamp = other.to_date_time_stamp(default_offset);
21 self_date_time_stamp.cmp(&other_date_time_stamp)
22 }
23}
24
25pub(crate) trait ToDateTimeStamp {
26 fn to_date_time_stamp(
27 &self,
28 default_offset: chrono::FixedOffset,
29 ) -> chrono::DateTime<chrono::FixedOffset>;
30
31 fn to_naive_date_time(&self, default_offset: chrono::FixedOffset) -> chrono::NaiveDateTime {
32 self.to_date_time_stamp(default_offset).naive_utc()
33 }
34}
35
36impl<T> EqWithDefaultOffset for T where T: ToDateTimeStamp {}
37impl<T> OrdWithDefaultOffset for T where T: ToDateTimeStamp {}
38
39#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub struct YearMonthDuration {
41 pub(crate) months: i64,
42}
43
44impl YearMonthDuration {
45 pub(crate) fn new(months: i64) -> Self {
46 Self { months }
47 }
48
49 pub(crate) fn years(&self) -> i64 {
50 self.months / 12
51 }
52
53 pub(crate) fn months(&self) -> i64 {
54 self.months % 12
55 }
56}
57
58impl From<YearMonthDuration> for Atomic {
59 fn from(year_month_duration: YearMonthDuration) -> Self {
60 Atomic::YearMonthDuration(year_month_duration)
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub struct Duration {
70 pub(crate) year_month: YearMonthDuration,
71 pub(crate) day_time: chrono::Duration,
72}
73
74impl Duration {
75 pub(crate) fn new(months: i64, day_time: chrono::Duration) -> Self {
76 Self {
77 year_month: YearMonthDuration { months },
78 day_time,
79 }
80 }
81
82 pub(crate) fn from_year_month(year_month_duration: YearMonthDuration) -> Self {
83 Self {
84 year_month: year_month_duration,
85 day_time: chrono::Duration::zero(),
86 }
87 }
88
89 pub(crate) fn from_day_time(duration: chrono::Duration) -> Self {
90 Self {
91 year_month: YearMonthDuration { months: 0 },
92 day_time: duration,
93 }
94 }
95}
96
97impl From<Duration> for Atomic {
98 fn from(duration: Duration) -> Self {
99 Atomic::Duration(duration.into())
100 }
101}
102
103impl TryFrom<Atomic> for Duration {
104 type Error = error::Error;
105
106 fn try_from(a: Atomic) -> Result<Self, Self::Error> {
107 match a {
108 Atomic::Duration(d) => Ok(d.as_ref().clone()),
109 Atomic::YearMonthDuration(d) => Ok(Duration::from_year_month(d)),
110 Atomic::DayTimeDuration(d) => Ok(Duration::from_day_time(*d)),
111 _ => Err(error::Error::XPTY0004),
112 }
113 }
114}
115
116impl TryFrom<Atomic> for chrono::Duration {
117 type Error = error::Error;
118
119 fn try_from(a: Atomic) -> Result<Self, Self::Error> {
120 match a {
121 Atomic::DayTimeDuration(d) => Ok(*d.as_ref()),
122 _ => Err(error::Error::XPTY0004),
123 }
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
132pub struct NaiveDateTimeWithOffset {
133 pub(crate) date_time: chrono::NaiveDateTime,
134 pub(crate) offset: Option<chrono::FixedOffset>,
135}
136
137impl From<NaiveDateTimeWithOffset> for chrono::DateTime<chrono::FixedOffset> {
138 fn from(naive_date_time_with_offset: NaiveDateTimeWithOffset) -> Self {
139 let offset = naive_date_time_with_offset
140 .offset
141 .unwrap_or_else(|| chrono::offset::Utc.fix());
142 chrono::DateTime::from_naive_utc_and_offset(naive_date_time_with_offset.date_time, offset)
143 }
144}
145
146impl From<chrono::DateTime<chrono::FixedOffset>> for NaiveDateTimeWithOffset {
147 fn from(date_time: chrono::DateTime<chrono::FixedOffset>) -> Self {
148 NaiveDateTimeWithOffset::new(date_time.naive_local(), Some(*date_time.offset()))
149 }
150}
151
152impl From<NaiveDateTimeWithOffset> for Atomic {
153 fn from(date_time: NaiveDateTimeWithOffset) -> Self {
154 Atomic::DateTime(date_time.into())
155 }
156}
157
158impl TryFrom<Atomic> for NaiveDateTimeWithOffset {
159 type Error = error::Error;
160
161 fn try_from(a: Atomic) -> Result<Self, Self::Error> {
162 match a {
163 Atomic::DateTime(d) => Ok(d.as_ref().clone()),
164 Atomic::DateTimeStamp(d) => Ok((*d.as_ref()).into()),
165 _ => Err(error::Error::XPTY0004),
166 }
167 }
168}
169
170impl ToDateTimeStamp for NaiveDateTimeWithOffset {
171 fn to_date_time_stamp(
172 &self,
173 default_offset: chrono::FixedOffset,
174 ) -> chrono::DateTime<chrono::FixedOffset> {
175 let offset = self.offset.unwrap_or(default_offset);
176 offset.from_local_datetime(&self.date_time).unwrap()
177 }
178}
179
180impl NaiveDateTimeWithOffset {
181 pub(crate) fn new(
182 date_time: chrono::NaiveDateTime,
183 offset: Option<chrono::FixedOffset>,
184 ) -> Self {
185 Self { date_time, offset }
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
194pub struct NaiveTimeWithOffset {
195 pub(crate) time: chrono::NaiveTime,
196 pub(crate) offset: Option<chrono::FixedOffset>,
197}
198
199impl TryFrom<Atomic> for NaiveTimeWithOffset {
200 type Error = error::Error;
201
202 fn try_from(a: Atomic) -> Result<Self, Self::Error> {
203 match a {
204 Atomic::Time(d) => Ok(d.as_ref().clone()),
205 _ => Err(error::Error::XPTY0004),
206 }
207 }
208}
209
210impl NaiveTimeWithOffset {
211 pub(crate) fn new(time: chrono::NaiveTime, offset: Option<chrono::FixedOffset>) -> Self {
212 Self { time, offset }
213 }
214}
215
216impl ToDateTimeStamp for NaiveTimeWithOffset {
217 fn to_date_time_stamp(
218 &self,
219 default_offset: chrono::FixedOffset,
220 ) -> chrono::DateTime<chrono::FixedOffset> {
221 let offset = self.offset.unwrap_or(default_offset);
222 let date_time = chrono::NaiveDate::from_ymd_opt(1972, 12, 31)
224 .unwrap()
225 .and_time(self.time);
226 offset.from_local_datetime(&date_time).unwrap()
227 }
228}
229
230impl From<NaiveTimeWithOffset> for Atomic {
231 fn from(time: NaiveTimeWithOffset) -> Self {
232 Atomic::Time(time.into())
233 }
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
241pub struct NaiveDateWithOffset {
242 pub(crate) date: chrono::NaiveDate,
243 pub(crate) offset: Option<chrono::FixedOffset>,
244}
245
246impl TryFrom<Atomic> for NaiveDateWithOffset {
247 type Error = error::Error;
248
249 fn try_from(a: Atomic) -> Result<Self, Self::Error> {
250 match a {
251 Atomic::Date(d) => Ok(d.as_ref().clone()),
252 _ => Err(error::Error::XPTY0004),
253 }
254 }
255}
256
257impl NaiveDateWithOffset {
258 pub(crate) fn new(date: chrono::NaiveDate, offset: Option<chrono::FixedOffset>) -> Self {
259 Self { date, offset }
260 }
261}
262
263impl ToDateTimeStamp for NaiveDateWithOffset {
264 fn to_date_time_stamp(
265 &self,
266 default_offset: chrono::FixedOffset,
267 ) -> chrono::DateTime<chrono::FixedOffset> {
268 let offset = self.offset.unwrap_or(default_offset);
269 let date_time = self.date.and_hms_opt(0, 0, 0).unwrap();
270 offset.from_local_datetime(&date_time).unwrap()
271 }
272}
273
274impl From<NaiveDateWithOffset> for Atomic {
275 fn from(date: NaiveDateWithOffset) -> Self {
276 Atomic::Date(date.into())
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
285pub struct GYearMonth {
286 pub(crate) year: i32,
287 pub(crate) month: u32,
288 pub(crate) offset: Option<chrono::FixedOffset>,
289}
290
291impl GYearMonth {
292 pub(crate) fn new(year: i32, month: u32, offset: Option<chrono::FixedOffset>) -> Self {
293 Self {
294 year,
295 month,
296 offset,
297 }
298 }
299}
300
301impl From<GYearMonth> for Atomic {
302 fn from(g_year_month: GYearMonth) -> Self {
303 Atomic::GYearMonth(g_year_month.into())
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
312pub struct GYear {
313 pub(crate) year: i32,
314 pub(crate) offset: Option<chrono::FixedOffset>,
315}
316
317impl GYear {
318 pub(crate) fn new(year: i32, offset: Option<chrono::FixedOffset>) -> Self {
319 Self { year, offset }
320 }
321}
322
323impl From<GYear> for Atomic {
324 fn from(g_year: GYear) -> Self {
325 Atomic::GYear(g_year.into())
326 }
327}
328
329#[derive(Debug, Clone, PartialEq, Eq, Hash)]
334pub struct GMonthDay {
335 pub(crate) month: u32,
336 pub(crate) day: u32,
337 pub(crate) offset: Option<chrono::FixedOffset>,
338}
339
340impl GMonthDay {
341 pub(crate) fn new(month: u32, day: u32, offset: Option<chrono::FixedOffset>) -> Self {
342 Self { month, day, offset }
343 }
344}
345
346impl From<GMonthDay> for Atomic {
347 fn from(g_month_day: GMonthDay) -> Self {
348 Atomic::GMonthDay(g_month_day.into())
349 }
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Hash)]
357pub struct GDay {
358 pub(crate) day: u32,
359 pub(crate) offset: Option<chrono::FixedOffset>,
360}
361
362impl GDay {
363 pub(crate) fn new(day: u32, offset: Option<chrono::FixedOffset>) -> Self {
364 Self { day, offset }
365 }
366}
367
368impl From<GDay> for Atomic {
369 fn from(g_day: GDay) -> Self {
370 Atomic::GDay(g_day.into())
371 }
372}
373
374#[derive(Debug, Clone, PartialEq, Eq, Hash)]
379pub struct GMonth {
380 pub(crate) month: u32,
381 pub(crate) offset: Option<chrono::FixedOffset>,
382}
383
384impl GMonth {
385 pub(crate) fn new(month: u32, offset: Option<chrono::FixedOffset>) -> Self {
386 Self { month, offset }
387 }
388}
389
390impl From<GMonth> for Atomic {
391 fn from(g_month: GMonth) -> Self {
392 Atomic::GMonth(g_month.into())
393 }
394}
395
396impl From<chrono::Duration> for Atomic {
397 fn from(duration: chrono::Duration) -> Self {
398 Atomic::DayTimeDuration(duration.into())
399 }
400}
401
402impl From<chrono::DateTime<chrono::FixedOffset>> for Atomic {
403 fn from(date_time: chrono::DateTime<chrono::FixedOffset>) -> Self {
404 Atomic::DateTimeStamp(date_time.into())
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use crate::atomic::{AtomicCompare, OpGt};
411
412 use super::*;
413
414 #[test]
415 fn test_compare_dates() {
416 let a_date = NaiveDateWithOffset::new(
417 chrono::NaiveDate::from_ymd_opt(2004, 12, 25).unwrap(),
418 Some(chrono::offset::Utc.fix()),
419 );
420 let b_date = NaiveDateWithOffset::new(
421 chrono::NaiveDate::from_ymd_opt(2004, 12, 25).unwrap(),
422 Some(chrono::FixedOffset::east_opt(60 * 60 * 7).unwrap()),
423 );
424
425 let a: Atomic = Atomic::Date(a_date.into());
426 let b: Atomic = Atomic::Date(b_date.into());
427
428 assert!(
429 OpGt::atomic_compare(a.clone(), b.clone(), str::cmp, chrono::offset::Utc.fix())
430 .unwrap()
431 );
432 }
433}