merged the new offset design branch.
This commit is contained in:
commit
664c4d0191
|
@ -15,5 +15,5 @@ license = "MIT/Apache-2.0"
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1.15"
|
time = "0.1.16"
|
||||||
|
|
||||||
|
|
63
README.md
63
README.md
|
@ -36,7 +36,7 @@ chrono = "0.2"
|
||||||
|
|
||||||
And this in your crate root:
|
And this in your crate root:
|
||||||
|
|
||||||
```toml
|
```rust
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -53,14 +53,14 @@ Chrono simply reexports it.
|
||||||
Chrono provides a `DateTime` type for the combined date and time.
|
Chrono provides a `DateTime` type for the combined date and time.
|
||||||
|
|
||||||
`DateTime`, among others, is timezone-aware and
|
`DateTime`, among others, is timezone-aware and
|
||||||
must be constructed from the timezone object (`Offset`).
|
must be constructed from the `TimeZone` object.
|
||||||
`DateTime`s with different offsets do not mix, but can be converted to each other.
|
`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()`)
|
You can get the current date and time in the UTC timezone (`UTC::now()`)
|
||||||
or in the local timezone (`Local::now()`).
|
or in the local timezone (`Local::now()`).
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Local, DateTime};
|
use chrono::*;
|
||||||
|
|
||||||
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
|
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`
|
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.
|
but in turn we get a rich combination of initialization methods.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.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`
|
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")
|
// 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:
|
The following illustrates most supported operations to the date and time:
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration};
|
use chrono::*;
|
||||||
|
|
||||||
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
||||||
let dt = Local::now();
|
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.ordinal(), 332); // the day of year
|
||||||
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
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.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)
|
// a sample of property manipulations (validates dynamically)
|
||||||
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
|
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.
|
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Offset};
|
use chrono::*;
|
||||||
|
|
||||||
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");
|
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");
|
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
|
1. The standard `FromStr` trait (and `parse` method on a string) can be used for
|
||||||
returns `DateTime<FixedOffset>`.
|
parsing `DateTime<FixedOffset>` and `DateTime<UTC>` values.
|
||||||
This should be used when the offset is a part of input and the caller cannot guess that.
|
This parses what the `{:?}` (`std::fmt::Debug`) format specifier prints,
|
||||||
It *cannot* be used when the offset can be missing.
|
and requires the offset to be present.
|
||||||
|
|
||||||
- `Offset::datetime_from_str` is similar but returns `DateTime` of given offset.
|
2. `DateTime::parse_from_str` parses a date and time with offsets and
|
||||||
When the explicit offset is missing from the input, it simply uses given offset.
|
returns `DateTime<FixedOffset>`.
|
||||||
It issues an error when the input contains an explicit offset different from the current offset.
|
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.
|
More detailed control over the parsing process is available via `format` module.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Offset, DateTime};
|
use chrono::*;
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
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("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!(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!
|
// oops, the year is missing!
|
||||||
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
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
|
### Individual date and time
|
||||||
|
|
||||||
Chrono also provides an individual date type (`Date`) and time type (`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`
|
Most operations available to `DateTime` are also available to `Date` and `Time`
|
||||||
whenever appropriate.
|
whenever appropriate.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Local, Offset, LocalResult, Datelike, Weekday};
|
use chrono::*;
|
||||||
|
|
||||||
assert_eq!(UTC::today(), UTC::now().date());
|
assert_eq!(UTC::today(), UTC::now().date());
|
||||||
assert_eq!(Local::today(), Local::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.
|
as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively.
|
||||||
|
|
||||||
They have almost equivalent interfaces as their timezone-aware twins,
|
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.
|
They are mostly useful for building blocks for higher-level types.
|
||||||
|
|
||||||
## Limitations
|
## 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
|
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`.
|
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).
|
||||||
|
|
||||||
|
|
227
src/date.rs
227
src/date.rs
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and LICENSE.txt for details.
|
// 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};
|
use std::{fmt, hash};
|
||||||
|
@ -12,18 +12,19 @@ use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use {Weekday, Datelike};
|
use {Weekday, Datelike};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use offset::{Offset, UTC};
|
use offset::{TimeZone, Offset};
|
||||||
|
use offset::utc::UTC;
|
||||||
use naive;
|
use naive;
|
||||||
use naive::date::NaiveDate;
|
use naive::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use datetime::DateTime;
|
use datetime::DateTime;
|
||||||
use format::{Item, DelayedFormat, StrftimeItems};
|
use format::{Item, DelayedFormat, StrftimeItems};
|
||||||
|
|
||||||
/// ISO 8601 calendar date with timezone.
|
/// ISO 8601 calendar date with time zone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Date<Off> {
|
pub struct Date<Tz: TimeZone> {
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
offset: Off,
|
offset: Tz::Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum possible `Date`.
|
/// The minimum possible `Date`.
|
||||||
|
@ -31,11 +32,13 @@ pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
|
||||||
/// The maximum possible `Date`.
|
/// The maximum possible `Date`.
|
||||||
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };
|
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.
|
/// 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]
|
#[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 }
|
Date { date: date, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +47,9 @@ impl<Off:Offset> Date<Off> {
|
||||||
///
|
///
|
||||||
/// Fails on invalid datetime.
|
/// Fails on invalid datetime.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Off>> {
|
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
|
||||||
let localdt = self.offset.to_local_date(&self.date).and_time(time);
|
let localdt = self.naive_local().and_time(time);
|
||||||
self.offset.from_local_datetime(&localdt).single()
|
self.timezone().from_local_datetime(&localdt).single()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `DateTime` from the current date, hour, minute and second.
|
/// 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.
|
/// Fails on invalid hour, minute and/or second.
|
||||||
#[inline]
|
#[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")
|
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.
|
/// Returns `None` on invalid hour, minute and/or second.
|
||||||
#[inline]
|
#[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))
|
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.
|
/// Fails on invalid hour, minute, second and/or millisecond.
|
||||||
#[inline]
|
#[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")
|
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.
|
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_milli_opt(&self, hour: u32, min: u32, sec: u32,
|
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))
|
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.
|
/// Fails on invalid hour, minute, second and/or microsecond.
|
||||||
#[inline]
|
#[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")
|
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.
|
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_micro_opt(&self, hour: u32, min: u32, sec: u32,
|
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))
|
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.
|
/// Fails on invalid hour, minute, second and/or nanosecond.
|
||||||
#[inline]
|
#[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")
|
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.
|
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_nano_opt(&self, hour: u32, min: u32, sec: u32,
|
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))
|
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.
|
/// Fails when `self` is the last representable date.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn succ(&self) -> Date<Off> {
|
pub fn succ(&self) -> Date<Tz> {
|
||||||
self.succ_opt().expect("out of bound")
|
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.
|
/// Returns `None` when `self` is the last representable date.
|
||||||
#[inline]
|
#[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()))
|
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.
|
/// Fails when `self` is the first representable date.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pred(&self) -> Date<Off> {
|
pub fn pred(&self) -> Date<Tz> {
|
||||||
self.pred_opt().expect("out of bound")
|
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.
|
/// Returns `None` when `self` is the first representable date.
|
||||||
#[inline]
|
#[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()))
|
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset from UTC.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
|
||||||
&self.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).
|
/// This does not change the actual `Date` (but will change the string representation).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Date<Off2> {
|
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
|
||||||
Date::from_utc(self.date, offset)
|
tz.from_utc_date(&self.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds given `Duration` to the current date.
|
/// Adds given `Duration` to the current date.
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[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));
|
let date = try_opt!(self.date.checked_add(rhs));
|
||||||
Some(Date { date: date, offset: self.offset })
|
Some(Date { date: date, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
@ -188,23 +197,36 @@ impl<Off:Offset> Date<Off> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[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));
|
let date = try_opt!(self.date.checked_sub(rhs));
|
||||||
Some(Date { date: date, offset: self.offset })
|
Some(Date { date: date, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a view to the local date.
|
/// Returns a view to the naive UTC date.
|
||||||
fn local(&self) -> NaiveDate {
|
#[inline]
|
||||||
self.offset.to_local_date(&self.date)
|
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.
|
/// Formats the date with the specified formatting items.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
||||||
where I: Iterator<Item=Item<'a>> + Clone {
|
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.
|
/// 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> {
|
impl<Tz: TimeZone> Datelike for Date<Tz> {
|
||||||
#[inline] fn year(&self) -> i32 { self.local().year() }
|
#[inline] fn year(&self) -> i32 { self.naive_local().year() }
|
||||||
#[inline] fn month(&self) -> u32 { self.local().month() }
|
#[inline] fn month(&self) -> u32 { self.naive_local().month() }
|
||||||
#[inline] fn month0(&self) -> u32 { self.local().month0() }
|
#[inline] fn month0(&self) -> u32 { self.naive_local().month0() }
|
||||||
#[inline] fn day(&self) -> u32 { self.local().day() }
|
#[inline] fn day(&self) -> u32 { self.naive_local().day() }
|
||||||
#[inline] fn day0(&self) -> u32 { self.local().day0() }
|
#[inline] fn day0(&self) -> u32 { self.naive_local().day0() }
|
||||||
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
|
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
||||||
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
|
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
||||||
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
|
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
||||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
|
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_year(&self, year: i32) -> Option<Date<Off>> {
|
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
|
||||||
self.local().with_year(year)
|
map_local(self, |date| date.with_year(year))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_month(&self, month: u32) -> Option<Date<Off>> {
|
fn with_month(&self, month: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_month(month)
|
map_local(self, |date| date.with_month(month))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_month0(&self, month0: u32) -> Option<Date<Off>> {
|
fn with_month0(&self, month0: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_month0(month0)
|
map_local(self, |date| date.with_month0(month0))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_day(&self, day: u32) -> Option<Date<Off>> {
|
fn with_day(&self, day: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_day(day)
|
map_local(self, |date| date.with_day(day))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_day0(&self, day0: u32) -> Option<Date<Off>> {
|
fn with_day0(&self, day0: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_day0(day0)
|
map_local(self, |date| date.with_day0(day0))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Off>> {
|
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_ordinal(ordinal)
|
map_local(self, |date| date.with_ordinal(ordinal))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Off>> {
|
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> {
|
||||||
self.local().with_ordinal0(ordinal0)
|
map_local(self, |date| date.with_ordinal0(ordinal0))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset, Off2:Offset> PartialEq<Date<Off2>> for Date<Off> {
|
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> {
|
||||||
fn eq(&self, other: &Date<Off2>) -> bool { self.date == other.date }
|
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> {
|
impl<Tz: TimeZone> PartialOrd for Date<Tz> {
|
||||||
fn partial_cmp(&self, other: &Date<Off>) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
|
||||||
self.date.partial_cmp(&other.date)
|
self.date.partial_cmp(&other.date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Ord for Date<Off> {
|
impl<Tz: TimeZone> Ord for Date<Tz> {
|
||||||
fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
|
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) }
|
fn hash(&self, state: &mut H) { self.date.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Add<Duration> for Date<Off> {
|
impl<Tz: TimeZone> Add<Duration> for Date<Tz> {
|
||||||
type Output = Date<Off>;
|
type Output = Date<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(self, rhs: Duration) -> Date<Off> {
|
fn add(self, rhs: Duration) -> Date<Tz> {
|
||||||
self.checked_add(rhs).expect("`Date + Duration` overflowed")
|
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;
|
type Output = Duration;
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
impl<Tz: TimeZone> Sub<Duration> for Date<Tz> {
|
||||||
type Output = Date<Off>;
|
type Output = Date<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(self, rhs: Duration) -> Date<Off> {
|
fn sub(self, rhs: Duration) -> Date<Tz> {
|
||||||
self.checked_sub(rhs).expect("`Date - Duration` overflowed")
|
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 {
|
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 {
|
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::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use naive::datetime::NaiveDateTime;
|
use naive::datetime::NaiveDateTime;
|
||||||
use super::Date;
|
use offset::{TimeZone, Offset, LocalResult};
|
||||||
use time::Time;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use offset::{Offset, LocalResult};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
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 {
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::zero() }
|
struct OneYear;
|
||||||
|
|
||||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
|
impl TimeZone for UTC1y {
|
||||||
LocalResult::Single(Date::from_utc(*local - Duration::days(365), 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>> {
|
fn offset_from_local_time(&self, _local: &NaiveTime) -> LocalResult<OneYear> {
|
||||||
LocalResult::Single(Time::from_utc(local.clone(), UTC1y))
|
LocalResult::Single(OneYear)
|
||||||
}
|
}
|
||||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<UTC1y>> {
|
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<OneYear> {
|
||||||
LocalResult::Single(DateTime::from_utc(*local - Duration::days(365), UTC1y))
|
LocalResult::Single(OneYear)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { *utc + Duration::days(365) }
|
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
|
||||||
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
|
fn offset_from_utc_time(&self, _utc: &NaiveTime) -> OneYear { OneYear }
|
||||||
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
|
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
|
||||||
*utc + Duration::days(365)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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") }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
201
src/datetime.rs
201
src/datetime.rs
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and LICENSE.txt for details.
|
// 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};
|
use std::{str, fmt, hash};
|
||||||
|
@ -11,7 +11,9 @@ use std::cmp::Ordering;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use {Weekday, Timelike, Datelike};
|
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 duration::Duration;
|
||||||
use naive::datetime::NaiveDateTime;
|
use naive::datetime::NaiveDateTime;
|
||||||
use time::Time;
|
use time::Time;
|
||||||
|
@ -19,30 +21,32 @@ use date::Date;
|
||||||
use format::{Item, Numeric, Pad, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct DateTime<Off> {
|
pub struct DateTime<Tz: TimeZone> {
|
||||||
datetime: NaiveDateTime,
|
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.
|
/// 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]
|
#[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 }
|
DateTime { datetime: datetime, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a date component.
|
/// Retrieves a date component.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn date(&self) -> Date<Off> {
|
pub fn date(&self) -> Date<Tz> {
|
||||||
Date::from_utc(self.datetime.date().clone(), self.offset.clone())
|
Date::from_utc(self.datetime.date().clone(), self.offset.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a time component.
|
/// Retrieves a time component.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn time(&self) -> Time<Off> {
|
pub fn time(&self) -> Time<Tz> {
|
||||||
Time::from_utc(self.datetime.time().clone(), self.offset.clone())
|
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()
|
self.datetime.num_seconds_from_unix_epoch()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset from UTC.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
|
||||||
&self.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).
|
/// This does not change the actual `DateTime` (but will change the string representation).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> DateTime<Off2> {
|
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> {
|
||||||
DateTime::from_utc(self.datetime, offset)
|
tz.from_utc_datetime(&self.datetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds given `Duration` to the current date and time.
|
/// Adds given `Duration` to the current date and time.
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[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));
|
let datetime = try_opt!(self.datetime.checked_add(rhs));
|
||||||
Some(DateTime { datetime: datetime, offset: self.offset })
|
Some(DateTime { datetime: datetime, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
@ -78,15 +88,28 @@ impl<Off:Offset> DateTime<Off> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[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));
|
let datetime = try_opt!(self.datetime.checked_sub(rhs));
|
||||||
Some(DateTime { datetime: datetime, offset: self.offset })
|
Some(DateTime { datetime: datetime, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a view to the local datetime.
|
/// Returns a view to the naive UTC datetime.
|
||||||
fn local(&self) -> NaiveDateTime {
|
#[inline]
|
||||||
self.offset.to_local_datetime(&self.datetime)
|
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> {
|
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.
|
/// Formats the combined date and time with the specified formatting items.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
||||||
where I: Iterator<Item=Item<'a>> + Clone {
|
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)
|
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> {
|
impl<Tz: TimeZone> Datelike for DateTime<Tz> {
|
||||||
#[inline] fn year(&self) -> i32 { self.local().year() }
|
#[inline] fn year(&self) -> i32 { self.naive_local().year() }
|
||||||
#[inline] fn month(&self) -> u32 { self.local().month() }
|
#[inline] fn month(&self) -> u32 { self.naive_local().month() }
|
||||||
#[inline] fn month0(&self) -> u32 { self.local().month0() }
|
#[inline] fn month0(&self) -> u32 { self.naive_local().month0() }
|
||||||
#[inline] fn day(&self) -> u32 { self.local().day() }
|
#[inline] fn day(&self) -> u32 { self.naive_local().day() }
|
||||||
#[inline] fn day0(&self) -> u32 { self.local().day0() }
|
#[inline] fn day0(&self) -> u32 { self.naive_local().day0() }
|
||||||
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
|
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
||||||
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
|
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
||||||
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
|
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
||||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
|
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_year(&self, year: i32) -> Option<DateTime<Off>> {
|
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_year(year)
|
map_local(self, |datetime| datetime.with_year(year))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_month(&self, month: u32) -> Option<DateTime<Off>> {
|
fn with_month(&self, month: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_month(month)
|
map_local(self, |datetime| datetime.with_month(month))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_month0(&self, month0: u32) -> Option<DateTime<Off>> {
|
fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_month0(month0)
|
map_local(self, |datetime| datetime.with_month0(month0))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_day(&self, day: u32) -> Option<DateTime<Off>> {
|
fn with_day(&self, day: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_day(day)
|
map_local(self, |datetime| datetime.with_day(day))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_day0(&self, day0: u32) -> Option<DateTime<Off>> {
|
fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_day0(day0)
|
map_local(self, |datetime| datetime.with_day0(day0))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Off>> {
|
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_ordinal(ordinal)
|
map_local(self, |datetime| datetime.with_ordinal(ordinal))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Off>> {
|
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_ordinal0(ordinal0)
|
map_local(self, |datetime| datetime.with_ordinal0(ordinal0))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Timelike for DateTime<Off> {
|
impl<Tz: TimeZone> Timelike for DateTime<Tz> {
|
||||||
#[inline] fn hour(&self) -> u32 { self.local().hour() }
|
#[inline] fn hour(&self) -> u32 { self.naive_local().hour() }
|
||||||
#[inline] fn minute(&self) -> u32 { self.local().minute() }
|
#[inline] fn minute(&self) -> u32 { self.naive_local().minute() }
|
||||||
#[inline] fn second(&self) -> u32 { self.local().second() }
|
#[inline] fn second(&self) -> u32 { self.naive_local().second() }
|
||||||
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
|
#[inline] fn nanosecond(&self) -> u32 { self.naive_local().nanosecond() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_hour(&self, hour: u32) -> Option<DateTime<Off>> {
|
fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_hour(hour)
|
map_local(self, |datetime| datetime.with_hour(hour))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_minute(&self, min: u32) -> Option<DateTime<Off>> {
|
fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_minute(min)
|
map_local(self, |datetime| datetime.with_minute(min))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_second(&self, sec: u32) -> Option<DateTime<Off>> {
|
fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_second(sec)
|
map_local(self, |datetime| datetime.with_second(sec))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Off>> {
|
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> {
|
||||||
self.local().with_nanosecond(nano)
|
map_local(self, |datetime| datetime.with_nanosecond(nano))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset, Off2:Offset> PartialEq<DateTime<Off2>> for DateTime<Off> {
|
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
|
||||||
fn eq(&self, other: &DateTime<Off2>) -> bool { self.datetime == other.datetime }
|
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> {
|
impl<Tz: TimeZone> PartialOrd for DateTime<Tz> {
|
||||||
fn partial_cmp(&self, other: &DateTime<Off>) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &DateTime<Tz>) -> Option<Ordering> {
|
||||||
self.datetime.partial_cmp(&other.datetime)
|
self.datetime.partial_cmp(&other.datetime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Ord for DateTime<Off> {
|
impl<Tz: TimeZone> Ord for DateTime<Tz> {
|
||||||
fn cmp(&self, other: &DateTime<Off>) -> Ordering { self.datetime.cmp(&other.datetime) }
|
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) }
|
fn hash(&self, state: &mut H) { self.datetime.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Add<Duration> for DateTime<Off> {
|
impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
|
||||||
type Output = DateTime<Off>;
|
type Output = DateTime<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(self, rhs: Duration) -> DateTime<Off> {
|
fn add(self, rhs: Duration) -> DateTime<Tz> {
|
||||||
self.checked_add(rhs).expect("`DateTime + Duration` overflowed")
|
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;
|
type Output = Duration;
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
impl<Tz: TimeZone> Sub<Duration> for DateTime<Tz> {
|
||||||
type Output = DateTime<Off>;
|
type Output = DateTime<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(self, rhs: Duration) -> DateTime<Off> {
|
fn sub(self, rhs: Duration) -> DateTime<Tz> {
|
||||||
self.checked_sub(rhs).expect("`DateTime - Duration` overflowed")
|
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 {
|
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 {
|
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>> {
|
fn from_str(s: &str) -> ParseResult<DateTime<UTC>> {
|
||||||
// we parse non-UTC time zones then convert them into UTC
|
// we parse non-UTC time zones then convert them into UTC
|
||||||
let dt: DateTime<FixedOffset> = try!(s.parse());
|
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 super::DateTime;
|
||||||
use Datelike;
|
use Datelike;
|
||||||
use duration::Duration;
|
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]
|
#[test]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -366,7 +381,7 @@ mod tests {
|
||||||
fn test_datetime_format_with_local() {
|
fn test_datetime_format_with_local() {
|
||||||
// if we are not around the year boundary, local and UTC date should have the same year
|
// 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();
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -590,7 +590,7 @@ fn test_parse() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rfc2822() {
|
fn test_rfc2822() {
|
||||||
use datetime::DateTime;
|
use datetime::DateTime;
|
||||||
use offset::FixedOffset;
|
use offset::fixed::FixedOffset;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::NOT_ENOUGH;
|
use super::NOT_ENOUGH;
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ fn test_rfc2822() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rfc3339() {
|
fn test_rfc3339() {
|
||||||
use datetime::DateTime;
|
use datetime::DateTime;
|
||||||
use offset::FixedOffset;
|
use offset::fixed::FixedOffset;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
||||||
|
|
|
@ -13,7 +13,8 @@ use {Datelike, Timelike};
|
||||||
use Weekday;
|
use Weekday;
|
||||||
use div::div_rem;
|
use div::div_rem;
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use offset::{Offset, FixedOffset, LocalResult};
|
use offset::{TimeZone, Offset, LocalResult};
|
||||||
|
use offset::fixed::FixedOffset;
|
||||||
use naive::date::NaiveDate;
|
use naive::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use naive::datetime::NaiveDateTime;
|
use naive::datetime::NaiveDateTime;
|
||||||
|
@ -537,21 +538,55 @@ impl Parsed {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parsed timezone-aware date and time out of given fields,
|
/// 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
|
/// 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.
|
/// 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.
|
/// 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`.
|
/// 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>> {
|
pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
|
||||||
let delta = offset.local_minus_utc().num_seconds();
|
// if we have `timestamp` specified, guess an offset from that.
|
||||||
let delta = try!(delta.to_i32().ok_or(OUT_OF_RANGE));
|
let mut guessed_offset = 0;
|
||||||
if self.offset.unwrap_or(delta) != delta { return Err(IMPOSSIBLE); }
|
if let Some(timestamp) = self.timestamp {
|
||||||
let datetime = try!(self.to_naive_datetime_with_offset(delta));
|
// make a naive `DateTime` from given timestamp and (if any) nanosecond.
|
||||||
match offset.from_local_datetime(&datetime) {
|
// 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::None => Err(IMPOSSIBLE),
|
||||||
LocalResult::Single(t) => Ok(t),
|
LocalResult::Single(t) => if check_offset(&t) {Ok(t)} else {Err(IMPOSSIBLE)},
|
||||||
LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
|
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 Weekday::*;
|
||||||
use naive::date::{self, NaiveDate};
|
use naive::date::{self, NaiveDate};
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use offset::{Offset, FixedOffset};
|
use offset::TimeZone;
|
||||||
|
use offset::utc::UTC;
|
||||||
|
use offset::fixed::FixedOffset;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parsed_set_fields() {
|
fn test_parsed_set_fields() {
|
||||||
|
@ -964,5 +1001,45 @@ mod tests {
|
||||||
minute: 26, second: 40, nanosecond: 12_345_678, offset: 86400),
|
minute: 26, second: 40, nanosecond: 12_345_678, offset: 86400),
|
||||||
Err(OUT_OF_RANGE)); // `FixedOffset` does not support such huge offset
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
src/lib.rs
44
src/lib.rs
|
@ -32,7 +32,7 @@ chrono = "0.2"
|
||||||
|
|
||||||
And this in your crate root:
|
And this in your crate root:
|
||||||
|
|
||||||
```toml
|
```rust
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -49,14 +49,14 @@ Chrono simply reexports it.
|
||||||
Chrono provides a `DateTime` type for the combined date and time.
|
Chrono provides a `DateTime` type for the combined date and time.
|
||||||
|
|
||||||
`DateTime`, among others, is timezone-aware and
|
`DateTime`, among others, is timezone-aware and
|
||||||
must be constructed from the timezone object (`Offset`).
|
must be constructed from the `TimeZone` object.
|
||||||
`DateTime`s with different offsets do not mix, but can be converted to each other.
|
`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()`)
|
You can get the current date and time in the UTC timezone (`UTC::now()`)
|
||||||
or in the local timezone (`Local::now()`).
|
or in the local timezone (`Local::now()`).
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Local, DateTime};
|
use chrono::*;
|
||||||
|
|
||||||
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
|
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`
|
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.
|
but in turn we get a rich combination of initialization methods.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.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`
|
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")
|
// 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:
|
The following illustrates most supported operations to the date and time:
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
# /* we intentionally fake the datetime...
|
use chrono::*;
|
||||||
use chrono::{UTC, Local, Datelike, Timelike, Weekday, Duration};
|
|
||||||
|
|
||||||
|
# /* we intentionally fake the datetime...
|
||||||
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
||||||
let dt = Local::now();
|
let dt = Local::now();
|
||||||
# */ // up to here. we now define a fixed datetime for the illustrative purpose.
|
# */ // 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);
|
# let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806);
|
||||||
|
|
||||||
// property accessors
|
// 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.ordinal(), 332); // the day of year
|
||||||
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
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.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)
|
// a sample of property manipulations (validates dynamically)
|
||||||
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
|
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.
|
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Offset};
|
use chrono::*;
|
||||||
|
|
||||||
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");
|
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.
|
More detailed control over the parsing process is available via `format` module.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.rust}
|
||||||
use chrono::{UTC, Offset, DateTime};
|
use chrono::*;
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
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()));
|
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
|
||||||
|
|
||||||
// method 3
|
// method 3
|
||||||
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00",
|
assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"),
|
||||||
"%Y-%m-%d %H:%M:%S %z").map(|dt| dt.with_offset(UTC)), Ok(dt));
|
Ok(dt.with_timezone(&FixedOffset::east(9*3600))));
|
||||||
|
|
||||||
// oops, the year is missing!
|
// oops, the year is missing!
|
||||||
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
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
|
### Individual date and time
|
||||||
|
|
||||||
Chrono also provides an individual date type (`Date`) and time type (`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`
|
Most operations available to `DateTime` are also available to `Date` and `Time`
|
||||||
whenever appropriate.
|
whenever appropriate.
|
||||||
|
|
||||||
~~~~ {.rust}
|
~~~~ {.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 ;)
|
# // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
|
||||||
assert_eq!(UTC::today(), UTC::now().date());
|
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.
|
as `NaiveDate`, `NaiveTime` and `NaiveDateTime` respectively.
|
||||||
|
|
||||||
They have almost equivalent interfaces as their timezone-aware twins,
|
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.
|
They are mostly useful for building blocks for higher-level types.
|
||||||
|
|
||||||
## Limitations
|
## 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
|
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`.
|
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;
|
extern crate "time" as stdtime;
|
||||||
|
|
||||||
pub use duration::Duration;
|
pub use duration::Duration;
|
||||||
pub use offset::{Offset, LocalResult};
|
pub use offset::{TimeZone, Offset, LocalResult};
|
||||||
pub use offset::{UTC, FixedOffset, Local};
|
pub use offset::utc::UTC;
|
||||||
|
pub use offset::fixed::FixedOffset;
|
||||||
|
pub use offset::local::Local;
|
||||||
pub use naive::date::NaiveDate;
|
pub use naive::date::NaiveDate;
|
||||||
pub use naive::time::NaiveTime;
|
pub use naive::time::NaiveTime;
|
||||||
pub use naive::datetime::NaiveDateTime;
|
pub use naive::datetime::NaiveDateTime;
|
||||||
|
@ -278,7 +280,7 @@ pub mod offset;
|
||||||
pub mod naive {
|
pub mod naive {
|
||||||
//! Date and time types which do not concern about the timezones.
|
//! 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.
|
//! but can be also used for the simpler date and time handling.
|
||||||
pub mod date;
|
pub mod date;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
|
@ -12,7 +12,6 @@ use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
use div::div_mod_floor;
|
use div::div_mod_floor;
|
||||||
use offset::Offset;
|
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use format::{Item, Numeric, Pad, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
|
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,14 +3,26 @@
|
||||||
// See README.md and LICENSE.txt for details.
|
// 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 std::fmt;
|
||||||
use stdtime;
|
|
||||||
|
|
||||||
use {Weekday, Datelike, Timelike};
|
use Weekday;
|
||||||
use div::div_mod_floor;
|
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use naive::date::NaiveDate;
|
use naive::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
|
@ -48,15 +60,24 @@ impl<T> LocalResult<T> {
|
||||||
pub fn latest(self) -> Option<T> {
|
pub fn latest(self) -> Option<T> {
|
||||||
match self { LocalResult::Single(t) | LocalResult::Ambiguous(_,t) => Some(t), _ => None }
|
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`.
|
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||||
/// The offset in the current date is preserved.
|
/// The offset in the current date is preserved.
|
||||||
///
|
///
|
||||||
/// Propagates any error. Ambiguous result would be discarded.
|
/// Propagates any error. Ambiguous result would be discarded.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Off>> {
|
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
|
||||||
match self {
|
match self {
|
||||||
LocalResult::Single(d) => d.and_time(time)
|
LocalResult::Single(d) => d.and_time(time)
|
||||||
.map_or(LocalResult::None, LocalResult::Single),
|
.map_or(LocalResult::None, LocalResult::Single),
|
||||||
|
@ -69,7 +90,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
|
||||||
///
|
///
|
||||||
/// Propagates any error. Ambiguous result would be discarded.
|
/// Propagates any error. Ambiguous result would be discarded.
|
||||||
#[inline]
|
#[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 {
|
match self {
|
||||||
LocalResult::Single(d) => d.and_hms_opt(hour, min, sec)
|
LocalResult::Single(d) => d.and_hms_opt(hour, min, sec)
|
||||||
.map_or(LocalResult::None, LocalResult::Single),
|
.map_or(LocalResult::None, LocalResult::Single),
|
||||||
|
@ -84,7 +105,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
|
||||||
/// Propagates any error. Ambiguous result would be discarded.
|
/// Propagates any error. Ambiguous result would be discarded.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_milli_opt(self, hour: u32, min: u32, sec: u32,
|
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 {
|
match self {
|
||||||
LocalResult::Single(d) => d.and_hms_milli_opt(hour, min, sec, milli)
|
LocalResult::Single(d) => d.and_hms_milli_opt(hour, min, sec, milli)
|
||||||
.map_or(LocalResult::None, LocalResult::Single),
|
.map_or(LocalResult::None, LocalResult::Single),
|
||||||
|
@ -99,7 +120,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
|
||||||
/// Propagates any error. Ambiguous result would be discarded.
|
/// Propagates any error. Ambiguous result would be discarded.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_micro_opt(self, hour: u32, min: u32, sec: u32,
|
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 {
|
match self {
|
||||||
LocalResult::Single(d) => d.and_hms_micro_opt(hour, min, sec, micro)
|
LocalResult::Single(d) => d.and_hms_micro_opt(hour, min, sec, micro)
|
||||||
.map_or(LocalResult::None, LocalResult::Single),
|
.map_or(LocalResult::None, LocalResult::Single),
|
||||||
|
@ -114,7 +135,7 @@ impl<Off:Offset> LocalResult<Date<Off>> {
|
||||||
/// Propagates any error. Ambiguous result would be discarded.
|
/// Propagates any error. Ambiguous result would be discarded.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_hms_nano_opt(self, hour: u32, min: u32, sec: u32,
|
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 {
|
match self {
|
||||||
LocalResult::Single(d) => d.and_hms_nano_opt(hour, min, sec, nano)
|
LocalResult::Single(d) => d.and_hms_nano_opt(hour, min, sec, nano)
|
||||||
.map_or(LocalResult::None, LocalResult::Single),
|
.map_or(LocalResult::None, LocalResult::Single),
|
||||||
|
@ -138,11 +159,19 @@ impl<T: fmt::Debug> LocalResult<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset from the local time to UTC.
|
/// The offset from the local time to UTC.
|
||||||
pub trait Offset: Clone + fmt::Debug {
|
pub trait Offset: Sized + Clone + fmt::Debug {
|
||||||
/// Makes a new `Date` from year, month, day and the current offset.
|
/// 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.
|
/// 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.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Fails on the out-of-range date, invalid month and/or day.
|
/// 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()
|
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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Fails on the out-of-range date and/or invalid DOY.
|
/// 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()
|
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.
|
/// 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.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Returns `None` on the out-of-range date and/or invalid DOY.
|
/// 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
|
/// 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.
|
/// 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 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.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Fails on the out-of-range date and/or invalid week number.
|
/// 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
|
/// 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.
|
/// 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 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.
|
/// 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.
|
/// 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.
|
/// Fails on invalid hour, minute and/or second.
|
||||||
fn hms(&self, hour: u32, min: u32, sec: u32) -> Time<Self> {
|
fn hms(&self, hour: u32, min: u32, sec: u32) -> Time<Self> {
|
||||||
self.hms_opt(hour, min, sec).unwrap()
|
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.
|
/// Returns `None` on invalid hour, minute and/or second.
|
||||||
fn hms_opt(&self, hour: u32, min: u32, sec: u32) -> LocalResult<Time<Self>> {
|
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.
|
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||||
///
|
///
|
||||||
/// Fails on invalid hour, minute, second and/or millisecond.
|
/// 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()
|
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.
|
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||||
///
|
///
|
||||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
/// 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.
|
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||||
///
|
///
|
||||||
/// Fails on invalid hour, minute, second and/or microsecond.
|
/// 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()
|
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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// 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()
|
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.
|
/// 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.
|
/// 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>> {
|
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
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.
|
/// Reconstructs the time zone from the offset.
|
||||||
fn local_minus_utc(&self) -> Duration;
|
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.
|
/// 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.
|
/// 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.
|
/// 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.
|
/// Converts the UTC `NaiveDate` to the local time.
|
||||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate 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.
|
/// Converts the UTC `NaiveTime` to the local time.
|
||||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate 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.
|
/// Converts the UTC `NaiveDateTime` to the local time.
|
||||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate 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;
|
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
|
||||||
}
|
DateTime::from_utc(utc.clone(), self.offset_from_utc_datetime(utc))
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for UTC {
|
pub mod utc;
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::zero() }
|
pub mod fixed;
|
||||||
|
pub mod local;
|
||||||
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) }
|
|
||||||
}
|
|
||||||
|
|
|
@ -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") }
|
||||||
|
}
|
||||||
|
|
129
src/time.rs
129
src/time.rs
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* ISO 8601 time with timezone.
|
* ISO 8601 time with time zone.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{fmt, hash};
|
use std::{fmt, hash};
|
||||||
|
@ -11,51 +11,72 @@ use std::cmp::Ordering;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
use offset::Offset;
|
use offset::{TimeZone, Offset};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use format::{Item, DelayedFormat, StrftimeItems};
|
use format::{Item, DelayedFormat, StrftimeItems};
|
||||||
|
|
||||||
/// ISO 8601 time with timezone.
|
/// ISO 8601 time with timezone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Time<Off> {
|
pub struct Time<Tz: TimeZone> {
|
||||||
time: NaiveTime,
|
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.
|
/// 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]
|
#[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 }
|
Time { time: time, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset from UTC.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
|
||||||
&self.offset
|
&self.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the associated offset.
|
/// Retrieves an associated time zone.
|
||||||
/// This does not change the actual `Time` (but will change the string representation).
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Time<Off2> {
|
pub fn timezone(&self) -> Tz {
|
||||||
Time::from_utc(self.time, offset)
|
TimeZone::from_offset(&self.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a view to the local time.
|
/// Changes the associated time zone.
|
||||||
fn local(&self) -> NaiveTime {
|
/// This does not change the actual `Time` (but will change the string representation).
|
||||||
self.offset.to_local_time(&self.time)
|
#[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.
|
/// Formats the time with the specified formatting items.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
pub fn format_with_items<'a, I>(&'a self, items: I) -> DelayedFormat<'a, I>
|
||||||
where I: Iterator<Item=Item<'a>> + Clone {
|
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.
|
/// 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> {
|
impl<Tz: TimeZone> Timelike for Time<Tz> {
|
||||||
#[inline] fn hour(&self) -> u32 { self.local().hour() }
|
#[inline] fn hour(&self) -> u32 { self.naive_local().hour() }
|
||||||
#[inline] fn minute(&self) -> u32 { self.local().minute() }
|
#[inline] fn minute(&self) -> u32 { self.naive_local().minute() }
|
||||||
#[inline] fn second(&self) -> u32 { self.local().second() }
|
#[inline] fn second(&self) -> u32 { self.naive_local().second() }
|
||||||
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
|
#[inline] fn nanosecond(&self) -> u32 { self.naive_local().nanosecond() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_hour(&self, hour: u32) -> Option<Time<Off>> {
|
fn with_hour(&self, hour: u32) -> Option<Time<Tz>> {
|
||||||
self.local().with_hour(hour)
|
map_local(self, |time| time.with_hour(hour))
|
||||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_minute(&self, min: u32) -> Option<Time<Off>> {
|
fn with_minute(&self, min: u32) -> Option<Time<Tz>> {
|
||||||
self.local().with_minute(min)
|
map_local(self, |time| time.with_minute(min))
|
||||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_second(&self, sec: u32) -> Option<Time<Off>> {
|
fn with_second(&self, sec: u32) -> Option<Time<Tz>> {
|
||||||
self.local().with_second(sec)
|
map_local(self, |time| time.with_second(sec))
|
||||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_nanosecond(&self, nano: u32) -> Option<Time<Off>> {
|
fn with_nanosecond(&self, nano: u32) -> Option<Time<Tz>> {
|
||||||
self.local().with_nanosecond(nano)
|
map_local(self, |time| time.with_nanosecond(nano))
|
||||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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> {
|
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Time<Tz2>> for Time<Tz> {
|
||||||
fn eq(&self, other: &Time<Off2>) -> bool { self.time == other.time }
|
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> {
|
impl<Tz: TimeZone> PartialOrd for Time<Tz> {
|
||||||
fn partial_cmp(&self, other: &Time<Off>) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Time<Tz>) -> Option<Ordering> {
|
||||||
self.time.partial_cmp(&other.time)
|
self.time.partial_cmp(&other.time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Ord for Time<Off> {
|
impl<Tz: TimeZone> Ord for Time<Tz> {
|
||||||
fn cmp(&self, other: &Time<Off>) -> Ordering { self.time.cmp(&other.time) }
|
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) }
|
fn hash(&self, state: &mut H) { self.time.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off:Offset> Add<Duration> for Time<Off> {
|
impl<Tz: TimeZone> Add<Duration> for Time<Tz> {
|
||||||
type Output = Time<Off>;
|
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 }
|
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;
|
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> {
|
impl<Tz: TimeZone> Sub<Duration> for Time<Tz> {
|
||||||
type Output = Time<Off>;
|
type Output = Time<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[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 {
|
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 {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}{}", self.local(), self.offset)
|
write!(f, "{}{}", self.naive_local(), self.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue