1pub trait TimeSource {
10 fn unix_secs(&self) -> u64;
12}
13
14#[derive(Debug, Clone, Copy)]
16pub struct StaticTimeSource(pub u64);
17
18impl TimeSource for StaticTimeSource {
19 fn unix_secs(&self) -> u64 {
20 self.0
21 }
22}
23
24#[cfg(feature = "std")]
26#[derive(Debug, Clone, Copy, Default)]
27pub struct StdTimeSource;
28
29#[cfg(feature = "std")]
30impl TimeSource for StdTimeSource {
31 fn unix_secs(&self) -> u64 {
32 crate::noxtls_unix_timestamp_secs()
33 }
34}
35
36#[must_use]
38pub fn noxtls_format_unix_secs_as_generalized_time(unix_secs: u64) -> crate::GeneralizedTimeString {
39 const SECS_PER_MIN: u64 = 60;
41 const SECS_PER_HOUR: u64 = 3600;
42 const SECS_PER_DAY: u64 = 86400;
43
44 let days = unix_secs / SECS_PER_DAY;
45 let rem = unix_secs % SECS_PER_DAY;
46 let hour = rem / SECS_PER_HOUR;
47 let rem = rem % SECS_PER_HOUR;
48 let min = rem / SECS_PER_MIN;
49 let sec = rem % SECS_PER_MIN;
50
51 let mut year = 1970_u32;
52 let mut day_of_year = days as u32;
53 loop {
54 let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
55 let year_days = if leap { 366 } else { 365 };
56 if day_of_year < year_days {
57 break;
58 }
59 day_of_year -= year_days;
60 year += 1;
61 }
62 let month_days = if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
63 [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
64 } else {
65 [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
66 };
67 let mut month = 1_u32;
68 let mut day = day_of_year + 1;
69 for md in month_days {
70 if day <= md {
71 break;
72 }
73 day -= md;
74 month += 1;
75 }
76
77 #[cfg(feature = "std")]
78 {
79 std::format!("{year:04}{month:02}{day:02}{hour:02}{min:02}{sec:02}Z")
80 }
81 #[cfg(not(feature = "std"))]
82 {
83 #[cfg(feature = "alloc")]
84 return alloc::format!("{year:04}{month:02}{day:02}{hour:02}{min:02}{sec:02}Z");
85
86 #[cfg(not(feature = "alloc"))]
87 {
88 let mut out = [b'0'; 15];
89 write_4_digits(&mut out[0..4], year);
90 write_2_digits(&mut out[4..6], month);
91 write_2_digits(&mut out[6..8], day);
92 write_2_digits(&mut out[8..10], hour as u32);
93 write_2_digits(&mut out[10..12], min as u32);
94 write_2_digits(&mut out[12..14], sec as u32);
95 out[14] = b'Z';
96 crate::GeneralizedTimeString::from_bytes(out)
97 }
98 }
99}
100
101#[cfg(not(any(feature = "std", feature = "alloc")))]
102fn write_2_digits(dst: &mut [u8], value: u32) {
103 dst[0] = b'0' + ((value / 10) % 10) as u8;
104 dst[1] = b'0' + (value % 10) as u8;
105}
106
107#[cfg(not(any(feature = "std", feature = "alloc")))]
108fn write_4_digits(dst: &mut [u8], value: u32) {
109 dst[0] = b'0' + ((value / 1000) % 10) as u8;
110 dst[1] = b'0' + ((value / 100) % 10) as u8;
111 dst[2] = b'0' + ((value / 10) % 10) as u8;
112 dst[3] = b'0' + (value % 10) as u8;
113}
114
115#[cfg(test)]
116mod tests {
117 use super::noxtls_format_unix_secs_as_generalized_time;
118
119 #[test]
120 fn formats_unix_epoch_as_generalized_time() {
121 let formatted = noxtls_format_unix_secs_as_generalized_time(0);
122 assert_eq!(formatted.to_string(), "19700101000000Z");
123 }
124}