1pub fn utc_rfc3339_now() -> String {
5 let duration = std::time::SystemTime::now()
6 .duration_since(std::time::UNIX_EPOCH)
7 .expect("system clock before unix epoch");
8 format_rfc3339(duration.as_secs(), duration.subsec_nanos())
9}
10
11fn format_rfc3339(secs: u64, nanos: u32) -> String {
12 let (year, month, day, hour, minute, second) = unix_secs_to_utc(secs);
13 if nanos == 0 {
14 return format!("{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}Z");
15 }
16
17 format!(
18 "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{nanos:09}Z",
19 nanos = nanos
20 )
21}
22
23fn unix_secs_to_utc(secs: u64) -> (u32, u32, u32, u32, u32, u32) {
24 let days = secs / 86_400;
25 let rem = secs % 86_400;
26 let hour = (rem / 3_600) as u32;
27 let minute = ((rem % 3_600) / 60) as u32;
28 let second = (rem % 60) as u32;
29
30 let mut year = 1970u32;
31 let mut day_of_year = days;
32
33 loop {
34 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
35 if day_of_year < days_in_year {
36 break;
37 }
38 day_of_year -= days_in_year;
39 year += 1;
40 }
41
42 let mut month = 1u32;
43 for days_in_month in month_lengths(year) {
44 if day_of_year < days_in_month {
45 break;
46 }
47 day_of_year -= days_in_month;
48 month += 1;
49 }
50
51 (year, month, day_of_year as u32 + 1, hour, minute, second)
52}
53
54fn is_leap_year(year: u32) -> bool {
55 (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
56}
57
58fn month_lengths(year: u32) -> [u64; 12] {
59 [
60 31,
61 if is_leap_year(year) { 29 } else { 28 },
62 31,
63 30,
64 31,
65 30,
66 31,
67 31,
68 30,
69 31,
70 30,
71 31,
72 ]
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn utc_rfc3339_now_ends_with_z() {
81 let stamp = utc_rfc3339_now();
82 assert!(stamp.ends_with('Z'));
83 assert!(stamp.contains('T'));
84 }
85}