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