api_tools/value_objects/
datetime.rs1use chrono::{DateTime, TimeDelta, Utc};
4use serde::Deserialize;
5use std::fmt::{Display, Formatter};
6use std::ops::Add;
7use thiserror::Error;
8
9#[derive(Debug, Clone, PartialEq, Error)]
11pub enum UtcDateTimeError {
12 #[error("Invalid date time: {0}")]
13 InvalidDateTime(String),
14}
15
16#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Deserialize)]
18pub struct UtcDateTime {
19 value: DateTime<Utc>,
20}
21
22impl UtcDateTime {
23 pub fn now() -> Self {
25 Self { value: Utc::now() }
26 }
27
28 pub fn new(value: DateTime<Utc>) -> Self {
30 Self { value }
31 }
32
33 pub fn from_rfc3339(value: &str) -> Result<Self, UtcDateTimeError> {
45 let dt = DateTime::parse_from_rfc3339(value)
46 .map_err(|e| UtcDateTimeError::InvalidDateTime(format!("{e}: {value}")))?;
47
48 Ok(Self {
49 value: dt.with_timezone(&Utc),
50 })
51 }
52
53 pub fn timestamp(&self) -> i64 {
55 self.value.timestamp()
56 }
57
58 pub fn value(&self) -> DateTime<Utc> {
60 self.value
61 }
62
63 pub fn add(&self, rhs: TimeDelta) -> Self {
65 Self {
66 value: self.value.add(rhs),
67 }
68 }
69}
70
71impl From<DateTime<Utc>> for UtcDateTime {
72 fn from(value: DateTime<Utc>) -> Self {
73 Self { value }
74 }
75}
76
77impl Display for UtcDateTime {
78 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79 write!(f, "{}", self.value.to_rfc3339_opts(chrono::SecondsFormat::Secs, true))
80 }
81}
82
83#[cfg(test)]
84mod test {
85 use super::*;
86
87 #[test]
88 fn test_utc_date_time_display() {
89 let dt = DateTime::parse_from_rfc3339("2024-08-28T12:00:00Z")
90 .unwrap()
91 .with_timezone(&Utc);
92 let datetime = UtcDateTime::from(dt);
93
94 assert_eq!(datetime.to_string(), "2024-08-28T12:00:00Z");
95 }
96
97 #[test]
98 fn test_from_rfc3339() {
99 let datetime = UtcDateTime::from_rfc3339("2024-08-28T12:00:00Z");
100 assert!(datetime.is_ok());
101 assert_eq!(datetime.unwrap().to_string(), "2024-08-28T12:00:00Z".to_owned());
102
103 let invalid_datetime = UtcDateTime::from_rfc3339("2024-08-T12:00:00Z");
104 assert!(invalid_datetime.is_err());
105 }
106
107 #[test]
108 fn test_value() {
109 let dt = DateTime::parse_from_rfc3339("2024-08-28T12:00:00Z")
110 .unwrap()
111 .with_timezone(&Utc);
112 let datetime = UtcDateTime::from(dt);
113
114 assert_eq!(datetime.value(), dt);
115 }
116
117 #[test]
118 fn test_timestamp() {
119 let dt = DateTime::parse_from_rfc3339("2024-08-28T12:00:00Z")
120 .unwrap()
121 .with_timezone(&Utc);
122 let datetime = UtcDateTime::from(dt);
123
124 assert_eq!(datetime.timestamp(), 1724846400);
125 }
126
127 #[test]
128 fn test_add() {
129 let dt = DateTime::parse_from_rfc3339("2024-08-28T12:00:00Z")
130 .unwrap()
131 .with_timezone(&Utc);
132 let datetime = UtcDateTime::from(dt);
133 let new_datetime = datetime.add(TimeDelta::seconds(3600));
134 assert_eq!(new_datetime.to_string(), "2024-08-28T13:00:00Z".to_owned());
135 }
136}