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