1use std::cmp::Ordering;
2use std::hash::Hash;
3use std::marker::PhantomData;
4
5use chrono::{
6 DateTime as CrDateTime, Datelike, DurationRound, Months, NaiveDate, NaiveDateTime, NaiveTime,
7 Timelike, Utc,
8};
9use tea_error::{tbail, TResult};
10
11use super::timeunit::*;
12use crate::TimeDelta;
13
14#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[repr(transparent)]
17pub struct DateTime<U: TimeUnitTrait = Nanosecond>(pub i64, PhantomData<U>);
28
29impl<U: TimeUnitTrait> std::fmt::Debug for DateTime<U>
30where
31 Self: TryInto<CrDateTime<Utc>>,
32 <Self as TryInto<CrDateTime<Utc>>>::Error: std::fmt::Debug,
33{
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 if self.is_nat() {
36 write!(f, "NaT")
37 } else {
38 write!(f, "{}", self.strftime(None))
39 }
40 }
41}
42
43unsafe impl<U: TimeUnitTrait> Send for DateTime<U> {}
44unsafe impl<U: TimeUnitTrait> Sync for DateTime<U> {}
45
46const TIME_RULE_VEC: [&str; 11] = [
47 "%Y-%m-%d %H:%M:%S",
48 "%Y-%m-%d %H:%M:%S.%f",
49 "%Y-%m-%d",
50 "%Y%m%d",
51 "%Y%m%d %H%M%S",
52 "%d/%m/%Y",
53 "%d/%m/%Y H%M%S",
54 "%Y%m%d%H%M%S",
55 "%d/%m/%YH%M%S",
56 "%Y/%m/%d",
57 "%Y/%m/%d %H:%M:%S",
58];
59
60impl<U: TimeUnitTrait> DateTime<U> {
61 #[inline]
71 pub const fn new(dt: i64) -> Self {
72 Self(dt, PhantomData)
73 }
74
75 #[inline]
81 pub const fn is_nat(&self) -> bool {
82 self.0 == i64::MIN
83 }
84
85 #[inline]
91 pub const fn is_not_nat(&self) -> bool {
92 self.0 != i64::MIN
93 }
94
95 #[inline]
101 pub const fn nat() -> Self {
102 Self(i64::MIN, PhantomData)
103 }
104
105 #[inline]
111 pub const fn into_i64(self) -> i64 {
112 self.0
113 }
114
115 #[inline]
125 pub const fn from_opt_i64(v: Option<i64>) -> Self {
126 if let Some(v) = v {
127 Self::new(v)
128 } else {
129 Self::nat()
130 }
131 }
132
133 #[inline]
139 pub const fn into_opt_i64(self) -> Option<i64> {
140 if self.is_nat() {
141 None
142 } else {
143 Some(self.0)
144 }
145 }
146
147 #[inline]
153 #[deprecated(since = "0.5.0", note = "use `as_cr` instead")]
154 pub fn to_cr(&self) -> Option<CrDateTime<Utc>>
155 where
156 Self: TryInto<CrDateTime<Utc>>,
157 {
158 self.as_cr()
159 }
160
161 #[inline]
162 pub fn as_cr(&self) -> Option<CrDateTime<Utc>>
163 where
164 Self: TryInto<CrDateTime<Utc>>,
165 {
166 if self.is_nat() {
167 None
168 } else {
169 (*self).try_into().ok()
170 }
171 }
172
173 #[inline(always)]
184 pub fn parse(s: &str, fmt: Option<&str>) -> TResult<Self>
185 where
186 Self: From<CrDateTime<Utc>>,
187 {
188 if let Some(fmt) = fmt {
189 if let Ok(cr_dt) = NaiveDateTime::parse_from_str(s, fmt) {
190 Ok(cr_dt.into())
191 } else if let Ok(cr_date) = NaiveDate::parse_from_str(s, fmt) {
192 Ok(cr_date.into())
193 } else {
194 tbail!(ParseError:"Failed to parse datetime from string: {}", s)
195 }
196 } else {
197 for fmt in TIME_RULE_VEC.iter() {
198 if let Ok(cr_dt) = NaiveDateTime::parse_from_str(s, fmt) {
199 return Ok(cr_dt.into());
200 } else if let Ok(cr_date) = NaiveDate::parse_from_str(s, fmt) {
201 return Ok(cr_date.into());
202 }
203 }
204 tbail!(ParseError:"Failed to parse datetime from string: {}", s)
205 }
206 }
207
208 #[inline]
218 pub fn strftime(&self, fmt: Option<&str>) -> String
219 where
220 Self: TryInto<CrDateTime<Utc>>,
221 <Self as TryInto<CrDateTime<Utc>>>::Error: std::fmt::Debug,
222 {
223 if self.is_nat() {
224 "NaT".to_string()
225 } else {
226 let fmt = fmt.unwrap_or("%Y-%m-%d %H:%M:%S.%f");
227 self.as_cr().unwrap().format(fmt).to_string()
228 }
229 }
230
231 pub fn duration_trunc(self, duration: TimeDelta) -> Self
241 where
242 Self: TryInto<CrDateTime<Utc>> + From<CrDateTime<Utc>>,
243 <Self as TryInto<CrDateTime<Utc>>>::Error: std::fmt::Debug,
244 {
245 if self.is_nat() {
246 return self;
247 }
248 let mut dt = self.as_cr().unwrap();
249 let dm = duration.months;
250 if dm != 0 {
251 let (flag, dt_year) = dt.year_ce();
252 if dm < 0 {
253 unimplemented!("not support year before ce or negative month")
254 }
255 let dt_month = if flag {
256 (dt_year * 12 + dt.month()) as i32
257 } else {
258 dt_year as i32 * (-12) + dt.month() as i32
259 };
260 let delta_down = dt_month % dm;
261 dt = match delta_down.cmp(&0) {
262 Ordering::Equal => dt,
263 Ordering::Greater => dt - Months::new(delta_down as u32),
264 Ordering::Less => dt - Months::new((dm - delta_down.abs()) as u32),
265 };
266 if let Some(nd) = duration.inner.num_nanoseconds() {
267 if nd == 0 {
268 return dt.into();
269 }
270 }
271 }
272 dt.duration_trunc(duration.inner)
273 .expect("Rounding Error")
274 .into()
275 }
276}
277
278impl<U: TimeUnitTrait> DateTime<U>
279where
280 Self: TryInto<CrDateTime<Utc>>,
281{
282 #[inline(always)]
288 pub fn time(&self) -> Option<NaiveTime> {
289 self.as_cr().map(|dt| dt.time())
290 }
291
292 #[inline(always)]
298 pub fn year(&self) -> Option<i32> {
299 self.as_cr().map(|dt| dt.year())
300 }
301
302 #[inline(always)]
308 pub fn day(&self) -> Option<usize> {
309 self.as_cr().map(|dt| dt.day() as usize)
310 }
311
312 #[inline(always)]
318 pub fn month(&self) -> Option<usize> {
319 self.as_cr().map(|dt| dt.month() as usize)
320 }
321
322 #[inline(always)]
328 pub fn hour(&self) -> Option<usize> {
329 self.as_cr().map(|dt| dt.hour() as usize)
330 }
331
332 #[inline(always)]
338 pub fn minute(&self) -> Option<usize> {
339 self.as_cr().map(|dt| dt.minute() as usize)
340 }
341
342 #[inline(always)]
348 pub fn second(&self) -> Option<usize> {
349 self.as_cr().map(|dt| dt.second() as usize)
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 #[allow(unused_assignments, unused_variables)]
359 fn test_parse_datetime() -> TResult<()> {
360 let mut dt: DateTime = "2020-01-01 00:00:00".parse()?;
361 dt = DateTime::parse("2020-01-01", Some("%Y-%m-%d"))?;
362 dt = "2020-01-01".parse()?;
363 dt = "20220101".parse()?;
364 dt = "2021/02/03".parse()?;
365 Ok(())
366 }
367
368 #[test]
369 fn test_datetime_components() -> TResult<()> {
370 let dt: DateTime = "2023-05-15 14:30:45".parse()?;
371 assert_eq!(dt.year(), Some(2023));
372 assert_eq!(dt.month(), Some(5));
373 assert_eq!(dt.day(), Some(15));
374 assert_eq!(dt.hour(), Some(14));
375 assert_eq!(dt.minute(), Some(30));
376 assert_eq!(dt.second(), Some(45));
377 Ok(())
378 }
379
380 #[test]
381 fn test_nat_datetime() {
382 let nat_dt: DateTime = DateTime::nat();
383 assert!(nat_dt.is_nat());
384 assert_eq!(nat_dt.year(), None);
385 assert_eq!(nat_dt.month(), None);
386 assert_eq!(nat_dt.day(), None);
387 assert_eq!(nat_dt.hour(), None);
388 assert_eq!(nat_dt.minute(), None);
389 assert_eq!(nat_dt.second(), None);
390 }
391
392 #[test]
393 fn test_invalid_datetime_parse() {
394 assert!("invalid date".parse::<DateTime>().is_err());
395 }
396}