#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FatTimestamp {
pub unix_seconds: i64,
pub subsec_nanos: u32,
}
pub fn decode(date: u16, time: u16, tenths: u8) -> Option<FatTimestamp> {
if date == 0 {
return None;
}
let day = i64::from(date & 0x1F);
let month = i64::from((date >> 5) & 0x0F);
let year = 1980 + i64::from((date >> 9) & 0x7F);
let two_sec = i64::from(time & 0x1F);
let minute = i64::from((time >> 5) & 0x3F);
let hour = i64::from((time >> 11) & 0x1F);
let days = days_from_civil(year, month.clamp(1, 12), day.clamp(1, 31));
let base = days * 86_400 + hour.min(23) * 3600 + minute.min(59) * 60 + two_sec * 2;
let tenths = i64::from(tenths);
let unix_seconds = base + tenths / 100;
let subsec_nanos = u32::try_from((tenths % 100) * 10_000_000).unwrap_or(0);
Some(FatTimestamp {
unix_seconds,
subsec_nanos,
})
}
fn days_from_civil(y: i64, m: i64, d: i64) -> i64 {
let y = if m <= 2 { y - 1 } else { y };
let era = if y >= 0 { y } else { y - 399 } / 400;
let yoe = y - era * 400;
let doy = (153 * (if m > 2 { m - 3 } else { m + 9 }) + 2) / 5 + d - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146_097 + doe - 719_468
}
#[cfg(test)]
mod tests {
use super::{decode, FatTimestamp};
fn mk_date(year: u16, month: u16, day: u16) -> u16 {
((year - 1980) << 9) | (month << 5) | day
}
fn mk_time(hour: u16, minute: u16, second: u16) -> u16 {
(hour << 11) | (minute << 5) | (second / 2)
}
#[test]
fn decodes_a_known_datetime() {
let ts = decode(mk_date(2021, 6, 15), mk_time(13, 37, 20), 0).unwrap();
assert_eq!(ts.unix_seconds, 1_623_764_240);
assert_eq!(ts.subsec_nanos, 0);
}
#[test]
fn tenths_add_sub_second_and_carry() {
let ts = decode(mk_date(2021, 6, 15), mk_time(13, 37, 20), 150).unwrap();
assert_eq!(ts.unix_seconds, 1_623_764_241);
assert_eq!(ts.subsec_nanos, 500_000_000);
}
#[test]
fn zero_date_is_unset() {
assert!(decode(0, 0, 0).is_none());
}
#[test]
fn epoch_1980_is_positive() {
let ts = decode(mk_date(1980, 1, 1), 0, 0).unwrap();
assert_eq!(ts.unix_seconds, 315_532_800); }
#[test]
fn typed_fields_accessible() {
let _ = FatTimestamp {
unix_seconds: 0,
subsec_nanos: 0,
};
}
}