1use alloc::string::String;
10use alloc::vec::Vec;
11use core::convert::TryFrom;
12use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
13
14#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
46pub struct UTCTime {
47 datetime: OffsetDateTime,
48}
49
50impl UTCTime {
51 pub fn parse(buf: &[u8]) -> Option<Self> {
73 if buf.len() < 11 {
74 return None;
75 }
76 let i = if [b'+', b'-', b'Z'].contains(&buf[10]) { 10 } else { 12 };
78 if buf.len() < i+1 || ![b'+', b'-', b'Z'].contains(&buf[i]) {
79 return None;
80 }
81 let len = if buf[i] == b'Z' { i+1 } else { i+5 };
82 if len != buf.len() {
83 return None;
84 }
85 if !buf[..i].iter().all(|&b| b'0' <= b && b <= b'9') ||
86 !buf[i+1..].iter().all(|&b| b'0' <= b && b <= b'9')
87 {
88 return None;
89 }
90 let year_short: i32 =
91 ((buf[0] - b'0') as i32) * 10 + ((buf[1] - b'0') as i32);
92 let year = if year_short < 50 {
93 year_short + 2000
94 } else {
95 year_short + 1900
96 };
97 let month = Month::try_from((buf[2] - b'0') * 10 + (buf[3] - b'0')).ok()?;
98 let day = (buf[4] - b'0') * 10 + (buf[5] - b'0');
99 let hour = (buf[6] - b'0') * 10 + (buf[7] - b'0');
100 let minute = (buf[8] - b'0') * 10 + (buf[9] - b'0');
101 let second = if i == 12 {
102 (buf[10] - b'0') * 10 + (buf[11] - b'0')
103 } else {
104 0
105 };
106 let offset_hour: i8 = if buf[i] == b'Z' {
107 0
108 } else {
109 ((buf[i+1] - b'0') as i8) * 10 + ((buf[i+2] - b'0') as i8)
110 };
111 let offset_minute: i8 = if buf[i] == b'Z' {
112 0
113 } else {
114 ((buf[i+3] - b'0') as i8) * 10 + ((buf[i+4] - b'0') as i8)
115 };
116 let date = Date::from_calendar_date(year, month, day).ok()?;
117 let time = Time::from_hms(hour, minute, second).ok()?;
118 let datetime = PrimitiveDateTime::new(date, time);
119 if !(offset_hour < 24 && offset_minute < 60) {
120 return None;
121 }
122 let offset = if buf[i] == b'+' {
123 UtcOffset::from_hms(offset_hour, offset_minute, 0).ok()?
124 } else {
125 UtcOffset::from_hms(-offset_hour, -offset_minute, 0).ok()?
126 };
127 let datetime = datetime.assume_offset(offset).to_offset(UtcOffset::UTC);
128 if !(1950 <= datetime.year() && datetime.year() < 2050) {
131 return None;
132 }
133 return Some(UTCTime {
134 datetime: datetime,
135 });
136 }
137
138 pub fn from_datetime(datetime: OffsetDateTime) -> Self {
148 let datetime = datetime.to_offset(UtcOffset::UTC);
149 assert!(1950 <= datetime.year() && datetime.year() < 2050,
150 "Can't express a year {:?} in UTCTime", datetime.year());
151 assert!(datetime.nanosecond() < 1_000_000_000,
152 "Can't express a leap second in UTCTime");
153 assert!(datetime.nanosecond() == 0,
154 "Can't express a non-zero nanosecond in UTCTime");
155 return UTCTime {
156 datetime: datetime,
157 };
158 }
159
160 pub fn from_datetime_opt(datetime: OffsetDateTime) -> Option<Self> {
170 let datetime = datetime.to_offset(UtcOffset::UTC);
171 if !(1950 <= datetime.year() && datetime.year() < 2050) {
172 return None;
173 }
174 if !(datetime.nanosecond() == 0) {
175 return None;
176 }
177 return Some(UTCTime {
178 datetime: datetime,
179 });
180 }
181
182 pub fn datetime(&self) -> &OffsetDateTime {
184 &self.datetime
185 }
186
187 pub fn to_bytes(&self) -> Vec<u8> {
189 let mut buf = Vec::with_capacity(13);
190 buf.push((self.datetime.year() / 10 % 10) as u8 + b'0');
191 buf.push((self.datetime.year() % 10) as u8 + b'0');
192 buf.push((self.datetime.month() as u8 / 10 % 10) + b'0');
193 buf.push((self.datetime.month() as u8 % 10) + b'0');
194 buf.push((self.datetime.day() / 10 % 10) as u8 + b'0');
195 buf.push((self.datetime.day() % 10) as u8 + b'0');
196 buf.push((self.datetime.hour() / 10 % 10) as u8 + b'0');
197 buf.push((self.datetime.hour() % 10) as u8 + b'0');
198 buf.push((self.datetime.minute() / 10 % 10) as u8 + b'0');
199 buf.push((self.datetime.minute() % 10) as u8 + b'0');
200 buf.push((self.datetime.second() / 10 % 10) as u8 + b'0');
201 buf.push((self.datetime.second() % 10) as u8 + b'0');
202 buf.push(b'Z');
203 return buf;
204 }
205
206 pub fn to_string(&self) -> String {
208 String::from_utf8(self.to_bytes()).unwrap()
209 }
210}
211
212#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
250pub struct GeneralizedTime {
251 datetime: OffsetDateTime,
252 sub_nano: Vec<u8>,
253 is_leap_second: bool,
255}
256
257impl GeneralizedTime {
258 fn parse_general(buf: &[u8], default_offset: Option<UtcOffset>) -> Option<Self> {
262 if buf.len() < 10 {
263 return None;
264 }
265 if !buf[..10].iter().all(|&b| b'0' <= b && b <= b'9') {
266 return None;
267 }
268 let year: i32 =
269 ((buf[0] - b'0') as i32) * 1000 + ((buf[1] - b'0') as i32) * 100
270 + ((buf[2] - b'0') as i32) * 10 + ((buf[3] - b'0') as i32);
271 let month = Month::try_from((buf[4] - b'0') * 10 + (buf[5] - b'0')).ok()?;
272 let day = (buf[6] - b'0') * 10 + (buf[7] - b'0');
273 let hour = (buf[8] - b'0') * 10 + (buf[9] - b'0');
274 let mut i = 10;
276 let mut fraction_scale : i64 = 1_000_000_000;
278 let mut minute: u8;
279 if i+2 <= buf.len() &&
280 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
281 minute = (buf[i] - b'0') * 10 + (buf[i + 1] - b'0');
282 i += 2;
283 } else {
284 fraction_scale = 3_600_000_000_000;
285 minute = 0;
286 }
287 let mut second: u8;
288 if i+2 <= buf.len() &&
289 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
290 second = (buf[i] - b'0') * 10 + (buf[i + 1] - b'0');
291 i += 2;
292 } else {
293 if fraction_scale == 1_000_000_000 {
294 fraction_scale = 60_000_000_000;
295 }
296 second = 0;
297 }
298 let mut nanosecond = 0;
299 let mut sub_nano = Vec::new();
300 if i+2 <= buf.len() && (buf[i] == b'.' || buf[i] == b',')
301 && b'0' <= buf[i+1] && buf[i+1] <= b'9' {
302 i += 1;
303 let mut j = 0;
304 while i+j < buf.len() && b'0' <= buf[i+j] && buf[i+j] <= b'9' {
305 sub_nano.push(b'0');
306 j += 1;
307 }
308 let mut carry : i64 = 0;
309 for k in (0..j).rev() {
310 let digit = (buf[i+k] - b'0') as i64;
311 let sum = digit * fraction_scale + carry;
312 carry = sum / 10;
313 sub_nano[k] = b'0' + ((sum % 10) as u8);
314 }
315 nanosecond = (carry % 1_000_000_000) as u32;
316 second += (carry / 1_000_000_000 % 60) as u8;
317 minute += (carry / 60_000_000_000) as u8;
318 while let Some(&digit) = sub_nano.last() {
319 if digit == b'0' {
320 sub_nano.pop();
321 } else {
322 break;
323 }
324 }
325 i += j;
326 }
327 let mut is_leap_second = false;
328 if second == 60 {
329 is_leap_second = true;
331 second = 59;
332 }
333 let date = Date::from_calendar_date(year, month, day).ok()?;
334 let time = Time::from_hms_nano(hour, minute, second, nanosecond).ok()?;
335 let naive_datetime = PrimitiveDateTime::new(date, time);
336 let datetime: OffsetDateTime;
337 if i == buf.len() {
338 datetime = naive_datetime
340 .assume_offset(default_offset?)
341 .to_offset(UtcOffset::UTC);
342 } else if i < buf.len() && buf[i] == b'Z' {
343 datetime = naive_datetime.assume_utc();
345 i += 1;
346 } else if i < buf.len() && (buf[i] == b'+' || buf[i] == b'-') {
347 let offset_sign = buf[i];
349 i += 1;
350 if !(i+2 <= buf.len() &&
351 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9')) {
352 return None;
353 }
354 let offset_hour =
355 ((buf[i] - b'0') as i8) * 10 + ((buf[i+1] - b'0') as i8);
356 i += 2;
357 let offset_minute;
358 if i+2 <= buf.len() &&
359 buf[i..i+2].iter().all(|&b| b'0' <= b && b <= b'9') {
360 offset_minute =
361 ((buf[i] - b'0') as i8) * 10 + ((buf[i+1] - b'0') as i8);
362 i += 2;
363 } else {
364 offset_minute = 0;
365 }
366 if !(offset_hour < 24 && offset_minute < 60) {
367 return None;
368 }
369 let offset = if offset_sign == b'+' {
370 UtcOffset::from_hms(offset_hour, offset_minute, 0)
371 } else {
372 UtcOffset::from_hms(-offset_hour, -offset_minute, 0)
373 };
374 datetime = naive_datetime
375 .assume_offset(offset.ok()?)
376 .to_offset(UtcOffset::UTC);
377 } else {
378 return None;
379 }
380 if i != buf.len() {
381 return None;
382 }
383 if !(0 <= datetime.year() && datetime.year() < 10000) {
386 return None;
387 }
388 return Some(GeneralizedTime {
389 datetime: datetime,
390 sub_nano: sub_nano,
391 is_leap_second,
392 });
393 }
394
395 pub fn parse(buf: &[u8]) -> Option<Self> {
410 Self::parse_general(buf, None)
411 }
412
413 pub fn parse_with_offset(buf: &[u8], default_offset: UtcOffset) -> Option<Self> {
429 Self::parse_general(buf, Some(default_offset))
430 }
431
432 pub fn from_datetime(datetime: OffsetDateTime) -> Self {
440 let datetime = datetime.to_offset(UtcOffset::UTC);
441 assert!(0 <= datetime.year() && datetime.year() < 10000,
442 "Can't express a year {:?} in GeneralizedTime", datetime.year());
443 return GeneralizedTime {
444 datetime: datetime,
445 sub_nano: Vec::new(),
446 is_leap_second: false,
447 };
448 }
449
450 pub fn from_datetime_opt(datetime: OffsetDateTime) -> Option<Self> {
459 let datetime = datetime.to_offset(UtcOffset::UTC);
460 if !(0 <= datetime.year() && datetime.year() < 10000) {
461 return None;
462 }
463 return Some(GeneralizedTime {
464 datetime: datetime,
465 sub_nano: Vec::new(),
466 is_leap_second: false,
467 });
468 }
469
470 pub fn from_datetime_and_sub_nano(datetime: OffsetDateTime, sub_nano: &[u8]) -> Self {
481 let datetime = datetime.to_offset(UtcOffset::UTC);
482 assert!(0 <= datetime.year() && datetime.year() < 10000,
483 "Can't express a year {:?} in GeneralizedTime", datetime.year());
484 assert!(sub_nano.iter().all(|&b| b'0' <= b && b <= b'9'),
485 "sub_nano contains a non-digit character");
486 let mut sub_nano = sub_nano.to_vec();
487 while sub_nano.len() > 0 && *sub_nano.last().unwrap() == b'0' {
488 sub_nano.pop();
489 }
490 return GeneralizedTime {
491 datetime: datetime,
492 sub_nano: sub_nano,
493 is_leap_second: false,
494 };
495 }
496
497 pub fn from_datetime_and_sub_nano_opt(
509 datetime: OffsetDateTime,
510 sub_nano: &[u8],
511 ) -> Option<Self> {
512 let datetime = datetime.to_offset(UtcOffset::UTC);
513 if !(0 <= datetime.year() && datetime.year() < 10000) {
514 return None;
515 }
516 if !(sub_nano.iter().all(|&b| b'0' <= b && b <= b'9')) {
517 return None;
518 }
519 let mut sub_nano = sub_nano.to_vec();
520 while sub_nano.len() > 0 && *sub_nano.last().unwrap() == b'0' {
521 sub_nano.pop();
522 }
523 return Some(GeneralizedTime {
524 datetime: datetime,
525 sub_nano: sub_nano,
526 is_leap_second: false,
527 });
528 }
529
530 pub fn datetime(&self) -> &OffsetDateTime {
534 &self.datetime
535 }
536
537 pub fn sub_nano(&self) -> &[u8] {
539 &self.sub_nano
540 }
541
542 pub fn to_bytes(&self) -> Vec<u8> {
544 let mut buf = Vec::with_capacity(24);
545 buf.push((self.datetime.year() / 1000 % 10) as u8 + b'0');
546 buf.push((self.datetime.year() / 100 % 10) as u8 + b'0');
547 buf.push((self.datetime.year() / 10 % 10) as u8 + b'0');
548 buf.push((self.datetime.year() % 10) as u8 + b'0');
549 buf.push((self.datetime.month() as u8 / 10 % 10) + b'0');
550 buf.push((self.datetime.month() as u8 % 10) + b'0');
551 buf.push((self.datetime.day() / 10 % 10) as u8 + b'0');
552 buf.push((self.datetime.day() % 10) as u8 + b'0');
553 buf.push((self.datetime.hour() / 10 % 10) as u8 + b'0');
554 buf.push((self.datetime.hour() % 10) as u8 + b'0');
555 buf.push((self.datetime.minute() / 10 % 10) as u8 + b'0');
556 buf.push((self.datetime.minute() % 10) as u8 + b'0');
557 let mut second = self.datetime.second();
558 let nanosecond = self.datetime.nanosecond();
559 if self.is_leap_second {
561 debug_assert!(second == 59,
562 "is_leap_second is set, but second = {}", second);
563 second += 1;
564 }
565 buf.push((second / 10 % 10) as u8 + b'0');
566 buf.push((second % 10) as u8 + b'0');
567 buf.push(b'.');
568 buf.push((nanosecond / 100_000_000 % 10) as u8 + b'0');
569 buf.push((nanosecond / 10_000_000 % 10) as u8 + b'0');
570 buf.push((nanosecond / 1_000_000 % 10) as u8 + b'0');
571 buf.push((nanosecond / 100_000 % 10) as u8 + b'0');
572 buf.push((nanosecond / 10_000 % 10) as u8 + b'0');
573 buf.push((nanosecond / 1_000 % 10) as u8 + b'0');
574 buf.push((nanosecond / 100 % 10) as u8 + b'0');
575 buf.push((nanosecond / 10 % 10) as u8 + b'0');
576 buf.push((nanosecond % 10) as u8 + b'0');
577 buf.extend_from_slice(&self.sub_nano);
578 while buf.len() > 14 && {
580 let b = *buf.last().unwrap(); b == b'0' || b == b'.' } {
581 buf.pop();
582 }
583 buf.push(b'Z');
584 return buf;
585 }
586
587 pub fn to_string(&self) -> String {
589 String::from_utf8(self.to_bytes()).unwrap()
590 }
591}
592
593#[test]
594fn test_utctime_parse() {
595 let datetime = *UTCTime::parse(b"8201021200Z").unwrap().datetime();
596 assert_eq!(datetime.year(), 1982);
597 assert_eq!(datetime.month() as u8, 1);
598 assert_eq!(datetime.day(), 2);
599 assert_eq!(datetime.hour(), 12);
600 assert_eq!(datetime.minute(), 0);
601 assert_eq!(datetime.second(), 0);
602 assert_eq!(datetime.nanosecond(), 0);
603
604 let datetime = *UTCTime::parse(b"0101021200Z").unwrap().datetime();
605 assert_eq!(datetime.year(), 2001);
606 assert_eq!(datetime.month() as u8, 1);
607 assert_eq!(datetime.day(), 2);
608 assert_eq!(datetime.hour(), 12);
609 assert_eq!(datetime.minute(), 0);
610 assert_eq!(datetime.second(), 0);
611 assert_eq!(datetime.nanosecond(), 0);
612
613 let datetime = UTCTime::parse(b"8201021200Z").unwrap();
614 assert_eq!(&datetime.to_string(), "820102120000Z");
615
616 let datetime = UTCTime::parse(b"8201020700-0500").unwrap();
617 assert_eq!(&datetime.to_string(), "820102120000Z");
618
619 let datetime = UTCTime::parse(b"0101021200Z").unwrap();
620 assert_eq!(&datetime.to_string(), "010102120000Z");
621
622 let datetime = UTCTime::parse(b"010102120034Z").unwrap();
623 assert_eq!(&datetime.to_string(), "010102120034Z");
624
625 let datetime = UTCTime::parse(b"000229123456Z").unwrap();
626 assert_eq!(&datetime.to_string(), "000229123456Z");
627}
628
629#[test]
630fn test_generalized_time_parse() {
631 let datetime =
632 *GeneralizedTime::parse(b"19851106210627.3Z").unwrap().datetime();
633 assert_eq!(datetime.year(), 1985);
634 assert_eq!(datetime.month() as u8, 11);
635 assert_eq!(datetime.day(), 6);
636 assert_eq!(datetime.hour(), 21);
637 assert_eq!(datetime.minute(), 6);
638 assert_eq!(datetime.second(), 27);
639 assert_eq!(datetime.nanosecond(), 300_000_000);
640
641 let datetime = GeneralizedTime::parse(b"19851106210627.3-0500").unwrap();
642 assert_eq!(&datetime.to_string(), "19851107020627.3Z");
643
644 let datetime = GeneralizedTime::parse(b"198511062106Z").unwrap();
645 assert_eq!(&datetime.to_string(), "19851106210600Z");
646
647 let datetime = GeneralizedTime::parse(b"198511062106.456Z").unwrap();
648 assert_eq!(&datetime.to_string(), "19851106210627.36Z");
649
650 let datetime = GeneralizedTime::parse(b"1985110621Z").unwrap();
651 assert_eq!(&datetime.to_string(), "19851106210000Z");
652
653 let datetime = GeneralizedTime::parse(b"1985110621.14159Z").unwrap();
654 assert_eq!(&datetime.to_string(), "19851106210829.724Z");
655
656 let datetime =
657 GeneralizedTime::parse(b"19990101085960.1234+0900").unwrap();
658 assert_eq!(&datetime.to_string(), "19981231235960.1234Z");
659
660 let datetime =
661 GeneralizedTime::parse(
662 b"20080229033411.3625431984612391672391625532918636000680000-0500"
663 ).unwrap();
664 assert_eq!(&datetime.to_string(),
665 "20080229083411.362543198461239167239162553291863600068Z");
666}