1use std::{
2 fmt::Display,
3 time::{Duration, SystemTime},
4};
5
6use regex::Regex;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9#[derive(Clone, Copy, Default, Debug, PartialEq, PartialOrd)]
21pub struct Datetime {
22 year: i64,
23 month: u8,
24 day: u8,
25 hour: u8,
26 minute: u8,
27 second: u8,
28}
29
30impl Datetime {
31 pub fn add_years(&mut self, years: i64) -> &mut Self {
33 if self.year < 0 {
34 self.year += years;
35 if self.year >= 0 {
36 self.year += 1;
37 }
38 } else if self.year > 0 {
39 self.year += years;
40 if self.year <= 0 {
41 self.year -= 1;
42 }
43 } else {
44 self.year = years;
45 }
46
47 if self.month == 2 && self.day > 28 {
48 let yz = if self.year < 0 {
49 self.year + 1
50 } else {
51 self.year
52 };
53
54 if self.day >= 29 && yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
55 self.day = 29;
56 } else {
57 self.day = 28;
58 }
59 }
60
61 self
62 }
63
64 pub fn add_months(&mut self, months: i64) -> &mut Self {
66 let ys = (self.month as i64 + months) / 12;
67 let mut ms = (self.month as i64 + months) % 12;
68
69 if ms < 1 {
70 ms += 12;
71 self.month = ms as u8;
72
73 self.add_years(ys - 1);
74 } else {
75 self.month = ms as u8;
76
77 self.add_years(ys);
78 }
79
80 match self.month {
81 4 | 6 | 9 | 11 if self.day > 30 => {
82 self.day = 30;
83 }
84 _ => {}
85 }
86
87 self
88 }
89
90 pub fn add_days(&mut self, days: i64) -> &mut Self {
92 let mut ds = self.day as i64 + days;
93
94 self.day = 1;
95
96 if ds >= 1 {
97 loop {
98 let mut yz = if self.year < 0 {
99 self.year + 1
100 } else {
101 self.year
102 };
103
104 if self.month > 2 {
105 yz += 1;
106 }
107
108 if yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
109 if ds <= 366 {
110 break;
111 }
112 ds -= 366;
113 } else {
114 if ds <= 365 {
115 break;
116 }
117 ds -= 365;
118 }
119
120 self.add_years(1);
121 }
122
123 loop {
124 let yz = if self.year < 0 {
125 self.year + 1
126 } else {
127 self.year
128 };
129
130 match self.month {
131 1 | 3 | 5 | 7 | 8 | 10 | 12 => {
132 if ds <= 31 {
133 break;
134 }
135 ds -= 31;
136 }
137 2 => {
138 if yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
139 if ds <= 29 {
140 break;
141 }
142 ds -= 29;
143 } else {
144 if ds <= 28 {
145 break;
146 }
147 ds -= 28;
148 }
149 }
150 4 | 6 | 9 | 11 => {
151 if ds <= 30 {
152 break;
153 }
154 ds -= 30;
155 }
156 _ => {}
157 }
158
159 self.add_months(1);
160 }
161 } else {
162 loop {
163 let mut yz = if self.year < 0 {
164 self.year + 1
165 } else {
166 self.year
167 };
168
169 if self.month <= 2 {
170 yz -= 1;
171 }
172
173 if yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
174 if ds >= -366 {
175 break;
176 }
177 ds += 366;
178 } else {
179 if ds >= -365 {
180 break;
181 }
182 ds += 365;
183 }
184
185 self.add_years(-1);
186 }
187
188 loop {
189 if ds >= 1 {
190 break;
191 }
192
193 self.add_months(-1);
194
195 let yz = if self.year < 0 {
196 self.year + 1
197 } else {
198 self.year
199 };
200
201 match self.month {
202 1 | 3 | 5 | 7 | 8 | 10 | 12 => {
203 ds += 31;
204 }
205 2 => {
206 if yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
207 ds += 29;
208 } else {
209 ds += 28;
210 }
211 }
212 4 | 6 | 9 | 11 => {
213 ds += 30;
214 }
215 _ => {}
216 }
217 }
218 }
219
220 self.day = ds as u8;
221
222 self
223 }
224
225 pub fn add_hours(&mut self, hours: i64) -> &mut Self {
232 let mut hs = (self.hour as i64 + hours) % 24;
233
234 if hs < 0 {
235 hs += 24;
236
237 self.add_days((self.hour as i64 + hours) / 24 - 1);
238 } else {
239 self.add_days((self.hour as i64 + hours) / 24);
240 }
241
242 self.hour = hs as u8;
243
244 self
245 }
246
247 pub fn add_minutes(&mut self, minutes: i64) -> &mut Self {
249 let mut ms = (self.minute as i64 + minutes) % 60;
250
251 if ms < 0 {
252 ms += 60;
253
254 self.add_hours((self.minute as i64 + minutes) / 60 - 1);
255 } else {
256 self.add_hours((self.minute as i64 + minutes) / 60);
257 }
258
259 self.minute = ms as u8;
260
261 self
262 }
263
264 pub fn add_seconds(&mut self, seconds: i64) -> &mut Self {
266 let mut ss = (self.second as i64 + seconds) % 60;
267
268 if ss < 0 {
269 ss += 60;
270
271 self.add_minutes((self.second as i64 + seconds) / 60 - 1);
272 } else {
273 self.add_minutes((self.second as i64 + seconds) / 60);
274 }
275
276 self.second = ss as u8;
277
278 self
279 }
280
281 #[inline(always)]
282 pub fn year(&self) -> i64 {
283 self.year
284 }
285
286 #[inline(always)]
287 pub fn month(&self) -> i64 {
288 self.month as i64
289 }
290
291 #[inline(always)]
292 pub fn day(&self) -> i64 {
293 self.day as i64
294 }
295
296 #[inline(always)]
297 pub fn hour(&self) -> i64 {
298 self.hour as i64
299 }
300
301 #[inline(always)]
302 pub fn minute(&self) -> i64 {
303 self.minute as i64
304 }
305
306 #[inline(always)]
307 pub fn second(&self) -> i64 {
308 self.second as i64
309 }
310
311 pub fn day_of_week(&self) -> &'static str {
319 let mut year = self.year();
320 let mut month = self.month();
321 let day = self.day();
322
323 let dayofweek = [
324 "Sunday",
325 "Monday",
326 "Tuesday",
327 "Wednesday",
328 "Thursday",
329 "Friday",
330 "Saturday",
331 ];
332
333 month -= 2;
335 if month < 1 {
336 month += 12;
337 year -= 1;
338 }
339
340 let cent = year / 100;
342 year %= 100;
343
344 dayofweek
345 [((26 * month - 2) / 10 + day + year + year / 4 + cent / 4 + 5 * cent) as usize % 7]
346 }
347
348 pub fn seconds_since(&self, earlier: Datetime) -> i64 {
356 let stop = Self {
357 year: self.year,
358 month: self.month,
359 day: self.day,
360 hour: 0,
361 minute: 0,
362 second: 0,
363 };
364 let mut start = Self {
365 year: earlier.year,
366 month: earlier.month,
367 day: earlier.day,
368 hour: 0,
369 minute: 0,
370 second: 0,
371 };
372
373 let mut ss =
374 (stop.year() - start.year()) * 365 + (stop.month() - start.month()) * 30 + stop.day()
375 - start.day();
376 start.add_days(ss);
377
378 while stop > start {
379 start.add_days(1);
380 ss += 1;
381 }
382 while stop < start {
383 start.add_days(-1);
384 ss -= 1;
385 }
386
387 ss = ss * 24 + self.hour() - earlier.hour();
388 ss = ss * 60 + self.minute() - earlier.minute();
389 ss = ss * 60 + self.second() - earlier.second();
390
391 ss
392 }
393
394 pub fn is_valid(&self) -> bool {
402 if self.year == 0 || self.month < 1 || self.month > 12 || self.day < 1 {
403 return false;
404 }
405 match self.month {
406 1 | 3 | 5 | 7 | 8 | 10 | 12 => {
407 if self.day > 31 {
408 return false;
409 }
410 }
411 2 => {
412 let yz = if self.year < 0 {
413 self.year + 1
414 } else {
415 self.year
416 };
417 if yz % 4 == 0 && (yz % 100 != 0 || yz % 400 == 0) {
418 if self.day > 29 {
419 return false;
420 }
421 } else {
422 if self.day > 28 {
423 return false;
424 }
425 }
426 }
427 4 | 6 | 9 | 11 => {
428 if self.day > 30 {
429 return false;
430 }
431 }
432 _ => {}
433 }
434 if self.hour >= 24 || self.minute >= 60 || self.second >= 60 {
435 return false;
436 }
437 true
438 }
439
440 pub fn from_str(dt: &str) -> Option<Self> {
442 if let Ok(re) = Regex::new("(\\d+)\\D+(\\d+)\\D+(\\d+)\\D*(\\d*)\\D*(\\d*)\\D*(\\d*)(\\D*)")
443 {
444 if let Some(caps) = re.captures(dt) {
445 let year = if matches!(caps.get(7),Some(b) if b.as_str().contains("BC")) {
446 -caps
447 .get(1)
448 .map_or(0, |m| m.as_str().parse().unwrap_or_default())
449 } else {
450 caps.get(1)
451 .map_or(0, |m| m.as_str().parse().unwrap_or_default())
452 };
453 let month = caps
454 .get(2)
455 .map_or(0, |m| m.as_str().parse().unwrap_or_default());
456 let day = caps
457 .get(3)
458 .map_or(0, |m| m.as_str().parse().unwrap_or_default());
459 let hour = caps
460 .get(4)
461 .map_or(0, |m| m.as_str().parse().unwrap_or_default());
462 let minute = caps
463 .get(5)
464 .map_or(0, |m| m.as_str().parse().unwrap_or_default());
465 let second = caps
466 .get(6)
467 .map_or(0, |m| m.as_str().parse().unwrap_or_default());
468
469 return Some(Self {
470 year,
471 month,
472 day,
473 hour,
474 minute,
475 second,
476 });
477 }
478 }
479
480 None
481 }
482
483 pub fn from_rfc3339(rfc: &str) -> Option<Self> {
493 if rfc.len() >= 10 {
494 let mut dt = Self {
495 year: rfc[0..4].parse().ok()?,
496 month: rfc[5..7].parse().ok()?,
497 day: rfc[8..10].parse().ok()?,
498 hour: 0,
499 minute: 0,
500 second: 0,
501 };
502
503 if rfc.len() >= 19 {
504 dt.hour = rfc[11..13].parse().ok()?;
505 dt.minute = rfc[14..16].parse().ok()?;
506 dt.second = rfc[17..19].parse().ok()?;
507
508 if rfc.len() > 19 {
509 let tail = &rfc[19..];
510 if let Some(p) = tail.find(&['+', '-']) {
511 let z: Vec<&str> = tail[p + 1..].split(':').collect();
512 if z.len() > 0 {
513 if &tail[p..p + 1] == "+" {
514 dt.add_hours(-z[0].parse().ok()?);
515 if z.len() > 1 {
516 dt.add_minutes(-z[1].parse().ok()?);
517 }
518 } else if &tail[p..p + 1] == "-" {
519 dt.add_hours(z[0].parse::<i64>().ok()?);
520 if z.len() > 1 {
521 dt.add_minutes(z[1].parse::<i64>().ok()?);
522 }
523 }
524 }
525 }
526 }
527 }
528
529 return Some(dt);
530 }
531 None
532 }
533
534 pub fn timestamp() -> Duration {
536 SystemTime::now()
537 .duration_since(SystemTime::UNIX_EPOCH)
538 .unwrap_or_default()
539 }
540
541 pub fn now() -> Self {
543 let mut epoch = Self {
544 year: 1970,
545 month: 1,
546 day: 1,
547 hour: 0,
548 minute: 0,
549 second: 0,
550 };
551 epoch.add_seconds(Datetime::timestamp().as_secs() as i64);
552 epoch
553 }
554}
555
556impl Display for Datetime {
557 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558 if self.year >= 0 {
559 write!(
560 f,
561 "{:0>4}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2}",
562 self.year, self.month, self.day, self.hour, self.minute, self.second
563 )
564 } else {
565 write!(
566 f,
567 "{:0>4}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2} BC",
568 -self.year, self.month, self.day, self.hour, self.minute, self.second
569 )
570 }
571 }
572}
573
574impl Serialize for Datetime {
575 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
576 where
577 S: Serializer,
578 {
579 let s = self.to_string();
580 String::serialize(&s, serializer)
581 }
582}
583
584impl<'de> Deserialize<'de> for Datetime {
585 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
586 where
587 D: Deserializer<'de>,
588 {
589 let s = String::deserialize(deserializer)?;
590 if s.len() == 0 {
591 Ok(Datetime::default())
592 } else {
593 if let Some(r) = Datetime::from_rfc3339(&s) {
594 Ok(r)
595 } else if let Some(r) = Datetime::from_str(&s) {
596 Ok(r)
597 } else {
598 Err(serde::de::Error::custom("The data format is not correct"))
599 }
600 }
601 }
602}
603
604#[cfg(feature = "postgres")]
605impl sqlx::Type<sqlx::Postgres> for Datetime {
606 fn type_info() -> sqlx::postgres::PgTypeInfo {
607 sqlx::postgres::PgTypeInfo::with_name("TIMESTAMP")
608 }
609
610 fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
611 matches!(
612 ty.to_string().as_str(),
613 "TIMESTAMP" | "TIMESTAMPTZ" | "DATE" | "VARCHAR" | "TEXT"
614 )
615 }
616}
617
618#[cfg(feature = "postgres")]
619impl sqlx::Encode<'_, sqlx::Postgres> for Datetime {
620 fn encode_by_ref(
621 &self,
622 buf: &mut sqlx::postgres::PgArgumentBuffer,
623 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
624 let s = self.seconds_since(Self {
625 year: 2000,
626 month: 1,
627 day: 1,
628 hour: 0,
629 minute: 0,
630 second: 0,
631 }) * 1000000;
632 sqlx::Encode::<sqlx::Postgres>::encode_by_ref(&s, buf)
633 }
634
635 fn size_hint(&self) -> usize {
636 8
637 }
638}
639
640#[cfg(feature = "postgres")]
641impl<'r> sqlx::Decode<'r, sqlx::Postgres> for Datetime
642where
643 i64: sqlx::Decode<'r, sqlx::Postgres>,
644 i32: sqlx::Decode<'r, sqlx::Postgres>,
645 &'r str: sqlx::Decode<'r, sqlx::Postgres>,
646{
647 fn decode(value: sqlx::postgres::PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
649 match sqlx::ValueRef::type_info(&value)
650 .as_ref()
651 .to_string()
652 .as_str()
653 {
654 "TIMESTAMP" | "TIMESTAMPTZ" => {
655 let mut epoch = Self {
656 year: 2000,
657 month: 1,
658 day: 1,
659 hour: 0,
660 minute: 0,
661 second: 0,
662 };
663 epoch.add_seconds(i64::decode(value)? / 1000000);
664 Ok(epoch)
665 }
666 "DATE" => {
667 let mut epoch = Self {
668 year: 2000,
669 month: 1,
670 day: 1,
671 hour: 0,
672 minute: 0,
673 second: 0,
674 };
675 epoch.add_days(i32::decode(value)? as i64);
676 Ok(epoch)
677 }
678 _ => {
679 let s = <&str>::decode(value)?;
680 let res = if let Some(r) = Datetime::from_rfc3339(s) {
681 r
682 } else if let Some(r) = Datetime::from_str(s) {
683 r
684 } else {
685 Datetime::default()
686 };
687 Ok(res)
688 }
689 }
690 }
691}
692
693#[cfg(all(feature = "sqlx", not(feature = "postgres")))]
694impl<'r, DB: sqlx::Database> sqlx::Type<DB> for Datetime
695where
696 DB: sqlx::Database,
697 &'r str: sqlx::Type<DB>,
698{
699 fn type_info() -> <DB as sqlx::Database>::TypeInfo {
700 <&str>::type_info()
701 }
702
703 fn compatible(ty: &DB::TypeInfo) -> bool {
704 matches!(
705 ty.to_string().as_str(),
706 "TIMESTAMP" | "DATETIME" | "DATE" | "VARCHAR" | "TEXT"
707 )
708 }
709}
710
711#[cfg(all(feature = "sqlx", not(feature = "postgres")))]
712impl<'r, DB> sqlx::Encode<'r, DB> for Datetime
713where
714 DB: sqlx::Database,
715 String: sqlx::Encode<'r, DB>,
716{
717 fn encode_by_ref(
718 &self,
719 buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'r>,
720 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
721 <String>::encode(self.to_string(), buf)
722 }
723}
724
725#[cfg(all(feature = "sqlx", not(feature = "postgres")))]
726impl<'r, DB> sqlx::Decode<'r, DB> for Datetime
727where
728 DB: sqlx::Database,
729 &'r [u8]: sqlx::Decode<'r, DB>,
730 &'r str: sqlx::Decode<'r, DB>,
731{
732 fn decode(
733 value: <DB as sqlx::Database>::ValueRef<'r>,
734 ) -> Result<Self, sqlx::error::BoxDynError> {
735 match sqlx::ValueRef::type_info(&value)
736 .as_ref()
737 .to_string()
738 .as_str()
739 {
740 #[cfg(feature = "mysql")]
741 "TIMESTAMP" | "DATETIME" | "DATE" => {
742 let buf = <&[u8]>::decode(value)?;
743 let len = buf[0];
744 let mut dt = Self {
745 year: ((buf[2] as i64) << 8) + buf[1] as i64,
746 month: buf[3],
747 day: buf[4],
748 hour: 0,
749 minute: 0,
750 second: 0,
751 };
752 if len > 4 {
753 dt.hour = buf[5];
754 dt.minute = buf[6];
755 dt.second = buf[7];
756 }
757 Ok(dt)
758 }
759 _ => {
760 let s = <&str>::decode(value)?;
761 let res = if let Some(r) = Datetime::from_rfc3339(s) {
762 r
763 } else if let Some(r) = Datetime::from_str(s) {
764 r
765 } else {
766 Datetime::default()
767 };
768 Ok(res)
769 }
770 }
771 }
772}