lettre/message/header/
date.rs

1use std::time::SystemTime;
2
3use httpdate::HttpDate;
4
5use super::{Header, HeaderName, HeaderValue};
6use crate::BoxError;
7
8/// Message `Date` header
9///
10/// Defined in [RFC2822](https://tools.ietf.org/html/rfc2822#section-3.3)
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Date(HttpDate);
13
14impl Date {
15    /// Build a `Date` from [`SystemTime`]
16    pub fn new(st: SystemTime) -> Self {
17        Self(st.into())
18    }
19
20    /// Get the current date
21    ///
22    /// Shortcut for `Date::new(SystemTime::now())`
23    pub fn now() -> Self {
24        Self::new(crate::time::now())
25    }
26}
27
28impl Header for Date {
29    fn name() -> HeaderName {
30        HeaderName::new_from_ascii_str("Date")
31    }
32
33    fn parse(s: &str) -> Result<Self, BoxError> {
34        let mut s = String::from(s);
35        if s.ends_with("+0000") {
36            // The httpdate crate expects the `Date` to end in ` GMT`, but email
37            // uses `+0000` to indicate UTC, so we crudely fix this issue here.
38
39            s.truncate(s.len() - "+0000".len());
40            s.push_str("GMT");
41        }
42
43        Ok(Self(s.parse::<HttpDate>()?))
44    }
45
46    fn display(&self) -> HeaderValue {
47        let mut val = self.0.to_string();
48        if val.ends_with(" GMT") {
49            // The httpdate crate always appends ` GMT` to the end of the string,
50            // but this is considered an obsolete date format for email
51            // https://tools.ietf.org/html/rfc2822#appendix-A.6.2,
52            // so we replace `GMT` with `+0000`
53            val.truncate(val.len() - "GMT".len());
54            val.push_str("+0000");
55        }
56
57        HeaderValue::dangerous_new_pre_encoded(Self::name(), val.clone(), val)
58    }
59}
60
61impl From<SystemTime> for Date {
62    fn from(st: SystemTime) -> Self {
63        Self::new(st)
64    }
65}
66
67impl From<Date> for SystemTime {
68    fn from(this: Date) -> SystemTime {
69        this.0.into()
70    }
71}
72
73#[cfg(test)]
74mod test {
75    use std::time::{Duration, SystemTime};
76
77    use pretty_assertions::assert_eq;
78
79    use super::Date;
80    use crate::message::header::{HeaderName, HeaderValue, Headers};
81
82    #[test]
83    fn format_date() {
84        let mut headers = Headers::new();
85
86        // Tue, 15 Nov 1994 08:12:31 GMT
87        headers.set(Date::from(
88            SystemTime::UNIX_EPOCH + Duration::from_secs(784887151),
89        ));
90
91        assert_eq!(
92            headers.to_string(),
93            "Date: Tue, 15 Nov 1994 08:12:31 +0000\r\n".to_owned()
94        );
95
96        // Tue, 15 Nov 1994 08:12:32 GMT
97        headers.set(Date::from(
98            SystemTime::UNIX_EPOCH + Duration::from_secs(784887152),
99        ));
100
101        assert_eq!(
102            headers.to_string(),
103            "Date: Tue, 15 Nov 1994 08:12:32 +0000\r\n"
104        );
105    }
106
107    #[test]
108    fn parse_date() {
109        let mut headers = Headers::new();
110
111        headers.insert_raw(HeaderValue::new(
112            HeaderName::new_from_ascii_str("Date"),
113            "Tue, 15 Nov 1994 08:12:31 +0000".to_owned(),
114        ));
115
116        assert_eq!(
117            headers.get::<Date>(),
118            Some(Date::from(
119                SystemTime::UNIX_EPOCH + Duration::from_secs(784887151),
120            ))
121        );
122
123        headers.insert_raw(HeaderValue::new(
124            HeaderName::new_from_ascii_str("Date"),
125            "Tue, 15 Nov 1994 08:12:32 +0000".to_owned(),
126        ));
127
128        assert_eq!(
129            headers.get::<Date>(),
130            Some(Date::from(
131                SystemTime::UNIX_EPOCH + Duration::from_secs(784887152),
132            ))
133        );
134    }
135}