use bitflags::bitflags;
use core::fmt::{self, Display, Formatter};
#[derive(Debug, Default, Copy, Clone, Eq)]
#[repr(C)]
pub struct Time {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub pad1: u8,
pub nanosecond: u32,
pub time_zone: i16,
pub daylight: Daylight,
pub pad2: u8,
}
impl Time {
pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
#[must_use]
pub const fn invalid() -> Self {
Self {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
pad1: 0,
nanosecond: 0,
time_zone: 0,
daylight: Daylight::empty(),
pad2: 0,
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
(1900..=9999).contains(&self.year)
&& (1..=12).contains(&self.month)
&& (1..=31).contains(&self.day)
&& self.hour <= 23
&& self.minute <= 59
&& self.second <= 59
&& self.nanosecond <= 999_999_999
&& ((-1440..=1440).contains(&self.time_zone)
|| self.time_zone == Self::UNSPECIFIED_TIMEZONE)
}
}
impl Display for Time {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02} ", self.year, self.month, self.day)?;
write!(
f,
"{:02}:{:02}:{:02}.{:09}",
self.hour, self.minute, self.second, self.nanosecond
)?;
if self.time_zone == Self::UNSPECIFIED_TIMEZONE {
write!(f, " (local)")?;
} else {
let offset_in_hours = self.time_zone as f32 / 60.0;
let integer_part = offset_in_hours as i16;
let fraction_part = offset_in_hours - (integer_part as f32);
if fraction_part == 0.0 {
write!(f, "UTC+{offset_in_hours}")?;
}
else {
write!(f, "UTC+{offset_in_hours:.1}")?;
}
}
Ok(())
}
}
impl PartialEq for Time {
fn eq(&self, other: &Self) -> bool {
self.year == other.year
&& self.month == other.month
&& self.day == other.day
&& self.hour == other.hour
&& self.minute == other.minute
&& self.second == other.second
&& self.nanosecond == other.nanosecond
&& self.time_zone == other.time_zone
&& self.daylight == other.daylight
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Daylight: u8 {
const ADJUST_DAYLIGHT = 0x01;
const IN_DAYLIGHT = 0x02;
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::string::ToString;
#[test]
fn test_time_display() {
let mut time = Time {
year: 2023,
month: 5,
day: 18,
hour: 11,
minute: 29,
second: 57,
nanosecond: 123_456_789,
time_zone: Time::UNSPECIFIED_TIMEZONE,
daylight: Daylight::empty(),
pad1: 0,
pad2: 0,
};
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789 (local)");
time.time_zone = 120;
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2");
time.time_zone = 150;
assert_eq!(time.to_string(), "2023-05-18 11:29:57.123456789UTC+2.5");
}
}