better handling (and bug fixes) of leap seconds in `format::parsed`.

This commit is contained in:
Kang Seonghoon 2015-01-31 02:40:19 +09:00
parent b8a2ad2220
commit dfa92ef56d
1 changed files with 54 additions and 6 deletions

View File

@ -401,20 +401,29 @@ impl Parsed {
} else if let Some(timestamp) = self.timestamp {
// reconstruct date and time fields from timestamp
let ts = try_opt!(timestamp.checked_add(offset as i64));
let datetime = try_opt!(NaiveDateTime::from_num_seconds_from_unix_epoch_opt(ts, 0));
let mut datetime = try_opt!(NaiveDateTime::from_num_seconds_from_unix_epoch_opt(ts, 0));
// fill year, ordinal, hour, minute and second fields from timestamp.
// if existing fields are consistent, this will allow the full date/time reconstruction.
let mut parsed = self.clone();
if parsed.second == Some(60) {
// `datetime.second()` cannot be 60, so this is the only case for a leap second.
match datetime.second() {
// it's okay, just do not try to overwrite the existing field.
59 => {}
// `datetime` is known to be off by one second.
0 => { datetime = datetime - Duration::seconds(1); }
// otherwise it is impossible.
_ => return None
}
// ...and we have the correct candidates for other fields.
} else {
if !parsed.set_second(datetime.second() as i64) { return None; }
}
if !parsed.set_year (datetime.year() as i64) { return None; }
if !parsed.set_ordinal(datetime.ordinal() as i64) { return None; }
if !parsed.set_hour (datetime.hour() as i64) { return None; }
if !parsed.set_minute (datetime.minute() as i64) { return None; }
if !(parsed.second == Some(60) && datetime.second() == 59) {
// `datetime.second` cannot be 60, so we can know if this is a leap second
// only when the original `parsed` had that. do not try to reset it.
if !parsed.set_second(datetime.second() as i64) { return None; }
}
if !parsed.set_nanosecond(0) { return None; } // no nanosecond precision in timestamp
// validate other fields (e.g. week) and return
@ -759,6 +768,45 @@ mod tests {
ymdhms(0,1,1, 0,0,0));
assert_eq!(parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
ymdhms(date::MAX.year(),12,31, 23,59,59));
// leap seconds #1: partial fields
assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), None);
assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012,6,30, 23,59,59));
assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), None);
assert_eq!(parse!(second: 60, timestamp: 1_341_100_799),
ymdhmsn(2012,6,30, 23,59,59,1_000_000_000));
assert_eq!(parse!(second: 60, timestamp: 1_341_100_800),
ymdhmsn(2012,6,30, 23,59,59,1_000_000_000));
assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012,7,1, 0,0,0));
assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), None);
assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), None);
// leap seconds #2: full fields
// we need to have separate tests for them since it uses another control flow.
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 59, timestamp: 1_341_100_798),
None);
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 59, timestamp: 1_341_100_799),
ymdhms(2012,6,30, 23,59,59));
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 59, timestamp: 1_341_100_800),
None);
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 60, timestamp: 1_341_100_799),
ymdhmsn(2012,6,30, 23,59,59,1_000_000_000));
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 60, timestamp: 1_341_100_800),
ymdhmsn(2012,6,30, 23,59,59,1_000_000_000));
assert_eq!(parse!(year_mod_100: 12, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
minute: 0, second: 0, timestamp: 1_341_100_800),
ymdhms(2012,7,1, 0,0,0));
assert_eq!(parse!(year_mod_100: 12, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
minute: 0, second: 1, timestamp: 1_341_100_800),
None);
assert_eq!(parse!(year_mod_100: 12, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
minute: 59, second: 60, timestamp: 1_341_100_801),
None);
}
#[test]