0.1.13: language changes and fmt::String supports.

- Every type modulo `LocalResult` and `Offset` now implements
  `std::fmt::String`, so `.to_string()` can be used.
  The exact format has been changed for better looking output.

- `std::fmt::Show` are intended for "stricter" output,
  which mostly means the strict ISO 8601 format.
  `DelayedFormat` also implements this, but only for inspection.

- `Offset` should implement `Show` but can omit `String`.
  The old `name` method is merged into `String` implementations.
This commit is contained in:
Kang Seonghoon 2015-01-10 03:27:24 +09:00
parent d79c460fc4
commit ca84749869
12 changed files with 221 additions and 154 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "chrono" name = "chrono"
version = "0.1.12" version = "0.1.13"
authors = ["Kang Seonghoon <public+rust@mearie.org>"] authors = ["Kang Seonghoon <public+rust@mearie.org>"]
description = "Date and time library for Rust" description = "Date and time library for Rust"
@ -15,5 +15,5 @@ license = "MIT/Apache-2.0"
name = "chrono" name = "chrono"
[dependencies] [dependencies]
time = "0.1.11" time = "0.1.12"

View File

@ -1,4 +1,4 @@
[Chrono][doc] 0.1.12 [Chrono][doc] 0.1.13
==================== ====================
[![Chrono on Travis CI][travis-image]][travis] [![Chrono on Travis CI][travis-image]][travis]
@ -115,17 +115,18 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00
Formatting is done via the `format` method, Formatting is done via the `format` method,
which format is equivalent to the familiar `strftime` format. which format is equivalent to the familiar `strftime` format.
The default `to_string` method also gives a reasonable representation. The default `to_string` method and `{:?}` specifier also give a reasonable representation.
~~~~ {.rust} ~~~~ {.rust}
use chrono::{UTC, Offset}; use chrono::{UTC, Offset};
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09".to_string()); assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014".to_string()); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
assert_eq!(dt.to_string(), "2014-11-28T12:00:09Z".to_string()); assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~ ~~~~
### Individual date and time ### Individual date and time
@ -143,7 +144,7 @@ assert_eq!(Local::today(), Local::now().date());
assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri); assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809".to_string()); assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809");
~~~~ ~~~~
`DateTime` has two methods, `date` and `time`, `DateTime` has two methods, `date` and `time`,

View File

@ -175,17 +175,19 @@ impl<Off:Offset> Date<Off> {
Date::from_utc(self.date, offset) Date::from_utc(self.date, offset)
} }
/// Returns a view to the local date.
fn local(&self) -> NaiveDate {
self.offset.to_local_date(&self.date)
}
}
impl<Off: Offset + fmt::String> Date<Off> {
/// Formats the date in the specified format string. /// Formats the date in the specified format string.
/// See the `format` module on the supported escape sequences. /// See the `format` module on the supported escape sequences.
#[inline] #[inline]
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> { pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, fmt) DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, fmt)
} }
/// Returns a view to the local date.
fn local(&self) -> NaiveDate {
self.offset.to_local_date(&self.date)
}
} }
impl<Off:Offset> Datelike for Date<Off> { impl<Off:Offset> Datelike for Date<Off> {
@ -259,8 +261,8 @@ impl<Off:Offset> Ord for Date<Off> {
fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) } fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
} }
impl<Off:Offset> hash::Hash for Date<Off> { impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for Date<Off> {
fn hash(&self, state: &mut hash::sip::SipState) { self.date.hash(state) } fn hash(&self, state: &mut H) { self.date.hash(state) }
} }
impl<Off:Offset> Add<Duration> for Date<Off> { impl<Off:Offset> Add<Duration> for Date<Off> {
@ -284,7 +286,13 @@ impl<Off:Offset> Sub<Duration> for Date<Off> {
fn sub(self, rhs: Duration) -> Date<Off> { self.add(-rhs) } fn sub(self, rhs: Duration) -> Date<Off> { self.add(-rhs) }
} }
impl<Off:Offset> fmt::Show for Date<Off> { impl<Off: Offset> fmt::Show for Date<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.local(), self.offset)
}
}
impl<Off: Offset + fmt::String> fmt::String for Date<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.local(), self.offset) write!(f, "{}{}", self.local(), self.offset)
} }
@ -292,9 +300,7 @@ impl<Off:Offset> fmt::Show for Date<Off> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::borrow::IntoCow;
use std::fmt; use std::fmt;
use std::string::CowString;
use duration::Duration; use duration::Duration;
use naive::date::NaiveDate; use naive::date::NaiveDate;
@ -309,7 +315,6 @@ mod tests {
struct UTC1y; // same to UTC but with an offset of 365 days struct UTC1y; // same to UTC but with an offset of 365 days
impl Offset for UTC1y { impl Offset for UTC1y {
fn name(&self) -> CowString<'static> { "UTC+8760".into_cow() } // yes, no kidding
fn local_minus_utc(&self) -> Duration { Duration::zero() } fn local_minus_utc(&self) -> Duration { Duration::zero() }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
@ -335,13 +340,13 @@ mod tests {
#[test] #[test]
fn test_date_weird_offset() { fn test_date_weird_offset() {
assert_eq!(UTC1y.ymd(2012, 2, 29).to_string(), assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29)),
"2012-02-29+8760:00".to_string()); "2012-02-29+8760:00".to_string());
assert_eq!(UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7).to_string(), assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7)),
"2012-02-29T05:06:07+8760:00".to_string()); "2012-02-29T05:06:07+8760:00".to_string());
assert_eq!(UTC1y.ymd(2012, 3, 4).to_string(), assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4)),
"2012-03-04+8760:00".to_string()); "2012-03-04+8760:00".to_string());
assert_eq!(UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7).to_string(), assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)),
"2012-03-04T05:06:07+8760:00".to_string()); "2012-03-04T05:06:07+8760:00".to_string());
} }
} }

View File

@ -64,6 +64,13 @@ impl<Off:Offset> DateTime<Off> {
DateTime::from_utc(self.datetime, offset) DateTime::from_utc(self.datetime, offset)
} }
/// Returns a view to the local datetime.
fn local(&self) -> NaiveDateTime {
self.offset.to_local_datetime(&self.datetime)
}
}
impl<Off: Offset + fmt::String> DateTime<Off> {
/// Formats the combined date and time in the specified format string. /// Formats the combined date and time in the specified format string.
/// See the `format` module on the supported escape sequences. /// See the `format` module on the supported escape sequences.
#[inline] #[inline]
@ -71,11 +78,6 @@ impl<Off:Offset> DateTime<Off> {
let local = self.local(); let local = self.local();
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, fmt) DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, fmt)
} }
/// Returns a view to the local datetime.
fn local(&self) -> NaiveDateTime {
self.offset.to_local_datetime(&self.datetime)
}
} }
impl<Off:Offset> Datelike for DateTime<Off> { impl<Off:Offset> Datelike for DateTime<Off> {
@ -180,8 +182,8 @@ impl<Off:Offset> Ord for DateTime<Off> {
fn cmp(&self, other: &DateTime<Off>) -> Ordering { self.datetime.cmp(&other.datetime) } fn cmp(&self, other: &DateTime<Off>) -> Ordering { self.datetime.cmp(&other.datetime) }
} }
impl<Off:Offset> hash::Hash for DateTime<Off> { impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for DateTime<Off> {
fn hash(&self, state: &mut hash::sip::SipState) { self.datetime.hash(state) } fn hash(&self, state: &mut H) { self.datetime.hash(state) }
} }
impl<Off:Offset> Add<Duration> for DateTime<Off> { impl<Off:Offset> Add<Duration> for DateTime<Off> {
@ -205,9 +207,15 @@ impl<Off:Offset> Sub<Duration> for DateTime<Off> {
fn sub(self, rhs: Duration) -> DateTime<Off> { self.add(-rhs) } fn sub(self, rhs: Duration) -> DateTime<Off> { self.add(-rhs) }
} }
impl<Off:Offset> fmt::Show for DateTime<Off> { impl<Off: Offset> fmt::Show for DateTime<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.local(), self.offset) write!(f, "{:?}{:?}", self.local(), self.offset)
}
}
impl<Off: Offset + fmt::String> fmt::String for DateTime<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.local(), self.offset)
} }
} }
@ -222,10 +230,15 @@ mod tests {
let EST = FixedOffset::east(5*60*60); let EST = FixedOffset::east(5*60*60);
let EDT = FixedOffset::east(4*60*60); let EDT = FixedOffset::east(4*60*60);
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).to_string(), assert_eq!(format!("{}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09Z".to_string()); "2014-05-06 07:08:09 UTC");
assert_eq!(EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).to_string(), assert_eq!(format!("{}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09+04:00".to_string()); "2014-05-06 07:08:09 +04:00");
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09Z");
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09+04:00");
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(11, 8, 9)); assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(11, 8, 9));
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1), assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1),
UTC.ymd(2014, 5, 6).and_hms(8, 9, 10)); UTC.ymd(2014, 5, 6).and_hms(8, 9, 10));

View File

@ -43,28 +43,28 @@ mod tests {
#[test] #[test]
fn test_mod_floor() { fn test_mod_floor() {
assert_eq!(mod_floor( 8i, 3), 2); assert_eq!(mod_floor( 8, 3), 2);
assert_eq!(mod_floor( 8i, -3), -1); assert_eq!(mod_floor( 8, -3), -1);
assert_eq!(mod_floor(-8i, 3), 1); assert_eq!(mod_floor(-8, 3), 1);
assert_eq!(mod_floor(-8i, -3), -2); assert_eq!(mod_floor(-8, -3), -2);
assert_eq!(mod_floor( 1i, 2), 1); assert_eq!(mod_floor( 1, 2), 1);
assert_eq!(mod_floor( 1i, -2), -1); assert_eq!(mod_floor( 1, -2), -1);
assert_eq!(mod_floor(-1i, 2), 1); assert_eq!(mod_floor(-1, 2), 1);
assert_eq!(mod_floor(-1i, -2), -1); assert_eq!(mod_floor(-1, -2), -1);
} }
#[test] #[test]
fn test_div_mod_floor() { fn test_div_mod_floor() {
assert_eq!(div_mod_floor( 8i, 3), ( 2, 2)); assert_eq!(div_mod_floor( 8, 3), ( 2, 2));
assert_eq!(div_mod_floor( 8i, -3), (-3, -1)); assert_eq!(div_mod_floor( 8, -3), (-3, -1));
assert_eq!(div_mod_floor(-8i, 3), (-3, 1)); assert_eq!(div_mod_floor(-8, 3), (-3, 1));
assert_eq!(div_mod_floor(-8i, -3), ( 2, -2)); assert_eq!(div_mod_floor(-8, -3), ( 2, -2));
assert_eq!(div_mod_floor( 1i, 2), ( 0, 1)); assert_eq!(div_mod_floor( 1, 2), ( 0, 1));
assert_eq!(div_mod_floor( 1i, -2), (-1, -1)); assert_eq!(div_mod_floor( 1, -2), (-1, -1));
assert_eq!(div_mod_floor(-1i, 2), (-1, 1)); assert_eq!(div_mod_floor(-1, 2), (-1, 1));
assert_eq!(div_mod_floor(-1i, -2), ( 0, -1)); assert_eq!(div_mod_floor(-1, -2), ( 0, -1));
} }
} }

View File

@ -7,7 +7,6 @@
*/ */
use std::fmt; use std::fmt;
use std::string::CowString;
use {Datelike, Timelike}; use {Datelike, Timelike};
use duration::Duration; use duration::Duration;
@ -17,7 +16,7 @@ use naive::time::NaiveTime;
/// The internal workhouse for `DelayedFormat`. /// The internal workhouse for `DelayedFormat`.
fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>, fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
off: Option<&(CowString<'static>, Duration)>, fmt: &str) -> fmt::Result { off: Option<&(String, Duration)>, fmt: &str) -> fmt::Result {
static SHORT_MONTHS: [&'static str; 12] = static SHORT_MONTHS: [&'static str; 12] =
["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
static LONG_MONTHS: [&'static str; 12] = static LONG_MONTHS: [&'static str; 12] =
@ -57,9 +56,9 @@ fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveT
// month // month
(Some('m'), Some(d), _, _) => try!(write!(w, "{:02}", d.month())), (Some('m'), Some(d), _, _) => try!(write!(w, "{:02}", d.month())),
(Some('b'), Some(d), _, _) | (Some('h'), Some(d), _, _) => (Some('b'), Some(d), _, _) | (Some('h'), Some(d), _, _) =>
try!(write!(w, "{}", SHORT_MONTHS[d.month0() as uint])), try!(write!(w, "{}", SHORT_MONTHS[d.month0() as usize])),
(Some('B'), Some(d), _, _) => (Some('B'), Some(d), _, _) =>
try!(write!(w, "{}", LONG_MONTHS[d.month0() as uint])), try!(write!(w, "{}", LONG_MONTHS[d.month0() as usize])),
// day of month // day of month
(Some('d'), Some(d), _, _) => try!(write!(w, "{:02}", d.day())), (Some('d'), Some(d), _, _) => try!(write!(w, "{:02}", d.day())),
@ -76,9 +75,9 @@ fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveT
// day of week // day of week
(Some('a'), Some(d), _, _) => (Some('a'), Some(d), _, _) =>
try!(write!(w, "{}", SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as uint])), try!(write!(w, "{}", SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize])),
(Some('A'), Some(d), _, _) => (Some('A'), Some(d), _, _) =>
try!(write!(w, "{}", LONG_WEEKDAYS[d.weekday().num_days_from_monday() as uint])), try!(write!(w, "{}", LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize])),
(Some('w'), Some(d), _, _) => try!(write!(w, "{}", d.weekday().num_days_from_sunday())), (Some('w'), Some(d), _, _) => try!(write!(w, "{}", d.weekday().num_days_from_sunday())),
(Some('u'), Some(d), _, _) => try!(write!(w, "{}", d.weekday().number_from_monday())), (Some('u'), Some(d), _, _) => try!(write!(w, "{}", d.weekday().number_from_monday())),
@ -91,7 +90,7 @@ fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveT
(Some('F'), Some(d), _, _) => // `%Y-%m-%d' (Some('F'), Some(d), _, _) => // `%Y-%m-%d'
try!(write!(w, "{:04}-{:02}-{:02}", d.year(), d.month(), d.day())), try!(write!(w, "{:04}-{:02}-{:02}", d.year(), d.month(), d.day())),
(Some('v'), Some(d), _, _) => // `%e-%b-%Y' (Some('v'), Some(d), _, _) => // `%e-%b-%Y'
try!(write!(w, "{:2}-{}-{:04}", d.day(), SHORT_MONTHS[d.month0() as uint], try!(write!(w, "{:2}-{}-{:04}", d.day(), SHORT_MONTHS[d.month0() as usize],
d.year())), d.year())),
// hour // hour
@ -141,8 +140,8 @@ fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveT
// combined date and time // combined date and time
(Some('c'), Some(d), Some(t), _) => // `%a %b %e %T %Y` (Some('c'), Some(d), Some(t), _) => // `%a %b %e %T %Y`
try!(write!(w, "{} {} {:2} {:02}:{:02}:{:02} {:04}", try!(write!(w, "{} {} {:2} {:02}:{:02}:{:02} {:04}",
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as uint], SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
SHORT_MONTHS[d.month0() as uint], d.day(), SHORT_MONTHS[d.month0() as usize], d.day(),
t.hour(), t.minute(), t.second(), d.year())), t.hour(), t.minute(), t.second(), d.year())),
(Some('+'), Some(d), Some(t), (Some('+'), Some(d), Some(t),
Some(&(_, ref local_minus_utc))) => { // `%Y-%m-%dT%H:%M:%S` plus tz Some(&(_, ref local_minus_utc))) => { // `%Y-%m-%dT%H:%M:%S` plus tz
@ -180,13 +179,14 @@ fn format(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveT
/// A *temporary* object which can be used as an argument to `format!` or others. /// A *temporary* object which can be used as an argument to `format!` or others.
/// This is normally constructed via `format` methods of each date and time type. /// This is normally constructed via `format` methods of each date and time type.
#[derive(Show)]
pub struct DelayedFormat<'a> { pub struct DelayedFormat<'a> {
/// The date view, if any. /// The date view, if any.
date: Option<NaiveDate>, date: Option<NaiveDate>,
/// The time view, if any. /// The time view, if any.
time: Option<NaiveTime>, time: Option<NaiveTime>,
/// The name and local-to-UTC difference for the offset (timezone), if any. /// The name and local-to-UTC difference for the offset (timezone), if any.
off: Option<(CowString<'static>, Duration)>, off: Option<(String, Duration)>,
/// The format string. /// The format string.
fmt: &'a str, fmt: &'a str,
} }
@ -199,14 +199,15 @@ impl<'a> DelayedFormat<'a> {
} }
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset. /// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
pub fn new_with_offset<Off:Offset>(date: Option<NaiveDate>, time: Option<NaiveTime>, pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
offset: &Off, fmt: &'a str) -> DelayedFormat<'a> { offset: &Off, fmt: &'a str) -> DelayedFormat<'a>
let name_and_diff = (offset.name(), offset.local_minus_utc()); where Off: Offset + fmt::String {
let name_and_diff = (offset.to_string(), offset.local_minus_utc());
DelayedFormat { date: date, time: time, off: Some(name_and_diff), fmt: fmt } DelayedFormat { date: date, time: time, off: Some(name_and_diff), fmt: fmt }
} }
} }
impl<'a> fmt::Show for DelayedFormat<'a> { impl<'a> fmt::String for DelayedFormat<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ret = format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.fmt); let ret = format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.fmt);
ret.map_err(|_| fmt::Error) // we don't have any good means to pass detailed errors... ret.map_err(|_| fmt::Error) // we don't have any good means to pass detailed errors...

View File

@ -4,7 +4,7 @@
/*! /*!
# Chrono 0.1.12 # Chrono 0.1.13
Date and time handling for Rust. (also known as `rust-chrono`) Date and time handling for Rust. (also known as `rust-chrono`)
It aims to be a feature-complete superset of the [time](https://github.com/rust-lang/time) library. It aims to be a feature-complete superset of the [time](https://github.com/rust-lang/time) library.
@ -78,6 +78,7 @@ Addition and subtraction is also supported.
The following illustrates most supported operations to the date and time: The following illustrates most supported operations to the date and time:
~~~~ {.rust} ~~~~ {.rust}
# #![allow(unstable)]
# /* we intentionally fake the datetime... # /* we intentionally fake the datetime...
use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration}; use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration};
@ -116,17 +117,19 @@ assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_00
Formatting is done via the `format` method, Formatting is done via the `format` method,
which format is equivalent to the familiar `strftime` format. which format is equivalent to the familiar `strftime` format.
The default `to_string` method also gives a reasonable representation. The default `to_string` method and `{:?}` specifier also give a reasonable representation.
~~~~ {.rust} ~~~~ {.rust}
# #![allow(unstable)]
use chrono::{UTC, Offset}; use chrono::{UTC, Offset};
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9); let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09".to_string()); assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014".to_string()); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
assert_eq!(dt.to_string(), "2014-11-28T12:00:09Z".to_string()); assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~ ~~~~
### Individual date and time ### Individual date and time
@ -137,6 +140,7 @@ Most operations available to `DateTime` are also available to `Date` and `Time`
whenever appropriate. whenever appropriate.
~~~~ {.rust} ~~~~ {.rust}
# #![allow(unstable)]
use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday}; use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday};
# // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;) # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
@ -145,7 +149,7 @@ assert_eq!(Local::today(), Local::now().date());
assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri); assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None); assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809".to_string()); assert_eq!(UTC.hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), "070809");
~~~~ ~~~~
`DateTime` has two methods, `date` and `time`, `DateTime` has two methods, `date` and `time`,
@ -184,6 +188,7 @@ Advanced offset handling and date/time parsing is not yet supported (but is plan
#![doc(html_root_url = "https://lifthrasiir.github.io/rust-chrono/")] #![doc(html_root_url = "https://lifthrasiir.github.io/rust-chrono/")]
#![allow(unstable)]
#![deny(missing_docs)] #![deny(missing_docs)]
extern crate "time" as stdtime; extern crate "time" as stdtime;

View File

@ -6,7 +6,7 @@
* ISO 8601 calendar date without timezone. * ISO 8601 calendar date without timezone.
*/ */
use std::fmt; use std::{fmt, hash};
use std::num::{Int, ToPrimitive}; use std::num::{Int, ToPrimitive};
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -25,7 +25,7 @@ const MIN_YEAR: i32 = internals::MIN_YEAR as i32;
/// ISO 8601 calendar date without timezone. /// ISO 8601 calendar date without timezone.
/// Allows for every proleptic Gregorian date from Jan 1, 262145 BCE to Dec 31, 262143 CE. /// Allows for every proleptic Gregorian date from Jan 1, 262145 BCE to Dec 31, 262143 CE.
/// Also supports the conversion from ISO 8601 ordinal and week date. /// Also supports the conversion from ISO 8601 ordinal and week date.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct NaiveDate { pub struct NaiveDate {
ymdf: DateImpl, // (year << 13) | of ymdf: DateImpl, // (year << 13) | of
} }
@ -40,8 +40,10 @@ pub const MAX: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o1
fn test_date_bounds() { fn test_date_bounds() {
let calculated_min = NaiveDate::from_ymd(MIN_YEAR, 1, 1); let calculated_min = NaiveDate::from_ymd(MIN_YEAR, 1, 1);
let calculated_max = NaiveDate::from_ymd(MAX_YEAR, 12, 31); let calculated_max = NaiveDate::from_ymd(MAX_YEAR, 12, 31);
assert!(MIN == calculated_min, "`MIN` should have a year flag {}", calculated_min.of().flags()); assert!(MIN == calculated_min,
assert!(MAX == calculated_max, "`MAX` should have a year flag {}", calculated_max.of().flags()); "`MIN` should have a year flag {:?}", calculated_min.of().flags());
assert!(MAX == calculated_max,
"`MAX` should have a year flag {:?}", calculated_max.of().flags());
} }
impl NaiveDate { impl NaiveDate {
@ -378,6 +380,10 @@ impl Datelike for NaiveDate {
} }
} }
impl<H: hash::Hasher + hash::Writer> hash::Hash<H> for NaiveDate {
fn hash(&self, state: &mut H) { self.ymdf.hash(state) }
}
impl Add<Duration> for NaiveDate { impl Add<Duration> for NaiveDate {
type Output = NaiveDate; type Output = NaiveDate;
@ -432,6 +438,18 @@ impl fmt::Show for NaiveDate {
} }
} }
impl fmt::String for NaiveDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let year = self.year();
let mdf = self.mdf();
if year >= 0 {
write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day())
} else {
write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day())
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{NaiveDate, MIN, MAX}; use super::{NaiveDate, MIN, MAX};
@ -736,35 +754,40 @@ mod tests {
#[test] #[test]
fn test_date_fmt() { fn test_date_fmt() {
assert_eq!(NaiveDate::from_ymd(2012, 3, 4).to_string(), "2012-03-04".to_string()); assert_eq!(format!("{:?}", NaiveDate::from_ymd(2012, 3, 4)), "2012-03-04");
assert_eq!(NaiveDate::from_ymd(0, 3, 4).to_string(), "0000-03-04".to_string()); assert_eq!(format!("{:?}", NaiveDate::from_ymd(0, 3, 4)), "0000-03-04");
assert_eq!(NaiveDate::from_ymd(-307, 3, 4).to_string(), "-0307-03-04".to_string()); assert_eq!(format!("{:?}", NaiveDate::from_ymd(-307, 3, 4)), "-0307-03-04");
assert_eq!(NaiveDate::from_ymd(12345, 3, 4).to_string(), "+12345-03-04".to_string()); assert_eq!(format!("{:?}", NaiveDate::from_ymd(12345, 3, 4)), "+12345-03-04");
assert_eq!(NaiveDate::from_ymd(2012, 3, 4).to_string(), "2012-03-04");
assert_eq!(NaiveDate::from_ymd(0, 3, 4).to_string(), "0000-03-04");
assert_eq!(NaiveDate::from_ymd(-307, 3, 4).to_string(), "-0307-03-04");
assert_eq!(NaiveDate::from_ymd(12345, 3, 4).to_string(), "12345-03-04");
// the format specifier should have no effect on `NaiveTime` // the format specifier should have no effect on `NaiveTime`
assert_eq!(format!("{:+30}", NaiveDate::from_ymd(1234, 5, 6)), "1234-05-06".to_string()); assert_eq!(format!("{:+30?}", NaiveDate::from_ymd(1234, 5, 6)), "1234-05-06");
assert_eq!(format!("{:30}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07".to_string()); assert_eq!(format!("{:30?}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07");
} }
#[test] #[test]
fn test_date_format() { fn test_date_format() {
let d = NaiveDate::from_ymd(2012, 3, 4); let d = NaiveDate::from_ymd(2012, 3, 4);
assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12".to_string()); assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12");
assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March".to_string()); assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March");
assert_eq!(d.format("%d,%e").to_string(), "04, 4".to_string()); assert_eq!(d.format("%d,%e").to_string(), "04, 4");
assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09".to_string()); assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09");
assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7".to_string()); assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7");
assert_eq!(d.format("%j").to_string(), "064".to_string()); // since 2012 is a leap year assert_eq!(d.format("%j").to_string(), "064"); // since 2012 is a leap year
assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12".to_string()); assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12");
assert_eq!(d.format("%F").to_string(), "2012-03-04".to_string()); assert_eq!(d.format("%F").to_string(), "2012-03-04");
assert_eq!(d.format("%v").to_string(), " 4-Mar-2012".to_string()); assert_eq!(d.format("%v").to_string(), " 4-Mar-2012");
assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t".to_string()); assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
// corner cases // corner cases
assert_eq!(NaiveDate::from_ymd(2007, 12, 31).format("%G,%g,%U,%W,%V").to_string(), assert_eq!(NaiveDate::from_ymd(2007, 12, 31).format("%G,%g,%U,%W,%V").to_string(),
"2008,08,53,53,01".to_string()); "2008,08,53,53,01");
assert_eq!(NaiveDate::from_ymd(2010, 1, 3).format("%G,%g,%U,%W,%V").to_string(), assert_eq!(NaiveDate::from_ymd(2010, 1, 3).format("%G,%g,%U,%W,%V").to_string(),
"2009,09,01,00,53".to_string()); "2009,09,01,00,53");
} }
} }
@ -860,10 +883,10 @@ mod internals {
pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365); let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
let delta = YEAR_DELTAS[year_mod_400 as uint] as u32; let delta = YEAR_DELTAS[year_mod_400 as usize] as u32;
if ordinal0 < delta { if ordinal0 < delta {
year_mod_400 -= 1; year_mod_400 -= 1;
ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as uint] as u32; ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
} else { } else {
ordinal0 -= delta; ordinal0 -= delta;
} }
@ -871,7 +894,7 @@ mod internals {
} }
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as uint] as u32 + ordinal - 1 year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1
} }
impl YearFlags { impl YearFlags {
@ -883,7 +906,7 @@ mod internals {
#[inline] #[inline]
pub fn from_year_mod_400(year: i32) -> YearFlags { pub fn from_year_mod_400(year: i32) -> YearFlags {
YEAR_TO_FLAGS[year as uint] YEAR_TO_FLAGS[year as usize]
} }
#[inline] #[inline]
@ -903,7 +926,7 @@ mod internals {
#[inline] #[inline]
pub fn nisoweeks(&self) -> u32 { pub fn nisoweeks(&self) -> u32 {
let YearFlags(flags) = *self; let YearFlags(flags) = *self;
52 + ((0b00000100_00000110 >> flags as uint) & 1) 52 + ((0b00000100_00000110 >> flags as usize) & 1)
} }
} }
@ -930,7 +953,7 @@ mod internals {
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
const XX: i8 = -128; const XX: i8 = -128;
static MDL_TO_OL: [i8; (MAX_MDL as uint + 1u)] = [ static MDL_TO_OL: [i8; (MAX_MDL as usize + 1us)] = [
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
@ -985,7 +1008,7 @@ mod internals {
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12
]; ];
static OL_TO_MDL: [u8; (MAX_OL as uint + 1u)] = [ static OL_TO_MDL: [u8; (MAX_OL as usize + 1us)] = [
0, 0, // 0 0, 0, // 0
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
@ -1059,7 +1082,7 @@ mod internals {
#[inline] #[inline]
pub fn from_mdf(Mdf(mdf): Mdf) -> Of { pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
let mdl = mdf >> 3; let mdl = mdf >> 3;
match MDL_TO_OL.get(mdl as uint) { match MDL_TO_OL.get(mdl as usize) {
Some(&v) => Of(mdf - ((v as i32 as u32 & 0x3ff) << 3)), Some(&v) => Of(mdf - ((v as i32 as u32 & 0x3ff) << 3)),
None => Of(0) None => Of(0)
} }
@ -1132,7 +1155,7 @@ mod internals {
impl fmt::Show for Of { impl fmt::Show for Of {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Of(of) = *self; let Of(of) = *self;
write!(f, "Of(({} << 4) | {:#04o} /*{}*/)", write!(f, "Of(({} << 4) | {:#04o} /*{:?}*/)",
of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8)) of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8))
} }
} }
@ -1166,7 +1189,7 @@ mod internals {
#[inline] #[inline]
pub fn from_of(Of(of): Of) -> Mdf { pub fn from_of(Of(of): Of) -> Mdf {
let ol = of >> 3; let ol = of >> 3;
match OL_TO_MDL.get(ol as uint) { match OL_TO_MDL.get(ol as usize) {
Some(&v) => Mdf(of + ((v as u32) << 3)), Some(&v) => Mdf(of + ((v as u32) << 3)),
None => Mdf(0) None => Mdf(0)
} }
@ -1176,7 +1199,7 @@ mod internals {
pub fn valid(&self) -> bool { pub fn valid(&self) -> bool {
let Mdf(mdf) = *self; let Mdf(mdf) = *self;
let mdl = mdf >> 3; let mdl = mdf >> 3;
match MDL_TO_OL.get(mdl as uint) { match MDL_TO_OL.get(mdl as usize) {
Some(&v) => v >= 0, Some(&v) => v >= 0,
None => false None => false
} }
@ -1229,7 +1252,7 @@ mod internals {
impl fmt::Show for Mdf { impl fmt::Show for Mdf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Mdf(mdf) = *self; let Mdf(mdf) = *self;
write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{}*/)", write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
mdf >> 9, (mdf >> 4) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8)) mdf >> 9, (mdf >> 4) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8))
} }
} }
@ -1298,7 +1321,7 @@ mod internals {
for ordinal in range_inclusive(ordinal1, ordinal2) { for ordinal in range_inclusive(ordinal1, ordinal2) {
let of = Of::new(ordinal, flags); let of = Of::new(ordinal, flags);
assert!(of.valid() == expected, assert!(of.valid() == expected,
"ordinal {} = {} should be {} for dominical year {}", "ordinal {} = {:?} should be {} for dominical year {:?}",
ordinal, of, if expected {"valid"} else {"invalid"}, flags); ordinal, of, if expected {"valid"} else {"invalid"}, flags);
} }
} }
@ -1326,7 +1349,7 @@ mod internals {
for day in range_inclusive(day1, day2) { for day in range_inclusive(day1, day2) {
let mdf = Mdf::new(month, day, flags); let mdf = Mdf::new(month, day, flags);
assert!(mdf.valid() == expected, assert!(mdf.valid() == expected,
"month {} day {} = {} should be {} for dominical year {}", "month {} day {} = {:?} should be {} for dominical year {:?}",
month, day, mdf, if expected {"valid"} else {"invalid"}, flags); month, day, mdf, if expected {"valid"} else {"invalid"}, flags);
} }
} }

View File

@ -6,7 +6,7 @@
* ISO 8601 date and time without timezone. * ISO 8601 date and time without timezone.
*/ */
use std::fmt; use std::{fmt, hash};
use std::num::{Int, ToPrimitive}; use std::num::{Int, ToPrimitive};
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -18,7 +18,7 @@ use naive::date::NaiveDate;
use format::DelayedFormat; use format::DelayedFormat;
/// ISO 8601 combined date and time without timezone. /// ISO 8601 combined date and time without timezone.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct NaiveDateTime { pub struct NaiveDateTime {
date: NaiveDate, date: NaiveDate,
time: NaiveTime, time: NaiveTime,
@ -163,6 +163,10 @@ impl Timelike for NaiveDateTime {
} }
} }
impl<H: hash::Hasher + hash::Writer> hash::Hash<H> for NaiveDateTime {
fn hash(&self, state: &mut H) { self.date.hash(state); self.time.hash(state) }
}
impl Add<Duration> for NaiveDateTime { impl Add<Duration> for NaiveDateTime {
type Output = NaiveDateTime; type Output = NaiveDateTime;
@ -203,7 +207,13 @@ impl Sub<Duration> for NaiveDateTime {
impl fmt::Show for NaiveDateTime { impl fmt::Show for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}T{}", self.date, self.time) write!(f, "{:?}T{:?}", self.date, self.time)
}
}
impl fmt::String for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.date, self.time)
} }
} }
@ -270,8 +280,8 @@ mod tests {
#[test] #[test]
fn test_datetime_format() { fn test_datetime_format() {
let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321); let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321);
assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010".to_string()); assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010");
assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t".to_string()); assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
} }
} }

View File

@ -6,7 +6,7 @@
* ISO 8601 time without timezone. * ISO 8601 time without timezone.
*/ */
use std::fmt; use std::{fmt, hash};
use std::num::Int; use std::num::Int;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -18,7 +18,7 @@ use format::DelayedFormat;
/// ISO 8601 time without timezone. /// ISO 8601 time without timezone.
/// Allows for the nanosecond precision and optional leap second representation. /// Allows for the nanosecond precision and optional leap second representation.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct NaiveTime { pub struct NaiveTime {
secs: u32, secs: u32,
frac: u32, frac: u32,
@ -172,6 +172,10 @@ impl Timelike for NaiveTime {
} }
} }
impl<H: hash::Hasher + hash::Writer> hash::Hash<H> for NaiveTime {
fn hash(&self, state: &mut H) { self.secs.hash(state); self.frac.hash(state) }
}
impl Add<Duration> for NaiveTime { impl Add<Duration> for NaiveTime {
type Output = NaiveTime; type Output = NaiveTime;
@ -241,6 +245,10 @@ impl fmt::Show for NaiveTime {
} }
} }
impl fmt::String for NaiveTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Show::fmt(self, f) }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::NaiveTime; use super::NaiveTime;
@ -350,36 +358,29 @@ mod tests {
#[test] #[test]
fn test_time_fmt() { fn test_time_fmt() {
assert_eq!(NaiveTime::from_hms_milli(23, 59, 59, 999).to_string(), assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 999)), "23:59:59.999");
"23:59:59.999".to_string()); assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_000)), "23:59:60");
assert_eq!(NaiveTime::from_hms_milli(23, 59, 59, 1_000).to_string(), assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_001)), "23:59:60.001");
"23:59:60".to_string()); assert_eq!(format!("{}", NaiveTime::from_hms_micro(0, 0, 0, 43210)), "00:00:00.043210");
assert_eq!(NaiveTime::from_hms_milli(23, 59, 59, 1_001).to_string(), assert_eq!(format!("{}", NaiveTime::from_hms_nano(0, 0, 0, 6543210)), "00:00:00.006543210");
"23:59:60.001".to_string());
assert_eq!(NaiveTime::from_hms_micro(0, 0, 0, 43210).to_string(),
"00:00:00.043210".to_string());
assert_eq!(NaiveTime::from_hms_nano(0, 0, 0, 6543210).to_string(),
"00:00:00.006543210".to_string());
// the format specifier should have no effect on `NaiveTime` // the format specifier should have no effect on `NaiveTime`
assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), "03:05:07.009");
"03:05:07.009".to_string());
} }
#[test] #[test]
fn test_time_format() { fn test_time_format() {
let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432);
assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM".to_string()); assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM");
assert_eq!(t.format("%M").to_string(), "05".to_string()); assert_eq!(t.format("%M").to_string(), "05");
assert_eq!(t.format("%S,%f").to_string(), "07,098765432".to_string()); assert_eq!(t.format("%S,%f").to_string(), "07,098765432");
assert_eq!(t.format("%R").to_string(), "03:05".to_string()); assert_eq!(t.format("%R").to_string(), "03:05");
assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07".to_string()); assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07");
assert_eq!(t.format("%r").to_string(), "03:05:07 AM".to_string()); assert_eq!(t.format("%r").to_string(), "03:05:07 AM");
assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t".to_string()); assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
// corner cases // corner cases
assert_eq!(NaiveTime::from_hms(13, 57, 9).format("%r").to_string(), assert_eq!(NaiveTime::from_hms(13, 57, 9).format("%r").to_string(), "01:57:09 PM");
"01:57:09 PM".to_string());
} }
} }

View File

@ -6,9 +6,7 @@
* Offsets from the local time to UTC. * Offsets from the local time to UTC.
*/ */
use std::borrow::IntoCow;
use std::fmt; use std::fmt;
use std::string::CowString;
use stdtime; use stdtime;
use {Weekday, Datelike, Timelike}; use {Weekday, Datelike, Timelike};
@ -125,14 +123,14 @@ impl<Off:Offset> LocalResult<Date<Off>> {
} }
impl<T:fmt::Show> LocalResult<T> { impl<T: fmt::Show> LocalResult<T> {
/// Returns the single unique conversion result, or fails accordingly. /// Returns the single unique conversion result, or fails accordingly.
pub fn unwrap(self) -> T { pub fn unwrap(self) -> T {
match self { match self {
LocalResult::None => panic!("No such local time"), LocalResult::None => panic!("No such local time"),
LocalResult::Single(t) => t, LocalResult::Single(t) => t,
LocalResult::Ambiguous(t1,t2) => { LocalResult::Ambiguous(t1,t2) => {
panic!("Ambiguous local time, ranging from {} to {}", t1, t2) panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
} }
} }
} }
@ -293,9 +291,6 @@ pub trait Offset: Clone + fmt::Show {
} }
} }
/// Returns a name or abbreviation of this offset.
fn name(&self) -> CowString<'static>;
/// Returns the *current* offset from UTC to the local time. /// Returns the *current* offset from UTC to the local time.
fn local_minus_utc(&self) -> Duration; fn local_minus_utc(&self) -> Duration;
@ -338,7 +333,6 @@ impl UTC {
} }
impl Offset for UTC { impl Offset for UTC {
fn name(&self) -> CowString<'static> { "UTC".into_cow() }
fn local_minus_utc(&self) -> Duration { Duration::zero() } fn local_minus_utc(&self) -> Duration { Duration::zero() }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC>> {
@ -360,6 +354,10 @@ impl fmt::Show for UTC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
} }
impl fmt::String for UTC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
}
/// The fixed offset, from UTC-23:59:59 to UTC+23:59:59. /// The fixed offset, from UTC-23:59:59 to UTC+23:59:59.
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub struct FixedOffset { pub struct FixedOffset {
@ -409,7 +407,6 @@ impl FixedOffset {
} }
impl Offset for FixedOffset { impl Offset for FixedOffset {
fn name(&self) -> CowString<'static> { "UTC".into_cow() } // XXX
fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) } fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<FixedOffset>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<FixedOffset>> {
@ -451,6 +448,10 @@ impl fmt::Show for FixedOffset {
} }
} }
impl fmt::String for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Show::fmt(self, f) }
}
/// The local timescale. This is implemented via the standard `time` crate. /// The local timescale. This is implemented via the standard `time` crate.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Local { pub struct Local {
@ -505,7 +506,6 @@ impl Local {
} }
impl Offset for Local { impl Offset for Local {
fn name(&self) -> CowString<'static> { "LMT".into_cow() } // XXX XXX
fn local_minus_utc(&self) -> Duration { self.cached.local_minus_utc() } fn local_minus_utc(&self) -> Duration { self.cached.local_minus_utc() }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {

View File

@ -44,17 +44,19 @@ impl<Off:Offset> Time<Off> {
Time::from_utc(self.time, offset) Time::from_utc(self.time, offset)
} }
/// Returns a view to the local time.
fn local(&self) -> NaiveTime {
self.offset.to_local_time(&self.time)
}
}
impl<Off: Offset + fmt::String> Time<Off> {
/// Formats the time in the specified format string. /// Formats the time in the specified format string.
/// See the `format` module on the supported escape sequences. /// See the `format` module on the supported escape sequences.
#[inline] #[inline]
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> { pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
DelayedFormat::new_with_offset(None, Some(self.local()), &self.offset, fmt) DelayedFormat::new_with_offset(None, Some(self.local()), &self.offset, fmt)
} }
/// Returns a view to the local time.
fn local(&self) -> NaiveTime {
self.offset.to_local_time(&self.time)
}
} }
impl<Off:Offset> Timelike for Time<Off> { impl<Off:Offset> Timelike for Time<Off> {
@ -108,8 +110,8 @@ impl<Off:Offset> Ord for Time<Off> {
fn cmp(&self, other: &Time<Off>) -> Ordering { self.time.cmp(&other.time) } fn cmp(&self, other: &Time<Off>) -> Ordering { self.time.cmp(&other.time) }
} }
impl<Off:Offset> hash::Hash for Time<Off> { impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for Time<Off> {
fn hash(&self, state: &mut hash::sip::SipState) { self.time.hash(state) } fn hash(&self, state: &mut H) { self.time.hash(state) }
} }
impl<Off:Offset> Add<Duration> for Time<Off> { impl<Off:Offset> Add<Duration> for Time<Off> {
@ -133,7 +135,13 @@ impl<Off:Offset> Sub<Duration> for Time<Off> {
fn sub(self, rhs: Duration) -> Time<Off> { self.add(-rhs) } fn sub(self, rhs: Duration) -> Time<Off> { self.add(-rhs) }
} }
impl<Off:Offset> fmt::Show for Time<Off> { impl<Off: Offset> fmt::Show for Time<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.local(), self.offset)
}
}
impl<Off: Offset + fmt::String> fmt::String for Time<Off> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.local(), self.offset) write!(f, "{}{}", self.local(), self.offset)
} }