time_preview/
display.rs

1use std::fmt::{self, Write};
2
3use super::{TmFmt, Tm, Fmt};
4
5impl<'a> fmt::Display for TmFmt<'a> {
6    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
45fn is_leap_year(year: i32) -> bool {
46    (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
47}
48
49fn days_in_year(year: i32) -> i32 {
50    if is_leap_year(year) { 366 }
51    else                  { 365 }
52}
53
54fn 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
71fn 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
97fn 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
258fn abs(i: i32) -> i32 {
259    if i < 0 {-i} else {i}
260}