1use chrono::offset::TimeZone;
6use chrono::Local;
7use fatfs::{Date, DateTime, FatfsError, Time};
8use std::io::{self, ErrorKind};
9use zx::Status;
10
11pub fn dos_to_unix_time(dos_time: DateTime) -> u64 {
13 let datetime = chrono::DateTime::<Local>::from(dos_time);
14 datetime.timestamp_nanos_opt().unwrap() as u64
15}
16
17pub fn dos_date_to_unix_time(dos_date: Date) -> u64 {
19 let datetime = chrono::DateTime::<Local>::from(DateTime {
20 date: dos_date,
21 time: Time { hour: 0, min: 0, sec: 0, millis: 0 },
22 });
23 datetime.timestamp_nanos_opt().unwrap() as u64
24}
25
26pub fn unix_to_dos_time(timestamp: u64) -> DateTime {
28 DateTime::from(Local.timestamp_nanos(timestamp as i64))
29}
30
31pub fn fatfs_error_to_status(error: io::Error) -> Status {
32 match error.kind() {
33 ErrorKind::AddrInUse => Status::ADDRESS_IN_USE,
34 ErrorKind::AddrNotAvailable => Status::UNAVAILABLE,
35 ErrorKind::AlreadyExists => Status::ALREADY_EXISTS,
36 ErrorKind::BrokenPipe => Status::PEER_CLOSED,
37 ErrorKind::ConnectionAborted => Status::CONNECTION_ABORTED,
38 ErrorKind::ConnectionRefused => Status::CONNECTION_REFUSED,
39 ErrorKind::ConnectionReset => Status::CONNECTION_RESET,
40 ErrorKind::Interrupted => Status::INTERRUPTED_RETRY,
41 ErrorKind::InvalidData => Status::IO_INVALID,
42 ErrorKind::InvalidInput => Status::INVALID_ARGS,
43 ErrorKind::NotConnected => Status::NOT_CONNECTED,
44 ErrorKind::NotFound => Status::NOT_FOUND,
45 ErrorKind::PermissionDenied => Status::ACCESS_DENIED,
46 ErrorKind::TimedOut => Status::TIMED_OUT,
47 ErrorKind::UnexpectedEof => Status::BUFFER_TOO_SMALL,
48 ErrorKind::WouldBlock => Status::BAD_STATE,
49 ErrorKind::WriteZero => Status::NO_SPACE,
50 ErrorKind::Other => {
51 error
52 .into_inner()
53 .and_then(|b| b.downcast::<FatfsError>().ok())
54 .map(|b| {
55 match *b {
56 FatfsError::UnknownVersion => Status::NOT_SUPPORTED,
58 FatfsError::InvalidBootSectorSig => Status::NOT_SUPPORTED,
59 FatfsError::VolumeTooSmall => Status::NO_SPACE,
60 FatfsError::IsDirectory => Status::NOT_FILE,
62 FatfsError::NotDirectory => Status::NOT_DIR,
63 FatfsError::DirectoryNotEmpty => Status::NOT_EMPTY,
64 FatfsError::FileNameTooLong => Status::BAD_PATH,
65 FatfsError::FileNameEmpty => Status::BAD_PATH,
66 FatfsError::FileNameBadCharacter => Status::BAD_PATH,
67 FatfsError::TooManySectors => Status::OUT_OF_RANGE,
69 FatfsError::NoSpace => Status::NO_SPACE,
71 _ => Status::INVALID_ARGS,
75 }
76 })
77 .unwrap_or(Status::IO)
78 }
79 _ => Status::IO,
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use fatfs::{Date, Time};
87
88 fn dos_datetime(
89 year: u16,
90 month: u16,
91 day: u16,
92 hour: u16,
93 min: u16,
94 sec: u16,
95 millis: u16,
96 ) -> DateTime {
97 DateTime { date: Date { year, month, day }, time: Time { hour, min, sec, millis } }
98 }
99
100 const NS_PER_MS: u64 = 1_000_000;
101
102 fn ms_to_ns(ts: u64) -> u64 {
103 ts * NS_PER_MS
104 }
105
106 #[fuchsia::test]
107 fn test_dos_to_unix_time() {
108 let earliest_time = dos_datetime(1980, 1, 1, 0, 0, 0, 0);
109 assert_eq!(dos_to_unix_time(earliest_time), ms_to_ns(315532800000));
110
111 let latest_time = dos_datetime(2107, 12, 31, 23, 59, 59, 999);
112 assert_eq!(dos_to_unix_time(latest_time), ms_to_ns(4354819199999));
113
114 let time = dos_datetime(2038, 1, 19, 3, 14, 7, 0);
115 assert_eq!(dos_to_unix_time(time), ms_to_ns(2147483647000));
116 }
117
118 #[fuchsia::test]
119 fn test_unix_to_dos_time() {
120 let earliest_time = dos_datetime(1980, 1, 1, 0, 0, 0, 0);
121 assert_eq!(earliest_time, unix_to_dos_time(ms_to_ns(315532800000)));
122
123 let latest_time = dos_datetime(2107, 12, 31, 23, 59, 59, 999);
124 assert_eq!(latest_time, unix_to_dos_time(ms_to_ns(4354819199999)));
125
126 let time = dos_datetime(2038, 1, 19, 3, 14, 7, 0);
127 assert_eq!(time, unix_to_dos_time(ms_to_ns(2147483647000)));
128 }
129}