Skip to main content

jobberdb/
date_time.rs

1//! Date and time
2
3use super::prelude::*;
4use chrono::{Datelike, Local, NaiveDateTime, TimeZone, Utc};
5use serde::{Deserialize, Serialize};
6
7/// Date and time (hours & minutes).
8#[derive(Clone, Copy, PartialOrd, PartialEq, Serialize, Deserialize, Ord, Eq)]
9#[serde(transparent)]
10pub struct DateTime(chrono::DateTime<Utc>);
11
12impl std::fmt::Display for DateTime {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        write!(f, "{}", self.into_local().format("%a %b %d %Y, %H:%M"))
15    }
16}
17
18impl std::fmt::Debug for DateTime {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(f, "{}", self.into_local())
21    }
22}
23
24impl DateTime {
25    /// Create with current date and time.
26    pub fn now() -> Self {
27        DateTime(Utc::now())
28    }
29    /// Return year.
30    pub fn year(&self) -> i32 {
31        self.0.year()
32    }
33    /// Return month.
34    pub fn month(&self) -> u32 {
35        self.0.month()
36    }
37    /// Return day.
38    pub fn day(&self) -> u32 {
39        self.0.day()
40    }
41    /// Return date only.
42    pub fn date(&self) -> Date {
43        Date(self.into_local().date())
44    }
45    /// Convert into naive local date and time.
46    pub fn into_local(&self) -> NaiveDateTime {
47        Local.from_utc_datetime(&self.0.naive_local()).naive_local()
48    }
49    /// Convert from naive local date and time.
50    pub fn from_local(local: &NaiveDateTime) -> Self {
51        let local = Local.from_local_datetime(local).unwrap();
52        Self(chrono::DateTime::from(local))
53    }
54    /// Convert from naive local date and time string.
55    fn from_local_str(local: &str) -> Self {
56        Self(
57            Utc.from_local_datetime(
58                &Local
59                    .datetime_from_str(local, "%Y-%m-%d %H:%M")
60                    .unwrap()
61                    .naive_utc(),
62            )
63            .unwrap(),
64        )
65    }
66    /// Convert from naive RFC3339 date and time.
67    pub fn from_rfc3339(rfc3339: &str) -> Result<Self, Error> {
68        Ok(Self(
69            chrono::DateTime::parse_from_rfc3339(rfc3339)
70                .map_err(Error::DateTimeParse)?
71                .into(),
72        ))
73    }
74    /// Format with the given format string (see `format::strftime()` for available formats)
75    pub fn format(&self, format: &str) -> String {
76        self.into_local().format(format).to_string()
77    }
78}
79
80impl From<chrono::DateTime<Utc>> for DateTime {
81    fn from(value: chrono::DateTime<Utc>) -> Self {
82        Self(value)
83    }
84}
85
86impl From<&str> for DateTime {
87    fn from(local: &str) -> Self {
88        Self::from_local_str(local)
89    }
90}
91
92impl From<DateTime> for chrono::DateTime<Utc> {
93    fn from(val: DateTime) -> Self {
94        val.0
95    }
96}
97
98impl From<DateTime> for chrono::DateTime<Local> {
99    fn from(val: DateTime) -> Self {
100        val.0.into()
101    }
102}
103
104impl std::ops::SubAssign<Duration> for DateTime {
105    fn sub_assign(&mut self, other: Duration) {
106        match other {
107            Duration::Zero => (),
108            Duration::HM { hours, minutes } => {
109                self.0 -= chrono::Duration::hours(hours) + chrono::Duration::minutes(minutes)
110            }
111        }
112    }
113}
114
115impl std::ops::AddAssign<Duration> for DateTime {
116    fn add_assign(&mut self, other: Duration) {
117        match other {
118            Duration::Zero => (),
119            Duration::HM { hours, minutes } => {
120                self.0 += chrono::Duration::hours(hours) + chrono::Duration::minutes(minutes)
121            }
122        }
123    }
124}
125
126impl std::ops::Add<Duration> for DateTime {
127    type Output = DateTime;
128    fn add(self, other: Duration) -> Self::Output {
129        match other {
130            Duration::Zero => self,
131            Duration::HM { hours, minutes } => {
132                Self(self.0 + chrono::Duration::hours(hours) + chrono::Duration::minutes(minutes))
133            }
134        }
135    }
136}
137
138impl std::ops::Sub<Duration> for DateTime {
139    type Output = DateTime;
140    fn sub(self, other: Duration) -> Self::Output {
141        match other {
142            Duration::Zero => self,
143            Duration::HM { hours, minutes } => {
144                Self(self.0 - chrono::Duration::hours(hours) - chrono::Duration::minutes(minutes))
145            }
146        }
147    }
148}
149
150impl std::ops::Add<chrono::Duration> for DateTime {
151    type Output = DateTime;
152    fn add(self, other: chrono::Duration) -> Self::Output {
153        Self(self.0 + other)
154    }
155}
156
157impl std::ops::Sub<chrono::Duration> for DateTime {
158    type Output = DateTime;
159    fn sub(self, other: chrono::Duration) -> Self::Output {
160        Self(self.0 - other)
161    }
162}
163
164impl std::ops::Sub for &DateTime {
165    type Output = Duration;
166    fn sub(self, other: &DateTime) -> Self::Output {
167        let minutes = (self.0 - other.0).num_minutes();
168        Duration::HM {
169            hours: minutes / 60,
170            minutes: minutes % 60,
171        }
172    }
173}
174
175/// Date only.
176#[derive(Debug, PartialEq, Clone, PartialOrd, Ord, Eq)]
177pub struct Date(chrono::NaiveDate);
178
179impl Date {
180    pub fn first_day_of_month(&self) -> DateTime {
181        DateTime::from_local(
182            &chrono::NaiveDate::from_ymd_opt(self.0.year(), self.0.month(), 1)
183                .unwrap()
184                .and_hms_opt(0, 0, 0)
185                .unwrap(),
186        )
187    }
188    pub fn first_day_of_previous_month(&self) -> DateTime {
189        DateTime::from_local(
190            &chrono::NaiveDate::from_ymd_opt(self.0.year(), self.0.month() - 1, 1)
191                .unwrap()
192                .and_hms_opt(0, 0, 0)
193                .unwrap(),
194        )
195    }
196}
197
198impl From<DateTime> for Date {
199    fn from(value: DateTime) -> Self {
200        let datetime: chrono::DateTime<Utc> = value.into();
201        Date(datetime.date_naive())
202    }
203}
204
205impl std::fmt::Display for Date {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        write!(f, "{}", self.0)
208    }
209}