xref: /drstd/dlibc/src/unix/header/time/strftime.rs (revision 9670759b785600bf6315e4173e46a602f16add7a)
1 use alloc::string::String;
2 
3 use crate::unix::platform::{self, WriteByte};
4 
5 use super::tm;
6 
strftime<W: WriteByte>(w: &mut W, format: *const ::c_char, t: *const tm) -> ::size_t7 pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const ::c_char, t: *const tm) -> ::size_t {
8     pub unsafe fn inner_strftime<W: WriteByte>(
9         w: &mut W,
10         mut format: *const ::c_char,
11         t: *const tm,
12     ) -> bool {
13         macro_rules! w {
14             (byte $b:expr) => {{
15                 if w.write_u8($b).is_err() {
16                     return false;
17                 }
18             }};
19             (char $chr:expr) => {{
20                 if w.write_char($chr).is_err() {
21                     return false;
22                 }
23             }};
24             (recurse $fmt:expr) => {{
25                 let mut fmt = String::with_capacity($fmt.len() + 1);
26                 fmt.push_str($fmt);
27                 fmt.push('\0');
28 
29                 if !inner_strftime(w, fmt.as_ptr() as *mut ::c_char, t) {
30                     return false;
31                 }
32             }};
33             ($str:expr) => {{
34                 if w.write_str($str).is_err() {
35                     return false;
36                 }
37             }};
38             ($fmt:expr, $($args:expr),+) => {{
39                 // Would use write!() if I could get the length written
40                 if write!(w, $fmt, $($args),+).is_err() {
41                     return false;
42                 }
43             }};
44         }
45         const WDAYS: [&str; 7] = [
46             "Sunday",
47             "Monday",
48             "Tuesday",
49             "Wednesday",
50             "Thursday",
51             "Friday",
52             "Saturday",
53         ];
54         const MONTHS: [&str; 12] = [
55             "January",
56             "Febuary",
57             "March",
58             "April",
59             "May",
60             "June",
61             "July",
62             "August",
63             "September",
64             "October",
65             "November",
66             "December",
67         ];
68 
69         while *format != 0 {
70             if *format as u8 != b'%' {
71                 w!(byte * format as u8);
72                 format = format.offset(1);
73                 continue;
74             }
75 
76             format = format.offset(1);
77 
78             if *format as u8 == b'E' || *format as u8 == b'O' {
79                 // Ignore because these do nothing without locale
80                 format = format.offset(1);
81             }
82 
83             match *format as u8 {
84                 b'%' => w!(byte b'%'),
85                 b'n' => w!(byte b'\n'),
86                 b't' => w!(byte b'\t'),
87                 b'a' => w!(&WDAYS[(*t).tm_wday as usize][..3]),
88                 b'A' => w!(WDAYS[(*t).tm_wday as usize]),
89                 b'b' | b'h' => w!(&MONTHS[(*t).tm_mon as usize][..3]),
90                 b'B' => w!(MONTHS[(*t).tm_mon as usize]),
91                 b'C' => {
92                     let mut year = (*t).tm_year / 100;
93                     // Round up
94                     if (*t).tm_year % 100 != 0 {
95                         year += 1;
96                     }
97                     w!("{:02}", year + 19);
98                 }
99                 b'd' => w!("{:02}", (*t).tm_mday),
100                 b'D' => w!(recurse "%m/%d/%y"),
101                 b'e' => w!("{:2}", (*t).tm_mday),
102                 b'F' => w!(recurse "%Y-%m-%d"),
103                 b'H' => w!("{:02}", (*t).tm_hour),
104                 b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1),
105                 b'j' => w!("{:03}", (*t).tm_yday),
106                 b'k' => w!("{:2}", (*t).tm_hour),
107                 b'l' => w!("{:2}", ((*t).tm_hour + 12 - 1) % 12 + 1),
108                 b'm' => w!("{:02}", (*t).tm_mon + 1),
109                 b'M' => w!("{:02}", (*t).tm_min),
110                 b'p' => w!(if (*t).tm_hour < 12 { "AM" } else { "PM" }),
111                 b'P' => w!(if (*t).tm_hour < 12 { "am" } else { "pm" }),
112                 b'r' => w!(recurse "%I:%M:%S %p"),
113                 b'R' => w!(recurse "%H:%M"),
114                 // Nothing is modified in mktime, but the C standard of course requires a mutable pointer ._.
115                 b's' => w!("{}", super::mktime(t as *mut tm)),
116                 b'S' => w!("{:02}", (*t).tm_sec),
117                 b'T' => w!(recurse "%H:%M:%S"),
118                 b'u' => w!("{}", ((*t).tm_wday + 7 - 1) % 7 + 1),
119                 b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7),
120                 b'w' => w!("{}", (*t).tm_wday),
121                 b'W' => w!("{}", ((*t).tm_yday + 7 - ((*t).tm_wday + 6) % 7) / 7),
122                 b'y' => w!("{:02}", (*t).tm_year % 100),
123                 b'Y' => w!("{}", (*t).tm_year + 1900),
124                 b'z' => w!("+0000"), // TODO
125                 b'Z' => w!("UTC"),   // TODO
126                 b'+' => w!(recurse "%a %b %d %T %Z %Y"),
127                 _ => return false,
128             }
129 
130             format = format.offset(1);
131         }
132         true
133     }
134 
135     let mut w = platform::CountingWriter::new(w);
136     if !inner_strftime(&mut w, format, t) {
137         return 0;
138     }
139 
140     w.written
141 }
142