1#![doc = include_str!("../README.md")]
2
3use chrono::NaiveDateTime;
4use core::str::FromStr;
5
6pub mod diesel_impls;
7
8#[repr(transparent)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(
11 feature = "diesel",
12 derive(diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)
13)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature="diesel", diesel(sql_type = crate::diesel_impls::TimestampUTC))]
16pub struct TimestampUTC(chrono::DateTime<chrono::Utc>);
18
19impl TimestampUTC {
20 #[must_use]
21 pub fn now() -> Self {
31 Self(chrono::Utc::now())
32 }
33}
34
35impl Default for TimestampUTC {
36 fn default() -> Self {
37 Self::now()
38 }
39}
40
41impl From<chrono::DateTime<chrono::Utc>> for TimestampUTC {
42 fn from(value: chrono::DateTime<chrono::Utc>) -> Self {
43 Self(value)
44 }
45}
46
47impl From<TimestampUTC> for chrono::DateTime<chrono::Utc> {
48 fn from(value: TimestampUTC) -> Self {
49 value.0
50 }
51}
52
53impl AsRef<chrono::DateTime<chrono::Utc>> for TimestampUTC {
54 fn as_ref(&self) -> &chrono::DateTime<chrono::Utc> {
55 &self.0
56 }
57}
58
59impl AsMut<chrono::DateTime<chrono::Utc>> for TimestampUTC {
60 fn as_mut(&mut self) -> &mut chrono::DateTime<chrono::Utc> {
61 &mut self.0
62 }
63}
64
65impl From<NaiveDateTime> for TimestampUTC {
66 fn from(value: NaiveDateTime) -> Self {
67 use chrono::TimeZone;
68 Self(chrono::Utc.from_utc_datetime(&value))
69 }
70}
71
72impl core::ops::Deref for TimestampUTC {
73 type Target = chrono::DateTime<chrono::Utc>;
74
75 fn deref(&self) -> &Self::Target {
76 &self.0
77 }
78}
79
80impl core::ops::DerefMut for TimestampUTC {
81 fn deref_mut(&mut self) -> &mut Self::Target {
82 &mut self.0
83 }
84}
85
86impl core::fmt::Display for TimestampUTC {
87 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 self.0.fmt(f)
89 }
90}
91
92impl FromStr for TimestampUTC {
93 type Err = chrono::ParseError;
94
95 fn from_str(s: &str) -> Result<Self, Self::Err> {
96 Ok(Self(
97 chrono::DateTime::parse_from_rfc3339(s)?.with_timezone(&chrono::Utc),
98 ))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use std::cmp::Ordering;
106 use std::collections::HashSet;
107
108 #[test]
109 fn test_default() {
110 let t = TimestampUTC::default();
111 assert!(t.timestamp() > 0);
113 }
114
115 #[test]
116 fn test_from_str() {
117 let s = "2023-10-27T10:00:00+00:00";
118 let t = TimestampUTC::from_str(s).unwrap();
119 assert_eq!(t.to_rfc3339(), s);
120 }
121
122 #[test]
123 fn test_from_conversions() {
124 let inner = chrono::Utc::now();
125 let wrapper: TimestampUTC = inner.into();
126 assert_eq!(wrapper.0, inner);
127
128 let back: chrono::DateTime<chrono::Utc> = wrapper.into();
129 assert_eq!(back, inner);
130 }
131
132 #[test]
133 fn test_naive_conversion() {
134 let naive =
135 NaiveDateTime::parse_from_str("2023-10-27 10:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
136 let t: TimestampUTC = naive.into();
137 assert_eq!(
138 t.format("%Y-%m-%d %H:%M:%S").to_string(),
139 "2023-10-27 10:00:00"
140 );
141 }
142
143 #[test]
144 fn test_as_ref_as_mut() {
145 let mut t = TimestampUTC::now();
146
147 let r: &chrono::DateTime<chrono::Utc> = t.as_ref();
148 assert_eq!(*r, t.0);
149
150 let m: &mut chrono::DateTime<chrono::Utc> = t.as_mut();
151 *m = chrono::Utc::now(); }
153
154 #[test]
155 fn test_deref_deref_mut() {
156 let mut t = TimestampUTC::now();
157
158 assert!(t.timestamp() > 0);
160
161 let old_time = t;
164 *t = old_time.0 - chrono::Duration::days(1);
165 assert!(*t < *old_time);
166 }
167
168 #[test]
169 fn test_display() {
170 let s = "2023-10-27T10:00:00+00:00";
171 let t = TimestampUTC::from_str(s).unwrap();
172 assert_eq!(format!("{t}"), "2023-10-27 10:00:00 UTC");
173 }
174
175 #[test]
176 fn test_standard_traits() {
177 let t1 = TimestampUTC::now();
178 let t2 = Clone::clone(&t1); let t3 = t1; assert_eq!(t1, t2); assert_eq!(t1, t3); assert!(t1 == t2); let mut set = HashSet::new();
186 set.insert(t1); assert!(set.contains(&t2));
188 }
189
190 #[test]
191 fn test_ord() {
192 let t1 = TimestampUTC::from_str("2023-01-01T00:00:00+00:00").unwrap();
193 let t2 = TimestampUTC::from_str("2023-01-02T00:00:00+00:00").unwrap();
194
195 assert_eq!(t1.cmp(&t2), Ordering::Less); assert!(t1 < t2); }
198}