Add nanosecond and time_offset fields in struct DateTime.
This commit is contained in:
parent
836244fde3
commit
1ad83d8a82
|
@ -49,6 +49,7 @@
|
|||
//!
|
||||
//! * Enum Error has two new variants: TooBig and NotSupported.
|
||||
//! * Value::Undefined has the 2nd member to keep the offset of the value.
|
||||
//! * Struct DateTime has two new fields: nanosecond and offset.
|
||||
|
||||
pub use error::Error;
|
||||
pub use jpeg::get_exif_attr as get_exif_attr_from_jpeg;
|
||||
|
|
91
src/tiff.rs
91
src/tiff.rs
|
@ -32,7 +32,7 @@ use tag;
|
|||
use tag_priv::{Context, Tag};
|
||||
use value::Value;
|
||||
use value::get_type_info;
|
||||
use util::atou16;
|
||||
use util::{atou16, ctou32};
|
||||
|
||||
// TIFF header magic numbers [EXIF23 4.5.2].
|
||||
const TIFF_BE: u16 = 0x4d4d;
|
||||
|
@ -177,6 +177,11 @@ pub struct DateTime {
|
|||
pub hour: u8,
|
||||
pub minute: u8,
|
||||
pub second: u8,
|
||||
/// The subsecond data in nanoseconds. If the Exif attribute has
|
||||
/// more sigfinicant digits, they are rounded down.
|
||||
pub nanosecond: Option<u32>,
|
||||
/// The offset of the time zone in minutes.
|
||||
pub offset: Option<i16>,
|
||||
}
|
||||
|
||||
impl DateTime {
|
||||
|
@ -198,8 +203,55 @@ impl DateTime {
|
|||
hour: try!(atou16(&data[11..13])) as u8,
|
||||
minute: try!(atou16(&data[14..16])) as u8,
|
||||
second: try!(atou16(&data[17..19])) as u8,
|
||||
nanosecond: None,
|
||||
offset: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an SubsecTime-like field.
|
||||
pub fn parse_subsec(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
let mut subsec = 0;
|
||||
let mut ndigits = 0;
|
||||
for &c in data {
|
||||
if c == b' ' {
|
||||
break;
|
||||
}
|
||||
subsec = subsec * 10 + try!(ctou32(c));
|
||||
ndigits += 1;
|
||||
if ndigits >= 9 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ndigits == 0 {
|
||||
self.nanosecond = None;
|
||||
} else {
|
||||
for _ in ndigits..9 {
|
||||
subsec *= 10;
|
||||
}
|
||||
self.nanosecond = Some(subsec);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses an OffsetTime-like field.
|
||||
pub fn parse_offset(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
if data == b" : " || data == b" " {
|
||||
return Err(Error::BlankValue("OffsetTime is blank"));
|
||||
} else if data.len() < 6 {
|
||||
return Err(Error::InvalidFormat("OffsetTime too short"));
|
||||
} else if data[3] != b':' {
|
||||
return Err(Error::InvalidFormat("Invalid OffsetTime delimiter"));
|
||||
}
|
||||
let hour = try!(atou16(&data[1..3]));
|
||||
let min = try!(atou16(&data[4..6]));
|
||||
let offset = (hour * 60 + min) as i16;
|
||||
self.offset = Some(match data[0] {
|
||||
b'+' => offset,
|
||||
b'-' => -offset,
|
||||
_ => return Err(Error::InvalidFormat("Invalid OffsetTime sign")),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DateTime {
|
||||
|
@ -232,4 +284,41 @@ mod tests {
|
|||
assert_eq!(v.len(), 1);
|
||||
assert_pat!(v[0].value, Value::Unknown(0xffff, 1, 0x12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_time() {
|
||||
let mut dt = DateTime::from_ascii(b"2016:05:04 03:02:01").unwrap();
|
||||
assert_eq!(dt.year, 2016);
|
||||
assert_eq!(format!("{}", dt), "2016-05-04 03:02:01");
|
||||
|
||||
dt.parse_subsec(b"987").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 987000000);
|
||||
dt.parse_subsec(b"000987").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 987000);
|
||||
dt.parse_subsec(b"987654321").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 987654321);
|
||||
dt.parse_subsec(b"9876543219").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 987654321);
|
||||
dt.parse_subsec(b"130 ").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 130000000);
|
||||
dt.parse_subsec(b"0").unwrap();
|
||||
assert_eq!(dt.nanosecond.unwrap(), 0);
|
||||
dt.parse_subsec(b"").unwrap();
|
||||
assert!(dt.nanosecond.is_none());
|
||||
dt.parse_subsec(b" ").unwrap();
|
||||
assert!(dt.nanosecond.is_none());
|
||||
|
||||
dt.parse_offset(b"+00:00").unwrap();
|
||||
assert_eq!(dt.offset.unwrap(), 0);
|
||||
dt.parse_offset(b"+01:23").unwrap();
|
||||
assert_eq!(dt.offset.unwrap(), 83);
|
||||
dt.parse_offset(b"+99:99").unwrap();
|
||||
assert_eq!(dt.offset.unwrap(), 6039);
|
||||
dt.parse_offset(b"-01:23").unwrap();
|
||||
assert_eq!(dt.offset.unwrap(), -83);
|
||||
dt.parse_offset(b"-99:99").unwrap();
|
||||
assert_eq!(dt.offset.unwrap(), -6039);
|
||||
assert_err_pat!(dt.parse_offset(b" : "), Error::BlankValue(_));
|
||||
assert_err_pat!(dt.parse_offset(b" "), Error::BlankValue(_));
|
||||
}
|
||||
}
|
||||
|
|
12
src/util.rs
12
src/util.rs
|
@ -28,6 +28,8 @@ use std::io;
|
|||
|
||||
use error::Error;
|
||||
|
||||
const ASCII_0: u8 = 0x30;
|
||||
const ASCII_9: u8 = 0x39;
|
||||
const ASCII_A: u8 = 0x41;
|
||||
const ASCII_Z: u8 = 0x5a;
|
||||
|
||||
|
@ -44,9 +46,6 @@ pub fn read16<R>(reader: &mut R) -> Result<u16, io::Error> where R: io::Read {
|
|||
|
||||
// This function must not be called with more than 4 bytes.
|
||||
pub fn atou16(bytes: &[u8]) -> Result<u16, Error> {
|
||||
const ASCII_0: u8 = 0x30;
|
||||
const ASCII_9: u8 = 0x39;
|
||||
|
||||
if cfg!(debug_assertions) && bytes.len() >= 5 {
|
||||
panic!("atou16 accepts up to 4 bytes");
|
||||
}
|
||||
|
@ -63,6 +62,13 @@ pub fn atou16(bytes: &[u8]) -> Result<u16, Error> {
|
|||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn ctou32(c: u8) -> Result<u32, Error> {
|
||||
if c < ASCII_0 || ASCII_9 < c {
|
||||
return Err(Error::InvalidFormat("Not a number"));
|
||||
}
|
||||
Ok((c - ASCII_0) as u32)
|
||||
}
|
||||
|
||||
pub fn isupper(c: u8) -> bool {
|
||||
ASCII_A <= c && c <= ASCII_Z
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue