1 // Copyright 2016 Masaki Hara
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use alloc::string::String;
10 use alloc::vec::Vec;
11 use chrono::{DateTime,FixedOffset,NaiveDate,NaiveTime,NaiveDateTime};
12 use chrono::offset::Utc;
13 use chrono::{TimeZone,Datelike,Timelike,LocalResult};
14 
15 /// Date and time between 1950-01-01T00:00:00Z and 2049-12-31T23:59:59Z.
16 /// It cannot express fractional seconds and leap seconds.
17 /// It doesn't carry timezone information.
18 ///
19 /// Corresponds to ASN.1 UTCTime type. Often used in conjunction with
20 /// [`GeneralizedTime`].
21 ///
22 /// # Features
23 ///
24 /// This struct is enabled by `chrono` feature.
25 ///
26 /// ```toml
27 /// [dependencies]
28 /// yasna = { version = "*", features = ["chrono"] }
29 /// ```
30 ///
31 /// # Examples
32 ///
33 /// ```
34 /// # fn main() {
35 /// use yasna::models::UTCTime;
36 /// use chrono::{Datelike,Timelike};
37 /// let datetime = *UTCTime::parse(b"8201021200Z").unwrap().datetime();
38 /// assert_eq!(datetime.year(), 1982);
39 /// assert_eq!(datetime.month(), 1);
40 /// assert_eq!(datetime.day(), 2);
41 /// assert_eq!(datetime.hour(), 12);
42 /// assert_eq!(datetime.minute(), 0);
43 /// assert_eq!(datetime.second(), 0);
44 /// assert_eq!(datetime.nanosecond(), 0);
45 /// # }
46 /// ```
47 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
48 pub struct UTCTime {
49     datetime: DateTime<Utc>,
50 }
51 
52 impl UTCTime {
53     /// Parses ASN.1 string representation of UTCTime.
54     ///
55     /// # Examples
56     ///
57     /// ```
58     /// use yasna::models::UTCTime;
59     /// let datetime = UTCTime::parse(b"000229123456Z").unwrap();
60     /// assert_eq!(&datetime.to_string(), "000229123456Z");
61     /// ```
62     ///
63     /// # Errors
64     ///
65     /// It returns `None` if the given string does not specify a correct
66     /// datetime.
67     ///
68     /// # Interpretation
69     ///
70     /// While neither X.680 nor X.690 specify interpretation of 2-digits year,
71     /// X.501 specifies that UTCTime in Time shall be interpreted as between
72     /// 1950 and 2049. This method parses the string according to the X.501
73     /// rule.
parse(buf: &[u8]) -> Option<Self>74     pub fn parse(buf: &[u8]) -> Option<Self> {
75         if buf.len() < 11 {
76             return None;
77         }
78         // i: a position of [Z+-].
79         let i = if [b'+', b'-', b'Z'].contains(&buf[10]) { 10 } else { 12 };
80         if buf.len() < i+1 || ![b'+', b'-', b'Z'].contains(&buf[i]) {
81             return None;
82         }
83         let len = if buf[i] == b'Z' { i+1 } else { i+5 };
84         if len != buf.len() {
85             return None;
86         }
87         if !buf[..i].iter().all(|&b| b'0' <= b && b <= b'9') ||
88             !buf[i+1..].iter().all(|&b| b'0' <= b && b <= b'9') {
89             return None;
90         }
91         let year_short: i32 =
92             ((buf[0] - b'0') as i32) * 10 + ((buf[1] - b'0') as i32);
93         let year = if year_short < 50 {
94             year_short + 2000
95         } else {
96             year_short + 1900
97         };
98         let month: u32 =
99             ((buf[2] - b'0') as u32) * 10 + ((buf[3] - b'0') as u32);
100         let day: u32 =
101             ((buf[4] - b'0') as u32) * 10 + ((buf[5] - b'0') as u32);
102         let hour: u32 =
103             ((buf[6] - b'0') as u32) * 10 + ((buf[7] - b'0') as u32);
104         let minute: u32 =
105             ((buf[8] - b'0') as u32) * 10 + ((buf[9] - b'0') as u32);
106         let second : u32 = if i == 12 {
107             ((buf[10] - b'0') as u32) * 10 + ((buf[11] - b'0') as u32)
108         } else {
109             0
110         };
111         let offset_hour: i32 = if buf[i] == b'Z' {
112             0
113         } else {
114             ((buf[i+1] - b'0') as i32) * 10 + ((buf[i+2] - b'0') as i32)
115         };
116         let offset_minute: i32 = if buf[i] == b'Z' {
117             0
118         } else {
119             ((buf[i+3] - b'0') as i32) * 10 + ((buf[i+4] - b'0') as i32)
120         };
121         let date = if let Some(date) = NaiveDate::from_ymd_opt(
122             year, month, day) { date } else { return None; };
123         let time = if let Some(time) = NaiveTime::from_hms_opt(
124             hour, minute, second) { time } else { return None; };
125         let datetime = NaiveDateTime::new(date, time);
126         if !(offset_hour < 24 && offset_minute < 60) {
127             return None;
128         }
129         let offset = if buf[i] == b'+' {
130             FixedOffset::east((offset_hour * 60 + offset_minute) * 60)
131         } else {
132             FixedOffset::west((offset_hour * 60 + offset_minute) * 60)
133         };
134         let datetime = offset.from_local_datetime(&datetime).unwrap();
135         let datetime = datetime.with_timezone(&Utc);
136         // While the given local datatime is in [1950, 2050) by definition,
137         // the UTC datetime can be out of bounds. We check this.
138         if !(1950 <= datetime.year() && datetime.year() < 2050) {
139             return None;
140         }
141         return Some(UTCTime {
142             datetime: datetime,
143         });
144     }
145 
146     /// Constructs `UTCTime` from a datetime.
147     ///
148     /// # Panics
149     ///
150     /// Panics when UTCTime can't represent the datetime. That is:
151     ///
152     /// - The year is not between 1950 and 2049.
153     /// - It is in a leap second.
154     /// - It has a non-zero nanosecond value.
from_datetime<Tz:TimeZone>(datetime: &DateTime<Tz>) -> Self155     pub fn from_datetime<Tz:TimeZone>(datetime: &DateTime<Tz>) -> Self {
156         let datetime = datetime.with_timezone(&Utc);
157         assert!(1950 <= datetime.year() && datetime.year() < 2050,
158             "Can't express a year {:?} in UTCTime", datetime.year());
159         assert!(datetime.nanosecond() < 1_000_000_000,
160             "Can't express a leap second in UTCTime");
161         assert!(datetime.nanosecond() == 0,
162             "Can't express a non-zero nanosecond in UTCTime");
163         return UTCTime {
164             datetime: datetime,
165         };
166     }
167 
168     /// Constructs `UTCTime` from a datetime.
169     ///
170     /// # Errors
171     ///
172     /// It returns `None` when UTCTime can't represent the datetime. That is:
173     ///
174     /// - The year is not between 1950 and 2049.
175     /// - It is in a leap second.
176     /// - It has a non-zero nanosecond value.
from_datetime_opt<Tz:TimeZone> (datetime: &DateTime<Tz>) -> Option<Self>177     pub fn from_datetime_opt<Tz:TimeZone>
178             (datetime: &DateTime<Tz>) -> Option<Self> {
179         let datetime = datetime.with_timezone(&Utc);
180         if !(1950 <= datetime.year() && datetime.year() < 2050) {
181             return None;
182         }
183         if !(datetime.nanosecond() == 0) {
184             return None;
185         }
186         return Some(UTCTime {
187             datetime: datetime,
188         });
189     }
190 
191     /// Returns the datetime it represents.
datetime(&self) -> &DateTime<Utc>192     pub fn datetime(&self) -> &DateTime<Utc> {
193         &self.datetime
194     }
195 
196     /// Returns ASN.1 canonical representation of the datetime as `Vec<u8>`.
to_bytes(&self) -> Vec<u8>197     pub fn to_bytes(&self) -> Vec<u8> {
198         let mut buf = Vec::with_capacity(13);
199         buf.push((self.datetime.year() / 10 % 10) as u8 + b'0');
200         buf.push((self.datetime.year() % 10) as u8 + b'0');
201         buf.push((self.datetime.month() / 10 % 10) as u8 + b'0');
202         buf.push((self.datetime.month() % 10) as u8 + b'0');
203         buf.push((self.datetime.day() / 10 % 10) as u8 + b'0');
204         buf.push((self.datetime.day() % 10) as u8 + b'0');
205         buf.push((self.datetime.hour() / 10 % 10) as u8 + b'0');
206         buf.push((self.datetime.hour() % 10) as u8 + b'0');
207         buf.push((self.datetime.minute() / 10 % 10) as u8 + b'0');
208         buf.push((self.datetime.minute() % 10) as u8 + b'0');
209         buf.push((self.datetime.second() / 10 % 10) as u8 + b'0');
210         buf.push((self.datetime.second() % 10) as u8 + b'0');
211         buf.push(b'Z');
212         return buf;
213     }
214 
215     /// Returns ASN.1 canonical representation of the datetime as `String`.
to_string(&self) -> String216     pub fn to_string(&self) -> String {
217         String::from_utf8(self.to_bytes()).unwrap()
218     }
219 }
220 
221 /// Date and time between 0000-01-01T00:00:00Z and 9999-12-31T23:59:60.999...Z.
222 ///
223 /// It can contain arbitrary length of decimal fractional seconds.
224 /// However, it doesn't carry accuracy information.
225 /// It can also contain leap seconds.
226 ///
227 /// The datetime is canonicalized to UTC.
228 /// It doesn't carry timezone information.
229 ///
230 /// Corresponds to ASN.1 GeneralizedTime type. Often used in conjunction with
231 /// [`UTCTime`].
232 ///
233 /// # Features
234 ///
235 /// This struct is enabled by `chrono` feature.
236 ///
237 /// ```toml
238 /// [dependencies]
239 /// yasna = { version = "*", features = ["chrono"] }
240 /// ```
241 ///
242 /// # Examples
243 ///
244 /// ```
245 /// # fn main() {
246 /// use yasna::models::GeneralizedTime;
247 /// use chrono::{Datelike,Timelike};
248 /// let datetime =
249 ///     *GeneralizedTime::parse(b"19851106210627.3Z").unwrap().datetime();
250 /// assert_eq!(datetime.year(), 1985);
251 /// assert_eq!(datetime.month(), 11);
252 /// assert_eq!(datetime.day(), 6);
253 /// assert_eq!(datetime.hour(), 21);
254 /// assert_eq!(datetime.minute(), 6);
255 /// assert_eq!(datetime.second(), 27);
256 /// assert_eq!(datetime.nanosecond(), 300_000_000);
257 /// # }
258 /// ```
259 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
260 pub struct GeneralizedTime {
261     datetime: DateTime<Utc>,
262     sub_nano: Vec<u8>,
263 }
264 
265 impl GeneralizedTime {
266     /// Almost same as `parse`. It takes `default_tz` however.
267     /// GeneralizedTime value can omit timezone in local time.
268     /// In that case, `default_tz` is used instead.
parse_general<Tz:TimeZone>(buf: &[u8], default_tz: Option<&Tz>) -> Option<Self>269     fn parse_general<Tz:TimeZone>(buf: &[u8], default_tz: Option<&Tz>)
270             -> Option<Self> {
271         if buf.len() < 10 {
272             return None;
273         }
274         if !buf[..10].iter().all(|&b| b'0' <= b && b <= b'9') {
275             return None;
276         }
277         let year: i32 =
278             ((buf[0] - b'0') as i32) * 1000 + ((buf[1] - b'0') as i32) * 100
279             + ((buf[2] - b'0') as i32) * 10 + ((buf[3] - b'0') as i32);
280         let month: u32 =
281             ((buf[4] - b'0') as u32) * 10 + ((buf[5] - b'0') as u32);
282         let day: u32 =
283             ((buf[6] - b'0') as u32) * 10 + ((buf[7] - b'0') as u32);
284         let hour: u32 =
285             ((buf[8] - b'0') as u32) * 10 + ((buf[9] - b'0') as u32);
286         // i: current position on `buf`
287         let mut i = 10;
288         // The factor to scale the fraction part to nanoseconds.
289         let mut fraction_scale : i64 = 1_000_000_000;
290         let mut minute : u32;
291         if i+2 <= buf.len() &&
292                 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
293             minute =
294                 ((buf[i] - b'0') as u32) * 10 + ((buf[i+1] - b'0') as u32);
295             i += 2;
296         } else {
297             fraction_scale = 3_600_000_000_000;
298             minute = 0;
299         }
300         let mut second : u32;
301         if i+2 <= buf.len() &&
302                 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
303             second =
304                 ((buf[i] - b'0') as u32) * 10 + ((buf[i+1] - b'0') as u32);
305             i += 2;
306         } else {
307             if fraction_scale == 1_000_000_000 {
308                 fraction_scale = 60_000_000_000;
309             }
310             second = 0;
311         }
312         let mut nanosecond = 0;
313         let mut sub_nano = Vec::new();
314         if i+2 <= buf.len() && (buf[i] == b'.' || buf[i] == b',')
315                 && b'0' <= buf[i+1] && buf[i+1] <= b'9' {
316             i += 1;
317             let mut j = 0;
318             while i+j < buf.len() && b'0' <= buf[i+j] && buf[i+j] <= b'9' {
319                 sub_nano.push(b'0');
320                 j += 1;
321             }
322             let mut carry : i64 = 0;
323             for k in (0..j).rev() {
324                 let digit = (buf[i+k] - b'0') as i64;
325                 let sum = digit * fraction_scale + carry;
326                 carry = sum / 10;
327                 sub_nano[k] = b'0' + ((sum % 10) as u8);
328             }
329             nanosecond = (carry % 1_000_000_000) as u32;
330             second += (carry / 1_000_000_000 % 60) as u32;
331             minute += (carry / 60_000_000_000) as u32;
332             while let Some(&digit) = sub_nano.last() {
333                 if digit == b'0' {
334                     sub_nano.pop();
335                 } else {
336                     break;
337                 }
338             }
339             i += j;
340         }
341         // Cope with leap seconds.
342         if second == 60 {
343             second = 59;
344             nanosecond += 1_000_000_000;
345         }
346         let date = if let Some(date) = NaiveDate::from_ymd_opt(
347             year, month, day) { date } else { return None; };
348         let time = if let Some(time) = NaiveTime::from_hms_nano_opt(
349             hour, minute, second, nanosecond) { time } else { return None; };
350         let naive_datetime = NaiveDateTime::new(date, time);
351         let datetime : DateTime<Utc>;
352         if i == buf.len() {
353             // Local datetime with no timezone information.
354             if let Some(default_tz) = default_tz {
355                 if let LocalResult::Single(dt) =
356                         default_tz.from_local_datetime(&naive_datetime) {
357                     datetime = dt.with_timezone(&Utc);
358                 } else {
359                     return None;
360                 }
361             } else {
362                 return None;
363             }
364         } else if i < buf.len() && buf[i] == b'Z' {
365             // UTC time.
366             datetime = DateTime::from_utc(naive_datetime, Utc);
367             i += 1;
368         } else if i < buf.len() && (buf[i] == b'+' || buf[i] == b'-') {
369             // Local datetime with offset information.
370             let offset_sign = buf[i];
371             i += 1;
372             if !(i+2 <= buf.len() &&
373                     buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9')) {
374                 return None;
375             }
376             let offset_hour =
377                 ((buf[i] - b'0') as i32) * 10 + ((buf[i+1] - b'0') as i32);
378             i += 2;
379             let offset_minute;
380             if i+2 <= buf.len() &&
381                     buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
382                 offset_minute =
383                     ((buf[i] - b'0') as i32) * 10 + ((buf[i+1] - b'0') as i32);
384                 i += 2;
385             } else {
386                 offset_minute = 0;
387             }
388             if !(offset_hour < 24 && offset_minute < 60) {
389                 return None;
390             }
391             let offset = if offset_sign == b'+' {
392                 FixedOffset::east((offset_hour * 60 + offset_minute) * 60)
393             } else {
394                 FixedOffset::west((offset_hour * 60 + offset_minute) * 60)
395             };
396             datetime =
397                 offset.from_local_datetime(&naive_datetime).unwrap()
398                 .with_timezone(&Utc);
399         } else {
400             return None;
401         }
402         if i != buf.len() {
403             return None;
404         }
405         // While the given local datatime is in [0, 10000) by definition,
406         // the UTC datetime can be out of bounds. We check this.
407         if !(0 <= datetime.year() && datetime.year() < 10000) {
408             return None;
409         }
410         return Some(GeneralizedTime {
411             datetime: datetime,
412             sub_nano: sub_nano,
413         });
414     }
415 
416     /// Parses ASN.1 string representation of GeneralizedTime.
417     ///
418     /// # Examples
419     ///
420     /// ```
421     /// use yasna::models::GeneralizedTime;
422     /// let datetime = GeneralizedTime::parse(b"1985110621.14159Z").unwrap();
423     /// assert_eq!(&datetime.to_string(), "19851106210829.724Z");
424     /// ```
425     ///
426     /// # Errors
427     ///
428     /// It returns `None` if the given string does not specify a correct
429     /// datetime.
parse(buf: &[u8]) -> Option<Self>430     pub fn parse(buf: &[u8]) -> Option<Self> {
431         Self::parse_general::<Utc>(buf, None)
432     }
433 
434     /// Parses ASN.1 string representation of GeneralizedTime, with the
435     /// default timezone for local time given.
436     ///
437     /// # Examples
438     ///
439     /// ```
440     /// use yasna::models::GeneralizedTime;
441     /// let datetime = GeneralizedTime::parse(b"1985110621.14159Z").unwrap();
442     /// assert_eq!(&datetime.to_string(), "19851106210829.724Z");
443     /// ```
444     ///
445     /// # Errors
446     ///
447     /// It returns `None` if the given string does not specify a correct
448     /// datetime.
parse_with_timezone<Tz:TimeZone> (buf: &[u8], default_tz: &Tz) -> Option<Self>449     pub fn parse_with_timezone<Tz:TimeZone>
450             (buf: &[u8], default_tz: &Tz) -> Option<Self> {
451         Self::parse_general(buf, Some(default_tz))
452     }
453 
454     /// Constructs `GeneralizedTime` from a datetime.
455     ///
456     /// # Panics
457     ///
458     /// Panics when GeneralizedTime can't represent the datetime. That is:
459     ///
460     /// - The year is not between 0 and 9999.
from_datetime<Tz:TimeZone>(datetime: &DateTime<Tz>) -> Self461     pub fn from_datetime<Tz:TimeZone>(datetime: &DateTime<Tz>) -> Self {
462         let datetime = datetime.with_timezone(&Utc);
463         assert!(0 <= datetime.year() && datetime.year() < 10000,
464             "Can't express a year {:?} in GeneralizedTime", datetime.year());
465         return GeneralizedTime {
466             datetime: datetime,
467             sub_nano: Vec::new(),
468         };
469     }
470 
471     /// Constructs `GeneralizedTime` from a datetime.
472     ///
473     /// # Errors
474     ///
475     /// It returns `None` when GeneralizedTime can't represent the datetime.
476     /// That is:
477     ///
478     /// - The year is not between 0 and 9999.
from_datetime_opt<Tz:TimeZone>(datetime: &DateTime<Tz>) -> Option<Self>479     pub fn from_datetime_opt<Tz:TimeZone>(datetime: &DateTime<Tz>)
480             -> Option<Self> {
481         let datetime = datetime.with_timezone(&Utc);
482         if !(0 <= datetime.year() && datetime.year() < 10000) {
483             return None;
484         }
485         return Some(GeneralizedTime {
486             datetime: datetime,
487             sub_nano: Vec::new(),
488         });
489     }
490 
491     /// Constructs `GeneralizedTime` from a datetime and sub-nanoseconds
492     /// digits.
493     ///
494     /// # Panics
495     ///
496     /// Panics when GeneralizedTime can't represent the datetime. That is:
497     ///
498     /// - The year is not between 0 and 9999.
499     ///
500     /// It also panics if `sub_nano` contains a non-digit character.
from_datetime_and_sub_nano<Tz:TimeZone> (datetime: &DateTime<Tz>, sub_nano: &[u8]) -> Self501     pub fn from_datetime_and_sub_nano<Tz:TimeZone>
502             (datetime: &DateTime<Tz>, sub_nano: &[u8]) -> Self {
503         let datetime = datetime.with_timezone(&Utc);
504         assert!(0 <= datetime.year() && datetime.year() < 10000,
505             "Can't express a year {:?} in GeneralizedTime", datetime.year());
506         assert!(sub_nano.iter().all(|&b| b'0' <= b && b <= b'9'),
507             "sub_nano contains a non-digit character");
508         let mut sub_nano = sub_nano.to_vec();
509         while sub_nano.len() > 0 && *sub_nano.last().unwrap() == b'0' {
510             sub_nano.pop();
511         }
512         return GeneralizedTime {
513             datetime: datetime,
514             sub_nano: sub_nano,
515         };
516     }
517 
518     /// Constructs `GeneralizedTime` from a datetime and sub-nanoseconds
519     /// digits.
520     ///
521     /// # Errors
522     ///
523     /// It returns `None` when GeneralizedTime can't represent the datetime.
524     /// That is:
525     ///
526     /// - The year is not between 0 and 9999.
527     ///
528     /// It also returns `None` if `sub_nano` contains a non-digit character.
from_datetime_and_sub_nano_opt<Tz:TimeZone> (datetime: &DateTime<Tz>, sub_nano: &[u8]) -> Option<Self>529     pub fn from_datetime_and_sub_nano_opt<Tz:TimeZone>
530             (datetime: &DateTime<Tz>, sub_nano: &[u8]) -> Option<Self> {
531         let datetime = datetime.with_timezone(&Utc);
532         if !(0 <= datetime.year() && datetime.year() < 10000) {
533             return None;
534         }
535         if !(sub_nano.iter().all(|&b| b'0' <= b && b <= b'9')) {
536             return None;
537         }
538         let mut sub_nano = sub_nano.to_vec();
539         while sub_nano.len() > 0 && *sub_nano.last().unwrap() == b'0' {
540             sub_nano.pop();
541         }
542         return Some(GeneralizedTime {
543             datetime: datetime,
544             sub_nano: sub_nano,
545         });
546     }
547 
548     /// Returns the datetime it represents, discarding sub-nanoseconds digits.
datetime(&self) -> &DateTime<Utc>549     pub fn datetime(&self) -> &DateTime<Utc> {
550         &self.datetime
551     }
552 
553     /// Returns sub-nanoseconds digits of the datetime.
sub_nano(&self) -> &[u8]554     pub fn sub_nano(&self) -> &[u8] {
555         &self.sub_nano
556     }
557 
558     /// Returns ASN.1 canonical representation of the datetime as `Vec<u8>`.
to_bytes(&self) -> Vec<u8>559     pub fn to_bytes(&self) -> Vec<u8> {
560         let mut buf = Vec::with_capacity(24);
561         buf.push((self.datetime.year() / 1000 % 10) as u8 + b'0');
562         buf.push((self.datetime.year() / 100 % 10) as u8 + b'0');
563         buf.push((self.datetime.year() / 10 % 10) as u8 + b'0');
564         buf.push((self.datetime.year() % 10) as u8 + b'0');
565         buf.push((self.datetime.month() / 10 % 10) as u8 + b'0');
566         buf.push((self.datetime.month() % 10) as u8 + b'0');
567         buf.push((self.datetime.day() / 10 % 10) as u8 + b'0');
568         buf.push((self.datetime.day() % 10) as u8 + b'0');
569         buf.push((self.datetime.hour() / 10 % 10) as u8 + b'0');
570         buf.push((self.datetime.hour() % 10) as u8 + b'0');
571         buf.push((self.datetime.minute() / 10 % 10) as u8 + b'0');
572         buf.push((self.datetime.minute() % 10) as u8 + b'0');
573         let second = self.datetime.second();
574         let nanosecond = self.datetime.nanosecond();
575         // Cope with leap seconds.
576         let (second, nanosecond) = if nanosecond < 1_000_000_000 {
577             (second, nanosecond)
578         } else {
579             (second + 1, nanosecond - 1_000_000_000)
580         };
581         buf.push((second / 10 % 10) as u8 + b'0');
582         buf.push((second % 10) as u8 + b'0');
583         buf.push(b'.');
584         buf.push((nanosecond / 100_000_000 % 10) as u8 + b'0');
585         buf.push((nanosecond / 10_000_000 % 10) as u8 + b'0');
586         buf.push((nanosecond / 1_000_000 % 10) as u8 + b'0');
587         buf.push((nanosecond / 100_000 % 10) as u8 + b'0');
588         buf.push((nanosecond / 10_000 % 10) as u8 + b'0');
589         buf.push((nanosecond / 1_000 % 10) as u8 + b'0');
590         buf.push((nanosecond / 100 % 10) as u8 + b'0');
591         buf.push((nanosecond / 10 % 10) as u8 + b'0');
592         buf.push((nanosecond % 10) as u8 + b'0');
593         buf.extend_from_slice(&self.sub_nano);
594         // Truncates trailing zeros.
595         while buf.len() > 14 && {
596                 let b = *buf.last().unwrap(); b == b'0' || b == b'.' } {
597             buf.pop();
598         }
599         buf.push(b'Z');
600         return buf;
601     }
602 
603     /// Returns ASN.1 canonical representation of the datetime as `String`.
to_string(&self) -> String604     pub fn to_string(&self) -> String {
605         String::from_utf8(self.to_bytes()).unwrap()
606     }
607 }
608 
609 #[test]
test_utctime_parse()610 fn test_utctime_parse() {
611     let datetime = *UTCTime::parse(b"8201021200Z").unwrap().datetime();
612     assert_eq!(datetime.year(), 1982);
613     assert_eq!(datetime.month(), 1);
614     assert_eq!(datetime.day(), 2);
615     assert_eq!(datetime.hour(), 12);
616     assert_eq!(datetime.minute(), 0);
617     assert_eq!(datetime.second(), 0);
618     assert_eq!(datetime.nanosecond(), 0);
619 
620     let datetime = *UTCTime::parse(b"0101021200Z").unwrap().datetime();
621     assert_eq!(datetime.year(), 2001);
622     assert_eq!(datetime.month(), 1);
623     assert_eq!(datetime.day(), 2);
624     assert_eq!(datetime.hour(), 12);
625     assert_eq!(datetime.minute(), 0);
626     assert_eq!(datetime.second(), 0);
627     assert_eq!(datetime.nanosecond(), 0);
628 
629     let datetime = UTCTime::parse(b"8201021200Z").unwrap();
630     assert_eq!(&datetime.to_string(), "820102120000Z");
631 
632     let datetime = UTCTime::parse(b"8201020700-0500").unwrap();
633     assert_eq!(&datetime.to_string(), "820102120000Z");
634 
635     let datetime = UTCTime::parse(b"0101021200Z").unwrap();
636     assert_eq!(&datetime.to_string(), "010102120000Z");
637 
638     let datetime = UTCTime::parse(b"010102120034Z").unwrap();
639     assert_eq!(&datetime.to_string(), "010102120034Z");
640 
641     let datetime = UTCTime::parse(b"000229123456Z").unwrap();
642     assert_eq!(&datetime.to_string(), "000229123456Z");
643 }
644 
645 #[test]
test_generalized_time_parse()646 fn test_generalized_time_parse() {
647     let datetime =
648         *GeneralizedTime::parse(b"19851106210627.3Z").unwrap().datetime();
649     assert_eq!(datetime.year(), 1985);
650     assert_eq!(datetime.month(), 11);
651     assert_eq!(datetime.day(), 6);
652     assert_eq!(datetime.hour(), 21);
653     assert_eq!(datetime.minute(), 6);
654     assert_eq!(datetime.second(), 27);
655     assert_eq!(datetime.nanosecond(), 300_000_000);
656 
657     let datetime = GeneralizedTime::parse(b"19851106210627.3-0500").unwrap();
658     assert_eq!(&datetime.to_string(), "19851107020627.3Z");
659 
660     let datetime = GeneralizedTime::parse(b"198511062106Z").unwrap();
661     assert_eq!(&datetime.to_string(), "19851106210600Z");
662 
663     let datetime = GeneralizedTime::parse(b"198511062106.456Z").unwrap();
664     assert_eq!(&datetime.to_string(), "19851106210627.36Z");
665 
666     let datetime = GeneralizedTime::parse(b"1985110621Z").unwrap();
667     assert_eq!(&datetime.to_string(), "19851106210000Z");
668 
669     let datetime = GeneralizedTime::parse(b"1985110621.14159Z").unwrap();
670     assert_eq!(&datetime.to_string(), "19851106210829.724Z");
671 
672     let datetime =
673         GeneralizedTime::parse(b"19990101085960.1234+0900").unwrap();
674     assert_eq!(&datetime.to_string(), "19981231235960.1234Z");
675 
676     let datetime =
677         GeneralizedTime::parse(
678             b"20080229033411.3625431984612391672391625532918636000680000-0500"
679         ).unwrap();
680     assert_eq!(&datetime.to_string(),
681         "20080229083411.362543198461239167239162553291863600068Z");
682 }
683