| 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} |
| } |