1 use std::fmt::{self, Write};
2 
3 use super::{TmFmt, Tm, Fmt};
4 
5 impl<'a> fmt::Display for TmFmt<'a> {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result6     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
7         match self.format {
8             Fmt::Str(ref s) => {
9                 let mut chars = s.chars();
10                 while let Some(ch) = chars.next() {
11                     if ch == '%' {
12                         // we've already validated that % always precedes
13                         // another char
14                         try!(parse_type(fmt, chars.next().unwrap(), self.tm));
15                     } else {
16                         try!(fmt.write_char(ch));
17                     }
18                 }
19 
20                 Ok(())
21             }
22             Fmt::Ctime => self.tm.to_local().asctime().fmt(fmt),
23             Fmt::Rfc3339 => {
24                 if self.tm.tm_utcoff == 0 {
25                     TmFmt {
26                         tm: self.tm,
27                         format: Fmt::Str("%Y-%m-%dT%H:%M:%SZ"),
28                     }.fmt(fmt)
29                 } else {
30                     let s = TmFmt {
31                         tm: self.tm,
32                         format: Fmt::Str("%Y-%m-%dT%H:%M:%S"),
33                     };
34                     let sign = if self.tm.tm_utcoff > 0 { '+' } else { '-' };
35                     let mut m = abs(self.tm.tm_utcoff) / 60;
36                     let h = m / 60;
37                     m -= h * 60;
38                     write!(fmt, "{}{}{:02}:{:02}", s, sign, h, m)
39                 }
40             }
41         }
42     }
43 }
44 
is_leap_year(year: i32) -> bool45 fn is_leap_year(year: i32) -> bool {
46     (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
47 }
48 
days_in_year(year: i32) -> i3249 fn days_in_year(year: i32) -> i32 {
50     if is_leap_year(year) { 366 }
51     else                  { 365 }
52 }
53 
iso_week_days(yday: i32, wday: i32) -> i3254 fn iso_week_days(yday: i32, wday: i32) -> i32 {
55     /* The number of days from the first day of the first ISO week of this
56     * year to the year day YDAY with week day WDAY.
57     * ISO weeks start on Monday. The first ISO week has the year's first
58     * Thursday.
59     * YDAY may be as small as yday_minimum.
60     */
61     let iso_week_start_wday: i32 = 1;                     /* Monday */
62     let iso_week1_wday: i32 = 4;                          /* Thursday */
63     let yday_minimum: i32 = 366;
64     /* Add enough to the first operand of % to make it nonnegative. */
65     let big_enough_multiple_of_7: i32 = (yday_minimum / 7 + 2) * 7;
66 
67     yday - (yday - wday + iso_week1_wday + big_enough_multiple_of_7) % 7
68         + iso_week1_wday - iso_week_start_wday
69 }
70 
iso_week(fmt: &mut fmt::Formatter, ch:char, tm: &Tm) -> fmt::Result71 fn iso_week(fmt: &mut fmt::Formatter, ch:char, tm: &Tm) -> fmt::Result {
72     let mut year = tm.tm_year + 1900;
73     let mut days = iso_week_days(tm.tm_yday, tm.tm_wday);
74 
75     if days < 0 {
76         /* This ISO week belongs to the previous year. */
77         year -= 1;
78         days = iso_week_days(tm.tm_yday + (days_in_year(year)), tm.tm_wday);
79     } else {
80         let d = iso_week_days(tm.tm_yday - (days_in_year(year)),
81                               tm.tm_wday);
82         if 0 <= d {
83             /* This ISO week belongs to the next year. */
84             year += 1;
85             days = d;
86         }
87     }
88 
89     match ch {
90         'G' => write!(fmt, "{}", year),
91         'g' => write!(fmt, "{:02}", (year % 100 + 100) % 100),
92         'V' => write!(fmt, "{:02}", days / 7 + 1),
93         _ => Ok(())
94     }
95 }
96 
parse_type(fmt: &mut fmt::Formatter, ch: char, tm: &Tm) -> fmt::Result97 fn parse_type(fmt: &mut fmt::Formatter, ch: char, tm: &Tm) -> fmt::Result {
98     match ch {
99         'A' => fmt.write_str(match tm.tm_wday {
100             0 => "Sunday",
101             1 => "Monday",
102             2 => "Tuesday",
103             3 => "Wednesday",
104             4 => "Thursday",
105             5 => "Friday",
106             6 => "Saturday",
107             _ => unreachable!(),
108         }),
109         'a' => fmt.write_str(match tm.tm_wday {
110             0 => "Sun",
111             1 => "Mon",
112             2 => "Tue",
113             3 => "Wed",
114             4 => "Thu",
115             5 => "Fri",
116             6 => "Sat",
117             _ => unreachable!(),
118         }),
119         'B' => fmt.write_str(match tm.tm_mon {
120             0 => "January",
121             1 => "February",
122             2 => "March",
123             3 => "April",
124             4 => "May",
125             5 => "June",
126             6 => "July",
127             7 => "August",
128             8 => "September",
129             9 => "October",
130             10 => "November",
131             11 => "December",
132             _ => unreachable!(),
133         }),
134         'b' | 'h' => fmt.write_str(match tm.tm_mon {
135             0 => "Jan",
136             1 => "Feb",
137             2 => "Mar",
138             3 => "Apr",
139             4 => "May",
140             5 => "Jun",
141             6 => "Jul",
142             7 => "Aug",
143             8 => "Sep",
144             9 => "Oct",
145             10 => "Nov",
146             11 => "Dec",
147             _  => unreachable!(),
148         }),
149         'C' => write!(fmt, "{:02}", (tm.tm_year + 1900) / 100),
150         'c' => {
151             try!(parse_type(fmt, 'a', tm));
152             try!(fmt.write_str(" "));
153             try!(parse_type(fmt, 'b', tm));
154             try!(fmt.write_str(" "));
155             try!(parse_type(fmt, 'e', tm));
156             try!(fmt.write_str(" "));
157             try!(parse_type(fmt, 'T', tm));
158             try!(fmt.write_str(" "));
159             parse_type(fmt, 'Y', tm)
160         }
161         'D' | 'x' => {
162             try!(parse_type(fmt, 'm', tm));
163             try!(fmt.write_str("/"));
164             try!(parse_type(fmt, 'd', tm));
165             try!(fmt.write_str("/"));
166             parse_type(fmt, 'y', tm)
167         }
168         'd' => write!(fmt, "{:02}", tm.tm_mday),
169         'e' => write!(fmt, "{:2}", tm.tm_mday),
170         'f' => write!(fmt, "{:09}", tm.tm_nsec),
171         'F' => {
172             try!(parse_type(fmt, 'Y', tm));
173             try!(fmt.write_str("-"));
174             try!(parse_type(fmt, 'm', tm));
175             try!(fmt.write_str("-"));
176             parse_type(fmt, 'd', tm)
177         }
178         'G' => iso_week(fmt, 'G', tm),
179         'g' => iso_week(fmt, 'g', tm),
180         'H' => write!(fmt, "{:02}", tm.tm_hour),
181         'I' => {
182             let mut h = tm.tm_hour;
183             if h == 0 { h = 12 }
184             if h > 12 { h -= 12 }
185             write!(fmt, "{:02}", h)
186         }
187         'j' => write!(fmt, "{:03}", tm.tm_yday + 1),
188         'k' => write!(fmt, "{:2}", tm.tm_hour),
189         'l' => {
190             let mut h = tm.tm_hour;
191             if h == 0 { h = 12 }
192             if h > 12 { h -= 12 }
193             write!(fmt, "{:2}", h)
194         }
195         'M' => write!(fmt, "{:02}", tm.tm_min),
196         'm' => write!(fmt, "{:02}", tm.tm_mon + 1),
197         'n' => fmt.write_str("\n"),
198         'P' => fmt.write_str(if tm.tm_hour < 12 { "am" } else { "pm" }),
199         'p' => fmt.write_str(if (tm.tm_hour) < 12 { "AM" } else { "PM" }),
200         'R' => {
201             try!(parse_type(fmt, 'H', tm));
202             try!(fmt.write_str(":"));
203             parse_type(fmt, 'M', tm)
204         }
205         'r' => {
206             try!(parse_type(fmt, 'I', tm));
207             try!(fmt.write_str(":"));
208             try!(parse_type(fmt, 'M', tm));
209             try!(fmt.write_str(":"));
210             try!(parse_type(fmt, 'S', tm));
211             try!(fmt.write_str(" "));
212             parse_type(fmt, 'p', tm)
213         }
214         'S' => write!(fmt, "{:02}", tm.tm_sec),
215         's' => write!(fmt, "{}", tm.to_timespec().sec),
216         'T' | 'X' => {
217             try!(parse_type(fmt, 'H', tm));
218             try!(fmt.write_str(":"));
219             try!(parse_type(fmt, 'M', tm));
220             try!(fmt.write_str(":"));
221             parse_type(fmt, 'S', tm)
222         }
223         't' => fmt.write_str("\t"),
224         'U' => write!(fmt, "{:02}", (tm.tm_yday - tm.tm_wday + 7) / 7),
225         'u' => {
226             let i = tm.tm_wday;
227             write!(fmt, "{}", (if i == 0 { 7 } else { i }))
228         }
229         'V' => iso_week(fmt, 'V', tm),
230         'v' => {
231             try!(parse_type(fmt, 'e', tm));
232             try!(fmt.write_str("-"));
233             try!(parse_type(fmt, 'b', tm));
234             try!(fmt.write_str("-"));
235             parse_type(fmt, 'Y', tm)
236         }
237         'W' => {
238             write!(fmt, "{:02}", (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7)
239         }
240         'w' => write!(fmt, "{}", tm.tm_wday),
241         'Y' => write!(fmt, "{}", tm.tm_year + 1900),
242         'y' => write!(fmt, "{:02}", (tm.tm_year + 1900) % 100),
243         // FIXME (#2350): support locale
244         'Z' => fmt.write_str(if tm.tm_utcoff == 0 { "UTC"} else { "" }),
245         'z' => {
246             let sign = if tm.tm_utcoff > 0 { '+' } else { '-' };
247             let mut m = abs(tm.tm_utcoff) / 60;
248             let h = m / 60;
249             m -= h * 60;
250             write!(fmt, "{}{:02}{:02}", sign, h, m)
251         }
252         '+' => write!(fmt, "{}", tm.rfc3339()),
253         '%' => fmt.write_str("%"),
254         _   => unreachable!(),
255     }
256 }
257 
abs(i: i32) -> i32258 fn abs(i: i32) -> i32 {
259     if i < 0 {-i} else {i}
260 }
261