1use crate::{
4 asn1::Any,
5 datetime::{self, DateTime},
6 ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, FixedTag, Length, OrdIsValueOrd,
7 Result, Tag,
8};
9use core::time::Duration;
10
11#[cfg(feature = "std")]
12use std::time::SystemTime;
13
14#[cfg(feature = "time")]
15use time::PrimitiveDateTime;
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
29pub struct GeneralizedTime(DateTime);
30
31impl GeneralizedTime {
32 pub const LENGTH: Length = Length::new(15);
34
35 pub fn from_date_time(datetime: DateTime) -> Self {
37 Self(datetime)
38 }
39
40 pub fn to_date_time(&self) -> DateTime {
42 self.0
43 }
44
45 pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
48 DateTime::from_unix_duration(unix_duration)
49 .map(Into::into)
50 .map_err(|_| Self::TAG.value_error())
51 }
52
53 pub fn to_unix_duration(&self) -> Duration {
55 self.0.unix_duration()
56 }
57
58 #[cfg(feature = "std")]
60 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
61 pub fn from_system_time(time: SystemTime) -> Result<Self> {
62 DateTime::try_from(time)
63 .map(Into::into)
64 .map_err(|_| Self::TAG.value_error())
65 }
66
67 #[cfg(feature = "std")]
69 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
70 pub fn to_system_time(&self) -> SystemTime {
71 self.0.to_system_time()
72 }
73}
74
75impl DecodeValue<'_> for GeneralizedTime {
76 fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result<Self> {
77 match *ByteSlice::decode_value(decoder, length)?.as_bytes() {
78 [y1, y2, y3, y4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
80 let year = datetime::decode_decimal(Self::TAG, y1, y2)? as u16 * 100
81 + datetime::decode_decimal(Self::TAG, y3, y4)? as u16;
82 let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?;
83 let day = datetime::decode_decimal(Self::TAG, day1, day2)?;
84 let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?;
85 let minute = datetime::decode_decimal(Self::TAG, min1, min2)?;
86 let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?;
87
88 DateTime::new(year, month, day, hour, minute, second)
89 .map_err(|_| Self::TAG.value_error())
90 .and_then(|dt| Self::from_unix_duration(dt.unix_duration()))
91 }
92 _ => Err(Self::TAG.value_error()),
93 }
94 }
95}
96
97impl EncodeValue for GeneralizedTime {
98 fn value_len(&self) -> Result<Length> {
99 Ok(Self::LENGTH)
100 }
101
102 fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
103 let year_hi = (self.0.year() / 100) as u8;
104 let year_lo = (self.0.year() % 100) as u8;
105
106 datetime::encode_decimal(encoder, Self::TAG, year_hi)?;
107 datetime::encode_decimal(encoder, Self::TAG, year_lo)?;
108 datetime::encode_decimal(encoder, Self::TAG, self.0.month())?;
109 datetime::encode_decimal(encoder, Self::TAG, self.0.day())?;
110 datetime::encode_decimal(encoder, Self::TAG, self.0.hour())?;
111 datetime::encode_decimal(encoder, Self::TAG, self.0.minutes())?;
112 datetime::encode_decimal(encoder, Self::TAG, self.0.seconds())?;
113 encoder.byte(b'Z')
114 }
115}
116
117impl FixedTag for GeneralizedTime {
118 const TAG: Tag = Tag::GeneralizedTime;
119}
120
121impl OrdIsValueOrd for GeneralizedTime {}
122
123impl From<&GeneralizedTime> for GeneralizedTime {
124 fn from(value: &GeneralizedTime) -> GeneralizedTime {
125 *value
126 }
127}
128
129impl From<GeneralizedTime> for DateTime {
130 fn from(utc_time: GeneralizedTime) -> DateTime {
131 utc_time.0
132 }
133}
134
135impl From<&GeneralizedTime> for DateTime {
136 fn from(utc_time: &GeneralizedTime) -> DateTime {
137 utc_time.0
138 }
139}
140
141impl From<DateTime> for GeneralizedTime {
142 fn from(datetime: DateTime) -> Self {
143 Self::from_date_time(datetime)
144 }
145}
146
147impl From<&DateTime> for GeneralizedTime {
148 fn from(datetime: &DateTime) -> Self {
149 Self::from_date_time(*datetime)
150 }
151}
152
153impl TryFrom<Any<'_>> for GeneralizedTime {
154 type Error = Error;
155
156 fn try_from(any: Any<'_>) -> Result<GeneralizedTime> {
157 any.decode_into()
158 }
159}
160
161impl DecodeValue<'_> for DateTime {
162 fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result<Self> {
163 Ok(GeneralizedTime::decode_value(decoder, length)?.into())
164 }
165}
166
167impl EncodeValue for DateTime {
168 fn value_len(&self) -> Result<Length> {
169 GeneralizedTime::from(self).value_len()
170 }
171
172 fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
173 GeneralizedTime::from(self).encode_value(encoder)
174 }
175}
176
177impl FixedTag for DateTime {
178 const TAG: Tag = Tag::GeneralizedTime;
179}
180
181impl OrdIsValueOrd for DateTime {}
182
183#[cfg(feature = "std")]
184#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
185impl DecodeValue<'_> for SystemTime {
186 fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result<Self> {
187 Ok(GeneralizedTime::decode_value(decoder, length)?.into())
188 }
189}
190
191#[cfg(feature = "std")]
192#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
193impl EncodeValue for SystemTime {
194 fn value_len(&self) -> Result<Length> {
195 GeneralizedTime::try_from(self)?.value_len()
196 }
197
198 fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
199 GeneralizedTime::try_from(self)?.encode_value(encoder)
200 }
201}
202
203#[cfg(feature = "std")]
204#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
205impl From<GeneralizedTime> for SystemTime {
206 fn from(time: GeneralizedTime) -> SystemTime {
207 time.to_system_time()
208 }
209}
210
211#[cfg(feature = "std")]
212#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
213impl From<&GeneralizedTime> for SystemTime {
214 fn from(time: &GeneralizedTime) -> SystemTime {
215 time.to_system_time()
216 }
217}
218
219#[cfg(feature = "std")]
220#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
221impl TryFrom<SystemTime> for GeneralizedTime {
222 type Error = Error;
223
224 fn try_from(time: SystemTime) -> Result<GeneralizedTime> {
225 GeneralizedTime::from_system_time(time)
226 }
227}
228
229#[cfg(feature = "std")]
230#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
231impl TryFrom<&SystemTime> for GeneralizedTime {
232 type Error = Error;
233
234 fn try_from(time: &SystemTime) -> Result<GeneralizedTime> {
235 GeneralizedTime::from_system_time(*time)
236 }
237}
238
239#[cfg(feature = "std")]
240#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
241impl<'a> TryFrom<Any<'a>> for SystemTime {
242 type Error = Error;
243
244 fn try_from(any: Any<'a>) -> Result<SystemTime> {
245 GeneralizedTime::try_from(any).map(|s| s.to_system_time())
246 }
247}
248
249#[cfg(feature = "std")]
250#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
251impl FixedTag for SystemTime {
252 const TAG: Tag = Tag::GeneralizedTime;
253}
254
255#[cfg(feature = "std")]
256#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
257impl OrdIsValueOrd for SystemTime {}
258
259#[cfg(feature = "time")]
260#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
261impl DecodeValue<'_> for PrimitiveDateTime {
262 fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result<Self> {
263 GeneralizedTime::decode_value(decoder, length)?.try_into()
264 }
265}
266
267#[cfg(feature = "time")]
268#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
269impl EncodeValue for PrimitiveDateTime {
270 fn value_len(&self) -> Result<Length> {
271 GeneralizedTime::try_from(self)?.value_len()
272 }
273
274 fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
275 GeneralizedTime::try_from(self)?.encode_value(encoder)
276 }
277}
278
279#[cfg(feature = "time")]
280#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
281impl FixedTag for PrimitiveDateTime {
282 const TAG: Tag = Tag::GeneralizedTime;
283}
284
285#[cfg(feature = "time")]
286#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
287impl OrdIsValueOrd for PrimitiveDateTime {}
288
289#[cfg(feature = "time")]
290#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
291impl TryFrom<PrimitiveDateTime> for GeneralizedTime {
292 type Error = Error;
293
294 fn try_from(time: PrimitiveDateTime) -> Result<GeneralizedTime> {
295 Ok(GeneralizedTime::from_date_time(DateTime::try_from(time)?))
296 }
297}
298
299#[cfg(feature = "time")]
300#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
301impl TryFrom<&PrimitiveDateTime> for GeneralizedTime {
302 type Error = Error;
303
304 fn try_from(time: &PrimitiveDateTime) -> Result<GeneralizedTime> {
305 Self::try_from(*time)
306 }
307}
308
309#[cfg(feature = "time")]
310#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
311impl TryFrom<GeneralizedTime> for PrimitiveDateTime {
312 type Error = Error;
313
314 fn try_from(time: GeneralizedTime) -> Result<PrimitiveDateTime> {
315 time.to_date_time().try_into()
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::GeneralizedTime;
322 use crate::{Decodable, Encodable, Encoder};
323 use hex_literal::hex;
324
325 #[test]
326 fn round_trip() {
327 let example_bytes = hex!("18 0f 31 39 39 31 30 35 30 36 32 33 34 35 34 30 5a");
328 let utc_time = GeneralizedTime::from_der(&example_bytes).unwrap();
329 assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
330
331 let mut buf = [0u8; 128];
332 let mut encoder = Encoder::new(&mut buf);
333 utc_time.encode(&mut encoder).unwrap();
334 assert_eq!(example_bytes, encoder.finish().unwrap());
335 }
336}