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