1use crate::*;
21use std::fmt;
22use std::ops::{Add, AddAssign, Sub, SubAssign};
23use std::str::FromStr;
24use std::time::{Duration, SystemTime};
25
26#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct Timestamp(jiff::Timestamp);
29
30impl FromStr for Timestamp {
31 type Err = Error;
32
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 match s.parse() {
45 Ok(t) => Ok(Timestamp(t)),
46 Err(err) => Err(Error::new(
47 ErrorKind::Unexpected,
48 format!("parse '{s}' into timestamp failed"),
49 )
50 .set_source(err)),
51 }
52 }
53}
54
55impl fmt::Display for Timestamp {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "{}", self.0)
58 }
59}
60
61impl Timestamp {
62 pub const MIN: Self = Self(jiff::Timestamp::MIN);
64
65 pub const MAX: Self = Self(jiff::Timestamp::MAX);
67
68 pub fn now() -> Self {
70 Self(jiff::Timestamp::now())
71 }
72
73 pub fn format_http_date(self) -> String {
82 self.0.strftime("%a, %d %b %Y %T GMT").to_string()
83 }
84
85 pub fn new(second: i64, nanosecond: i32) -> Result<Self, Error> {
90 match jiff::Timestamp::new(second, nanosecond) {
91 Ok(t) => Ok(Timestamp(t)),
92 Err(err) => Err(Error::new(
93 ErrorKind::Unexpected,
94 format!(
95 "create timestamp from '{second}' seconds and '{nanosecond}' nanoseconds failed"
96 ),
97 )
98 .set_source(err)),
99 }
100 }
101
102 pub fn from_millisecond(millis: i64) -> Result<Self> {
109 match jiff::Timestamp::from_millisecond(millis) {
110 Ok(t) => Ok(Timestamp(t)),
111 Err(err) => Err(Error::new(
112 ErrorKind::Unexpected,
113 format!("convert '{millis}' milliseconds into timestamp failed"),
114 )
115 .set_source(err)),
116 }
117 }
118
119 pub fn from_second(second: i64) -> Result<Self> {
126 match jiff::Timestamp::from_second(second) {
127 Ok(t) => Ok(Timestamp(t)),
128 Err(err) => Err(Error::new(
129 ErrorKind::Unexpected,
130 format!("convert '{second}' seconds into timestamp failed"),
131 )
132 .set_source(err)),
133 }
134 }
135
136 pub fn parse_rfc2822(s: &str) -> Result<Timestamp> {
143 match jiff::fmt::rfc2822::parse(s) {
144 Ok(zoned) => Ok(Timestamp(zoned.timestamp())),
145 Err(err) => Err(Error::new(
146 ErrorKind::Unexpected,
147 format!("parse '{s}' into rfc2822 failed"),
148 )
149 .set_source(err)),
150 }
151 }
152
153 pub fn into_inner(self) -> jiff::Timestamp {
158 self.0
159 }
160}
161
162impl From<Timestamp> for jiff::Timestamp {
163 fn from(t: Timestamp) -> Self {
164 t.0
165 }
166}
167
168impl From<Timestamp> for SystemTime {
169 fn from(t: Timestamp) -> Self {
170 t.0.into()
171 }
172}
173
174impl From<jiff::Timestamp> for Timestamp {
175 fn from(t: jiff::Timestamp) -> Self {
176 Timestamp(t)
177 }
178}
179
180impl TryFrom<SystemTime> for Timestamp {
181 type Error = Error;
182
183 fn try_from(t: SystemTime) -> Result<Self> {
184 jiff::Timestamp::try_from(t).map(Timestamp).map_err(|err| {
185 Error::new(ErrorKind::Unexpected, "input timestamp overflow").set_source(err)
186 })
187 }
188}
189
190impl Add<Duration> for Timestamp {
191 type Output = Timestamp;
192
193 fn add(self, rhs: Duration) -> Timestamp {
194 let ts = self
195 .0
196 .checked_add(rhs)
197 .expect("adding unsigned duration to timestamp overflowed");
198
199 Timestamp(ts)
200 }
201}
202
203impl AddAssign<Duration> for Timestamp {
204 fn add_assign(&mut self, rhs: Duration) {
205 *self = *self + rhs
206 }
207}
208
209impl Sub<Duration> for Timestamp {
210 type Output = Timestamp;
211
212 fn sub(self, rhs: Duration) -> Timestamp {
213 let ts = self
214 .0
215 .checked_sub(rhs)
216 .expect("subtracting unsigned duration from timestamp overflowed");
217
218 Timestamp(ts)
219 }
220}
221
222impl SubAssign<Duration> for Timestamp {
223 fn sub_assign(&mut self, rhs: Duration) {
224 *self = *self - rhs
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 fn test_time() -> Timestamp {
233 Timestamp("2022-03-01T08:12:34Z".parse().unwrap())
234 }
235
236 #[test]
237 fn test_format_http_date() {
238 let t = test_time();
239 assert_eq!("Tue, 01 Mar 2022 08:12:34 GMT", t.format_http_date())
240 }
241
242 #[test]
243 fn test_parse_rfc3339() {
244 let t = test_time();
245
246 for v in [
247 "2022-03-01T08:12:34Z",
248 "2022-03-01T08:12:34+00:00",
249 "2022-03-01T08:12:34.00+00:00",
250 ] {
251 assert_eq!(t, v.parse().expect("must be valid time"));
252 }
253 }
254
255 #[test]
256 fn test_parse_rfc2822() {
257 let s = "Sat, 29 Oct 1994 19:43:31 +0000";
258 let v = Timestamp::parse_rfc2822(s).unwrap();
259 assert_eq!("Sat, 29 Oct 1994 19:43:31 GMT", v.format_http_date());
260 }
261}