pub trait TimeSource {
fn unix_secs(&self) -> u64;
}
#[derive(Debug, Clone, Copy)]
pub struct StaticTimeSource(pub u64);
impl TimeSource for StaticTimeSource {
fn unix_secs(&self) -> u64 {
self.0
}
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, Copy, Default)]
pub struct StdTimeSource;
#[cfg(feature = "std")]
impl TimeSource for StdTimeSource {
fn unix_secs(&self) -> u64 {
crate::noxtls_unix_timestamp_secs()
}
}
#[must_use]
pub fn noxtls_format_unix_secs_as_generalized_time(unix_secs: u64) -> crate::GeneralizedTimeString {
const SECS_PER_MIN: u64 = 60;
const SECS_PER_HOUR: u64 = 3600;
const SECS_PER_DAY: u64 = 86400;
let days = unix_secs / SECS_PER_DAY;
let rem = unix_secs % SECS_PER_DAY;
let hour = rem / SECS_PER_HOUR;
let rem = rem % SECS_PER_HOUR;
let min = rem / SECS_PER_MIN;
let sec = rem % SECS_PER_MIN;
let mut year = 1970_u32;
let mut day_of_year = days as u32;
loop {
let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
let year_days = if leap { 366 } else { 365 };
if day_of_year < year_days {
break;
}
day_of_year -= year_days;
year += 1;
}
let month_days = if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
} else {
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
};
let mut month = 1_u32;
let mut day = day_of_year + 1;
for md in month_days {
if day <= md {
break;
}
day -= md;
month += 1;
}
#[cfg(feature = "std")]
{
std::format!("{year:04}{month:02}{day:02}{hour:02}{min:02}{sec:02}Z")
}
#[cfg(not(feature = "std"))]
{
#[cfg(feature = "alloc")]
return alloc::format!("{year:04}{month:02}{day:02}{hour:02}{min:02}{sec:02}Z");
#[cfg(not(feature = "alloc"))]
{
let mut out = [b'0'; 15];
write_4_digits(&mut out[0..4], year);
write_2_digits(&mut out[4..6], month);
write_2_digits(&mut out[6..8], day);
write_2_digits(&mut out[8..10], hour as u32);
write_2_digits(&mut out[10..12], min as u32);
write_2_digits(&mut out[12..14], sec as u32);
out[14] = b'Z';
crate::GeneralizedTimeString::from_bytes(out)
}
}
}
#[cfg(not(any(feature = "std", feature = "alloc")))]
fn write_2_digits(dst: &mut [u8], value: u32) {
dst[0] = b'0' + ((value / 10) % 10) as u8;
dst[1] = b'0' + (value % 10) as u8;
}
#[cfg(not(any(feature = "std", feature = "alloc")))]
fn write_4_digits(dst: &mut [u8], value: u32) {
dst[0] = b'0' + ((value / 1000) % 10) as u8;
dst[1] = b'0' + ((value / 100) % 10) as u8;
dst[2] = b'0' + ((value / 10) % 10) as u8;
dst[3] = b'0' + (value % 10) as u8;
}
#[cfg(test)]
mod tests {
use super::noxtls_format_unix_secs_as_generalized_time;
#[test]
fn formats_unix_epoch_as_generalized_time() {
let formatted = noxtls_format_unix_secs_as_generalized_time(0);
assert_eq!(formatted.to_string(), "19700101000000Z");
}
}