Skip to main content

internationalization/
time.rs

1use alloc::format;
2use alloc::string::String;
3
4/// Formats a Unix timestamp using Python-like `strftime` tokens.
5///
6/// Supported tokens:
7/// - `%Y` year with century (e.g. `2026`)
8/// - `%m` month as zero-padded decimal (`01`..`12`)
9/// - `%d` day of month as zero-padded decimal (`01`..`31`)
10/// - `%H` hour (24-hour clock) as zero-padded decimal (`00`..`23`)
11/// - `%I` hour (12-hour clock) as zero-padded decimal (`01`..`12`)
12/// - `%M` minute as zero-padded decimal (`00`..`59`)
13/// - `%S` second as zero-padded decimal (`00`..`59`)
14/// - `%p` locale-independent `AM`/`PM`
15/// - `%%` literal `%`
16pub fn format_unix_timestamp(unix_timestamp: i64, pattern: &str) -> String {
17    let (year, month, day, hour, minute, second) = shared::decompose_unix_timestamp(unix_timestamp);
18
19    let mut output = String::with_capacity(pattern.len() + 16);
20    let mut characters = pattern.chars();
21
22    while let Some(character) = characters.next() {
23        if character != '%' {
24            output.push(character);
25            continue;
26        }
27
28        match characters.next() {
29            Some('Y') => output.push_str(&format!("{:04}", year)),
30            Some('m') => output.push_str(&format!("{:02}", month)),
31            Some('d') => output.push_str(&format!("{:02}", day)),
32            Some('H') => output.push_str(&format!("{:02}", hour)),
33            Some('I') => output.push_str(&format!("{:02}", hour_12(hour))),
34            Some('M') => output.push_str(&format!("{:02}", minute)),
35            Some('S') => output.push_str(&format!("{:02}", second)),
36            Some('p') => output.push_str(if hour < 12 { "AM" } else { "PM" }),
37            Some('%') => output.push('%'),
38            Some(other) => {
39                output.push('%');
40                output.push(other);
41            }
42            None => output.push('%'),
43        }
44    }
45
46    output
47}
48
49const fn hour_12(hour_24: u8) -> u8 {
50    match hour_24 % 12 {
51        0 => 12,
52        value => value,
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn format_24_hour_time() {
62        let timestamp = 13 * 3600 + 5 * 60;
63        assert_eq!(format_unix_timestamp(timestamp, "%H:%M"), "13:05");
64    }
65
66    #[test]
67    fn format_12_hour_time_with_am_pm() {
68        let midnight = 0;
69        let afternoon = 13 * 3600 + 5 * 60;
70
71        assert_eq!(format_unix_timestamp(midnight, "%I:%M %p"), "12:00 AM");
72        assert_eq!(format_unix_timestamp(afternoon, "%I:%M %p"), "01:05 PM");
73    }
74
75    #[test]
76    fn format_date_and_time() {
77        assert_eq!(
78            format_unix_timestamp(0, "%Y-%m-%d %H:%M:%S"),
79            "1970-01-01 00:00:00"
80        );
81    }
82
83    #[test]
84    fn format_negative_unix_time() {
85        assert_eq!(
86            format_unix_timestamp(-1, "%Y-%m-%d %H:%M:%S"),
87            "1969-12-31 23:59:59"
88        );
89    }
90}