#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FatDateTime {
pub date: u16,
pub time: u16,
pub time_tenth: u8,
}
impl FatDateTime {
pub const EPOCH: Self = Self {
date: (1u16 << 5) | 1,
time: 0,
time_tenth: 0,
};
pub fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Self {
let year_offset = year.saturating_sub(1980).min(127);
let date = (year_offset << 9) | ((month as u16 & 0x0F) << 5) | (day as u16 & 0x1F);
let time = ((hour as u16 & 0x1F) << 11)
| ((minute as u16 & 0x3F) << 5)
| ((second as u16 / 2) & 0x1F);
Self {
date,
time,
time_tenth: 0,
}
}
pub fn to_raw(&self) -> (u16, u16, u8) {
(self.date, self.time, self.time_tenth)
}
pub fn from_raw(date: u16, time: u16, time_tenth: u8) -> Self {
Self {
date,
time,
time_tenth,
}
}
#[cfg(feature = "std")]
pub fn now() -> Self {
ChronoTimeProvider.now()
}
#[cfg(not(feature = "std"))]
pub fn now() -> Self {
Self::EPOCH
}
}
impl Default for FatDateTime {
fn default() -> Self {
Self::now()
}
}
pub trait TimeProvider: core::fmt::Debug {
fn now(&self) -> FatDateTime;
}
#[cfg(feature = "std")]
#[derive(Debug, Default, Clone, Copy)]
pub struct ChronoTimeProvider;
#[cfg(feature = "std")]
impl TimeProvider for ChronoTimeProvider {
fn now(&self) -> FatDateTime {
use chrono::{Datelike, Local, Timelike};
let now = Local::now();
let mut dt = FatDateTime::new(
now.year() as u16,
now.month() as u8,
now.day() as u8,
now.hour() as u8,
now.minute() as u8,
now.second() as u8,
);
let millis = now.timestamp_subsec_millis();
dt.time_tenth = ((now.second() % 2) * 100 + millis / 10).min(199) as u8;
dt
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct EpochTimeProvider;
impl TimeProvider for EpochTimeProvider {
fn now(&self) -> FatDateTime {
FatDateTime::EPOCH
}
}
#[cfg(feature = "std")]
pub static DEFAULT_TIME_PROVIDER: ChronoTimeProvider = ChronoTimeProvider;
#[cfg(not(feature = "std"))]
pub static DEFAULT_TIME_PROVIDER: EpochTimeProvider = EpochTimeProvider;
#[derive(Debug, Clone, Copy)]
pub struct StaticTimeProvider(pub FatDateTime);
impl TimeProvider for StaticTimeProvider {
fn now(&self) -> FatDateTime {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn epoch_round_trips() {
let raw = FatDateTime::EPOCH.to_raw();
let back = FatDateTime::from_raw(raw.0, raw.1, raw.2);
assert_eq!(back, FatDateTime::EPOCH);
}
#[test]
fn epoch_provider_returns_epoch() {
assert_eq!(EpochTimeProvider.now(), FatDateTime::EPOCH);
}
#[test]
fn static_provider_returns_constructed_value() {
let dt = FatDateTime::new(2030, 6, 15, 12, 0, 0);
assert_eq!(StaticTimeProvider(dt).now(), dt);
}
#[test]
fn new_clamps_years_outside_fat_range() {
let pre = FatDateTime::new(1979, 1, 1, 0, 0, 0);
assert_eq!(pre.date >> 9, 0); let post = FatDateTime::new(2200, 1, 1, 0, 0, 0);
assert_eq!(post.date >> 9, 127); }
#[cfg(feature = "std")]
#[test]
fn chrono_provider_produces_year_in_fat_range() {
let dt = ChronoTimeProvider.now();
let year_offset = dt.date >> 9;
assert!(year_offset > 0 && year_offset <= 127);
}
}