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