1use std::error::Error as StdError;
2use std::fmt;
3use std::str;
4use std::time::{SystemTime, Duration, UNIX_EPOCH};
5
6#[cfg(target_os="cloudabi")]
7mod max {
8 pub const SECONDS: u64 = ::std::u64::MAX / 1_000_000_000;
9 #[allow(unused)]
10 pub const TIMESTAMP: &'static str = "2554-07-21T23:34:33Z";
11}
12#[cfg(all(
13 target_pointer_width="32",
14 not(target_os="cloudabi"),
15 not(target_os="windows"),
16 not(all(target_arch="wasm32", not(target_os="emscripten")))
17))]
18mod max {
19 pub const SECONDS: u64 = ::std::i32::MAX as u64;
20 #[allow(unused)]
21 pub const TIMESTAMP: &'static str = "2038-01-19T03:14:07Z";
22}
23
24#[cfg(any(
25 target_pointer_width="64",
26 target_os="windows",
27 all(target_arch="wasm32", not(target_os="emscripten")),
28))]
29mod max {
30 pub const SECONDS: u64 = 253_402_300_800-1; #[allow(unused)]
32 pub const TIMESTAMP: &str = "9999-12-31T23:59:59Z";
33}
34
35#[derive(Debug, PartialEq, Clone, Copy)]
37pub enum Error {
38 OutOfRange,
40 InvalidDigit,
42 InvalidFormat,
44}
45
46impl StdError for Error {}
47
48impl fmt::Display for Error {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Error::OutOfRange => write!(f, "numeric component is out of range"),
52 Error::InvalidDigit => write!(f, "bad character where digit is expected"),
53 Error::InvalidFormat => write!(f, "timestamp format is invalid"),
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59enum Precision {
60 Smart,
61 Seconds,
62 Millis,
63 Micros,
64 Nanos,
65}
66
67#[derive(Debug, Clone)]
69pub struct Rfc3339Timestamp(SystemTime, Precision);
70
71#[inline]
72fn two_digits(b1: u8, b2: u8) -> Result<u64, Error> {
73 if b1 < b'0' || b2 < b'0' || b1 > b'9' || b2 > b'9' {
74 return Err(Error::InvalidDigit);
75 }
76 Ok(((b1 - b'0')*10 + (b2 - b'0')) as u64)
77}
78
79pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> {
86 if s.len() < "2018-02-14T00:28:07Z".len() {
87 return Err(Error::InvalidFormat);
88 }
89 let b = s.as_bytes();
90 if b[10] != b'T' || b[b.len()-1] != b'Z' {
91 return Err(Error::InvalidFormat);
92 }
93 parse_rfc3339_weak(s)
94}
95
96pub fn parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error> {
110 if s.len() < "2018-02-14T00:28:07".len() {
111 return Err(Error::InvalidFormat);
112 }
113 let b = s.as_bytes(); if b[4] != b'-' || b[7] != b'-' || (b[10] != b'T' && b[10] != b' ') ||
115 b[13] != b':' || b[16] != b':'
116 {
117 return Err(Error::InvalidFormat);
118 }
119 let year = two_digits(b[0], b[1])? * 100 + two_digits(b[2], b[3])?;
120 let month = two_digits(b[5], b[6])?;
121 let day = two_digits(b[8], b[9])?;
122 let hour = two_digits(b[11], b[12])?;
123 let minute = two_digits(b[14], b[15])?;
124 let mut second = two_digits(b[17], b[18])?;
125
126 if year < 1970 || hour > 23 || minute > 59 || second > 60 {
127 return Err(Error::OutOfRange);
128 }
129 if second == 60 {
131 second = 59
132 };
133 let leap_years = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 +
134 ((year - 1) - 1600) / 400;
135 let leap = is_leap_year(year);
136 let (mut ydays, mdays) = match month {
137 1 => (0, 31),
138 2 if leap => (31, 29),
139 2 => (31, 28),
140 3 => (59, 31),
141 4 => (90, 30),
142 5 => (120, 31),
143 6 => (151, 30),
144 7 => (181, 31),
145 8 => (212, 31),
146 9 => (243, 30),
147 10 => (273, 31),
148 11 => (304, 30),
149 12 => (334, 31),
150 _ => return Err(Error::OutOfRange),
151 };
152 if day > mdays || day == 0 {
153 return Err(Error::OutOfRange);
154 }
155 ydays += day - 1;
156 if leap && month > 2 {
157 ydays += 1;
158 }
159 let days = (year - 1970) * 365 + leap_years + ydays;
160
161 let time = second + minute * 60 + hour * 3600;
162
163 let mut nanos = 0;
164 let mut mult = 100_000_000;
165 if b.get(19) == Some(&b'.') {
166 for idx in 20..b.len() {
167 if b[idx] == b'Z' {
168 if idx == b.len()-1 {
169 break;
170 } else {
171 return Err(Error::InvalidDigit);
172 }
173 }
174 if b[idx] < b'0' || b[idx] > b'9' {
175 return Err(Error::InvalidDigit);
176 }
177 nanos += mult * (b[idx] - b'0') as u32;
178 mult /= 10;
179 }
180 } else if b.len() != 19 && (b.len() > 20 || b[19] != b'Z') {
181 return Err(Error::InvalidFormat);
182 }
183
184 let total_seconds = time + days * 86400;
185 if total_seconds > max::SECONDS {
186 return Err(Error::OutOfRange);
187 }
188
189 Ok(UNIX_EPOCH + Duration::new(total_seconds, nanos))
190}
191
192fn is_leap_year(y: u64) -> bool {
193 y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)
194}
195
196pub fn format_rfc3339(system_time: SystemTime) -> Rfc3339Timestamp {
204 Rfc3339Timestamp(system_time, Precision::Smart)
205}
206
207pub fn format_rfc3339_seconds(system_time: SystemTime) -> Rfc3339Timestamp {
213 Rfc3339Timestamp(system_time, Precision::Seconds)
214}
215
216pub fn format_rfc3339_millis(system_time: SystemTime) -> Rfc3339Timestamp {
222 Rfc3339Timestamp(system_time, Precision::Millis)
223}
224
225pub fn format_rfc3339_micros(system_time: SystemTime) -> Rfc3339Timestamp {
231 Rfc3339Timestamp(system_time, Precision::Micros)
232}
233
234pub fn format_rfc3339_nanos(system_time: SystemTime) -> Rfc3339Timestamp {
240 Rfc3339Timestamp(system_time, Precision::Nanos)
241}
242
243impl fmt::Display for Rfc3339Timestamp {
244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 use self::Precision::*;
246
247 let dur = self.0.duration_since(UNIX_EPOCH)
248 .expect("all times should be after the epoch");
249 let secs_since_epoch = dur.as_secs();
250 let nanos = dur.subsec_nanos();
251
252 if secs_since_epoch >= 253_402_300_800 { return Err(fmt::Error);
254 }
255
256 const LEAPOCH: i64 = 11017;
258 const DAYS_PER_400Y: i64 = 365*400 + 97;
259 const DAYS_PER_100Y: i64 = 365*100 + 24;
260 const DAYS_PER_4Y: i64 = 365*4 + 1;
261
262 let days = (secs_since_epoch / 86400) as i64 - LEAPOCH;
263 let secs_of_day = secs_since_epoch % 86400;
264
265 let mut qc_cycles = days / DAYS_PER_400Y;
266 let mut remdays = days % DAYS_PER_400Y;
267
268 if remdays < 0 {
269 remdays += DAYS_PER_400Y;
270 qc_cycles -= 1;
271 }
272
273 let mut c_cycles = remdays / DAYS_PER_100Y;
274 if c_cycles == 4 { c_cycles -= 1; }
275 remdays -= c_cycles * DAYS_PER_100Y;
276
277 let mut q_cycles = remdays / DAYS_PER_4Y;
278 if q_cycles == 25 { q_cycles -= 1; }
279 remdays -= q_cycles * DAYS_PER_4Y;
280
281 let mut remyears = remdays / 365;
282 if remyears == 4 { remyears -= 1; }
283 remdays -= remyears * 365;
284
285 let mut year = 2000 +
286 remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles;
287
288 let months = [31,30,31,30,31,31,30,31,30,31,31,29];
289 let mut mon = 0;
290 for mon_len in months.iter() {
291 mon += 1;
292 if remdays < *mon_len {
293 break;
294 }
295 remdays -= *mon_len;
296 }
297 let mday = remdays+1;
298 let mon = if mon + 2 > 12 {
299 year += 1;
300 mon - 10
301 } else {
302 mon + 2
303 };
304
305 let mut buf: [u8; 30] = [
306 b'0', b'0', b'0', b'0', b'-', b'0', b'0', b'-', b'0', b'0', b'T',
308 b'0', b'0', b':', b'0', b'0', b':', b'0', b'0',
309 b'.', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'Z',
310 ];
311 buf[0] = b'0' + (year / 1000) as u8;
312 buf[1] = b'0' + (year / 100 % 10) as u8;
313 buf[2] = b'0' + (year / 10 % 10) as u8;
314 buf[3] = b'0' + (year % 10) as u8;
315 buf[5] = b'0' + (mon / 10) as u8;
316 buf[6] = b'0' + (mon % 10) as u8;
317 buf[8] = b'0' + (mday / 10) as u8;
318 buf[9] = b'0' + (mday % 10) as u8;
319 buf[11] = b'0' + (secs_of_day / 3600 / 10) as u8;
320 buf[12] = b'0' + (secs_of_day / 3600 % 10) as u8;
321 buf[14] = b'0' + (secs_of_day / 60 / 10 % 6) as u8;
322 buf[15] = b'0' + (secs_of_day / 60 % 10) as u8;
323 buf[17] = b'0' + (secs_of_day / 10 % 6) as u8;
324 buf[18] = b'0' + (secs_of_day % 10) as u8;
325
326 let offset = if self.1 == Seconds || nanos == 0 && self.1 == Smart {
327 buf[19] = b'Z';
328 19
329 } else if self.1 == Millis {
330 buf[20] = b'0' + (nanos / 100_000_000) as u8;
331 buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
332 buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
333 buf[23] = b'Z';
334 23
335 } else if self.1 == Micros {
336 buf[20] = b'0' + (nanos / 100_000_000) as u8;
337 buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
338 buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
339 buf[23] = b'0' + (nanos / 100_000 % 10) as u8;
340 buf[24] = b'0' + (nanos / 10_000 % 10) as u8;
341 buf[25] = b'0' + (nanos / 1_000 % 10) as u8;
342 buf[26] = b'Z';
343 26
344 } else {
345 buf[20] = b'0' + (nanos / 100_000_000) as u8;
346 buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
347 buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
348 buf[23] = b'0' + (nanos / 100_000 % 10) as u8;
349 buf[24] = b'0' + (nanos / 10_000 % 10) as u8;
350 buf[25] = b'0' + (nanos / 1_000 % 10) as u8;
351 buf[26] = b'0' + (nanos / 100 % 10) as u8;
352 buf[27] = b'0' + (nanos / 10 % 10) as u8;
353 buf[28] = b'0' + (nanos / 1 % 10) as u8;
354 29
356 };
357
358 f.write_str(str::from_utf8(&buf[..=offset]).expect("Conversion to utf8 failed"))
360 }
361}
362
363#[cfg(test)]
364mod test {
365 use std::str::from_utf8;
366 use std::time::{UNIX_EPOCH, SystemTime, Duration};
367
368 use rand::Rng;
369
370 use super::{parse_rfc3339, parse_rfc3339_weak, format_rfc3339};
371 use super::{format_rfc3339_millis, format_rfc3339_micros};
372 use super::{format_rfc3339_nanos};
373 use super::max;
374
375 fn from_sec(sec: u64) -> (String, SystemTime) {
376 let s = time::at_utc(time::Timespec { sec: sec as i64, nsec: 0 })
377 .rfc3339().to_string();
378 let time = UNIX_EPOCH + Duration::new(sec, 0);
379 (s, time)
380 }
381
382 #[test]
383 #[cfg(all(target_pointer_width="32", target_os="linux"))]
384 fn year_after_2038_fails_gracefully() {
385 assert_eq!(parse_rfc3339("2038-01-19T03:14:08Z").unwrap_err(),
387 super::Error::OutOfRange);
388 assert_eq!(parse_rfc3339("9999-12-31T23:59:59Z").unwrap_err(),
389 super::Error::OutOfRange);
390 }
391
392 #[test]
393 fn smoke_tests_parse() {
394 assert_eq!(parse_rfc3339("1970-01-01T00:00:00Z").unwrap(),
395 UNIX_EPOCH + Duration::new(0, 0));
396 assert_eq!(parse_rfc3339("1970-01-01T00:00:01Z").unwrap(),
397 UNIX_EPOCH + Duration::new(1, 0));
398 assert_eq!(parse_rfc3339("2018-02-13T23:08:32Z").unwrap(),
399 UNIX_EPOCH + Duration::new(1_518_563_312, 0));
400 assert_eq!(parse_rfc3339("2012-01-01T00:00:00Z").unwrap(),
401 UNIX_EPOCH + Duration::new(1_325_376_000, 0));
402 }
403
404 #[test]
405 fn smoke_tests_format() {
406 assert_eq!(
407 format_rfc3339(UNIX_EPOCH + Duration::new(0, 0)).to_string(),
408 "1970-01-01T00:00:00Z");
409 assert_eq!(
410 format_rfc3339(UNIX_EPOCH + Duration::new(1, 0)).to_string(),
411 "1970-01-01T00:00:01Z");
412 assert_eq!(
413 format_rfc3339(UNIX_EPOCH + Duration::new(1_518_563_312, 0)).to_string(),
414 "2018-02-13T23:08:32Z");
415 assert_eq!(
416 format_rfc3339(UNIX_EPOCH + Duration::new(1_325_376_000, 0)).to_string(),
417 "2012-01-01T00:00:00Z");
418 }
419
420 #[test]
421 fn smoke_tests_format_millis() {
422 assert_eq!(
423 format_rfc3339_millis(UNIX_EPOCH +
424 Duration::new(0, 0)).to_string(),
425 "1970-01-01T00:00:00.000Z");
426 assert_eq!(
427 format_rfc3339_millis(UNIX_EPOCH +
428 Duration::new(1_518_563_312, 123_000_000)).to_string(),
429 "2018-02-13T23:08:32.123Z");
430 }
431
432 #[test]
433 fn smoke_tests_format_micros() {
434 assert_eq!(
435 format_rfc3339_micros(UNIX_EPOCH +
436 Duration::new(0, 0)).to_string(),
437 "1970-01-01T00:00:00.000000Z");
438 assert_eq!(
439 format_rfc3339_micros(UNIX_EPOCH +
440 Duration::new(1_518_563_312, 123_000_000)).to_string(),
441 "2018-02-13T23:08:32.123000Z");
442 assert_eq!(
443 format_rfc3339_micros(UNIX_EPOCH +
444 Duration::new(1_518_563_312, 456_123_000)).to_string(),
445 "2018-02-13T23:08:32.456123Z");
446 }
447
448 #[test]
449 fn smoke_tests_format_nanos() {
450 assert_eq!(
451 format_rfc3339_nanos(UNIX_EPOCH +
452 Duration::new(0, 0)).to_string(),
453 "1970-01-01T00:00:00.000000000Z");
454 assert_eq!(
455 format_rfc3339_nanos(UNIX_EPOCH +
456 Duration::new(1_518_563_312, 123_000_000)).to_string(),
457 "2018-02-13T23:08:32.123000000Z");
458 assert_eq!(
459 format_rfc3339_nanos(UNIX_EPOCH +
460 Duration::new(1_518_563_312, 789_456_123)).to_string(),
461 "2018-02-13T23:08:32.789456123Z");
462 }
463
464 #[test]
465 fn upper_bound() {
466 let max = UNIX_EPOCH + Duration::new(max::SECONDS, 0);
467 assert_eq!(parse_rfc3339(&max::TIMESTAMP).unwrap(), max);
468 assert_eq!(format_rfc3339(max).to_string(), max::TIMESTAMP);
469 }
470
471 #[test]
472 fn leap_second() {
473 assert_eq!(parse_rfc3339("2016-12-31T23:59:60Z").unwrap(),
474 UNIX_EPOCH + Duration::new(1_483_228_799, 0));
475 }
476
477 #[test]
478 fn first_731_days() {
479 let year_start = 0; for day in 0..= 365 * 2 { let (s, time) = from_sec(year_start + day * 86400);
482 assert_eq!(parse_rfc3339(&s).unwrap(), time);
483 assert_eq!(format_rfc3339(time).to_string(), s);
484 }
485 }
486
487 #[test]
488 fn the_731_consecutive_days() {
489 let year_start = 1_325_376_000; for day in 0..= 365 * 2 { let (s, time) = from_sec(year_start + day * 86400);
492 assert_eq!(parse_rfc3339(&s).unwrap(), time);
493 assert_eq!(format_rfc3339(time).to_string(), s);
494 }
495 }
496
497 #[test]
498 fn all_86400_seconds() {
499 let day_start = 1_325_376_000;
500 for second in 0..86400 { let (s, time) = from_sec(day_start + second);
502 assert_eq!(parse_rfc3339(&s).unwrap(), time);
503 assert_eq!(format_rfc3339(time).to_string(), s);
504 }
505 }
506
507 #[test]
508 fn random_past() {
509 let upper = SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
510 .as_secs();
511 for _ in 0..10000 {
512 let sec = rand::thread_rng().gen_range(0, upper);
513 let (s, time) = from_sec(sec);
514 assert_eq!(parse_rfc3339(&s).unwrap(), time);
515 assert_eq!(format_rfc3339(time).to_string(), s);
516 }
517 }
518
519 #[test]
520 fn random_wide_range() {
521 for _ in 0..100_000 {
522 let sec = rand::thread_rng().gen_range(0, max::SECONDS);
523 let (s, time) = from_sec(sec);
524 assert_eq!(parse_rfc3339(&s).unwrap(), time);
525 assert_eq!(format_rfc3339(time).to_string(), s);
526 }
527 }
528
529 #[test]
530 fn milliseconds() {
531 assert_eq!(parse_rfc3339("1970-01-01T00:00:00.123Z").unwrap(),
532 UNIX_EPOCH + Duration::new(0, 123_000_000));
533 assert_eq!(format_rfc3339(UNIX_EPOCH + Duration::new(0, 123_000_000))
534 .to_string(), "1970-01-01T00:00:00.123000000Z");
535 }
536
537 #[test]
538 #[should_panic(expected="OutOfRange")]
539 fn zero_month() {
540 parse_rfc3339("1970-00-01T00:00:00Z").unwrap();
541 }
542
543 #[test]
544 #[should_panic(expected="OutOfRange")]
545 fn big_month() {
546 parse_rfc3339("1970-32-01T00:00:00Z").unwrap();
547 }
548
549 #[test]
550 #[should_panic(expected="OutOfRange")]
551 fn zero_day() {
552 parse_rfc3339("1970-01-00T00:00:00Z").unwrap();
553 }
554
555 #[test]
556 #[should_panic(expected="OutOfRange")]
557 fn big_day() {
558 parse_rfc3339("1970-12-35T00:00:00Z").unwrap();
559 }
560
561 #[test]
562 #[should_panic(expected="OutOfRange")]
563 fn big_day2() {
564 parse_rfc3339("1970-02-30T00:00:00Z").unwrap();
565 }
566
567 #[test]
568 #[should_panic(expected="OutOfRange")]
569 fn big_second() {
570 parse_rfc3339("1970-12-30T00:00:78Z").unwrap();
571 }
572
573 #[test]
574 #[should_panic(expected="OutOfRange")]
575 fn big_minute() {
576 parse_rfc3339("1970-12-30T00:78:00Z").unwrap();
577 }
578
579 #[test]
580 #[should_panic(expected="OutOfRange")]
581 fn big_hour() {
582 parse_rfc3339("1970-12-30T24:00:00Z").unwrap();
583 }
584
585 #[test]
586 fn break_data() {
587 for pos in 0.."2016-12-31T23:59:60Z".len() {
588 let mut s = b"2016-12-31T23:59:60Z".to_vec();
589 s[pos] = b'x';
590 parse_rfc3339(from_utf8(&s).unwrap()).unwrap_err();
591 }
592 }
593
594 #[test]
595 fn weak_smoke_tests() {
596 assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00").unwrap(),
597 UNIX_EPOCH + Duration::new(0, 0));
598 parse_rfc3339("1970-01-01 00:00:00").unwrap_err();
599
600 assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123").unwrap(),
601 UNIX_EPOCH + Duration::new(0, 123_000));
602 parse_rfc3339("1970-01-01 00:00:00.000123").unwrap_err();
603
604 assert_eq!(parse_rfc3339_weak("1970-01-01T00:00:00.000123").unwrap(),
605 UNIX_EPOCH + Duration::new(0, 123_000));
606 parse_rfc3339("1970-01-01T00:00:00.000123").unwrap_err();
607
608 assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123Z").unwrap(),
609 UNIX_EPOCH + Duration::new(0, 123_000));
610 parse_rfc3339("1970-01-01 00:00:00.000123Z").unwrap_err();
611
612 assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00Z").unwrap(),
613 UNIX_EPOCH + Duration::new(0, 0));
614 parse_rfc3339("1970-01-01 00:00:00Z").unwrap_err();
615 }
616}