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