blob: d1361e1eba4d5efe03bff0bdcf94683e8cfb4c62 [file] [log] [blame]
use std::prelude::v1::*;
use std::fmt::{self, Write};
use super::{TmFmt, Tm, Fmt};
impl<'a> fmt::Display for TmFmt<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.format {
Fmt::Str(ref s) => {
let mut chars = s.chars();
while let Some(ch) = chars.next() {
if ch == '%' {
// we've already validated that % always precedes
// another char
try!(parse_type(fmt, chars.next().unwrap(), self.tm));
} else {
try!(fmt.write_char(ch));
}
}
Ok(())
}
Fmt::Ctime => self.tm.to_local().asctime().fmt(fmt),
Fmt::Rfc3339 => {
if self.tm.tm_utcoff == 0 {
TmFmt {
tm: self.tm,
format: Fmt::Str("%Y-%m-%dT%H:%M:%SZ"),
}.fmt(fmt)
} else {
let s = TmFmt {
tm: self.tm,
format: Fmt::Str("%Y-%m-%dT%H:%M:%S"),
};
let sign = if self.tm.tm_utcoff > 0 { '+' } else { '-' };
let mut m = abs(self.tm.tm_utcoff) / 60;
let h = m / 60;
m -= h * 60;
write!(fmt, "{}{}{:02}:{:02}", s, sign, h, m)
}
}
}
}
}
fn is_leap_year(year: i32) -> bool {
(year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}
fn days_in_year(year: i32) -> i32 {
if is_leap_year(year) { 366 }
else { 365 }
}
fn iso_week_days(yday: i32, wday: i32) -> i32 {
/* The number of days from the first day of the first ISO week of this
* year to the year day YDAY with week day WDAY.
* ISO weeks start on Monday. The first ISO week has the year's first
* Thursday.
* YDAY may be as small as yday_minimum.
*/
let iso_week_start_wday: i32 = 1; /* Monday */
let iso_week1_wday: i32 = 4; /* Thursday */
let yday_minimum: i32 = 366;
/* Add enough to the first operand of % to make it nonnegative. */
let big_enough_multiple_of_7: i32 = (yday_minimum / 7 + 2) * 7;
yday - (yday - wday + iso_week1_wday + big_enough_multiple_of_7) % 7
+ iso_week1_wday - iso_week_start_wday
}
fn iso_week(fmt: &mut fmt::Formatter, ch:char, tm: &Tm) -> fmt::Result {
let mut year = tm.tm_year + 1900;
let mut days = iso_week_days(tm.tm_yday, tm.tm_wday);
if days < 0 {
/* This ISO week belongs to the previous year. */
year -= 1;
days = iso_week_days(tm.tm_yday + (days_in_year(year)), tm.tm_wday);
} else {
let d = iso_week_days(tm.tm_yday - (days_in_year(year)),
tm.tm_wday);
if 0 <= d {
/* This ISO week belongs to the next year. */
year += 1;
days = d;
}
}
match ch {
'G' => write!(fmt, "{}", year),
'g' => write!(fmt, "{:02}", (year % 100 + 100) % 100),
'V' => write!(fmt, "{:02}", days / 7 + 1),
_ => Ok(())
}
}
fn parse_type(fmt: &mut fmt::Formatter, ch: char, tm: &Tm) -> fmt::Result {
match ch {
'A' => fmt.write_str(match tm.tm_wday {
0 => "Sunday",
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday",
_ => unreachable!(),
}),
'a' => fmt.write_str(match tm.tm_wday {
0 => "Sun",
1 => "Mon",
2 => "Tue",
3 => "Wed",
4 => "Thu",
5 => "Fri",
6 => "Sat",
_ => unreachable!(),
}),
'B' => fmt.write_str(match tm.tm_mon {
0 => "January",
1 => "February",
2 => "March",
3 => "April",
4 => "May",
5 => "June",
6 => "July",
7 => "August",
8 => "September",
9 => "October",
10 => "November",
11 => "December",
_ => unreachable!(),
}),
'b' | 'h' => fmt.write_str(match tm.tm_mon {
0 => "Jan",
1 => "Feb",
2 => "Mar",
3 => "Apr",
4 => "May",
5 => "Jun",
6 => "Jul",
7 => "Aug",
8 => "Sep",
9 => "Oct",
10 => "Nov",
11 => "Dec",
_ => unreachable!(),
}),
'C' => write!(fmt, "{:02}", (tm.tm_year + 1900) / 100),
'c' => {
try!(parse_type(fmt, 'a', tm));
try!(fmt.write_str(" "));
try!(parse_type(fmt, 'b', tm));
try!(fmt.write_str(" "));
try!(parse_type(fmt, 'e', tm));
try!(fmt.write_str(" "));
try!(parse_type(fmt, 'T', tm));
try!(fmt.write_str(" "));
parse_type(fmt, 'Y', tm)
}
'D' | 'x' => {
try!(parse_type(fmt, 'm', tm));
try!(fmt.write_str("/"));
try!(parse_type(fmt, 'd', tm));
try!(fmt.write_str("/"));
parse_type(fmt, 'y', tm)
}
'd' => write!(fmt, "{:02}", tm.tm_mday),
'e' => write!(fmt, "{:2}", tm.tm_mday),
'f' => write!(fmt, "{:09}", tm.tm_nsec),
'F' => {
try!(parse_type(fmt, 'Y', tm));
try!(fmt.write_str("-"));
try!(parse_type(fmt, 'm', tm));
try!(fmt.write_str("-"));
parse_type(fmt, 'd', tm)
}
'G' => iso_week(fmt, 'G', tm),
'g' => iso_week(fmt, 'g', tm),
'H' => write!(fmt, "{:02}", tm.tm_hour),
'I' => {
let mut h = tm.tm_hour;
if h == 0 { h = 12 }
if h > 12 { h -= 12 }
write!(fmt, "{:02}", h)
}
'j' => write!(fmt, "{:03}", tm.tm_yday + 1),
'k' => write!(fmt, "{:2}", tm.tm_hour),
'l' => {
let mut h = tm.tm_hour;
if h == 0 { h = 12 }
if h > 12 { h -= 12 }
write!(fmt, "{:2}", h)
}
'M' => write!(fmt, "{:02}", tm.tm_min),
'm' => write!(fmt, "{:02}", tm.tm_mon + 1),
'n' => fmt.write_str("\n"),
'P' => fmt.write_str(if tm.tm_hour < 12 { "am" } else { "pm" }),
'p' => fmt.write_str(if (tm.tm_hour) < 12 { "AM" } else { "PM" }),
'R' => {
try!(parse_type(fmt, 'H', tm));
try!(fmt.write_str(":"));
parse_type(fmt, 'M', tm)
}
'r' => {
try!(parse_type(fmt, 'I', tm));
try!(fmt.write_str(":"));
try!(parse_type(fmt, 'M', tm));
try!(fmt.write_str(":"));
try!(parse_type(fmt, 'S', tm));
try!(fmt.write_str(" "));
parse_type(fmt, 'p', tm)
}
'S' => write!(fmt, "{:02}", tm.tm_sec),
's' => write!(fmt, "{}", tm.to_timespec().sec),
'T' | 'X' => {
try!(parse_type(fmt, 'H', tm));
try!(fmt.write_str(":"));
try!(parse_type(fmt, 'M', tm));
try!(fmt.write_str(":"));
parse_type(fmt, 'S', tm)
}
't' => fmt.write_str("\t"),
'U' => write!(fmt, "{:02}", (tm.tm_yday - tm.tm_wday + 7) / 7),
'u' => {
let i = tm.tm_wday;
write!(fmt, "{}", (if i == 0 { 7 } else { i }))
}
'V' => iso_week(fmt, 'V', tm),
'v' => {
try!(parse_type(fmt, 'e', tm));
try!(fmt.write_str("-"));
try!(parse_type(fmt, 'b', tm));
try!(fmt.write_str("-"));
parse_type(fmt, 'Y', tm)
}
'W' => {
write!(fmt, "{:02}", (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7)
}
'w' => write!(fmt, "{}", tm.tm_wday),
'Y' => write!(fmt, "{}", tm.tm_year + 1900),
'y' => write!(fmt, "{:02}", (tm.tm_year + 1900) % 100),
// FIXME (#2350): support locale
'Z' => fmt.write_str(if tm.tm_utcoff == 0 { "UTC"} else { "" }),
'z' => {
let sign = if tm.tm_utcoff > 0 { '+' } else { '-' };
let mut m = abs(tm.tm_utcoff) / 60;
let h = m / 60;
m -= h * 60;
write!(fmt, "{}{:02}{:02}", sign, h, m)
}
'+' => write!(fmt, "{}", tm.rfc3339()),
'%' => fmt.write_str("%"),
_ => unreachable!(),
}
}
fn abs(i: i32) -> i32 {
if i < 0 {-i} else {i}
}