use crate::err::Result;
use byteorder::{LittleEndian, ReadBytesExt}; use chrono::{DateTime, Duration, NaiveDate, Utc};
use std::fmt;
use std::fmt::{Debug, Display};
use std::io::{Cursor, Read};
#[derive(Clone)]
pub struct WinTimestamp(u64);
impl WinTimestamp {
pub fn new(buffer: &[u8]) -> Result<Self> {
Self::from_reader(&mut Cursor::new(buffer))
}
#[inline]
pub fn from_reader<R: Read>(reader: &mut R) -> Result<WinTimestamp> {
let win_timestamp = WinTimestamp(reader.read_u64::<LittleEndian>()?);
Ok(win_timestamp)
}
pub fn to_datetime(&self) -> DateTime<Utc> {
let nanos_since_windows_epoch = self.0;
DateTime::from_utc(
NaiveDate::from_ymd_opt(1601, 1, 1).and_then(|x| x.and_hms_nano_opt(0, 0, 0, 0)).expect("to_datetime() should work") + Duration::microseconds((nanos_since_windows_epoch / 10) as i64),
Utc,
)
}
}
impl Display for WinTimestamp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_datetime())
}
}
impl Debug for WinTimestamp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_datetime())
}
}
#[derive(Clone)]
pub struct DosDate(u16);
impl DosDate {
pub fn new(date: u16) -> Self {
DosDate(date)
}
pub fn from_reader<R: Read>(buffer: &mut R) -> Result<DosDate> {
Ok(DosDate::new(buffer.read_u16::<LittleEndian>()?))
}
#[inline]
pub fn to_date(&self) -> chrono::NaiveDate {
let mut day = self.0 & 0x1F;
if day == 0 {
day = 1
}
let mut month = (self.0 >> 5) & 0x0F;
if month == 0 {
month = 1
}
let year = (self.0 >> 9) + 1980;
chrono::NaiveDate::from_ymd_opt(i32::from(year), u32::from(month), u32::from(day)).expect("to_date() should work")
}
pub fn to_date_formatted(&self, format: &str) -> String {
self.to_date().format(format).to_string()
}
}
impl Display for DosDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_date())
}
}
impl Debug for DosDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_date())
}
}
#[derive(Clone)]
pub struct DosTime(u16);
impl DosTime {
pub fn new(value: u16) -> Self {
DosTime(value)
}
pub fn from_reader<R: Read>(buffer: &mut R) -> Result<DosTime> {
Ok(DosTime::new(buffer.read_u16::<LittleEndian>()?))
}
pub fn to_time(&self) -> chrono::NaiveTime {
let sec = (self.0 & 0x1F) * 2;
let min = (self.0 >> 5) & 0x3F;
let hour = (self.0 >> 11) & 0x1F;
chrono::NaiveTime::from_hms_opt(u32::from(hour), u32::from(min), u32::from(sec)).expect("to_time() should work")
}
}
impl Display for DosTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_time())
}
}
impl Debug for DosTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_time())
}
}
#[derive(Clone)]
pub struct DosDateTime {
date: u16,
time: u16,
}
impl DosDateTime {
pub fn new(date: u16, time: u16) -> Self {
DosDateTime { date, time }
}
pub fn from_reader<R: Read>(buffer: &mut R) -> Result<DosDateTime> {
let date = buffer.read_u16::<LittleEndian>()?;
let time = buffer.read_u16::<LittleEndian>()?;
Ok(DosDateTime::new(date, time))
}
pub fn to_datetime(&self) -> chrono::NaiveDateTime {
chrono::NaiveDateTime::new(DosDate(self.date).to_date(), DosTime(self.time).to_time())
}
}
impl From<u32> for DosDateTime {
fn from(datetime: u32) -> Self {
let date = (datetime & 0xffff) as u16;
let time = (datetime >> 16) as u16;
DosDateTime::new(date, time)
}
}
impl Display for DosDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_datetime())
}
}
impl Debug for DosDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_datetime())
}
}
#[cfg(test)]
mod tests {
use crate::timestamp::{DosDate, DosDateTime, DosTime, WinTimestamp};
use std::io::Cursor;
#[test]
fn test_win_timestamp() {
let raw_timestamp: &[u8] = &[0x53, 0xC7, 0x8B, 0x18, 0xC5, 0xCC, 0xCE, 0x01];
let timestamp = WinTimestamp::from_reader(&mut Cursor::new(raw_timestamp)).unwrap();
assert_eq!(format!("{}", timestamp), "2013-10-19 12:16:53.276040 UTC");
assert_eq!(format!("{:?}", timestamp), "2013-10-19 12:16:53.276040 UTC");
}
#[test]
fn test_dosdate() {
let dos_date = DosDate(16492);
assert_eq!(format!("{:?}", dos_date), "2012-03-12");
}
#[test]
fn test_dosdate_zeros() {
let raw_date: &[u8] = &[0x00, 0x00];
let date = DosDate::from_reader(&mut Cursor::new(raw_date)).unwrap();
assert_eq!(format!("{}", date), "1980-01-01");
assert_eq!(format!("{:?}", date), "1980-01-01");
}
#[test]
fn test_dostime() {
let dos_time = DosTime(43874);
assert_eq!(format!("{:?}", dos_time), "21:27:04");
}
#[test]
fn test_dostime_zeros() {
let raw_time: &[u8] = &[0x00, 0x00];
let time = DosTime::from_reader(&mut Cursor::new(raw_time)).unwrap();
assert_eq!(format!("{}", time), "00:00:00");
assert_eq!(format!("{:?}", time), "00:00:00");
}
#[test]
fn test_dosdatetime() {
let dos_time = DosDateTime::from(2_875_342_956);
assert_eq!(format!("{:?}", dos_time), "2012-03-12 21:27:04");
}
}