merged the new offset design branch.

This commit is contained in:
Kang Seonghoon 2015-02-19 01:48:29 +09:00
commit 664c4d0191
13 changed files with 861 additions and 581 deletions

View File

@ -15,5 +15,5 @@ license = "MIT/Apache-2.0"
name = "chrono"
[dependencies]
time = "0.1.15"
time = "0.1.16"

View File

@ -36,7 +36,7 @@ chrono = "0.2"
And this in your crate root:
```toml
```rust
extern crate chrono;
```
@ -53,14 +53,14 @@ Chrono simply reexports it.
Chrono provides a `DateTime` type for the combined date and time.
`DateTime`, among others, is timezone-aware and
must be constructed from the timezone object (`Offset`).
`DateTime`s with different offsets do not mix, but can be converted to each other.
must be constructed from the `TimeZone` object.
`DateTime`s with different time zones do not mix, but can be converted to each other.
You can get the current date and time in the UTC timezone (`UTC::now()`)
or in the local timezone (`Local::now()`).
~~~~ {.rust}
use chrono::{UTC, Local, DateTime};
use chrono::*;
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
@ -71,7 +71,7 @@ This is a bit verbose due to Rust's lack of function and method overloading,
but in turn we get a rich combination of initialization methods.
~~~~ {.rust}
use chrono::{UTC, Offset, Weekday, LocalResult};
use chrono::*;
let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
@ -96,7 +96,7 @@ Addition and subtraction is also supported.
The following illustrates most supported operations to the date and time:
~~~~ {.rust}
use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration};
use chrono::*;
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
let dt = Local::now();
@ -110,9 +110,10 @@ assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sat=7
assert_eq!(dt.ordinal(), 332); // the day of year
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
// offset accessor and manipulation
// time zone accessor and manipulation
assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
assert_eq!(dt.with_offset(UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
// a sample of property manipulations (validates dynamically)
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
@ -134,7 +135,7 @@ which format is equivalent to the familiar `strftime` format.
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
~~~~ {.rust}
use chrono::{UTC, Offset};
use chrono::*;
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");
@ -145,27 +146,40 @@ assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~
Parsing can be done with two methods:
Parsing can be done with three methods:
- `DateTime::parse_from_str` parses a date and time with offsets and
returns `DateTime<FixedOffset>`.
This should be used when the offset is a part of input and the caller cannot guess that.
It *cannot* be used when the offset can be missing.
1. The standard `FromStr` trait (and `parse` method on a string) can be used for
parsing `DateTime<FixedOffset>` and `DateTime<UTC>` values.
This parses what the `{:?}` (`std::fmt::Debug`) format specifier prints,
and requires the offset to be present.
- `Offset::datetime_from_str` is similar but returns `DateTime` of given offset.
When the explicit offset is missing from the input, it simply uses given offset.
It issues an error when the input contains an explicit offset different from the current offset.
2. `DateTime::parse_from_str` parses a date and time with offsets and
returns `DateTime<FixedOffset>`.
This should be used when the offset is a part of input and the caller cannot guess that.
It *cannot* be used when the offset can be missing.
3. `Offset::datetime_from_str` is similar but returns `DateTime` of given offset.
When the explicit offset is missing from the input, it simply uses given offset.
It issues an error when the input contains an explicit offset different from the current offset.
More detailed control over the parsing process is available via `format` module.
~~~~ {.rust}
use chrono::{UTC, Offset, DateTime};
use chrono::*;
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
// method 1
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
// method 2
assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00",
"%Y-%m-%d %H:%M:%S %z").map(|dt| dt.with_offset(UTC)), Ok(dt));
// method 3
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"),
Ok(dt.with_timezone(&FixedOffset::east(9*3600))));
// oops, the year is missing!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
@ -178,12 +192,12 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e
### Individual date and time
Chrono also provides an individual date type (`Date`) and time type (`Time`).
They also have offsets attached, and have to be constructed via offsets.
They also have time zones attached, and have to be constructed via time zones.
Most operations available to `DateTime` are also available to `Date` and `Time`
whenever appropriate.
~~~~ {.rust}
use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday};
use chrono::*;
assert_eq!(UTC::today(), UTC::now().date());
assert_eq!(Local::today(), Local::now().date());
@ -202,7 +216,7 @@ Chrono provides naive counterparts to `Date`, `Time` and `DateTime`
as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively.
They have almost equivalent interfaces as their timezone-aware twins,
but are not associated to offsets obviously and can be quite low-level.
but are not associated to time zones obviously and can be quite low-level.
They are mostly useful for building blocks for higher-level types.
## Limitations
@ -223,4 +237,5 @@ Any operation that can be ambiguous will return `None` in such cases.
For example, "a month later" of 2014-01-30 is not well-defined
and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
Advanced offset handling is not yet supported (but is planned in 0.3).
Advanced time zone handling is not yet supported (but is planned in 0.3).

View File

@ -3,7 +3,7 @@
// See README.md and LICENSE.txt for details.
/*!
* ISO 8601 calendar date with timezone.
* ISO 8601 calendar date with time zone.
*/
use std::{fmt, hash};
@ -12,18 +12,19 @@ use std::ops::{Add, Sub};
use {Weekday, Datelike};
use duration::Duration;
use offset::{Offset, UTC};
use offset::{TimeZone, Offset};
use offset::utc::UTC;
use naive;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use datetime::DateTime;
use format::{Item, DelayedFormat, StrftimeItems};
/// ISO 8601 calendar date with timezone.
/// ISO 8601 calendar date with time zone.
#[derive(Clone)]
pub struct Date<Off> {
pub struct Date<Tz: TimeZone> {
date: NaiveDate,
offset: Off,
offset: Tz::Offset,
}
/// The minimum possible `Date`.
@ -31,11 +32,13 @@ pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
/// The maximum possible `Date`.
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };
impl<Off:Offset> Date<Off> {
impl<Tz: TimeZone> Date<Tz> {
/// Makes a new `Date` with given *UTC* date and offset.
/// The local date should be constructed via the `Offset` trait.
/// The local date should be constructed via the `TimeZone` trait.
//
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
#[inline]
pub fn from_utc(date: NaiveDate, offset: Off) -> Date<Off> {
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
Date { date: date, offset: offset }
}
@ -44,9 +47,9 @@ impl<Off:Offset> Date<Off> {
///
/// Fails on invalid datetime.
#[inline]
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Off>> {
let localdt = self.offset.to_local_date(&self.date).and_time(time);
self.offset.from_local_datetime(&localdt).single()
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
let localdt = self.naive_local().and_time(time);
self.timezone().from_local_datetime(&localdt).single()
}
/// Makes a new `DateTime` from the current date, hour, minute and second.
@ -54,7 +57,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails on invalid hour, minute and/or second.
#[inline]
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Off> {
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> {
self.and_hms_opt(hour, min, sec).expect("invalid time")
}
@ -63,7 +66,7 @@ impl<Off:Offset> Date<Off> {
///
/// Returns `None` on invalid hour, minute and/or second.
#[inline]
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Off>> {
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
}
@ -73,7 +76,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails on invalid hour, minute, second and/or millisecond.
#[inline]
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Off> {
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> {
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
@ -84,7 +87,7 @@ impl<Off:Offset> Date<Off> {
/// Returns `None` on invalid hour, minute, second and/or millisecond.
#[inline]
pub fn and_hms_milli_opt(&self, hour: u32, min: u32, sec: u32,
milli: u32) -> Option<DateTime<Off>> {
milli: u32) -> Option<DateTime<Tz>> {
NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
}
@ -94,7 +97,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails on invalid hour, minute, second and/or microsecond.
#[inline]
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Off> {
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> {
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
@ -105,7 +108,7 @@ impl<Off:Offset> Date<Off> {
/// Returns `None` on invalid hour, minute, second and/or microsecond.
#[inline]
pub fn and_hms_micro_opt(&self, hour: u32, min: u32, sec: u32,
micro: u32) -> Option<DateTime<Off>> {
micro: u32) -> Option<DateTime<Tz>> {
NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
}
@ -115,7 +118,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails on invalid hour, minute, second and/or nanosecond.
#[inline]
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Off> {
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> {
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
@ -126,7 +129,7 @@ impl<Off:Offset> Date<Off> {
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
#[inline]
pub fn and_hms_nano_opt(&self, hour: u32, min: u32, sec: u32,
nano: u32) -> Option<DateTime<Off>> {
nano: u32) -> Option<DateTime<Tz>> {
NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
}
@ -134,7 +137,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails when `self` is the last representable date.
#[inline]
pub fn succ(&self) -> Date<Off> {
pub fn succ(&self) -> Date<Tz> {
self.succ_opt().expect("out of bound")
}
@ -142,7 +145,7 @@ impl<Off:Offset> Date<Off> {
///
/// Returns `None` when `self` is the last representable date.
#[inline]
pub fn succ_opt(&self) -> Option<Date<Off>> {
pub fn succ_opt(&self) -> Option<Date<Tz>> {
self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
}
@ -150,7 +153,7 @@ impl<Off:Offset> Date<Off> {
///
/// Fails when `self` is the first representable date.
#[inline]
pub fn pred(&self) -> Date<Off> {
pub fn pred(&self) -> Date<Tz> {
self.pred_opt().expect("out of bound")
}
@ -158,28 +161,34 @@ impl<Off:Offset> Date<Off> {
///
/// Returns `None` when `self` is the first representable date.
#[inline]
pub fn pred_opt(&self) -> Option<Date<Off>> {
pub fn pred_opt(&self) -> Option<Date<Tz>> {
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
}
/// Retrieves an associated offset.
/// Retrieves an associated offset from UTC.
#[inline]
pub fn offset<'a>(&'a self) -> &'a Off {
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
&self.offset
}
/// Changes the associated offset.
/// Retrieves an associated time zone.
#[inline]
pub fn timezone(&self) -> Tz {
TimeZone::from_offset(&self.offset)
}
/// Changes the associated time zone.
/// This does not change the actual `Date` (but will change the string representation).
#[inline]
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Date<Off2> {
Date::from_utc(self.date, offset)
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
tz.from_utc_date(&self.date)
}
/// Adds given `Duration` to the current date.
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Date<Off>> {
pub fn checked_add(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_add(rhs));
Some(Date { date: date, offset: self.offset })
}
@ -188,23 +197,36 @@ impl<Off:Offset> Date<Off> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Date<Off>> {
pub fn checked_sub(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_sub(rhs));
Some(Date { date: date, offset: self.offset })
}
/// Returns a view to the local date.
fn local(&self) -> NaiveDate {
self.offset.to_local_date(&self.date)
/// Returns a view to the naive UTC date.
#[inline]
pub fn naive_utc(&self) -> NaiveDate {
self.date
}
/// Returns a view to the naive local date.
#[inline]
pub fn naive_local(&self) -> NaiveDate {
self.date + self.offset.local_minus_utc()
}
}
impl<Off: Offset + fmt::Display> Date<Off> {
/// Maps the local date to other date with given conversion function.
fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>>
where F: FnMut(NaiveDate) -> Option<NaiveDate> {
f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single())
}
impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
/// Formats the date with the specified formatting items.
#[inline]
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
where I: Iterator<Item=Item<'a>> + Clone {
DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, items)
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
}
/// Formats the date with the specified format string.
@ -215,115 +237,108 @@ impl<Off: Offset + fmt::Display> Date<Off> {
}
}
impl<Off:Offset> Datelike for Date<Off> {
#[inline] fn year(&self) -> i32 { self.local().year() }
#[inline] fn month(&self) -> u32 { self.local().month() }
#[inline] fn month0(&self) -> u32 { self.local().month0() }
#[inline] fn day(&self) -> u32 { self.local().day() }
#[inline] fn day0(&self) -> u32 { self.local().day0() }
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
impl<Tz: TimeZone> Datelike for Date<Tz> {
#[inline] fn year(&self) -> i32 { self.naive_local().year() }
#[inline] fn month(&self) -> u32 { self.naive_local().month() }
#[inline] fn month0(&self) -> u32 { self.naive_local().month0() }
#[inline] fn day(&self) -> u32 { self.naive_local().day() }
#[inline] fn day0(&self) -> u32 { self.naive_local().day0() }
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
#[inline]
fn with_year(&self, year: i32) -> Option<Date<Off>> {
self.local().with_year(year)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_year(year))
}
#[inline]
fn with_month(&self, month: u32) -> Option<Date<Off>> {
self.local().with_month(month)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_month(&self, month: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_month(month))
}
#[inline]
fn with_month0(&self, month0: u32) -> Option<Date<Off>> {
self.local().with_month0(month0)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_month0(&self, month0: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_month0(month0))
}
#[inline]
fn with_day(&self, day: u32) -> Option<Date<Off>> {
self.local().with_day(day)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_day(&self, day: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_day(day))
}
#[inline]
fn with_day0(&self, day0: u32) -> Option<Date<Off>> {
self.local().with_day0(day0)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_day0(&self, day0: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_day0(day0))
}
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Off>> {
self.local().with_ordinal(ordinal)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_ordinal(ordinal))
}
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Off>> {
self.local().with_ordinal0(ordinal0)
.and_then(|date| self.offset.from_local_date(&date).single())
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> {
map_local(self, |date| date.with_ordinal0(ordinal0))
}
}
impl<Off:Offset, Off2:Offset> PartialEq<Date<Off2>> for Date<Off> {
fn eq(&self, other: &Date<Off2>) -> bool { self.date == other.date }
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> {
fn eq(&self, other: &Date<Tz2>) -> bool { self.date == other.date }
}
impl<Off:Offset> Eq for Date<Off> {
impl<Tz: TimeZone> Eq for Date<Tz> {
}
impl<Off:Offset> PartialOrd for Date<Off> {
fn partial_cmp(&self, other: &Date<Off>) -> Option<Ordering> {
impl<Tz: TimeZone> PartialOrd for Date<Tz> {
fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
self.date.partial_cmp(&other.date)
}
}
impl<Off:Offset> Ord for Date<Off> {
fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
impl<Tz: TimeZone> Ord for Date<Tz> {
fn cmp(&self, other: &Date<Tz>) -> Ordering { self.date.cmp(&other.date) }
}
impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for Date<Off> {
impl<Tz: TimeZone, H: hash::Hasher + hash::Writer> hash::Hash<H> for Date<Tz> {
fn hash(&self, state: &mut H) { self.date.hash(state) }
}
impl<Off:Offset> Add<Duration> for Date<Off> {
type Output = Date<Off>;
impl<Tz: TimeZone> Add<Duration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn add(self, rhs: Duration) -> Date<Off> {
fn add(self, rhs: Duration) -> Date<Tz> {
self.checked_add(rhs).expect("`Date + Duration` overflowed")
}
}
impl<Off:Offset, Off2:Offset> Sub<Date<Off2>> for Date<Off> {
impl<Tz: TimeZone, Tz2: TimeZone> Sub<Date<Tz2>> for Date<Tz> {
type Output = Duration;
#[inline]
fn sub(self, rhs: Date<Off2>) -> Duration { self.date - rhs.date }
fn sub(self, rhs: Date<Tz2>) -> Duration { self.date - rhs.date }
}
impl<Off:Offset> Sub<Duration> for Date<Off> {
type Output = Date<Off>;
impl<Tz: TimeZone> Sub<Duration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> Date<Off> {
fn sub(self, rhs: Duration) -> Date<Tz> {
self.checked_sub(rhs).expect("`Date - Duration` overflowed")
}
}
impl<Off: Offset> fmt::Debug for Date<Off> {
impl<Tz: TimeZone> fmt::Debug for Date<Tz> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.local(), self.offset)
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
}
}
impl<Off: Offset + fmt::Display> fmt::Display for Date<Off> {
impl<Tz: TimeZone> fmt::Display for Date<Tz> where Tz::Offset: fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.local(), self.offset)
write!(f, "{}{}", self.naive_local(), self.offset)
}
}
@ -335,35 +350,39 @@ mod tests {
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use super::Date;
use time::Time;
use datetime::DateTime;
use offset::{Offset, LocalResult};
use offset::{TimeZone, Offset, LocalResult};
#[derive(Copy, Clone, PartialEq, Eq)]
struct UTC1y; // same to UTC but with an offset of 365 days
impl Offset for UTC1y {
fn local_minus_utc(&self) -> Duration { Duration::zero() }
#[derive(Copy, Clone, PartialEq, Eq)]
struct OneYear;
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
LocalResult::Single(Date::from_utc(*local - Duration::days(365), UTC1y))
impl TimeZone for UTC1y {
type Offset = OneYear;
fn from_offset(_offset: &OneYear) -> UTC1y { UTC1y }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<OneYear> {
LocalResult::Single(OneYear)
}
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<UTC1y>> {
LocalResult::Single(Time::from_utc(local.clone(), UTC1y))
fn offset_from_local_time(&self, _local: &NaiveTime) -> LocalResult<OneYear> {
LocalResult::Single(OneYear)
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<UTC1y>> {
LocalResult::Single(DateTime::from_utc(*local - Duration::days(365), UTC1y))
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<OneYear> {
LocalResult::Single(OneYear)
}
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { *utc + Duration::days(365) }
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
*utc + Duration::days(365)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
fn offset_from_utc_time(&self, _utc: &NaiveTime) -> OneYear { OneYear }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
}
impl fmt::Debug for UTC1y {
impl Offset for OneYear {
fn local_minus_utc(&self) -> Duration { Duration::days(365) }
}
impl fmt::Debug for OneYear {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
}

View File

@ -3,7 +3,7 @@
// See README.md and LICENSE.txt for details.
/*!
* ISO 8601 date and time.
* ISO 8601 date and time with time zone.
*/
use std::{str, fmt, hash};
@ -11,7 +11,9 @@ use std::cmp::Ordering;
use std::ops::{Add, Sub};
use {Weekday, Timelike, Datelike};
use offset::{Offset, FixedOffset, UTC};
use offset::{TimeZone, Offset};
use offset::utc::UTC;
use offset::fixed::FixedOffset;
use duration::Duration;
use naive::datetime::NaiveDateTime;
use time::Time;
@ -19,30 +21,32 @@ use date::Date;
use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 combined date and time with timezone.
/// ISO 8601 combined date and time with time zone.
#[derive(Clone)]
pub struct DateTime<Off> {
pub struct DateTime<Tz: TimeZone> {
datetime: NaiveDateTime,
offset: Off,
offset: Tz::Offset,
}
impl<Off:Offset> DateTime<Off> {
impl<Tz: TimeZone> DateTime<Tz> {
/// Makes a new `DateTime` with given *UTC* datetime and offset.
/// The local datetime should be constructed via the `Offset` trait.
/// The local datetime should be constructed via the `TimeZone` trait.
//
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
#[inline]
pub fn from_utc(datetime: NaiveDateTime, offset: Off) -> DateTime<Off> {
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
DateTime { datetime: datetime, offset: offset }
}
/// Retrieves a date component.
#[inline]
pub fn date(&self) -> Date<Off> {
pub fn date(&self) -> Date<Tz> {
Date::from_utc(self.datetime.date().clone(), self.offset.clone())
}
/// Retrieves a time component.
#[inline]
pub fn time(&self) -> Time<Off> {
pub fn time(&self) -> Time<Tz> {
Time::from_utc(self.datetime.time().clone(), self.offset.clone())
}
@ -52,24 +56,30 @@ impl<Off:Offset> DateTime<Off> {
self.datetime.num_seconds_from_unix_epoch()
}
/// Retrieves an associated offset.
/// Retrieves an associated offset from UTC.
#[inline]
pub fn offset<'a>(&'a self) -> &'a Off {
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
&self.offset
}
/// Changes the associated offset.
/// Retrieves an associated time zone.
#[inline]
pub fn timezone(&self) -> Tz {
TimeZone::from_offset(&self.offset)
}
/// Changes the associated time zone.
/// This does not change the actual `DateTime` (but will change the string representation).
#[inline]
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> DateTime<Off2> {
DateTime::from_utc(self.datetime, offset)
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> {
tz.from_utc_datetime(&self.datetime)
}
/// Adds given `Duration` to the current date and time.
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<DateTime<Off>> {
pub fn checked_add(self, rhs: Duration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_add(rhs));
Some(DateTime { datetime: datetime, offset: self.offset })
}
@ -78,15 +88,28 @@ impl<Off:Offset> DateTime<Off> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<DateTime<Off>> {
pub fn checked_sub(self, rhs: Duration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_sub(rhs));
Some(DateTime { datetime: datetime, offset: self.offset })
}
/// Returns a view to the local datetime.
fn local(&self) -> NaiveDateTime {
self.offset.to_local_datetime(&self.datetime)
/// Returns a view to the naive UTC datetime.
#[inline]
pub fn naive_utc(&self) -> NaiveDateTime {
self.datetime
}
/// Returns a view to the naive local datetime.
#[inline]
pub fn naive_local(&self) -> NaiveDateTime {
self.datetime + self.offset.local_minus_utc()
}
}
/// Maps the local datetime to other datetime with given conversion function.
fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz>>
where F: FnMut(NaiveDateTime) -> Option<NaiveDateTime> {
f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single())
}
impl DateTime<FixedOffset> {
@ -102,12 +125,12 @@ impl DateTime<FixedOffset> {
}
}
impl<Off: Offset + fmt::Display> DateTime<Off> {
impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
/// Formats the combined date and time with the specified formatting items.
#[inline]
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
where I: Iterator<Item=Item<'a>> + Clone {
let local = self.local();
let local = self.naive_local();
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
}
@ -119,146 +142,135 @@ impl<Off: Offset + fmt::Display> DateTime<Off> {
}
}
impl<Off:Offset> Datelike for DateTime<Off> {
#[inline] fn year(&self) -> i32 { self.local().year() }
#[inline] fn month(&self) -> u32 { self.local().month() }
#[inline] fn month0(&self) -> u32 { self.local().month0() }
#[inline] fn day(&self) -> u32 { self.local().day() }
#[inline] fn day0(&self) -> u32 { self.local().day0() }
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
impl<Tz: TimeZone> Datelike for DateTime<Tz> {
#[inline] fn year(&self) -> i32 { self.naive_local().year() }
#[inline] fn month(&self) -> u32 { self.naive_local().month() }
#[inline] fn month0(&self) -> u32 { self.naive_local().month0() }
#[inline] fn day(&self) -> u32 { self.naive_local().day() }
#[inline] fn day0(&self) -> u32 { self.naive_local().day0() }
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
#[inline]
fn with_year(&self, year: i32) -> Option<DateTime<Off>> {
self.local().with_year(year)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_year(year))
}
#[inline]
fn with_month(&self, month: u32) -> Option<DateTime<Off>> {
self.local().with_month(month)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_month(&self, month: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_month(month))
}
#[inline]
fn with_month0(&self, month0: u32) -> Option<DateTime<Off>> {
self.local().with_month0(month0)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_month0(month0))
}
#[inline]
fn with_day(&self, day: u32) -> Option<DateTime<Off>> {
self.local().with_day(day)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_day(&self, day: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_day(day))
}
#[inline]
fn with_day0(&self, day0: u32) -> Option<DateTime<Off>> {
self.local().with_day0(day0)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_day0(day0))
}
#[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Off>> {
self.local().with_ordinal(ordinal)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_ordinal(ordinal))
}
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Off>> {
self.local().with_ordinal0(ordinal0)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_ordinal0(ordinal0))
}
}
impl<Off:Offset> Timelike for DateTime<Off> {
#[inline] fn hour(&self) -> u32 { self.local().hour() }
#[inline] fn minute(&self) -> u32 { self.local().minute() }
#[inline] fn second(&self) -> u32 { self.local().second() }
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
impl<Tz: TimeZone> Timelike for DateTime<Tz> {
#[inline] fn hour(&self) -> u32 { self.naive_local().hour() }
#[inline] fn minute(&self) -> u32 { self.naive_local().minute() }
#[inline] fn second(&self) -> u32 { self.naive_local().second() }
#[inline] fn nanosecond(&self) -> u32 { self.naive_local().nanosecond() }
#[inline]
fn with_hour(&self, hour: u32) -> Option<DateTime<Off>> {
self.local().with_hour(hour)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_hour(hour))
}
#[inline]
fn with_minute(&self, min: u32) -> Option<DateTime<Off>> {
self.local().with_minute(min)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_minute(min))
}
#[inline]
fn with_second(&self, sec: u32) -> Option<DateTime<Off>> {
self.local().with_second(sec)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_second(sec))
}
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Off>> {
self.local().with_nanosecond(nano)
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> {
map_local(self, |datetime| datetime.with_nanosecond(nano))
}
}
impl<Off:Offset, Off2:Offset> PartialEq<DateTime<Off2>> for DateTime<Off> {
fn eq(&self, other: &DateTime<Off2>) -> bool { self.datetime == other.datetime }
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
fn eq(&self, other: &DateTime<Tz2>) -> bool { self.datetime == other.datetime }
}
impl<Off:Offset> Eq for DateTime<Off> {
impl<Tz: TimeZone> Eq for DateTime<Tz> {
}
impl<Off:Offset> PartialOrd for DateTime<Off> {
fn partial_cmp(&self, other: &DateTime<Off>) -> Option<Ordering> {
impl<Tz: TimeZone> PartialOrd for DateTime<Tz> {
fn partial_cmp(&self, other: &DateTime<Tz>) -> Option<Ordering> {
self.datetime.partial_cmp(&other.datetime)
}
}
impl<Off:Offset> Ord for DateTime<Off> {
fn cmp(&self, other: &DateTime<Off>) -> Ordering { self.datetime.cmp(&other.datetime) }
impl<Tz: TimeZone> Ord for DateTime<Tz> {
fn cmp(&self, other: &DateTime<Tz>) -> Ordering { self.datetime.cmp(&other.datetime) }
}
impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for DateTime<Off> {
impl<Tz: TimeZone, H: hash::Hasher + hash::Writer> hash::Hash<H> for DateTime<Tz> {
fn hash(&self, state: &mut H) { self.datetime.hash(state) }
}
impl<Off:Offset> Add<Duration> for DateTime<Off> {
type Output = DateTime<Off>;
impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn add(self, rhs: Duration) -> DateTime<Off> {
fn add(self, rhs: Duration) -> DateTime<Tz> {
self.checked_add(rhs).expect("`DateTime + Duration` overflowed")
}
}
impl<Off:Offset, Off2:Offset> Sub<DateTime<Off2>> for DateTime<Off> {
impl<Tz: TimeZone, Tz2: TimeZone> Sub<DateTime<Tz2>> for DateTime<Tz> {
type Output = Duration;
#[inline]
fn sub(self, rhs: DateTime<Off2>) -> Duration { self.datetime - rhs.datetime }
fn sub(self, rhs: DateTime<Tz2>) -> Duration { self.datetime - rhs.datetime }
}
impl<Off:Offset> Sub<Duration> for DateTime<Off> {
type Output = DateTime<Off>;
impl<Tz: TimeZone> Sub<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> DateTime<Off> {
fn sub(self, rhs: Duration) -> DateTime<Tz> {
self.checked_sub(rhs).expect("`DateTime - Duration` overflowed")
}
}
impl<Off: Offset> fmt::Debug for DateTime<Off> {
impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.local(), self.offset)
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
}
}
impl<Off: Offset + fmt::Display> fmt::Display for DateTime<Off> {
impl<Tz: TimeZone> fmt::Display for DateTime<Tz> where Tz::Offset: fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.local(), self.offset)
write!(f, "{} {}", self.naive_local(), self.offset)
}
}
@ -295,7 +307,7 @@ impl str::FromStr for DateTime<UTC> {
fn from_str(s: &str) -> ParseResult<DateTime<UTC>> {
// we parse non-UTC time zones then convert them into UTC
let dt: DateTime<FixedOffset> = try!(s.parse());
Ok(dt.with_offset(UTC))
Ok(dt.with_timezone(&UTC))
}
}
@ -306,7 +318,10 @@ mod tests {
use super::DateTime;
use Datelike;
use duration::Duration;
use offset::{Offset, UTC, Local, FixedOffset};
use offset::TimeZone;
use offset::utc::UTC;
use offset::local::Local;
use offset::fixed::FixedOffset;
#[test]
#[allow(non_snake_case)]
@ -366,7 +381,7 @@ mod tests {
fn test_datetime_format_with_local() {
// if we are not around the year boundary, local and UTC date should have the same year
let dt = Local::now().with_month(5).unwrap();
assert_eq!(dt.format("%Y").to_string(), dt.with_offset(UTC).format("%Y").to_string());
assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&UTC).format("%Y").to_string());
}
}

View File

@ -590,7 +590,7 @@ fn test_parse() {
#[test]
fn test_rfc2822() {
use datetime::DateTime;
use offset::FixedOffset;
use offset::fixed::FixedOffset;
use super::*;
use super::NOT_ENOUGH;
@ -640,7 +640,7 @@ fn test_rfc2822() {
#[test]
fn test_rfc3339() {
use datetime::DateTime;
use offset::FixedOffset;
use offset::fixed::FixedOffset;
use super::*;
// Test data - (input, Ok(expected result after parse and format) or Err(error code))

View File

@ -13,7 +13,8 @@ use {Datelike, Timelike};
use Weekday;
use div::div_rem;
use duration::Duration;
use offset::{Offset, FixedOffset, LocalResult};
use offset::{TimeZone, Offset, LocalResult};
use offset::fixed::FixedOffset;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
@ -537,21 +538,55 @@ impl Parsed {
}
/// Returns a parsed timezone-aware date and time out of given fields,
/// with an additional `Offset` used to interpret and validate the local date.
/// with an additional `TimeZone` used to interpret and validate the local date.
///
/// This method is able to determine the combined date and time
/// from date and time fields or a single `timestamp` field, plus a time zone offset.
/// Either way those fields have to be consistent to each other.
/// If parsed fields include an UTC offset, it also has to be consistent to `offset`.
pub fn to_datetime_with_offset<Off: Offset>(&self, offset: Off) -> ParseResult<DateTime<Off>> {
let delta = offset.local_minus_utc().num_seconds();
let delta = try!(delta.to_i32().ok_or(OUT_OF_RANGE));
if self.offset.unwrap_or(delta) != delta { return Err(IMPOSSIBLE); }
let datetime = try!(self.to_naive_datetime_with_offset(delta));
match offset.from_local_datetime(&datetime) {
pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
// if we have `timestamp` specified, guess an offset from that.
let mut guessed_offset = 0;
if let Some(timestamp) = self.timestamp {
// make a naive `DateTime` from given timestamp and (if any) nanosecond.
// an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
let nanosecond = self.nanosecond.unwrap_or(0);
let dt = NaiveDateTime::from_num_seconds_from_unix_epoch_opt(timestamp, nanosecond);
let dt = try!(dt.ok_or(OUT_OF_RANGE));
// we cannot handle offsets larger than i32 at all. give up if so.
// we can instead make `to_naive_datetime_with_offset` to accept i64, but this makes
// the algorithm too complex and tons of edge cases. i32 should be enough for all.
let offset = tz.offset_from_utc_datetime(&dt).local_minus_utc().num_seconds();
guessed_offset = try!(offset.to_i32().ok_or(OUT_OF_RANGE));
}
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
let check_offset = |dt: &DateTime<Tz>| {
if let Some(offset) = self.offset {
let delta = dt.offset().local_minus_utc().num_seconds();
// if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway.
delta.to_i32() == Some(offset)
} else {
true
}
};
// `guessed_offset` should be correct when `self.timestamp` is given.
// it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
let datetime = try!(self.to_naive_datetime_with_offset(guessed_offset));
match tz.from_local_datetime(&datetime) {
LocalResult::None => Err(IMPOSSIBLE),
LocalResult::Single(t) => Ok(t),
LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
LocalResult::Single(t) => if check_offset(&t) {Ok(t)} else {Err(IMPOSSIBLE)},
LocalResult::Ambiguous(min, max) => {
// try to disambiguate two possible local dates by offset.
match (check_offset(&min), check_offset(&max)) {
(false, false) => Err(IMPOSSIBLE),
(false, true) => Ok(max),
(true, false) => Ok(min),
(true, true) => Err(NOT_ENOUGH),
}
}
}
}
}
@ -564,7 +599,9 @@ mod tests {
use Weekday::*;
use naive::date::{self, NaiveDate};
use naive::time::NaiveTime;
use offset::{Offset, FixedOffset};
use offset::TimeZone;
use offset::utc::UTC;
use offset::fixed::FixedOffset;
#[test]
fn test_parsed_set_fields() {
@ -964,5 +1001,45 @@ mod tests {
minute: 26, second: 40, nanosecond: 12_345_678, offset: 86400),
Err(OUT_OF_RANGE)); // `FixedOffset` does not support such huge offset
}
#[test]
fn test_parsed_to_datetime_with_timezone() {
macro_rules! parse {
($tz:expr; $($k:ident: $v:expr),*) => (
Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz)
)
}
// single result from ymdhms
assert_eq!(parse!(UTC;
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
assert_eq!(parse!(UTC;
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400);
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400);
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
Ok(FixedOffset::east(32400).ymd(2014, 12, 31)
.and_hms_nano(13, 26, 40, 12_345_678)));
// single result from timestamp
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms(4, 26, 40)));
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 32400),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 32400),
Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms(13, 26, 40)));
// TODO test with a variable time zone (for None and Ambiguous cases)
}
}

View File

@ -32,7 +32,7 @@ chrono = "0.2"
And this in your crate root:
```toml
```rust
extern crate chrono;
```
@ -49,14 +49,14 @@ Chrono simply reexports it.
Chrono provides a `DateTime` type for the combined date and time.
`DateTime`, among others, is timezone-aware and
must be constructed from the timezone object (`Offset`).
`DateTime`s with different offsets do not mix, but can be converted to each other.
must be constructed from the `TimeZone` object.
`DateTime`s with different time zones do not mix, but can be converted to each other.
You can get the current date and time in the UTC timezone (`UTC::now()`)
or in the local timezone (`Local::now()`).
~~~~ {.rust}
use chrono::{UTC, Local, DateTime};
use chrono::*;
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
@ -68,7 +68,7 @@ This is a bit verbose due to Rust's lack of function and method overloading,
but in turn we get a rich combination of initialization methods.
~~~~ {.rust}
use chrono::{UTC, Offset, Weekday, LocalResult};
use chrono::*;
let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
@ -93,13 +93,12 @@ Addition and subtraction is also supported.
The following illustrates most supported operations to the date and time:
~~~~ {.rust}
# /* we intentionally fake the datetime...
use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration};
use chrono::*;
# /* we intentionally fake the datetime...
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
let dt = Local::now();
# */ // up to here. we now define a fixed datetime for the illustrative purpose.
# use chrono::{UTC, FixedOffset, Offset, Datelike, Timelike, Weekday, Duration};
# let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806);
// property accessors
@ -111,9 +110,10 @@ assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sat=7
assert_eq!(dt.ordinal(), 332); // the day of year
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
// offset accessor and manipulation
// time zone accessor and manipulation
assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
assert_eq!(dt.with_offset(UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
// a sample of property manipulations (validates dynamically)
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
@ -135,7 +135,7 @@ which format is equivalent to the familiar `strftime` format.
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
~~~~ {.rust}
use chrono::{UTC, Offset};
use chrono::*;
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");
@ -165,7 +165,7 @@ Parsing can be done with three methods:
More detailed control over the parsing process is available via `format` module.
~~~~ {.rust}
use chrono::{UTC, Offset, DateTime};
use chrono::*;
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
@ -178,8 +178,8 @@ assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
// method 3
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00",
"%Y-%m-%d %H:%M:%S %z").map(|dt| dt.with_offset(UTC)), Ok(dt));
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"),
Ok(dt.with_timezone(&FixedOffset::east(9*3600))));
// oops, the year is missing!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
@ -192,12 +192,12 @@ assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_e
### Individual date and time
Chrono also provides an individual date type (`Date`) and time type (`Time`).
They also have offsets attached, and have to be constructed via offsets.
They also have time zones attached, and have to be constructed via time zones.
Most operations available to `DateTime` are also available to `Date` and `Time`
whenever appropriate.
~~~~ {.rust}
use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday};
use chrono::*;
# // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
assert_eq!(UTC::today(), UTC::now().date());
@ -217,7 +217,7 @@ Chrono provides naive counterparts to `Date`, `Time` and `DateTime`
as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively.
They have almost equivalent interfaces as their timezone-aware twins,
but are not associated to offsets obviously and can be quite low-level.
but are not associated to time zones obviously and can be quite low-level.
They are mostly useful for building blocks for higher-level types.
## Limitations
@ -238,7 +238,7 @@ Any operation that can be ambiguous will return `None` in such cases.
For example, "a month later" of 2014-01-30 is not well-defined
and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
Advanced offset handling is not yet supported (but is planned in 0.3).
Advanced time zone handling is not yet supported (but is planned in 0.3).
*/
@ -251,8 +251,10 @@ Advanced offset handling is not yet supported (but is planned in 0.3).
extern crate "time" as stdtime;
pub use duration::Duration;
pub use offset::{Offset, LocalResult};
pub use offset::{UTC, FixedOffset, Local};
pub use offset::{TimeZone, Offset, LocalResult};
pub use offset::utc::UTC;
pub use offset::fixed::FixedOffset;
pub use offset::local::Local;
pub use naive::date::NaiveDate;
pub use naive::time::NaiveTime;
pub use naive::datetime::NaiveDateTime;
@ -278,7 +280,7 @@ pub mod offset;
pub mod naive {
//! Date and time types which do not concern about the timezones.
//!
//! They are primarily building blocks for other types (e.g. `Offset`),
//! They are primarily building blocks for other types (e.g. `TimeZone`),
//! but can be also used for the simpler date and time handling.
pub mod date;
pub mod time;

View File

@ -12,7 +12,6 @@ use std::ops::{Add, Sub};
use Timelike;
use div::div_mod_floor;
use offset::Offset;
use duration::Duration;
use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};

107
src/offset/fixed.rs Normal file
View File

@ -0,0 +1,107 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// See README.md and LICENSE.txt for details.
/*!
* The time zone which has a fixed offset from UTC.
*/
use std::fmt;
use div::div_mod_floor;
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use super::{TimeZone, Offset, LocalResult};
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct FixedOffset {
local_minus_utc: i32,
}
impl FixedOffset {
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere.
///
/// Fails on the out-of-bound `secs`.
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
Some(FixedOffset { local_minus_utc: secs })
} else {
None
}
}
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
/// The negative `secs` means the Eastern Hemisphere.
///
/// Fails on the out-of-bound `secs`.
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
/// The negative `secs` means the Eastern Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
Some(FixedOffset { local_minus_utc: -secs })
} else {
None
}
}
}
impl TimeZone for FixedOffset {
type Offset = FixedOffset;
fn from_offset(offset: &FixedOffset) -> FixedOffset { offset.clone() }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
LocalResult::Single(self.clone())
}
fn offset_from_local_time(&self, _local: &NaiveTime) -> LocalResult<FixedOffset> {
LocalResult::Single(self.clone())
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
LocalResult::Single(self.clone())
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { self.clone() }
fn offset_from_utc_time(&self, _utc: &NaiveTime) -> FixedOffset { self.clone() }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { self.clone() }
}
impl Offset for FixedOffset {
fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) }
}
impl fmt::Debug for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let offset = self.local_minus_utc;
let (sign, offset) = if offset < 0 {('-', -offset)} else {('+', offset)};
let (mins, sec) = div_mod_floor(offset, 60);
let (hour, min) = div_mod_floor(mins, 60);
if sec == 0 {
write!(f, "{}{:02}:{:02}", sign, hour, min)
} else {
write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
}
}
}
impl fmt::Display for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}

122
src/offset/local.rs Normal file
View File

@ -0,0 +1,122 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// See README.md and LICENSE.txt for details.
/*!
* The local (system) time zone.
*/
use stdtime;
use {Datelike, Timelike};
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use time::Time;
use datetime::DateTime;
use super::{TimeZone, LocalResult};
use super::fixed::FixedOffset;
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
/// This assumes that `time` is working correctly, i.e. any error is fatal.
fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
if tm.tm_sec >= 60 {
tm.tm_sec = 59;
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
}
// from_yo is more efficient than from_ymd (since it's the internal representation).
let date = NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1);
let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32,
tm.tm_sec as u32, tm.tm_nsec as u32);
let offset = FixedOffset::east(tm.tm_utcoff);
DateTime::from_utc(date.and_time(time) + Duration::seconds(-tm.tm_utcoff as i64), offset)
}
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
fn datetime_to_timespec(d: &NaiveDateTime) -> stdtime::Timespec {
let tm = stdtime::Tm {
tm_sec: d.second() as i32,
tm_min: d.minute() as i32,
tm_hour: d.hour() as i32,
tm_mday: d.day() as i32,
tm_mon: d.month0() as i32, // yes, C is that strange...
tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
tm_wday: 0, // to_local ignores this
tm_yday: 0, // and this
tm_isdst: -1,
tm_utcoff: 1, // this is arbitrary but should be nonzero
// in order to make `to_timespec` use `rust_mktime` internally.
tm_nsec: d.nanosecond() as i32,
};
tm.to_timespec()
}
/// The local timescale. This is implemented via the standard `time` crate.
#[derive(Copy, Clone)]
pub struct Local;
impl Local {
/// Returns a `Date` which corresponds to the current date.
pub fn today() -> Date<Local> {
Local::now().date()
}
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<Local> {
tm_to_datetime(stdtime::now())
}
}
impl TimeZone for Local {
type Offset = FixedOffset;
fn from_offset(_offset: &FixedOffset) -> Local { Local }
// they are easier to define in terms of the finished date and time unlike other offsets
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
self.from_local_date(local).map(|&: date| *date.offset())
}
fn offset_from_local_time(&self, local: &NaiveTime) -> LocalResult<FixedOffset> {
self.from_local_time(local).map(|&: time| *time.offset())
}
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
self.from_local_datetime(local).map(|&: datetime| *datetime.offset())
}
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
*self.from_utc_date(utc).offset()
}
fn offset_from_utc_time(&self, utc: &NaiveTime) -> FixedOffset {
*self.from_utc_time(utc).offset()
}
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
*self.from_utc_datetime(utc).offset()
}
// override them for avoiding redundant works
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
self.from_local_datetime(&local.and_hms(0, 0, 0)).map(|datetime| datetime.date())
}
fn from_local_time(&self, _local: &NaiveTime) -> LocalResult<Time<Local>> {
LocalResult::None // we have no information about this time
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
let timespec = datetime_to_timespec(local);
LocalResult::Single(tm_to_datetime(stdtime::at(timespec)))
}
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
self.from_utc_datetime(&utc.and_hms(0, 0, 0)).date()
}
fn from_utc_time(&self, _utc: &NaiveTime) -> Time<Local> {
unimplemented!() // we have no information about this time
}
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
let timespec = datetime_to_timespec(utc);
tm_to_datetime(stdtime::at_utc(timespec))
}
}

View File

@ -3,14 +3,26 @@
// See README.md and LICENSE.txt for details.
/*!
* Offsets from the local time to UTC.
* The time zone, which calculates offsets from the local time to UTC.
*
* There are three operations provided by the `TimeZone` trait:
*
* 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
* 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
* 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
*
* 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
* 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
* which implements `Offset` (which then passed to `TimeZone` for actual implementations).
* Technically speaking `TimeZone` has a total knowledge about given timescale,
* but `Offset` is used as a cache to avoid the repeated conversion
* and provides implementations for 1 and 3.
* An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
*/
use std::fmt;
use stdtime;
use {Weekday, Datelike, Timelike};
use div::div_mod_floor;
use Weekday;
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
@ -48,15 +60,24 @@ impl<T> LocalResult<T> {
pub fn latest(self) -> Option<T> {
match self { LocalResult::Single(t) | LocalResult::Ambiguous(_,t) => Some(t), _ => None }
}
/// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
match self {
LocalResult::None => LocalResult::None,
LocalResult::Single(v) => LocalResult::Single(f(v)),
LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
}
}
}
impl<Off:Offset> LocalResult<Date<Off>> {
impl<Tz: TimeZone> LocalResult<Date<Tz>> {
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
/// The offset in the current date is preserved.
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Off>> {
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => d.and_time(time)
.map_or(LocalResult::None, LocalResult::Single),
@ -69,7 +90,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Off>> {
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => d.and_hms_opt(hour, min, sec)
.map_or(LocalResult::None, LocalResult::Single),
@ -84,7 +105,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
pub fn and_hms_milli_opt(self, hour: u32, min: u32, sec: u32,
milli: u32) -> LocalResult<DateTime<Off>> {
milli: u32) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => d.and_hms_milli_opt(hour, min, sec, milli)
.map_or(LocalResult::None, LocalResult::Single),
@ -99,7 +120,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
pub fn and_hms_micro_opt(self, hour: u32, min: u32, sec: u32,
micro: u32) -> LocalResult<DateTime<Off>> {
micro: u32) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => d.and_hms_micro_opt(hour, min, sec, micro)
.map_or(LocalResult::None, LocalResult::Single),
@ -114,7 +135,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
pub fn and_hms_nano_opt(self, hour: u32, min: u32, sec: u32,
nano: u32) -> LocalResult<DateTime<Off>> {
nano: u32) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => d.and_hms_nano_opt(hour, min, sec, nano)
.map_or(LocalResult::None, LocalResult::Single),
@ -138,11 +159,19 @@ impl<T: fmt::Debug> LocalResult<T> {
}
/// The offset from the local time to UTC.
pub trait Offset: Clone + fmt::Debug {
/// Makes a new `Date` from year, month, day and the current offset.
pub trait Offset: Sized + Clone + fmt::Debug {
/// Returns the offset from UTC to the local time stored.
fn local_minus_utc(&self) -> Duration;
}
/// The time zone.
pub trait TimeZone: Sized {
type Offset: Offset;
/// Makes a new `Date` from year, month, day and the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Fails on the out-of-range date, invalid month and/or day.
@ -150,10 +179,10 @@ pub trait Offset: Clone + fmt::Debug {
self.ymd_opt(year, month, day).unwrap()
}
/// Makes a new `Date` from year, month, day and the current offset.
/// Makes a new `Date` from year, month, day and the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Returns `None` on the out-of-range date, invalid month and/or day.
@ -164,10 +193,10 @@ pub trait Offset: Clone + fmt::Debug {
}
}
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current offset.
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Fails on the out-of-range date and/or invalid DOY.
@ -175,10 +204,10 @@ pub trait Offset: Clone + fmt::Debug {
self.yo_opt(year, ordinal).unwrap()
}
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current offset.
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Returns `None` on the out-of-range date and/or invalid DOY.
@ -190,11 +219,11 @@ pub trait Offset: Clone + fmt::Debug {
}
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
/// the current offset.
/// the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
/// The resulting `Date` may have a different year from the input year.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Fails on the out-of-range date and/or invalid week number.
@ -203,11 +232,11 @@ pub trait Offset: Clone + fmt::Debug {
}
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
/// the current offset.
/// the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
/// The resulting `Date` may have a different year from the input year.
///
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Returns `None` on the out-of-range date and/or invalid week number.
@ -218,14 +247,14 @@ pub trait Offset: Clone + fmt::Debug {
}
}
/// Makes a new `Time` from hour, minute, second and the current offset.
/// Makes a new `Time` from hour, minute, second and the current time zone.
///
/// Fails on invalid hour, minute and/or second.
fn hms(&self, hour: u32, min: u32, sec: u32) -> Time<Self> {
self.hms_opt(hour, min, sec).unwrap()
}
/// Makes a new `Time` from hour, minute, second and the current offset.
/// Makes a new `Time` from hour, minute, second and the current time zone.
///
/// Returns `None` on invalid hour, minute and/or second.
fn hms_opt(&self, hour: u32, min: u32, sec: u32) -> LocalResult<Time<Self>> {
@ -235,7 +264,7 @@ pub trait Offset: Clone + fmt::Debug {
}
}
/// Makes a new `Time` from hour, minute, second, millisecond and the current offset.
/// Makes a new `Time` from hour, minute, second, millisecond and the current time zone.
/// The millisecond part can exceed 1,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or millisecond.
@ -243,7 +272,7 @@ pub trait Offset: Clone + fmt::Debug {
self.hms_milli_opt(hour, min, sec, milli).unwrap()
}
/// Makes a new `Time` from hour, minute, second, millisecond and the current offset.
/// Makes a new `Time` from hour, minute, second, millisecond and the current time zone.
/// The millisecond part can exceed 1,000 in order to represent the leap second.
///
/// Returns `None` on invalid hour, minute, second and/or millisecond.
@ -254,7 +283,7 @@ pub trait Offset: Clone + fmt::Debug {
}
}
/// Makes a new `Time` from hour, minute, second, microsecond and the current offset.
/// Makes a new `Time` from hour, minute, second, microsecond and the current time zone.
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or microsecond.
@ -262,7 +291,7 @@ pub trait Offset: Clone + fmt::Debug {
self.hms_micro_opt(hour, min, sec, micro).unwrap()
}
/// Makes a new `Time` from hour, minute, second, microsecond and the current offset.
/// Makes a new `Time` from hour, minute, second, microsecond and the current time zone.
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
///
/// Returns `None` on invalid hour, minute, second and/or microsecond.
@ -273,7 +302,7 @@ pub trait Offset: Clone + fmt::Debug {
}
}
/// Makes a new `Time` from hour, minute, second, nanosecond and the current offset.
/// Makes a new `Time` from hour, minute, second, nanosecond and the current time zone.
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
///
/// Fails on invalid hour, minute, second and/or nanosecond.
@ -281,7 +310,7 @@ pub trait Offset: Clone + fmt::Debug {
self.hms_nano_opt(hour, min, sec, nano).unwrap()
}
/// Makes a new `Time` from hour, minute, second, nanosecond and the current offset.
/// Makes a new `Time` from hour, minute, second, nanosecond and the current time zone.
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
///
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
@ -304,261 +333,71 @@ pub trait Offset: Clone + fmt::Debug {
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parsed.to_datetime_with_offset(self.clone())
parsed.to_datetime_with_timezone(self)
}
/// Returns the *current* offset from UTC to the local time.
fn local_minus_utc(&self) -> Duration;
/// Reconstructs the time zone from the offset.
fn from_offset(offset: &Self::Offset) -> Self;
/// Creates the offset(s) for given local `NaiveDate` if possible.
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
/// Creates the offset(s) for given local `NaiveTime` if possible.
fn offset_from_local_time(&self, local: &NaiveTime) -> LocalResult<Self::Offset>;
/// Creates the offset(s) for given local `NaiveDateTime` if possible.
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>>;
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
self.offset_from_local_date(local).map(|offset| {
Date::from_utc(*local - offset.local_minus_utc(), offset)
})
}
/// Converts the local `NaiveTime` to the timezone-aware `Time` if possible.
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<Self>>;
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<Self>> {
self.offset_from_local_time(local).map(|offset| {
Time::from_utc(*local - offset.local_minus_utc(), offset)
})
}
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>>;
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
self.offset_from_local_datetime(local).map(|offset| {
DateTime::from_utc(*local - offset.local_minus_utc(), offset)
})
}
/// Creates the offset for given UTC `NaiveDate`. This cannot fail.
fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
/// Creates the offset for given UTC `NaiveTime`. This cannot fail.
fn offset_from_utc_time(&self, utc: &NaiveTime) -> Self::Offset;
/// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
/// Converts the UTC `NaiveDate` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate;
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
Date::from_utc(utc.clone(), self.offset_from_utc_date(utc))
}
/// Converts the UTC `NaiveTime` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime;
fn from_utc_time(&self, utc: &NaiveTime) -> Time<Self> {
Time::from_utc(utc.clone(), self.offset_from_utc_time(utc))
}
/// Converts the UTC `NaiveDateTime` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime;
}
/// The UTC timescale. This is the most efficient offset when you don't need the local time.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct UTC;
impl UTC {
/// Returns a `Date` which corresponds to the current date.
pub fn today() -> Date<UTC> { UTC::now().date() }
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<UTC> {
let spec = stdtime::get_time();
let naive = NaiveDateTime::from_num_seconds_from_unix_epoch(spec.sec, spec.nsec as u32);
DateTime::from_utc(naive, UTC)
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
DateTime::from_utc(utc.clone(), self.offset_from_utc_datetime(utc))
}
}
impl Offset for UTC {
fn local_minus_utc(&self) -> Duration { Duration::zero() }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC>> {
LocalResult::Single(Date::from_utc(local.clone(), UTC))
}
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<UTC>> {
LocalResult::Single(Time::from_utc(local.clone(), UTC))
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<UTC>> {
LocalResult::Single(DateTime::from_utc(local.clone(), UTC))
}
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { utc.clone() }
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime { utc.clone() }
}
impl fmt::Debug for UTC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
}
impl fmt::Display 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.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct FixedOffset {
local_minus_utc: i32,
}
impl FixedOffset {
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere.
///
/// Fails on the out-of-bound `secs`.
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
Some(FixedOffset { local_minus_utc: secs })
} else {
None
}
}
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
/// The negative `secs` means the Eastern Hemisphere.
///
/// Fails on the out-of-bound `secs`.
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
/// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
/// The negative `secs` means the Eastern Hemisphere.
///
/// Returns `None` on the out-of-bound `secs`.
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
Some(FixedOffset { local_minus_utc: -secs })
} else {
None
}
}
}
impl Offset for FixedOffset {
fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<FixedOffset>> {
LocalResult::Single(Date::from_utc(local.clone(), self.clone()))
}
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<FixedOffset>> {
let t = Time::from_utc(*local + Duration::seconds(-self.local_minus_utc as i64),
self.clone());
LocalResult::Single(t)
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<FixedOffset>> {
let dt = DateTime::from_utc(*local + Duration::seconds(-self.local_minus_utc as i64),
self.clone());
LocalResult::Single(dt)
}
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate {
utc.clone()
}
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime {
*utc + Duration::seconds(self.local_minus_utc as i64)
}
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
*utc + Duration::seconds(self.local_minus_utc as i64)
}
}
impl fmt::Debug for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let offset = self.local_minus_utc;
let (sign, offset) = if offset < 0 {('-', -offset)} else {('+', offset)};
let (mins, sec) = div_mod_floor(offset, 60);
let (hour, min) = div_mod_floor(mins, 60);
if sec == 0 {
write!(f, "{}{:02}:{:02}", sign, hour, min)
} else {
write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
}
}
}
impl fmt::Display for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}
/// The local timescale. This is implemented via the standard `time` crate.
#[derive(Copy, Clone)]
pub struct Local {
cached: FixedOffset,
}
impl Local {
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
/// This assumes that `time` is working correctly, i.e. any error is fatal.
fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
if tm.tm_sec >= 60 {
tm.tm_sec = 59;
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
}
// from_yo is more efficient than from_ymd (since it's the internal representation).
let date = NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1);
let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32,
tm.tm_sec as u32, tm.tm_nsec as u32);
let offset = Local { cached: FixedOffset::east(tm.tm_utcoff) };
DateTime::from_utc(date.and_time(time) + Duration::seconds(-tm.tm_utcoff as i64), offset)
}
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
fn datetime_to_timespec(d: &NaiveDateTime) -> stdtime::Timespec {
let tm = stdtime::Tm {
tm_sec: d.second() as i32,
tm_min: d.minute() as i32,
tm_hour: d.hour() as i32,
tm_mday: d.day() as i32,
tm_mon: d.month0() as i32, // yes, C is that strange...
tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
tm_wday: 0, // to_local ignores this
tm_yday: 0, // and this
tm_isdst: -1,
tm_utcoff: 1, // this is arbitrary but should be nonzero
// in order to make `to_timespec` use `rust_mktime` internally.
tm_nsec: d.nanosecond() as i32,
};
tm.to_timespec()
}
/// Returns a `Date` which corresponds to the current date.
pub fn today() -> Date<Local> {
Local::now().date()
}
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<Local> {
Local::tm_to_datetime(stdtime::now())
}
}
impl Offset for Local {
fn local_minus_utc(&self) -> Duration { self.cached.local_minus_utc() }
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
match self.from_local_datetime(&local.and_hms(0, 0, 0)) {
LocalResult::None => LocalResult::None,
LocalResult::Single(dt) => LocalResult::Single(dt.date()),
LocalResult::Ambiguous(min, max) => {
let min = min.date();
let max = max.date();
if min == max {LocalResult::Single(min)} else {LocalResult::Ambiguous(min, max)}
}
}
}
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<Local>> {
// XXX we don't have enough information here, so we assume that the timezone remains same
LocalResult::Single(Time::from_utc(local.clone(), self.clone()))
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
let timespec = Local::datetime_to_timespec(local);
LocalResult::Single(Local::tm_to_datetime(stdtime::at(timespec)))
}
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { self.cached.to_local_date(utc) }
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { self.cached.to_local_time(utc) }
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
self.cached.to_local_datetime(utc)
}
}
impl fmt::Debug for Local {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.cached.fmt(f) }
}
impl fmt::Display for Local {
// TODO this should be a tz name whenever available
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.cached.fmt(f) }
}
pub mod utc;
pub mod fixed;
pub mod local;

68
src/offset/utc.rs Normal file
View File

@ -0,0 +1,68 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// See README.md and LICENSE.txt for details.
/*!
* The UTC (Coordinated Universal Time) time zone.
*/
use std::fmt;
use stdtime;
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use super::{TimeZone, Offset, LocalResult};
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
/// It is also used as an offset (which is also a dummy type).
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct UTC;
impl UTC {
/// Returns a `Date` which corresponds to the current date.
pub fn today() -> Date<UTC> { UTC::now().date() }
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<UTC> {
let spec = stdtime::get_time();
let naive = NaiveDateTime::from_num_seconds_from_unix_epoch(spec.sec, spec.nsec as u32);
DateTime::from_utc(naive, UTC)
}
}
impl TimeZone for UTC {
type Offset = UTC;
fn from_offset(_state: &UTC) -> UTC { UTC }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<UTC> {
LocalResult::Single(UTC)
}
fn offset_from_local_time(&self, _local: &NaiveTime) -> LocalResult<UTC> {
LocalResult::Single(UTC)
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<UTC> {
LocalResult::Single(UTC)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC }
fn offset_from_utc_time(&self, _utc: &NaiveTime) -> UTC { UTC }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC}
}
impl Offset for UTC {
fn local_minus_utc(&self) -> Duration { Duration::zero() }
}
impl fmt::Debug for UTC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
}
impl fmt::Display for UTC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
}

View File

@ -3,7 +3,7 @@
// See README.md and LICENSE.txt for details.
/*!
* ISO 8601 time with timezone.
* ISO 8601 time with time zone.
*/
use std::{fmt, hash};
@ -11,51 +11,72 @@ use std::cmp::Ordering;
use std::ops::{Add, Sub};
use Timelike;
use offset::Offset;
use offset::{TimeZone, Offset};
use duration::Duration;
use naive::time::NaiveTime;
use format::{Item, DelayedFormat, StrftimeItems};
/// ISO 8601 time with timezone.
#[derive(Clone)]
pub struct Time<Off> {
pub struct Time<Tz: TimeZone> {
time: NaiveTime,
offset: Off,
offset: Tz::Offset,
}
impl<Off:Offset> Time<Off> {
impl<Tz: TimeZone> Time<Tz> {
/// Makes a new `Time` with given *UTC* time and offset.
/// The local time should be constructed via the `Offset` trait.
/// The local time should be constructed via the `TimeZone` trait.
//
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
#[inline]
pub fn from_utc(time: NaiveTime, offset: Off) -> Time<Off> {
pub fn from_utc(time: NaiveTime, offset: Tz::Offset) -> Time<Tz> {
Time { time: time, offset: offset }
}
/// Retrieves an associated offset.
/// Retrieves an associated offset from UTC.
#[inline]
pub fn offset<'a>(&'a self) -> &'a Off {
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
&self.offset
}
/// Changes the associated offset.
/// This does not change the actual `Time` (but will change the string representation).
/// Retrieves an associated time zone.
#[inline]
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Time<Off2> {
Time::from_utc(self.time, offset)
pub fn timezone(&self) -> Tz {
TimeZone::from_offset(&self.offset)
}
/// Returns a view to the local time.
fn local(&self) -> NaiveTime {
self.offset.to_local_time(&self.time)
/// Changes the associated time zone.
/// This does not change the actual `Time` (but will change the string representation).
#[inline]
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Time<Tz2> {
tz.from_utc_time(&self.time)
}
/// Returns a view to the naive UTC time.
#[inline]
pub fn naive_utc(&self) -> NaiveTime {
self.time
}
/// Returns a view to the naive local time.
#[inline]
pub fn naive_local(&self) -> NaiveTime {
self.time + self.offset.local_minus_utc()
}
}
impl<Off: Offset + fmt::Display> Time<Off> {
/// Maps the local time to other time with given conversion function.
fn map_local<Tz: TimeZone, F>(t: &Time<Tz>, mut f: F) -> Option<Time<Tz>>
where F: FnMut(NaiveTime) -> Option<NaiveTime> {
f(t.naive_local()).and_then(|time| t.timezone().from_local_time(&time).single())
}
impl<Tz: TimeZone> Time<Tz> where Tz::Offset: fmt::Display {
/// Formats the time with the specified formatting items.
#[inline]
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
where I: Iterator<Item=Item<'a>> + Clone {
DelayedFormat::new_with_offset(None, Some(self.local()), &self.offset, items)
DelayedFormat::new_with_offset(None, Some(self.naive_local()), &self.offset, items)
}
/// Formats the time with the specified format string.
@ -66,91 +87,87 @@ impl<Off: Offset + fmt::Display> Time<Off> {
}
}
impl<Off:Offset> Timelike for Time<Off> {
#[inline] fn hour(&self) -> u32 { self.local().hour() }
#[inline] fn minute(&self) -> u32 { self.local().minute() }
#[inline] fn second(&self) -> u32 { self.local().second() }
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
impl<Tz: TimeZone> Timelike for Time<Tz> {
#[inline] fn hour(&self) -> u32 { self.naive_local().hour() }
#[inline] fn minute(&self) -> u32 { self.naive_local().minute() }
#[inline] fn second(&self) -> u32 { self.naive_local().second() }
#[inline] fn nanosecond(&self) -> u32 { self.naive_local().nanosecond() }
#[inline]
fn with_hour(&self, hour: u32) -> Option<Time<Off>> {
self.local().with_hour(hour)
.and_then(|time| self.offset.from_local_time(&time).single())
fn with_hour(&self, hour: u32) -> Option<Time<Tz>> {
map_local(self, |time| time.with_hour(hour))
}
#[inline]
fn with_minute(&self, min: u32) -> Option<Time<Off>> {
self.local().with_minute(min)
.and_then(|time| self.offset.from_local_time(&time).single())
fn with_minute(&self, min: u32) -> Option<Time<Tz>> {
map_local(self, |time| time.with_minute(min))
}
#[inline]
fn with_second(&self, sec: u32) -> Option<Time<Off>> {
self.local().with_second(sec)
.and_then(|time| self.offset.from_local_time(&time).single())
fn with_second(&self, sec: u32) -> Option<Time<Tz>> {
map_local(self, |time| time.with_second(sec))
}
#[inline]
fn with_nanosecond(&self, nano: u32) -> Option<Time<Off>> {
self.local().with_nanosecond(nano)
.and_then(|time| self.offset.from_local_time(&time).single())
fn with_nanosecond(&self, nano: u32) -> Option<Time<Tz>> {
map_local(self, |time| time.with_nanosecond(nano))
}
#[inline]
fn num_seconds_from_midnight(&self) -> u32 { self.local().num_seconds_from_midnight() }
fn num_seconds_from_midnight(&self) -> u32 { self.naive_local().num_seconds_from_midnight() }
}
impl<Off:Offset, Off2:Offset> PartialEq<Time<Off2>> for Time<Off> {
fn eq(&self, other: &Time<Off2>) -> bool { self.time == other.time }
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Time<Tz2>> for Time<Tz> {
fn eq(&self, other: &Time<Tz2>) -> bool { self.time == other.time }
}
impl<Off:Offset> Eq for Time<Off> {
impl<Tz: TimeZone> Eq for Time<Tz> {
}
impl<Off:Offset> PartialOrd for Time<Off> {
fn partial_cmp(&self, other: &Time<Off>) -> Option<Ordering> {
impl<Tz: TimeZone> PartialOrd for Time<Tz> {
fn partial_cmp(&self, other: &Time<Tz>) -> Option<Ordering> {
self.time.partial_cmp(&other.time)
}
}
impl<Off:Offset> Ord for Time<Off> {
fn cmp(&self, other: &Time<Off>) -> Ordering { self.time.cmp(&other.time) }
impl<Tz: TimeZone> Ord for Time<Tz> {
fn cmp(&self, other: &Time<Tz>) -> Ordering { self.time.cmp(&other.time) }
}
impl<Off: Offset, H: hash::Hasher + hash::Writer> hash::Hash<H> for Time<Off> {
impl<Tz: TimeZone, H: hash::Hasher + hash::Writer> hash::Hash<H> for Time<Tz> {
fn hash(&self, state: &mut H) { self.time.hash(state) }
}
impl<Off:Offset> Add<Duration> for Time<Off> {
type Output = Time<Off>;
impl<Tz: TimeZone> Add<Duration> for Time<Tz> {
type Output = Time<Tz>;
fn add(self, rhs: Duration) -> Time<Off> {
fn add(self, rhs: Duration) -> Time<Tz> {
Time { time: self.time + rhs, offset: self.offset }
}
}
impl<Off:Offset, Off2:Offset> Sub<Time<Off2>> for Time<Off> {
impl<Tz: TimeZone, Tz2: TimeZone> Sub<Time<Tz2>> for Time<Tz> {
type Output = Duration;
fn sub(self, rhs: Time<Off2>) -> Duration { self.time - rhs.time }
fn sub(self, rhs: Time<Tz2>) -> Duration { self.time - rhs.time }
}
impl<Off:Offset> Sub<Duration> for Time<Off> {
type Output = Time<Off>;
impl<Tz: TimeZone> Sub<Duration> for Time<Tz> {
type Output = Time<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> Time<Off> { self.add(-rhs) }
fn sub(self, rhs: Duration) -> Time<Tz> { self.add(-rhs) }
}
impl<Off: Offset> fmt::Debug for Time<Off> {
impl<Tz: TimeZone> fmt::Debug for Time<Tz> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.local(), self.offset)
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
}
}
impl<Off: Offset + fmt::Display> fmt::Display for Time<Off> {
impl<Tz: TimeZone> fmt::Display for Time<Tz> where Tz::Offset: fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.local(), self.offset)
write!(f, "{}{}", self.naive_local(), self.offset)
}
}