use crate::{SecondsSinceUnixEpoch, Time};
impl std::fmt::Display for Time {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = Vec::with_capacity(Time::MAX.size());
self.write_to(&mut buf).expect("write to memory cannot fail");
let raw = std::str::from_utf8(&buf).expect("time serializes as valid UTF-8");
f.write_str(raw)
}
}
impl Time {
pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
const SECONDS_PER_HOUR: u32 = 60 * 60;
let offset = self.offset.unsigned_abs();
let hours = offset / SECONDS_PER_HOUR;
let minutes = (offset - (hours * SECONDS_PER_HOUR)) / 60;
if hours > 99 {
return Err(std::io::Error::other("Cannot represent offsets larger than +-9900"));
}
let mut itoa = itoa::Buffer::new();
out.write_all(itoa.format(self.seconds).as_bytes())?;
out.write_all(b" ")?;
out.write_all(if self.offset < 0 { b"-" } else { b"+" })?;
const ZERO: &[u8; 1] = b"0";
if hours < 10 {
out.write_all(ZERO)?;
}
out.write_all(itoa.format(hours).as_bytes())?;
if minutes < 10 {
out.write_all(ZERO)?;
}
out.write_all(itoa.format(minutes).as_bytes()).map(|_| ())
}
pub const fn size(&self) -> usize {
let is_negative = self.seconds < 0;
Self::count_positive_digits(self.seconds.unsigned_abs()) + is_negative as usize + 6
}
const fn count_positive_digits(n: u64) -> usize {
const POW10: [u64; 20] = [
1,
10,
100,
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000,
10_000_000_000,
100_000_000_000,
1_000_000_000_000,
10_000_000_000_000,
100_000_000_000_000,
1_000_000_000_000_000,
10_000_000_000_000_000,
100_000_000_000_000_000,
1_000_000_000_000_000_000,
10_000_000_000_000_000_000,
];
let mut digits = 1;
while digits < 20 && n >= POW10[digits] {
digits += 1;
}
digits
}
pub const MAX: Time = Time {
seconds: SecondsSinceUnixEpoch::MAX,
offset: 99 * 60 * 60 + 59 * 60 + 59,
};
}