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