der/asn1/
utc_time.rs
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
14pub const MAX_YEAR: u16 = 2049;
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
33pub struct UtcTime(DateTime);
34
35impl UtcTime {
36 pub const LENGTH: Length = Length::new(13);
38
39 pub fn from_date_time(datetime: DateTime) -> Result<Self> {
41 if datetime.year() <= MAX_YEAR {
42 Ok(Self(datetime))
43 } else {
44 Err(Self::TAG.value_error())
45 }
46 }
47
48 pub fn to_date_time(&self) -> DateTime {
50 self.0
51 }
52
53 pub fn from_unix_duration(unix_duration: Duration) -> Result<Self> {
56 DateTime::from_unix_duration(unix_duration)?.try_into()
57 }
58
59 pub fn to_unix_duration(&self) -> Duration {
61 self.0.unix_duration()
62 }
63
64 #[cfg(feature = "std")]
66 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
67 pub fn from_system_time(time: SystemTime) -> Result<Self> {
68 DateTime::try_from(time)
69 .map_err(|_| Self::TAG.value_error())?
70 .try_into()
71 }
72
73 #[cfg(feature = "std")]
75 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
76 pub fn to_system_time(&self) -> SystemTime {
77 self.0.to_system_time()
78 }
79}
80
81impl DecodeValue<'_> for UtcTime {
82 fn decode_value(decoder: &mut Decoder<'_>, length: Length) -> Result<Self> {
83 match *ByteSlice::decode_value(decoder, length)?.as_bytes() {
84 [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
86 let year = datetime::decode_decimal(Self::TAG, year1, year2)?;
87 let month = datetime::decode_decimal(Self::TAG, mon1, mon2)?;
88 let day = datetime::decode_decimal(Self::TAG, day1, day2)?;
89 let hour = datetime::decode_decimal(Self::TAG, hour1, hour2)?;
90 let minute = datetime::decode_decimal(Self::TAG, min1, min2)?;
91 let second = datetime::decode_decimal(Self::TAG, sec1, sec2)?;
92
93 let year = if year >= 50 {
95 year as u16 + 1900
96 } else {
97 year as u16 + 2000
98 };
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 UtcTime {
110 fn value_len(&self) -> Result<Length> {
111 Ok(Self::LENGTH)
112 }
113
114 fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
115 let year = match self.0.year() {
116 y @ 1950..=1999 => y - 1900,
117 y @ 2000..=2049 => y - 2000,
118 _ => return Err(Self::TAG.value_error()),
119 } as u8;
120
121 datetime::encode_decimal(encoder, Self::TAG, year)?;
122 datetime::encode_decimal(encoder, Self::TAG, self.0.month())?;
123 datetime::encode_decimal(encoder, Self::TAG, self.0.day())?;
124 datetime::encode_decimal(encoder, Self::TAG, self.0.hour())?;
125 datetime::encode_decimal(encoder, Self::TAG, self.0.minutes())?;
126 datetime::encode_decimal(encoder, Self::TAG, self.0.seconds())?;
127 encoder.byte(b'Z')
128 }
129}
130
131impl FixedTag for UtcTime {
132 const TAG: Tag = Tag::UtcTime;
133}
134
135impl OrdIsValueOrd for UtcTime {}
136
137impl From<&UtcTime> for UtcTime {
138 fn from(value: &UtcTime) -> UtcTime {
139 *value
140 }
141}
142
143impl From<UtcTime> for DateTime {
144 fn from(utc_time: UtcTime) -> DateTime {
145 utc_time.0
146 }
147}
148
149impl From<&UtcTime> for DateTime {
150 fn from(utc_time: &UtcTime) -> DateTime {
151 utc_time.0
152 }
153}
154
155impl TryFrom<DateTime> for UtcTime {
156 type Error = Error;
157
158 fn try_from(datetime: DateTime) -> Result<Self> {
159 Self::from_date_time(datetime)
160 }
161}
162
163impl TryFrom<&DateTime> for UtcTime {
164 type Error = Error;
165
166 fn try_from(datetime: &DateTime) -> Result<Self> {
167 Self::from_date_time(*datetime)
168 }
169}
170
171#[cfg(feature = "std")]
172#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
173impl From<UtcTime> for SystemTime {
174 fn from(utc_time: UtcTime) -> SystemTime {
175 utc_time.to_system_time()
176 }
177}
178
179impl TryFrom<Any<'_>> for UtcTime {
180 type Error = Error;
181
182 fn try_from(any: Any<'_>) -> Result<UtcTime> {
183 any.decode_into()
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::UtcTime;
190 use crate::{Decodable, Encodable, Encoder};
191 use hex_literal::hex;
192
193 #[test]
194 fn round_trip_vector() {
195 let example_bytes = hex!("17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a");
196 let utc_time = UtcTime::from_der(&example_bytes).unwrap();
197 assert_eq!(utc_time.to_unix_duration().as_secs(), 673573540);
198
199 let mut buf = [0u8; 128];
200 let mut encoder = Encoder::new(&mut buf);
201 utc_time.encode(&mut encoder).unwrap();
202 assert_eq!(example_bytes, encoder.finish().unwrap());
203 }
204}