DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (2b8364cfdb04)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
#![warn(missing_docs)]

//! This crate converts a `Tm` struct to an `MsDosDateTime` and vice-versa
//!
//! MsDosDateTime is based on a FAT datetime and is a compact representation of a date.
//! It is currently mostly used in zip files.

extern crate time;
#[cfg(windows)] extern crate winapi;

use std::io;
use time::Tm;

/// Struct representing the date and time part of an MsDos datetime
#[derive(Copy, Clone, Debug)]
pub struct MsDosDateTime {
    /// Part representing the date
    pub datepart: u16,
    /// Part representing the time
    pub timepart: u16,
}

impl MsDosDateTime {
    /// Constructor of an MsDos datetime, from the raw representation
    pub fn new(time: u16, date: u16) -> MsDosDateTime {
        MsDosDateTime {
            datepart: date,
            timepart: time,
        }
    }
}

/// Trait to convert a time representation to and from a MsDosDateTime
pub trait TmMsDosExt : Sized {
    /// Convert a value to MsDosDateTime
    fn to_msdos(&self) -> Result<MsDosDateTime, io::Error>;
    /// Construct a value from an MsDosDateTime
    fn from_msdos(MsDosDateTime) -> Result<Self, io::Error>;
}

impl TmMsDosExt for Tm {
    fn to_msdos(&self) -> Result<MsDosDateTime, io::Error> {
        sys::tm_to_msdos(self)
    }

    fn from_msdos(ms: MsDosDateTime) -> Result<Self, io::Error> {
        sys::msdos_to_tm(ms)
    }
}

#[cfg(not(windows))]
mod sys {
    use super::MsDosDateTime;
    use time::{self, Tm};
    use std::io;

    pub fn msdos_to_tm(ms: MsDosDateTime) -> Result<Tm, io::Error> {
        let seconds = (ms.timepart & 0b0000000000011111) << 1;
        let minutes = (ms.timepart & 0b0000011111100000) >> 5;
        let hours =   (ms.timepart & 0b1111100000000000) >> 11;
        let days =    (ms.datepart & 0b0000000000011111) >> 0;
        let months =  (ms.datepart & 0b0000000111100000) >> 5;
        let years =   (ms.datepart & 0b1111111000000000) >> 9;

        // Month range: Dos [1..12], Tm [0..11]
        // Year zero: Dos 1980, Tm 1900

        let tm = Tm {
            tm_sec: seconds as i32,
            tm_min: minutes as i32,
            tm_hour: hours as i32,
            tm_mday: days as i32,
            tm_mon: months as i32 - 1,
            tm_year: years as i32 + 80,
            ..time::empty_tm()
        };

        // Re-parse the possibly incorrect timestamp to get a correct one.
        // This ensures every value will be in range
        // TODO: Check if this will only panic on Windows, or also other platforms
        Ok(time::at_utc(tm.to_timespec()))
    }

    pub fn tm_to_msdos(tm: &Tm) -> Result<MsDosDateTime, io::Error> {
        let timepart = ((tm.tm_sec >> 1) | (tm.tm_min << 5) | (tm.tm_hour << 11)) as u16;
        let datepart = (tm.tm_mday | ((tm.tm_mon + 1) << 5) | ((tm.tm_year - 80) << 9)) as u16;
        Ok(MsDosDateTime { datepart: datepart, timepart: timepart })
    }
}

#[cfg(windows)]
mod sys {
    use super::MsDosDateTime;
    use time::{self, Tm};
    use winapi::shared::minwindef::{WORD, FILETIME};
    use winapi::um::minwinbase::SYSTEMTIME;
    use winapi::um::timezoneapi::{FileTimeToSystemTime, SystemTimeToFileTime};
    use winapi::um::winbase::{DosDateTimeToFileTime, FileTimeToDosDateTime};
    use std::io;

    pub fn msdos_to_tm(ms: MsDosDateTime) -> Result<Tm, io::Error> {
        let datepart: WORD = ms.datepart;
        let timepart: WORD = ms.timepart;
        let mut filetime: FILETIME = unsafe { ::std::mem::zeroed() };
        let mut systemtime: SYSTEMTIME = unsafe { ::std::mem::zeroed() };

        if unsafe { DosDateTimeToFileTime(datepart, timepart, &mut filetime) } == 0 {
            return Err(io::Error::last_os_error());
        }

        if unsafe { FileTimeToSystemTime(&filetime, &mut systemtime) } == 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(Tm {
            tm_sec: systemtime.wSecond as i32,
            tm_min: systemtime.wMinute as i32,
            tm_hour: systemtime.wHour as i32,
            tm_mday: systemtime.wDay as i32,
            tm_wday: systemtime.wDayOfWeek as i32,
            tm_mon: (systemtime.wMonth - 1) as i32,
            tm_year: (systemtime.wYear - 1900) as i32,
            ..time::empty_tm()
        })
    }

    pub fn tm_to_msdos(tm: &Tm) -> Result<MsDosDateTime, io::Error> {
        let systemtime = SYSTEMTIME {
            wYear: (tm.tm_year + 1900) as WORD,
            wMonth: (tm.tm_mon + 1) as WORD,
            wDayOfWeek: tm.tm_wday as WORD,
            wDay: tm.tm_mday as WORD,
            wHour: tm.tm_hour as WORD,
            wMinute: tm.tm_min as WORD,
            wSecond: tm.tm_sec as WORD,
            wMilliseconds: 0,
        };
        let mut filetime = unsafe { ::std::mem::zeroed() };
        let mut datepart : WORD = 0;
        let mut timepart : WORD = 0;

        if unsafe { SystemTimeToFileTime(&systemtime, &mut filetime) } == 0 {
            return Err(io::Error::last_os_error());
        }

        if unsafe { FileTimeToDosDateTime(&filetime, &mut datepart, &mut timepart) } == 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(MsDosDateTime { datepart: datepart, timepart: timepart })
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use time::{self, Tm};

    fn check_date(input: Tm, day: i32, month: i32, year: i32) {
        assert_eq!(input.tm_mday, day);
        assert_eq!(input.tm_mon + 1, month);
        assert_eq!(input.tm_year + 1900, year);
    }

    fn check_time(input: Tm, hour: i32, minute: i32, second: i32) {
        assert_eq!(input.tm_hour, hour);
        assert_eq!(input.tm_min, minute);
        assert_eq!(input.tm_sec, second);
    }

    #[test]
    fn dos_zero() {
        // The 0 date is not a correct msdos date
        // We assert here that it does not panic. What it will return is undefined.
        let _ = Tm::from_msdos(MsDosDateTime::new(0, 0));
    }

    #[test]
    fn dos_smallest() {
        // This is the actual smallest date possible
        let tm = Tm::from_msdos(MsDosDateTime::new(0, 0b100001)).unwrap();
        check_date(tm, 1, 1, 1980);
        check_time(tm, 0, 0, 0);
    }

    #[test]
    fn dos_today() {
        let tm = Tm::from_msdos(MsDosDateTime::new(0b01001_100000_10101, 0b0100011_0110_11110)).unwrap();
        check_date(tm, 30, 6, 2015);
        check_time(tm, 9, 32, 42);
    }

    #[test]
    fn zero_dos() {
        let tm = Tm {
            tm_year: 80,
            tm_mon: 0,
            tm_mday: 1,
            tm_hour: 0,
            tm_min: 0,
            tm_sec: 0,
            ..time::empty_tm()
        };
        let ms = tm.to_msdos().unwrap();
        assert_eq!(ms.datepart, 0b100001);
        assert_eq!(ms.timepart, 0);
    }

    #[test]
    fn today_dos() {
        let tm = Tm {
            tm_year: 115,
            tm_mon: 5,
            tm_mday: 30,
            tm_hour: 9,
            tm_min: 32,
            tm_sec: 42,
            ..time::empty_tm()
        };
        let ms = tm.to_msdos().unwrap();
        assert_eq!(ms.datepart, 0b0100011_0110_11110);
        assert_eq!(ms.timepart, 0b01001_100000_10101);
    }
}