httpdt/
datetime.rs

1//! # Datetime
2//!
3//! A datetime struct for HTTP clients and servers.
4
5use crate::date::Date;
6use crate::time::Time;
7
8use std::time::SystemTime;
9use std::fmt::{self, Display, Formatter};
10use std::error::Error;
11
12/// Stores the date, time and raw seconds since the epoch,
13/// with constructor, core methods for update (`now`) and
14/// output as a HTTP Date header timestamp (`for_header`),
15/// utility methods for construction via diff (`set`) and
16/// current number of seconds since the epoch (`raw`) and
17/// a `Default` implementation for the Unix epoch values.
18///
19/// # Example
20///
21/// ```
22/// use httpdt::Datetime;
23///
24/// let dt = Datetime::new()
25///   .unwrap();
26///
27/// let ts_initial = dt
28///   .for_header();
29///
30/// // ...
31///
32/// let ts_updated = dt
33///   .now()
34///   .unwrap()
35///   .for_header();
36/// ```
37#[derive(Default, PartialEq, Debug)]
38pub struct Datetime {
39  pub date: Date,
40  pub time: Time,
41  pub secs: u64
42}
43
44impl Datetime {
45
46  pub fn new() -> Result<Self, Box<dyn Error>> {
47    let new = Self::default().now()?;
48    Ok (new)
49  }
50
51  pub fn raw() -> Result<u64, Box<dyn Error>> {
52    let raw = SystemTime::now()
53      .duration_since(SystemTime::UNIX_EPOCH)?
54      .as_secs();
55    Ok (raw)
56  }
57
58  pub fn now(&self) -> Result<Self, Box<dyn Error>> {
59    let raw = Self::raw()?;
60    let now = self.set(raw);
61    Ok (now)
62  }
63
64  pub fn set(&self, secs: u64) -> Self {
65    let date = self.date.skip(secs - self.secs);
66    let time = Time::from(secs);
67    Self { date, time, secs }
68  }
69
70  pub fn for_header(&self) -> String {
71    ImfFixdate(self).to_string()
72  }
73}
74
75// ImfFixdate
76
77struct ImfFixdate<'a>(&'a Datetime);
78
79impl Display for ImfFixdate<'_> {
80
81  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
82    let ImfFixdate(dt) = self;
83    write!(f, "{} {} GMT",
84      dt.date.for_header(),
85      dt.time.for_header()
86    )
87  }
88}
89
90#[cfg(test)]
91mod test {
92
93  use super::Datetime;
94  use crate::date::{self, D_AS_S, test::{M_28_AS_S, M_29_AS_S, M_30_AS_S, M_31_AS_S, Y_365_AS_S, Y_366_AS_S}};
95  use crate::time::{self, Time, M_AS_S, H_AS_M, D_AS_H};
96
97  use std::time::{SystemTime, Duration};
98  use std::thread::sleep;
99
100  // 1970
101  const JAN_01_1970_00_00_00: Datetime = Datetime {
102    date: date::test::JAN_01_1970_00_00_00,
103    time: time::test::JAN_01_1970_00_00_00,
104    secs: 0
105  };
106  const FEB_28_1970_23_59_59: Datetime = Datetime {
107    date: date::test::FEB_28_1970_23_59_59,
108    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs:                    M_31_AS_S                     + M_28_AS_S - D_AS_S },
109    secs: M_31_AS_S + M_28_AS_S - 1
110  };
111  const MAR_01_1970_00_00_00: Datetime = Datetime {
112    date: date::test::MAR_01_1970_00_00_00,
113    time: Time { h:          0, m:          0, s:          0, xs:                    M_31_AS_S                     + M_28_AS_S          },
114    secs: M_31_AS_S + M_28_AS_S
115  };
116  const APR_30_1970_23_59_59: Datetime = Datetime {
117    date: date::test::APR_30_1970_23_59_59,
118    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs:                    M_31_AS_S * 2 + M_30_AS_S     + M_28_AS_S - D_AS_S },
119    secs: M_31_AS_S * 2 + M_30_AS_S + M_28_AS_S - 1
120  };
121  const MAY_01_1970_00_00_00: Datetime = Datetime {
122    date: date::test::MAY_01_1970_00_00_00,
123    time: Time { h:          0, m:          0, s:          0, xs:                    M_31_AS_S * 2 + M_30_AS_S     + M_28_AS_S          },
124    secs: M_31_AS_S * 2 + M_30_AS_S + M_28_AS_S
125  };
126  const JUL_31_1970_23_59_59: Datetime = Datetime {
127    date: date::test::JUL_31_1970_23_59_59,
128    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs:                    M_31_AS_S * 4 + M_30_AS_S * 2 + M_28_AS_S - D_AS_S },
129    secs: M_31_AS_S * 4 + M_30_AS_S * 2 + M_28_AS_S - 1
130  };
131  const SEP_01_1970_00_00_00: Datetime = Datetime {
132    date: date::test::SEP_01_1970_00_00_00,
133    time: Time { h:          0, m:          0, s:          0, xs:                    M_31_AS_S * 5 + M_30_AS_S * 2 + M_28_AS_S          },
134    secs: M_31_AS_S * 5 + M_30_AS_S * 2 + M_28_AS_S
135  };
136  const DEC_31_1970_23_59_59: Datetime = Datetime {
137    date: date::test::DEC_31_1970_23_59_59,
138    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs: Y_365_AS_S                                                   - D_AS_S },
139    secs: Y_365_AS_S - 1
140  };
141
142  // 1972
143  const JAN_01_1972_00_00_00: Datetime = Datetime {
144    date: date::test::JAN_01_1972_00_00_00,
145    time: Time { h:          0, m:          0, s:          0, xs: Y_365_AS_S *  2                                                       },
146    secs: Y_365_AS_S * 2
147  };
148  const FEB_29_1972_23_59_59: Datetime = Datetime {
149    date: date::test::FEB_29_1972_23_59_59,
150    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs: Y_365_AS_S *  2 + M_31_AS_S                      + M_29_AS_S - D_AS_S },
151    secs: Y_365_AS_S * 2 + M_31_AS_S + M_29_AS_S - 1
152  };
153  const MAR_01_1972_00_00_00: Datetime = Datetime {
154    date: date::test::MAR_01_1972_00_00_00,
155    time: Time { h:          0, m:          0, s:          0, xs: Y_365_AS_S *  2 + M_31_AS_S                      + M_29_AS_S          },
156    secs: Y_365_AS_S * 2 + M_31_AS_S + M_29_AS_S
157  };
158  const DEC_31_1972_23_59_59: Datetime = Datetime {
159    date: date::test::DEC_31_1972_23_59_59,
160    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs: Y_365_AS_S *  2 + Y_366_AS_S                                 - D_AS_S },
161    secs: Y_365_AS_S * 2 + Y_366_AS_S - 1
162  };
163
164  // 2000
165  const JAN_01_2000_00_00_00: Datetime = Datetime {
166    date: date::test::JAN_01_2000_00_00_00,
167    time: Time { h:          0, m:          0, s:          0, xs: Y_365_AS_S * 23 + Y_366_AS_S *  7                                     },
168    secs: Y_365_AS_S * 23 + Y_366_AS_S * 7
169  };
170  const DEC_31_2000_23_59_59: Datetime = Datetime {
171    date: date::test::DEC_31_2000_23_59_59,
172    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs: Y_365_AS_S * 23 + Y_366_AS_S *  8                            - D_AS_S },
173    secs: Y_365_AS_S * 23 + Y_366_AS_S * 8 - 1
174  };
175
176  // 2024
177  const DEC_31_2024_23_59_59: Datetime = Datetime {
178    date: date::test::DEC_31_2024_23_59_59,
179    time: Time { h: D_AS_H - 1, m: H_AS_M - 1, s: M_AS_S - 1, xs: Y_365_AS_S * 41 + Y_366_AS_S * 14                            - D_AS_S },
180    secs: Y_365_AS_S * 41 + Y_366_AS_S * 14 - 1
181  };
182
183  #[test]
184  fn datetime_default() {
185
186    assert_eq!(JAN_01_1970_00_00_00, Datetime::default());
187  }
188
189  #[test]
190  fn datetime_raw() {
191
192    let st_raw = SystemTime::now()
193      .duration_since(SystemTime::UNIX_EPOCH).unwrap()
194      .as_secs();
195
196    assert_eq!(st_raw, Datetime::raw().unwrap());
197  }
198
199  #[test]
200  fn datetime_new() {
201
202    let st_raw = SystemTime::now()
203      .duration_since(SystemTime::UNIX_EPOCH).unwrap()
204      .as_secs();
205
206    let dt_new = Datetime::new().unwrap();
207
208    assert_eq!(st_raw, dt_new.secs);
209    assert_eq!(st_raw, dt_new.date.xs + dt_new.time.xs);
210  }
211
212  #[test]
213  fn datetime_now() {
214
215    let dt_new = Datetime::new().unwrap();
216
217    sleep(Duration::from_secs(1));
218
219    let dt_now = dt_new.now().unwrap();
220
221    assert_eq!(dt_new.secs + 1, dt_now.secs);
222    assert_eq!(dt_new.date.xs + dt_new.time.xs + 1, dt_now.date.xs + dt_now.time.xs);
223  }
224
225  #[test]
226  fn datetime_set() {
227
228    // 1970
229    assert_eq!(FEB_28_1970_23_59_59, JAN_01_1970_00_00_00.set(                  M_31_AS_S                     + M_28_AS_S - 1));
230    assert_eq!(MAR_01_1970_00_00_00, FEB_28_1970_23_59_59.set(                  M_31_AS_S                     + M_28_AS_S    ));
231    assert_eq!(APR_30_1970_23_59_59, MAR_01_1970_00_00_00.set(                  M_31_AS_S * 2 + M_30_AS_S     + M_28_AS_S - 1));
232    assert_eq!(MAY_01_1970_00_00_00, APR_30_1970_23_59_59.set(                  M_31_AS_S * 2 + M_30_AS_S     + M_28_AS_S    ));
233    assert_eq!(JUL_31_1970_23_59_59, MAY_01_1970_00_00_00.set(                  M_31_AS_S * 4 + M_30_AS_S * 2 + M_28_AS_S - 1));
234    assert_eq!(SEP_01_1970_00_00_00, JUL_31_1970_23_59_59.set(                  M_31_AS_S * 5 + M_30_AS_S * 2 + M_28_AS_S    ));
235    assert_eq!(DEC_31_1970_23_59_59, SEP_01_1970_00_00_00.set(Y_365_AS_S                                                  - 1));
236
237    // 1972
238    assert_eq!(JAN_01_1972_00_00_00, DEC_31_1970_23_59_59.set(Y_365_AS_S *  2                                                ));
239    assert_eq!(FEB_29_1972_23_59_59, JAN_01_1972_00_00_00.set(Y_365_AS_S *  2                 + M_31_AS_S     + M_29_AS_S - 1));
240    assert_eq!(MAR_01_1972_00_00_00, FEB_29_1972_23_59_59.set(Y_365_AS_S *  2                 + M_31_AS_S     + M_29_AS_S    ));
241    assert_eq!(DEC_31_1972_23_59_59, MAR_01_1972_00_00_00.set(Y_365_AS_S *  2 + Y_366_AS_S                                - 1));
242
243    // 2000
244    assert_eq!(JAN_01_2000_00_00_00, DEC_31_1972_23_59_59.set(Y_365_AS_S * 23 + Y_366_AS_S *  7                              ));
245    assert_eq!(DEC_31_2000_23_59_59, JAN_01_2000_00_00_00.set(Y_365_AS_S * 23 + Y_366_AS_S *  8                           - 1));
246
247    // 2024
248    assert_eq!(DEC_31_2024_23_59_59, DEC_31_2000_23_59_59.set(Y_365_AS_S * 41 + Y_366_AS_S * 14                           - 1));
249  }
250
251  #[test]
252  fn datetime_for_header() {
253
254    // 1970
255    assert_eq!(String::from("Thu, 01 Jan 1970 00:00:00 GMT"), JAN_01_1970_00_00_00.for_header());
256    assert_eq!(String::from("Sat, 28 Feb 1970 23:59:59 GMT"), FEB_28_1970_23_59_59.for_header());
257    assert_eq!(String::from("Sun, 01 Mar 1970 00:00:00 GMT"), MAR_01_1970_00_00_00.for_header());
258    assert_eq!(String::from("Thu, 30 Apr 1970 23:59:59 GMT"), APR_30_1970_23_59_59.for_header());
259    assert_eq!(String::from("Fri, 01 May 1970 00:00:00 GMT"), MAY_01_1970_00_00_00.for_header());
260    assert_eq!(String::from("Fri, 31 Jul 1970 23:59:59 GMT"), JUL_31_1970_23_59_59.for_header());
261    assert_eq!(String::from("Tue, 01 Sep 1970 00:00:00 GMT"), SEP_01_1970_00_00_00.for_header());
262    assert_eq!(String::from("Thu, 31 Dec 1970 23:59:59 GMT"), DEC_31_1970_23_59_59.for_header());
263
264    // 1972
265    assert_eq!(String::from("Sat, 01 Jan 1972 00:00:00 GMT"), JAN_01_1972_00_00_00.for_header());
266    assert_eq!(String::from("Tue, 29 Feb 1972 23:59:59 GMT"), FEB_29_1972_23_59_59.for_header());
267    assert_eq!(String::from("Wed, 01 Mar 1972 00:00:00 GMT"), MAR_01_1972_00_00_00.for_header());
268    assert_eq!(String::from("Sun, 31 Dec 1972 23:59:59 GMT"), DEC_31_1972_23_59_59.for_header());
269
270    // 2000
271    assert_eq!(String::from("Sat, 01 Jan 2000 00:00:00 GMT"), JAN_01_2000_00_00_00.for_header());
272    assert_eq!(String::from("Sun, 31 Dec 2000 23:59:59 GMT"), DEC_31_2000_23_59_59.for_header());
273
274    // 2024
275    assert_eq!(String::from("Tue, 31 Dec 2024 23:59:59 GMT"), DEC_31_2024_23_59_59.for_header());
276  }
277}