From 910c3eed3dc7895086a98bf7f5d373e22388a272 Mon Sep 17 00:00:00 2001 From: Edward Barnard Date: Sat, 7 Jan 2017 13:57:37 +0000 Subject: Improve binary plist datetime decoding. --- src/binary/reader.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'src/binary') diff --git a/src/binary/reader.rs b/src/binary/reader.rs index 56bb587..c3c0be4 100644 --- a/src/binary/reader.rs +++ b/src/binary/reader.rs @@ -1,5 +1,5 @@ use byteorder::{BigEndian, ReadBytesExt}; -use chrono::{TimeZone, UTC}; +use chrono::{Duration, TimeZone, UTC}; use std::io::{Cursor, Read, Seek, SeekFrom}; use std::string::{FromUtf8Error, FromUtf16Error}; @@ -189,18 +189,28 @@ impl EventReader { } (0x2, 3) => Some(PlistEvent::RealValue(try!(self.reader.read_f64::()))), (0x2, _) => return Err(Error::InvalidData), // odd length float - (0x3, 3) => { + (0x3, 3) => {; // Date - // Seconds since 1/1/2001 00:00:00 + // Seconds since 1/1/2001 00:00:00. let timestamp = try!(self.reader.read_f64::()); - let secs = timestamp.floor(); - let subsecs = timestamp - secs; + let millis = timestamp * 1_000.0; + // Chrono's Duration can only millisecond values between ::std::i64::MIN and + // ::std::i64::MAX. + if millis > ::std::i64::MAX as f64 || millis < ::std::i64::MIN as f64 { + return Err(Error::InvalidData); + } + + let whole_millis = millis.floor(); + let submilli_nanos = ((millis - whole_millis) * 1_000_000.0).floor(); + + let dur = Duration::milliseconds(whole_millis as i64); + let dur = dur + Duration::nanoseconds(submilli_nanos as i64); - let int_secs = (secs as i64) + (31 * 365 + 8) * 86400; - let int_nanos = (subsecs * 1_000_000_000f64) as u32; + let plist_epoch = UTC.ymd(2001, 1, 1).and_hms(0, 0, 0); + let date = try!(plist_epoch.checked_add(dur).ok_or(Error::InvalidData)); - Some(PlistEvent::DateValue(UTC.timestamp(int_secs, int_nanos))) + Some(PlistEvent::DateValue(date)) } (0x4, n) => { // Data -- cgit v1.2.3