Fixes #27.
This is due to somewhat ambiguous semantics of `Date`. It cannot really constructed without an intermediate `DateTime` much like the removed `Time`, but it is much more useful than `Time` so we need some reasonable meaning to it. This commit clarifies that meaning and corrects some problems around it: - The date itself is timezone-agnostic unless the timezone itself has an offset equal to or greater than one day. In all current time zones, the date conversion should be a no-op. - The date may be attached some offset; that offset should have been occurred within the corresponding day in either the local time or the UTC. - `TimeZone` is free to assign the offset within this constraint. For convenience, the current `Local` time zone assumes the local midnight or the UTC midnight.
This commit is contained in:
parent
fae01ad74d
commit
2be6e14446
|
@ -350,10 +350,12 @@ impl<Tz: TimeZone> fmt::Display for Date<Tz> where Tz::Offset: fmt::Display {
|
|||
mod tests {
|
||||
use std::fmt;
|
||||
|
||||
use Datelike;
|
||||
use duration::Duration;
|
||||
use naive::date::NaiveDate;
|
||||
use naive::datetime::NaiveDateTime;
|
||||
use offset::{TimeZone, Offset, LocalResult};
|
||||
use offset::local::Local;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
struct UTC1y; // same to UTC but with an offset of 365 days
|
||||
|
@ -396,5 +398,10 @@ mod tests {
|
|||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||
"2012-03-04T05:06:07+8760:00".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() { // issue #27
|
||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -378,23 +378,42 @@ mod tests {
|
|||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_datetime_offset() {
|
||||
let EST = FixedOffset::east(5*60*60);
|
||||
let EDT = FixedOffset::east(4*60*60);
|
||||
let EST = FixedOffset::west(5*60*60);
|
||||
let EDT = FixedOffset::west(4*60*60);
|
||||
let KST = FixedOffset::east(9*60*60);
|
||||
|
||||
assert_eq!(format!("{}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06 07:08:09 UTC");
|
||||
assert_eq!(format!("{}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06 07:08:09 +04:00");
|
||||
"2014-05-06 07:08:09 -04:00");
|
||||
assert_eq!(format!("{}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06 07:08:09 +09:00");
|
||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06T07:08:09Z");
|
||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06T07:08:09+04:00");
|
||||
"2014-05-06T07:08:09-04:00");
|
||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||
"2014-05-06T07:08:09+09:00");
|
||||
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(11, 8, 9));
|
||||
// edge cases
|
||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||
"2014-05-06T00:00:00Z");
|
||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||
"2014-05-06T00:00:00-04:00");
|
||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||
"2014-05-06T00:00:00+09:00");
|
||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||
"2014-05-06T23:59:59Z");
|
||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||
"2014-05-06T23:59:59-04:00");
|
||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||
"2014-05-06T23:59:59+09:00");
|
||||
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(3, 8, 9));
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1),
|
||||
UTC.ymd(2014, 5, 6).and_hms(8, 9, 10));
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) - EDT.ymd(2014, 5, 6).and_hms(10, 11, 12),
|
||||
Duration::seconds(3600 - 3*60 - 3));
|
||||
Duration::seconds(-7*3600 - 3*60 - 3));
|
||||
|
||||
assert_eq!(*UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), UTC);
|
||||
assert_eq!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), EDT);
|
||||
|
|
|
@ -95,7 +95,11 @@ impl TimeZone for Local {
|
|||
|
||||
// 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())
|
||||
// this sounds very strange, but required for keeping `TimeZone::ymd` sane.
|
||||
// in the other words, we use the offset at the local midnight
|
||||
// but keep the actual date unaltered (much like `FixedOffset`).
|
||||
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
|
||||
midnight.map(|datetime| Date::from_utc(*local, datetime.offset().clone()))
|
||||
}
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||
let timespec = datetime_to_timespec(local, true);
|
||||
|
@ -103,7 +107,8 @@ impl TimeZone for Local {
|
|||
}
|
||||
|
||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
|
||||
self.from_utc_datetime(&utc.and_hms(0, 0, 0)).date()
|
||||
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
|
||||
Date::from_utc(*utc, midnight.offset().clone())
|
||||
}
|
||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||
let timespec = datetime_to_timespec(utc, false);
|
||||
|
|
Loading…
Reference in New Issue