1 use std::fmt;
2 use std::str;
3 use std::time::{SystemTime, Duration, UNIX_EPOCH};
4 
5 #[cfg(target_os="cloudabi")]
6 mod max {
7     pub const SECONDS: u64 = ::std::u64::MAX / 1_000_000_000;
8     #[allow(unused)]
9     pub const TIMESTAMP: &'static str = "2554-07-21T23:34:33Z";
10 }
11 #[cfg(all(
12     target_pointer_width="32",
13     not(target_os="cloudabi"),
14     not(target_os="windows"),
15     not(all(target_arch="wasm32", not(target_os="emscripten")))
16 ))]
17 mod max {
18     pub const SECONDS: u64 = ::std::i32::MAX as u64;
19     #[allow(unused)]
20     pub const TIMESTAMP: &'static str = "2038-01-19T03:14:07Z";
21 }
22 
23 #[cfg(any(
24     target_pointer_width="64",
25     target_os="windows",
26     all(target_arch="wasm32", not(target_os="emscripten")),
27 ))]
28 mod max {
29     pub const SECONDS: u64 = 253402300800-1;  // last second of year 9999
30     #[allow(unused)]
31     pub const TIMESTAMP: &'static str = "9999-12-31T23:59:59Z";
32 }
33 
34 quick_error! {
35     /// Error parsing datetime (timestamp)
36     #[derive(Debug, PartialEq, Clone, Copy)]
37     pub enum Error {
38         /// Numeric component is out of range
39         OutOfRange {
40             display("numeric component is out of range")
41         }
42         /// Bad character where digit is expected
43         InvalidDigit {
44             display("bad character where digit is expected")
45         }
46         /// Other formatting errors
47         InvalidFormat {
48             display("timestamp format is invalid")
49         }
50     }
51 }
52 
53 #[derive(Debug, Clone, PartialEq, Eq)]
54 enum Precision {
55     Smart,
56     Seconds,
57     Nanos,
58 }
59 
60 /// A wrapper type that allows you to Display a SystemTime
61 #[derive(Debug, Clone)]
62 pub struct Rfc3339Timestamp(SystemTime, Precision);
63 
64 #[inline]
two_digits(b1: u8, b2: u8) -> Result<u64, Error>65 fn two_digits(b1: u8, b2: u8) -> Result<u64, Error> {
66     if b1 < b'0' || b2 < b'0' || b1 > b'9' || b2 > b'9' {
67         return Err(Error::InvalidDigit);
68     }
69     Ok(((b1 - b'0')*10 + (b2 - b'0')) as u64)
70 }
71 
72 /// Parse RFC3339 timestamp `2018-02-14T00:28:07Z`
73 ///
74 /// Supported feature: any precision of fractional
75 /// digits `2018-02-14T00:28:07.133Z`.
76 ///
77 /// Unsupported feature: localized timestamps. Only UTC is supported.
parse_rfc3339(s: &str) -> Result<SystemTime, Error>78 pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> {
79     if s.len() < "2018-02-14T00:28:07Z".len() {
80         return Err(Error::InvalidFormat);
81     }
82     let b = s.as_bytes();
83     if b[10] != b'T' || b[b.len()-1] != b'Z' {
84         return Err(Error::InvalidFormat);
85     }
86     return parse_rfc3339_weak(s);
87 }
88 
89 /// Parse RFC3339-like timestamp `2018-02-14 00:28:07`
90 ///
91 /// Supported features:
92 ///
93 /// 1. Any precision of fractional digits `2018-02-14 00:28:07.133`.
94 /// 2. Supports timestamp with or without either of `T` or `Z`
95 /// 3. Anything valid for `parse_3339` is valid for this function
96 ///
97 /// Unsupported feature: localized timestamps. Only UTC is supported, even if
98 /// `Z` is not specified.
99 ///
100 /// This function is intended to use for parsing human input. Whereas
101 /// `parse_rfc3339` is for strings generated programmatically.
parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error>102 pub fn parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error> {
103     if s.len() < "2018-02-14T00:28:07".len() {
104         return Err(Error::InvalidFormat);
105     }
106     let b = s.as_bytes();  // for careless slicing
107     if b[4] != b'-' || b[7] != b'-' || (b[10] != b'T' && b[10] != b' ') ||
108        b[13] != b':' || b[16] != b':'
109     {
110         return Err(Error::InvalidFormat);
111     }
112     let year = two_digits(b[0], b[1])? * 100 + two_digits(b[2], b[3])?;
113     let month = two_digits(b[5], b[6])?;
114     let day = two_digits(b[8], b[9])?;
115     let hour = two_digits(b[11], b[12])?;
116     let minute = two_digits(b[14], b[15])?;
117     let mut second = two_digits(b[17], b[18])?;
118 
119     if year < 1970 || hour > 23 || minute > 59 || second > 60 {
120         return Err(Error::OutOfRange);
121     }
122     // TODO(tailhook) should we check that leaps second is only on midnight ?
123     if second == 60 {
124         second = 59
125     };
126     let leap_years = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 +
127                      ((year - 1) - 1600) / 400;
128     let leap = is_leap_year(year);
129     let (mut ydays, mdays) = match month {
130         1 => (0, 31),
131         2 if leap => (31, 29),
132         2 => (31, 28),
133         3 => (59, 31),
134         4 => (90, 30),
135         5 => (120, 31),
136         6 => (151, 30),
137         7 => (181, 31),
138         8 => (212, 31),
139         9 => (243, 30),
140         10 => (273, 31),
141         11 => (304, 30),
142         12 => (334, 31),
143         _ => return Err(Error::OutOfRange),
144     };
145     if day > mdays || day == 0 {
146         return Err(Error::OutOfRange);
147     }
148     ydays += day - 1;
149     if leap && month > 2 {
150         ydays += 1;
151     }
152     let days = (year - 1970) * 365 + leap_years + ydays;
153 
154     let time = second + minute * 60 + hour * 3600;
155 
156     let mut nanos = 0;
157     let mut mult = 100_000_000;
158     if b.get(19) == Some(&b'.') {
159         for idx in 20..b.len() {
160             if b[idx] == b'Z' {
161                 if idx == b.len()-1 {
162                     break;
163                 } else {
164                     return Err(Error::InvalidDigit);
165                 }
166             }
167             if b[idx] < b'0' || b[idx] > b'9' {
168                 return Err(Error::InvalidDigit);
169             }
170             nanos += mult * (b[idx] - b'0') as u32;
171             mult /= 10;
172         }
173     } else {
174         if b.len() != 19 && (b.len() > 20 || b[19] != b'Z') {
175             return Err(Error::InvalidFormat);
176         }
177     }
178 
179     let total_seconds = time + days * 86400;
180     if total_seconds > max::SECONDS {
181         return Err(Error::OutOfRange);
182     }
183 
184     return Ok(UNIX_EPOCH + Duration::new(total_seconds, nanos));
185 }
186 
is_leap_year(y: u64) -> bool187 fn is_leap_year(y: u64) -> bool {
188     y % 4 == 0 && (!(y % 100 == 0) || y % 400 == 0)
189 }
190 
191 /// Format an RFC3339 timestamp `2018-02-14T00:28:07Z`
192 ///
193 /// This function formats timestamp with smart precision: i.e. if it has no
194 /// fractional seconds, they aren't written at all. And up to nine digits if
195 /// they are.
196 ///
197 /// The value is always UTC and ignores system timezone.
format_rfc3339(system_time: SystemTime) -> Rfc3339Timestamp198 pub fn format_rfc3339(system_time: SystemTime) -> Rfc3339Timestamp {
199     return Rfc3339Timestamp(system_time, Precision::Smart);
200 }
201 
202 /// Format an RFC3339 timestamp `2018-02-14T00:28:07Z`
203 ///
204 /// This format always shows timestamp without fractional seconds.
205 ///
206 /// The value is always UTC and ignores system timezone.
format_rfc3339_seconds(system_time: SystemTime) -> Rfc3339Timestamp207 pub fn format_rfc3339_seconds(system_time: SystemTime) -> Rfc3339Timestamp {
208     return Rfc3339Timestamp(system_time, Precision::Seconds);
209 }
210 
211 /// Format an RFC3339 timestamp `2018-02-14T00:28:07.000000000Z`
212 ///
213 /// This format always shows nanoseconds even if nanosecond value is zero.
214 ///
215 /// The value is always UTC and ignores system timezone.
format_rfc3339_nanos(system_time: SystemTime) -> Rfc3339Timestamp216 pub fn format_rfc3339_nanos(system_time: SystemTime) -> Rfc3339Timestamp {
217     return Rfc3339Timestamp(system_time, Precision::Nanos);
218 }
219 
220 impl fmt::Display for Rfc3339Timestamp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result221     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222         use self::Precision::*;
223 
224         let dur = self.0.duration_since(UNIX_EPOCH)
225             .expect("all times should be after the epoch");
226         let secs_since_epoch = dur.as_secs();
227         let nanos = dur.subsec_nanos();
228 
229         if secs_since_epoch >= 253402300800 { // year 9999
230             return Err(fmt::Error);
231         }
232 
233         /* 2000-03-01 (mod 400 year, immediately after feb29 */
234         const LEAPOCH: i64 = 11017;
235         const DAYS_PER_400Y: i64 = 365*400 + 97;
236         const DAYS_PER_100Y: i64 = 365*100 + 24;
237         const DAYS_PER_4Y: i64 = 365*4 + 1;
238 
239         let days = (secs_since_epoch / 86400) as i64 - LEAPOCH;
240         let secs_of_day = secs_since_epoch % 86400;
241 
242         let mut qc_cycles = days / DAYS_PER_400Y;
243         let mut remdays = days % DAYS_PER_400Y;
244 
245         if remdays < 0 {
246             remdays += DAYS_PER_400Y;
247             qc_cycles -= 1;
248         }
249 
250         let mut c_cycles = remdays / DAYS_PER_100Y;
251         if c_cycles == 4 { c_cycles -= 1; }
252         remdays -= c_cycles * DAYS_PER_100Y;
253 
254         let mut q_cycles = remdays / DAYS_PER_4Y;
255         if q_cycles == 25 { q_cycles -= 1; }
256         remdays -= q_cycles * DAYS_PER_4Y;
257 
258         let mut remyears = remdays / 365;
259         if remyears == 4 { remyears -= 1; }
260         remdays -= remyears * 365;
261 
262         let mut year = 2000 +
263             remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles;
264 
265         let months = [31,30,31,30,31,31,30,31,30,31,31,29];
266         let mut mon = 0;
267         for mon_len in months.iter() {
268             mon += 1;
269             if remdays < *mon_len {
270                 break;
271             }
272             remdays -= *mon_len;
273         }
274         let mday = remdays+1;
275         let mon = if mon + 2 > 12 {
276             year += 1;
277             mon - 10
278         } else {
279             mon + 2
280         };
281 
282         let mut buf: [u8; 30] = [
283             // Too long to write as: b"0000-00-00T00:00:00.000000000Z"
284             b'0', b'0', b'0', b'0', b'-', b'0', b'0', b'-', b'0', b'0', b'T',
285             b'0', b'0', b':', b'0', b'0', b':', b'0', b'0',
286             b'.', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'Z',
287         ];
288         buf[0] = b'0' + (year / 1000) as u8;
289         buf[1] = b'0' + (year / 100 % 10) as u8;
290         buf[2] = b'0' + (year / 10 % 10) as u8;
291         buf[3] = b'0' + (year % 10) as u8;
292         buf[5] = b'0' + (mon / 10) as u8;
293         buf[6] = b'0' + (mon % 10) as u8;
294         buf[8] = b'0' + (mday / 10) as u8;
295         buf[9] = b'0' + (mday % 10) as u8;
296         buf[11] = b'0' + (secs_of_day / 3600 / 10) as u8;
297         buf[12] = b'0' + (secs_of_day / 3600 % 10) as u8;
298         buf[14] = b'0' + (secs_of_day / 60 / 10 % 6) as u8;
299         buf[15] = b'0' + (secs_of_day / 60 % 10) as u8;
300         buf[17] = b'0' + (secs_of_day / 10 % 6) as u8;
301         buf[18] = b'0' + (secs_of_day % 10) as u8;
302 
303         if self.1 == Seconds || nanos == 0 && self.1 == Smart {
304             buf[19] = b'Z';
305             f.write_str(unsafe { str::from_utf8_unchecked(&buf[..20]) })
306         } else {
307             buf[20] = b'0' + (nanos / 100_000_000) as u8;
308             buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
309             buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
310             buf[23] = b'0' + (nanos / 100_000 % 10) as u8;
311             buf[24] = b'0' + (nanos / 10_000 % 10) as u8;
312             buf[25] = b'0' + (nanos / 1_000 % 10) as u8;
313             buf[26] = b'0' + (nanos / 100 % 10) as u8;
314             buf[27] = b'0' + (nanos / 10 % 10) as u8;
315             buf[28] = b'0' + (nanos / 1 % 10) as u8;
316             // we know our chars are all ascii
317             f.write_str(unsafe { str::from_utf8_unchecked(&buf[..]) })
318         }
319     }
320 }
321 
322 #[cfg(test)]
323 mod test {
324     extern crate time;
325     extern crate rand;
326 
327     use std::str::from_utf8;
328     use self::rand::Rng;
329     use std::time::{UNIX_EPOCH, SystemTime, Duration};
330     use super::{parse_rfc3339, parse_rfc3339_weak, format_rfc3339};
331     use super::max;
332 
from_sec(sec: u64) -> (String, SystemTime)333     fn from_sec(sec: u64) -> (String, SystemTime) {
334         let s = time::at_utc(time::Timespec { sec: sec as i64, nsec: 0 })
335                   .rfc3339().to_string();
336         let time = UNIX_EPOCH + Duration::new(sec, 0);
337         return (s, time)
338     }
339 
340     #[test]
341     #[cfg(all(target_pointer_width="32", target_os="linux"))]
year_after_2038_fails_gracefully()342     fn year_after_2038_fails_gracefully() {
343         // next second
344         assert_eq!(parse_rfc3339("2038-01-19T03:14:08Z").unwrap_err(),
345                    super::Error::OutOfRange);
346         assert_eq!(parse_rfc3339("9999-12-31T23:59:59Z").unwrap_err(),
347                    super::Error::OutOfRange);
348     }
349 
350     #[test]
smoke_tests_parse()351     fn smoke_tests_parse() {
352         assert_eq!(parse_rfc3339("1970-01-01T00:00:00Z").unwrap(),
353                    UNIX_EPOCH + Duration::new(0, 0));
354         assert_eq!(parse_rfc3339("1970-01-01T00:00:01Z").unwrap(),
355                    UNIX_EPOCH + Duration::new(1, 0));
356         assert_eq!(parse_rfc3339("2018-02-13T23:08:32Z").unwrap(),
357                    UNIX_EPOCH + Duration::new(1518563312, 0));
358         assert_eq!(parse_rfc3339("2012-01-01T00:00:00Z").unwrap(),
359                    UNIX_EPOCH + Duration::new(1325376000, 0));
360     }
361 
362     #[test]
smoke_tests_format()363     fn smoke_tests_format() {
364         assert_eq!(
365             format_rfc3339(UNIX_EPOCH + Duration::new(0, 0)).to_string(),
366             "1970-01-01T00:00:00Z");
367         assert_eq!(
368             format_rfc3339(UNIX_EPOCH + Duration::new(1, 0)).to_string(),
369             "1970-01-01T00:00:01Z");
370         assert_eq!(
371             format_rfc3339(UNIX_EPOCH + Duration::new(1518563312, 0)).to_string(),
372             "2018-02-13T23:08:32Z");
373         assert_eq!(
374             format_rfc3339(UNIX_EPOCH + Duration::new(1325376000, 0)).to_string(),
375             "2012-01-01T00:00:00Z");
376     }
377 
378     #[test]
upper_bound()379     fn upper_bound() {
380         let max = UNIX_EPOCH + Duration::new(max::SECONDS, 0);
381         assert_eq!(parse_rfc3339(&max::TIMESTAMP).unwrap(), max);
382         assert_eq!(format_rfc3339(max).to_string(), max::TIMESTAMP);
383     }
384 
385     #[test]
leap_second()386     fn leap_second() {
387         assert_eq!(parse_rfc3339("2016-12-31T23:59:60Z").unwrap(),
388                    UNIX_EPOCH + Duration::new(1483228799, 0));
389     }
390 
391     #[test]
first_731_days()392     fn first_731_days() {
393         let year_start = 0;  // 1970
394         for day in 0.. (365 * 2 + 1) {  // scan leap year and non-leap year
395             let (s, time) = from_sec(year_start + day * 86400);
396             assert_eq!(parse_rfc3339(&s).unwrap(), time);
397             assert_eq!(format_rfc3339(time).to_string(), s);
398         }
399     }
400 
401     #[test]
the_731_consecutive_days()402     fn the_731_consecutive_days() {
403         let year_start = 1325376000;  // 2012
404         for day in 0.. (365 * 2 + 1) {  // scan leap year and non-leap year
405             let (s, time) = from_sec(year_start + day * 86400);
406             assert_eq!(parse_rfc3339(&s).unwrap(), time);
407             assert_eq!(format_rfc3339(time).to_string(), s);
408         }
409     }
410 
411     #[test]
all_86400_seconds()412     fn all_86400_seconds() {
413         let day_start = 1325376000;
414         for second in 0..86400 {  // scan leap year and non-leap year
415             let (s, time) = from_sec(day_start + second);
416             assert_eq!(parse_rfc3339(&s).unwrap(), time);
417             assert_eq!(format_rfc3339(time).to_string(), s);
418         }
419     }
420 
421     #[test]
random_past()422     fn random_past() {
423         let upper = SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
424             .as_secs();
425         for _ in 0..10000 {
426             let sec = rand::thread_rng().gen_range(0, upper);
427             let (s, time) = from_sec(sec);
428             assert_eq!(parse_rfc3339(&s).unwrap(), time);
429             assert_eq!(format_rfc3339(time).to_string(), s);
430         }
431     }
432 
433     #[test]
random_wide_range()434     fn random_wide_range() {
435         for _ in 0..100000 {
436             let sec = rand::thread_rng().gen_range(0, max::SECONDS);
437             let (s, time) = from_sec(sec);
438             assert_eq!(parse_rfc3339(&s).unwrap(), time);
439             assert_eq!(format_rfc3339(time).to_string(), s);
440         }
441     }
442 
443     #[test]
milliseconds()444     fn milliseconds() {
445         assert_eq!(parse_rfc3339("1970-01-01T00:00:00.123Z").unwrap(),
446                    UNIX_EPOCH + Duration::new(0, 123000000));
447         assert_eq!(format_rfc3339(UNIX_EPOCH + Duration::new(0, 123000000))
448             .to_string(), "1970-01-01T00:00:00.123000000Z");
449     }
450 
451     #[test]
452     #[should_panic(expected="OutOfRange")]
zero_month()453     fn zero_month() {
454         parse_rfc3339("1970-00-01T00:00:00Z").unwrap();
455     }
456 
457     #[test]
458     #[should_panic(expected="OutOfRange")]
big_month()459     fn big_month() {
460         parse_rfc3339("1970-32-01T00:00:00Z").unwrap();
461     }
462 
463     #[test]
464     #[should_panic(expected="OutOfRange")]
zero_day()465     fn zero_day() {
466         parse_rfc3339("1970-01-00T00:00:00Z").unwrap();
467     }
468 
469     #[test]
470     #[should_panic(expected="OutOfRange")]
big_day()471     fn big_day() {
472         parse_rfc3339("1970-12-35T00:00:00Z").unwrap();
473     }
474 
475     #[test]
476     #[should_panic(expected="OutOfRange")]
big_day2()477     fn big_day2() {
478         parse_rfc3339("1970-02-30T00:00:00Z").unwrap();
479     }
480 
481     #[test]
482     #[should_panic(expected="OutOfRange")]
big_second()483     fn big_second() {
484         parse_rfc3339("1970-12-30T00:00:78Z").unwrap();
485     }
486 
487     #[test]
488     #[should_panic(expected="OutOfRange")]
big_minute()489     fn big_minute() {
490         parse_rfc3339("1970-12-30T00:78:00Z").unwrap();
491     }
492 
493     #[test]
494     #[should_panic(expected="OutOfRange")]
big_hour()495     fn big_hour() {
496         parse_rfc3339("1970-12-30T24:00:00Z").unwrap();
497     }
498 
499     #[test]
break_data()500     fn break_data() {
501         for pos in 0.."2016-12-31T23:59:60Z".len() {
502             let mut s = b"2016-12-31T23:59:60Z".to_vec();
503             s[pos] = b'x';
504             parse_rfc3339(from_utf8(&s).unwrap()).unwrap_err();
505         }
506     }
507 
508     #[test]
weak_smoke_tests()509     fn weak_smoke_tests() {
510         assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00").unwrap(),
511                    UNIX_EPOCH + Duration::new(0, 0));
512         parse_rfc3339("1970-01-01 00:00:00").unwrap_err();
513 
514         assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123").unwrap(),
515                    UNIX_EPOCH + Duration::new(0, 123000));
516         parse_rfc3339("1970-01-01 00:00:00.000123").unwrap_err();
517 
518         assert_eq!(parse_rfc3339_weak("1970-01-01T00:00:00.000123").unwrap(),
519                    UNIX_EPOCH + Duration::new(0, 123000));
520         parse_rfc3339("1970-01-01T00:00:00.000123").unwrap_err();
521 
522         assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123Z").unwrap(),
523                    UNIX_EPOCH + Duration::new(0, 123000));
524         parse_rfc3339("1970-01-01 00:00:00.000123Z").unwrap_err();
525 
526         assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00Z").unwrap(),
527                    UNIX_EPOCH + Duration::new(0, 0));
528         parse_rfc3339("1970-01-01 00:00:00Z").unwrap_err();
529     }
530 }
531