initial `Offset` implementations.
This commit is contained in:
parent
07d98709df
commit
32c3ee85e8
263
src/date.rs
263
src/date.rs
|
@ -6,11 +6,12 @@
|
|||
* ISO 8601 calendar date.
|
||||
*/
|
||||
|
||||
use std::{fmt, num};
|
||||
use std::{fmt, num, hash};
|
||||
use num::Integer;
|
||||
use duration::Duration;
|
||||
use offset::{Offset, UTC};
|
||||
use time::TimeZ;
|
||||
use datetime::DateTimeZ;
|
||||
use datetime::{DateTimeZ, DateTime};
|
||||
|
||||
use self::internals::{DateImpl, Of, Mdf, YearFlags};
|
||||
|
||||
|
@ -600,6 +601,264 @@ impl fmt::Show for DateZ {
|
|||
}
|
||||
}
|
||||
|
||||
/// ISO 8601 calendar date with timezone.
|
||||
#[deriving(Clone)]
|
||||
pub struct Date<Off> {
|
||||
date: DateZ,
|
||||
offset: Off,
|
||||
}
|
||||
|
||||
/// The minimum possible `Date`.
|
||||
pub static MIN: Date<UTC> = Date { date: MINZ, offset: UTC };
|
||||
/// The maximum possible `Date`.
|
||||
pub static MAX: Date<UTC> = Date { date: MAXZ, offset: UTC };
|
||||
|
||||
impl<Off:Offset> Date<Off> {
|
||||
/// Makes a new `Date` with given *UTC* date and offset.
|
||||
/// The local date should be constructed via the `Offset` trait.
|
||||
#[inline]
|
||||
pub fn from_utc(date: DateZ, offset: Off) -> Date<Off> {
|
||||
Date { date: date, offset: offset }
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date and given `TimeZ`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Fails on invalid datetime.
|
||||
#[inline]
|
||||
pub fn and_time(&self, time: TimeZ) -> Option<DateTime<Off>> {
|
||||
self.offset.from_local_datetime(&self.date.and_time(time)).single()
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute and second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Fails on invalid hour, minute and/or second.
|
||||
#[inline]
|
||||
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Off> {
|
||||
self.and_hms_opt(hour, min, sec).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute and second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
#[inline]
|
||||
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Off>> {
|
||||
TimeZ::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and millisecond.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Fails on invalid hour, minute, second and/or millisecond.
|
||||
#[inline]
|
||||
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Off> {
|
||||
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and millisecond.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
#[inline]
|
||||
pub fn and_hms_milli_opt(&self, hour: u32, min: u32, sec: u32,
|
||||
milli: u32) -> Option<DateTime<Off>> {
|
||||
TimeZ::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and microsecond.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Fails on invalid hour, minute, second and/or microsecond.
|
||||
#[inline]
|
||||
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Off> {
|
||||
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and microsecond.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||
#[inline]
|
||||
pub fn and_hms_micro_opt(&self, hour: u32, min: u32, sec: u32,
|
||||
micro: u32) -> Option<DateTime<Off>> {
|
||||
TimeZ::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and nanosecond.
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Fails on invalid hour, minute, second and/or nanosecond.
|
||||
#[inline]
|
||||
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Off> {
|
||||
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
|
||||
}
|
||||
|
||||
/// Makes a new `DateTimeZ` from the current date, hour, minute, second and nanosecond.
|
||||
/// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||
#[inline]
|
||||
pub fn and_hms_nano_opt(&self, hour: u32, min: u32, sec: u32,
|
||||
nano: u32) -> Option<DateTime<Off>> {
|
||||
TimeZ::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the next date.
|
||||
///
|
||||
/// Fails when `self` is the last representable date.
|
||||
#[inline]
|
||||
pub fn succ(&self) -> Date<Off> {
|
||||
self.succ_opt().expect("out of bound")
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the next date.
|
||||
///
|
||||
/// Returns `None` when `self` is the last representable date.
|
||||
#[inline]
|
||||
pub fn succ_opt(&self) -> Option<Date<Off>> {
|
||||
self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the prior date.
|
||||
///
|
||||
/// Fails when `self` is the first representable date.
|
||||
#[inline]
|
||||
pub fn pred(&self) -> Date<Off> {
|
||||
self.pred_opt().expect("out of bound")
|
||||
}
|
||||
|
||||
/// Makes a new `Date` for the prior date.
|
||||
///
|
||||
/// Returns `None` when `self` is the first representable date.
|
||||
#[inline]
|
||||
pub fn pred_opt(&self) -> Option<Date<Off>> {
|
||||
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
|
||||
}
|
||||
|
||||
/// Returns a view to the local date.
|
||||
fn local(&self) -> DateZ {
|
||||
self.offset.to_local_date(&self.date)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Datelike for Date<Off> {
|
||||
#[inline] fn year(&self) -> i32 { self.local().year() }
|
||||
#[inline] fn month(&self) -> u32 { self.local().month() }
|
||||
#[inline] fn month0(&self) -> u32 { self.local().month0() }
|
||||
#[inline] fn day(&self) -> u32 { self.local().day() }
|
||||
#[inline] fn day0(&self) -> u32 { self.local().day0() }
|
||||
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
|
||||
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
|
||||
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
|
||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
|
||||
|
||||
#[inline]
|
||||
fn with_year(&self, year: i32) -> Option<Date<Off>> {
|
||||
self.local().with_year(year)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month(&self, month: u32) -> Option<Date<Off>> {
|
||||
self.local().with_month(month)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month0(&self, month0: u32) -> Option<Date<Off>> {
|
||||
self.local().with_month0(month0)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day(&self, day: u32) -> Option<Date<Off>> {
|
||||
self.local().with_day(day)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day0(&self, day0: u32) -> Option<Date<Off>> {
|
||||
self.local().with_day0(day0)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal(&self, ordinal: u32) -> Option<Date<Off>> {
|
||||
self.local().with_ordinal(ordinal)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Off>> {
|
||||
self.local().with_ordinal0(ordinal0)
|
||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
||||
}
|
||||
}
|
||||
|
||||
impl num::Bounded for Date<UTC> {
|
||||
#[inline] fn min_value() -> Date<UTC> { MIN }
|
||||
#[inline] fn max_value() -> Date<UTC> { MAX }
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialEq for Date<Off> {
|
||||
fn eq(&self, other: &Date<Off>) -> bool { self.date == other.date }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Eq for Date<Off> {
|
||||
}
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Equiv<Date<Off2>> for Date<Off> {
|
||||
fn equiv(&self, other: &Date<Off2>) -> bool { self.date == other.date }
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialOrd for Date<Off> {
|
||||
fn partial_cmp(&self, other: &Date<Off>) -> Option<Ordering> {
|
||||
self.date.partial_cmp(&other.date)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Ord for Date<Off> {
|
||||
fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> hash::Hash for Date<Off> {
|
||||
fn hash(&self, state: &mut hash::sip::SipState) { self.date.hash(state) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Add<Duration,Date<Off>> for Date<Off> {
|
||||
fn add(&self, rhs: &Duration) -> Date<Off> {
|
||||
Date { date: self.date + *rhs, offset: self.offset.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Rust issue #7590, the current coherence checker can't handle multiple Add impls
|
||||
impl<Off:Offset> Add<Date<Off>,Date<Off>> for Duration {
|
||||
#[inline]
|
||||
fn add(&self, rhs: &Date<Off>) -> Date<Off> { rhs.add(self) }
|
||||
}
|
||||
*/
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Sub<Date<Off2>,Duration> for Date<Off> {
|
||||
fn sub(&self, rhs: &Date<Off2>) -> Duration {
|
||||
self.date - rhs.date
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> fmt::Show for Date<Off> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.local(), self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Datelike, DateZ, MIN_YEAR, MAX_YEAR};
|
||||
|
|
198
src/datetime.rs
198
src/datetime.rs
|
@ -6,10 +6,11 @@
|
|||
* ISO 8601 date and time.
|
||||
*/
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, hash};
|
||||
use offset::Offset;
|
||||
use duration::Duration;
|
||||
use time::{Timelike, TimeZ};
|
||||
use date::{Datelike, DateZ, Weekday};
|
||||
use time::{Timelike, TimeZ, Time};
|
||||
use date::{Datelike, DateZ, Date, Weekday};
|
||||
|
||||
/// ISO 8601 combined date and time without timezone.
|
||||
#[deriving(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
|
@ -224,6 +225,181 @@ impl fmt::Show for DateTimeZ {
|
|||
}
|
||||
}
|
||||
|
||||
/// ISO 8601 combined date and time with timezone.
|
||||
#[deriving(Clone)]
|
||||
pub struct DateTime<Off> {
|
||||
datetime: DateTimeZ,
|
||||
offset: Off,
|
||||
}
|
||||
|
||||
impl<Off:Offset> DateTime<Off> {
|
||||
/// Makes a new `DateTime` with given *UTC* datetime and offset.
|
||||
/// The local datetime should be constructed via the `Offset` trait.
|
||||
#[inline]
|
||||
pub fn from_utc(datetime: DateTimeZ, offset: Off) -> DateTime<Off> {
|
||||
DateTime { datetime: datetime, offset: offset }
|
||||
}
|
||||
|
||||
/// Retrieves a date component.
|
||||
#[inline]
|
||||
pub fn date(&self) -> Date<Off> {
|
||||
Date::from_utc(self.datetime.date().clone(), self.offset.clone())
|
||||
}
|
||||
|
||||
/// Retrieves a time component.
|
||||
#[inline]
|
||||
pub fn time(&self) -> Time<Off> {
|
||||
Time::from_utc(self.datetime.time().clone(), self.offset.clone())
|
||||
}
|
||||
|
||||
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC.
|
||||
#[inline]
|
||||
pub fn num_seconds_from_unix_epoch(&self) -> i64 {
|
||||
self.datetime.num_seconds_from_unix_epoch()
|
||||
}
|
||||
|
||||
/// Returns a view to the local datetime.
|
||||
fn local(&self) -> DateTimeZ {
|
||||
self.offset.to_local_datetime(&self.datetime)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Datelike for DateTime<Off> {
|
||||
#[inline] fn year(&self) -> i32 { self.local().year() }
|
||||
#[inline] fn month(&self) -> u32 { self.local().month() }
|
||||
#[inline] fn month0(&self) -> u32 { self.local().month0() }
|
||||
#[inline] fn day(&self) -> u32 { self.local().day() }
|
||||
#[inline] fn day0(&self) -> u32 { self.local().day0() }
|
||||
#[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
|
||||
#[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
|
||||
#[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
|
||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }
|
||||
|
||||
#[inline]
|
||||
fn with_year(&self, year: i32) -> Option<DateTime<Off>> {
|
||||
self.local().with_year(year)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month(&self, month: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_month(month)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_month0(&self, month0: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_month0(month0)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day(&self, day: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_day(day)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_day0(&self, day0: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_day0(day0)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_ordinal(ordinal)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_ordinal0(ordinal0)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Timelike for DateTime<Off> {
|
||||
#[inline] fn hour(&self) -> u32 { self.local().hour() }
|
||||
#[inline] fn minute(&self) -> u32 { self.local().minute() }
|
||||
#[inline] fn second(&self) -> u32 { self.local().second() }
|
||||
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
|
||||
|
||||
#[inline]
|
||||
fn with_hour(&self, hour: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_hour(hour)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_minute(&self, min: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_minute(min)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_second(&self, sec: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_second(sec)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Off>> {
|
||||
self.local().with_nanosecond(nano)
|
||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialEq for DateTime<Off> {
|
||||
fn eq(&self, other: &DateTime<Off>) -> bool { self.datetime == other.datetime }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Eq for DateTime<Off> {
|
||||
}
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Equiv<DateTime<Off2>> for DateTime<Off> {
|
||||
fn equiv(&self, other: &DateTime<Off2>) -> bool { self.datetime == other.datetime }
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialOrd for DateTime<Off> {
|
||||
fn partial_cmp(&self, other: &DateTime<Off>) -> Option<Ordering> {
|
||||
self.datetime.partial_cmp(&other.datetime)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Ord for DateTime<Off> {
|
||||
fn cmp(&self, other: &DateTime<Off>) -> Ordering { self.datetime.cmp(&other.datetime) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> hash::Hash for DateTime<Off> {
|
||||
fn hash(&self, state: &mut hash::sip::SipState) { self.datetime.hash(state) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Add<Duration,DateTime<Off>> for DateTime<Off> {
|
||||
fn add(&self, rhs: &Duration) -> DateTime<Off> {
|
||||
DateTime { datetime: self.datetime + *rhs, offset: self.offset.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Rust issue #7590, the current coherence checker can't handle multiple Add impls
|
||||
impl<Off:Offset> Add<DateTime<Off>,DateTime<Off>> for Duration {
|
||||
#[inline]
|
||||
fn add(&self, rhs: &DateTime<Off>) -> DateTime<Off> { rhs.add(self) }
|
||||
}
|
||||
*/
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Sub<DateTime<Off2>,Duration> for DateTime<Off> {
|
||||
fn sub(&self, rhs: &DateTime<Off2>) -> Duration {
|
||||
self.datetime - rhs.datetime
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> fmt::Show for DateTime<Off> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.local(), self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DateTimeZ;
|
||||
|
@ -268,5 +444,21 @@ mod tests {
|
|||
assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000);
|
||||
assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(uppercase_variables)]
|
||||
fn test_datetime_offset() {
|
||||
use offset::{Offset, UTC, FixedOffset};
|
||||
let EDT = FixedOffset::east(4*60*60);
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).to_string(),
|
||||
"2014-05-06T07:08:09Z".to_string());
|
||||
assert_eq!(EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).to_string(),
|
||||
"2014-05-06T07:08:09+04:00".to_string());
|
||||
assert!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).equiv(&EDT.ymd(2014, 5, 6).and_hms(11, 8, 9)));
|
||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1),
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ pub use time::{Timelike, TimeZ};
|
|||
pub use datetime::DateTimeZ;
|
||||
|
||||
pub mod duration;
|
||||
pub mod offset;
|
||||
pub mod date;
|
||||
pub mod time;
|
||||
pub mod datetime;
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
// This is a part of rust-chrono.
|
||||
// Copyright (c) 2014, Kang Seonghoon.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
/*!
|
||||
* Offsets from the local time to UTC.
|
||||
*/
|
||||
|
||||
use std::fmt;
|
||||
use num::Integer;
|
||||
use duration::Duration;
|
||||
use date::{DateZ, Date, Weekday};
|
||||
use time::{TimeZ, Time};
|
||||
use datetime::{DateTimeZ, DateTime};
|
||||
|
||||
/// The conversion result from the local time to the timezone-aware datetime types.
|
||||
pub enum LocalResult<T> {
|
||||
/// Given local time representation is invalid.
|
||||
/// This can occur when, for example, the positive timezone transition.
|
||||
NoResult,
|
||||
/// Given local time representation has a single unique result.
|
||||
Single(T),
|
||||
/// Given local time representation has multiple results and thus ambiguous.
|
||||
/// This can occur when, for example, the negative timezone transition.
|
||||
Ambiguous(T /*min*/, T /*max*/),
|
||||
}
|
||||
|
||||
impl<T> LocalResult<T> {
|
||||
/// Returns `Some` only when the conversion result is unique, or `None` otherwise.
|
||||
pub fn single(self) -> Option<T> {
|
||||
match self { Single(t) => Some(t), _ => None }
|
||||
}
|
||||
|
||||
/// Returns `Some` for the earliest possible conversion result, or `None` if none.
|
||||
pub fn earliest(self) -> Option<T> {
|
||||
match self { Single(t) | Ambiguous(t,_) => Some(t), _ => None }
|
||||
}
|
||||
|
||||
/// Returns `Some` for the latest possible conversion result, or `None` if none.
|
||||
pub fn latest(self) -> Option<T> {
|
||||
match self { Single(t) | Ambiguous(_,t) => Some(t), _ => None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:fmt::Show> LocalResult<T> {
|
||||
/// Returns the single unique conversion result, or fails accordingly.
|
||||
pub fn unwrap(self) -> T {
|
||||
match self {
|
||||
NoResult => fail!("No such local time"),
|
||||
Single(t) => t,
|
||||
Ambiguous(t1,t2) => fail!("Ambiguous local time, ranging from {} to {}", t1, t2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The offset from the local time to UTC.
|
||||
pub trait Offset: Clone + fmt::Show {
|
||||
/// Makes a new `Date` from year, month, day and the current offset.
|
||||
/// 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),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Fails on the out-of-range date, invalid month and/or day.
|
||||
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
|
||||
self.ymd_opt(year, month, day).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, month, day and the current offset.
|
||||
/// 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),
|
||||
/// 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.
|
||||
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
|
||||
match DateZ::from_ymd_opt(year, month, day) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current offset.
|
||||
/// 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),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Fails on the out-of-range date and/or invalid DOY.
|
||||
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
|
||||
self.yo_opt(year, ordinal).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current offset.
|
||||
/// 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),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid DOY.
|
||||
fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
|
||||
match DateZ::from_yo_opt(year, ordinal) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
|
||||
/// the current offset.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
/// The resulting `Date` may have a different year from the input year.
|
||||
///
|
||||
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||
///
|
||||
/// Fails on the out-of-range date and/or invalid week number.
|
||||
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
|
||||
self.isoywd_opt(year, week, weekday).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
|
||||
/// the current offset.
|
||||
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||
/// The resulting `Date` may have a different year from the input year.
|
||||
///
|
||||
/// The offset normally does not affect the date (unless it is between UTC-24 and UTC+24),
|
||||
/// 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.
|
||||
fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
|
||||
match DateZ::from_isoywd_opt(year, week, weekday) {
|
||||
Some(d) => self.from_local_date(&d),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second and the current offset.
|
||||
///
|
||||
/// Fails on invalid hour, minute and/or second.
|
||||
fn hms(&self, hour: u32, min: u32, sec: u32) -> Time<Self> {
|
||||
self.hms_opt(hour, min, sec).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second and the current offset.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
fn hms_opt(&self, hour: u32, min: u32, sec: u32) -> LocalResult<Time<Self>> {
|
||||
match TimeZ::from_hms_opt(hour, min, sec) {
|
||||
Some(t) => self.from_local_time(&t),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, millisecond and the current offset.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
///
|
||||
/// Fails on invalid hour, minute, second and/or millisecond.
|
||||
fn hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> Time<Self> {
|
||||
self.hms_milli_opt(hour, min, sec, milli).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, millisecond and the current offset.
|
||||
/// The millisecond part can exceed 1,000 in order to represent the leap second.
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
fn hms_milli_opt(&self, hour: u32, min: u32, sec: u32, milli: u32) -> LocalResult<Time<Self>> {
|
||||
match TimeZ::from_hms_milli_opt(hour, min, sec, milli) {
|
||||
Some(t) => self.from_local_time(&t),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, microsecond and the current offset.
|
||||
/// The microsecond part can exceed 1,000,000 in order to represent the leap second.
|
||||
///
|
||||
/// Fails on invalid hour, minute, second and/or microsecond.
|
||||
fn hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> Time<Self> {
|
||||
self.hms_micro_opt(hour, min, sec, micro).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, microsecond and the current offset.
|
||||
/// 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.
|
||||
fn hms_micro_opt(&self, hour: u32, min: u32, sec: u32, micro: u32) -> LocalResult<Time<Self>> {
|
||||
match TimeZ::from_hms_micro_opt(hour, min, sec, micro) {
|
||||
Some(t) => self.from_local_time(&t),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, nanosecond and the current offset.
|
||||
/// 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.
|
||||
fn hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> Time<Self> {
|
||||
self.hms_nano_opt(hour, min, sec, nano).unwrap()
|
||||
}
|
||||
|
||||
/// Makes a new `Time` from hour, minute, second, nanosecond and the current offset.
|
||||
/// 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.
|
||||
fn hms_nano_opt(&self, hour: u32, min: u32, sec: u32, nano: u32) -> LocalResult<Time<Self>> {
|
||||
match TimeZ::from_hms_nano_opt(hour, min, sec, nano) {
|
||||
Some(t) => self.from_local_time(&t),
|
||||
None => NoResult,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the local `DateZ` to the timezone-aware `Date` if possible.
|
||||
fn from_local_date(&self, local: &DateZ) -> LocalResult<Date<Self>>;
|
||||
|
||||
/// Converts the local `TimeZ` to the timezone-aware `Time` if possible.
|
||||
fn from_local_time(&self, local: &TimeZ) -> LocalResult<Time<Self>>;
|
||||
|
||||
/// Converts the local `DateTimeZ` to the timezone-aware `DateTime` if possible.
|
||||
fn from_local_datetime(&self, local: &DateTimeZ) -> LocalResult<DateTime<Self>>;
|
||||
|
||||
/// Converts the UTC `DateZ` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
fn to_local_date(&self, utc: &DateZ) -> DateZ;
|
||||
|
||||
/// Converts the UTC `TimeZ` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
fn to_local_time(&self, utc: &TimeZ) -> TimeZ;
|
||||
|
||||
/// Converts the UTC `DateTimeZ` to the local time.
|
||||
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
|
||||
fn to_local_datetime(&self, utc: &DateTimeZ) -> DateTimeZ;
|
||||
}
|
||||
|
||||
/// The UTC timescale. This is the most efficient offset when you don't need the local time.
|
||||
#[deriving(Clone)]
|
||||
pub struct UTC;
|
||||
|
||||
impl Offset for UTC {
|
||||
fn from_local_date(&self, local: &DateZ) -> LocalResult<Date<UTC>> {
|
||||
Single(Date::from_utc(local.clone(), UTC))
|
||||
}
|
||||
fn from_local_time(&self, local: &TimeZ) -> LocalResult<Time<UTC>> {
|
||||
Single(Time::from_utc(local.clone(), UTC))
|
||||
}
|
||||
fn from_local_datetime(&self, local: &DateTimeZ) -> LocalResult<DateTime<UTC>> {
|
||||
Single(DateTime::from_utc(local.clone(), UTC))
|
||||
}
|
||||
|
||||
fn to_local_date(&self, utc: &DateZ) -> DateZ { utc.clone() }
|
||||
fn to_local_time(&self, utc: &TimeZ) -> TimeZ { utc.clone() }
|
||||
fn to_local_datetime(&self, utc: &DateTimeZ) -> DateTimeZ { utc.clone() }
|
||||
}
|
||||
|
||||
impl fmt::Show for UTC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
|
||||
}
|
||||
|
||||
/// The fixed offset, from UTC-23:59:59 to UTC+23:59:59.
|
||||
#[deriving(Clone)]
|
||||
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 from_local_date(&self, local: &DateZ) -> LocalResult<Date<FixedOffset>> {
|
||||
Single(Date::from_utc(local.clone(), self.clone()))
|
||||
}
|
||||
fn from_local_time(&self, local: &TimeZ) -> LocalResult<Time<FixedOffset>> {
|
||||
Single(Time::from_utc(*local + Duration::seconds(-self.local_minus_utc), self.clone()))
|
||||
}
|
||||
fn from_local_datetime(&self, local: &DateTimeZ) -> LocalResult<DateTime<FixedOffset>> {
|
||||
Single(DateTime::from_utc(*local + Duration::seconds(-self.local_minus_utc), self.clone()))
|
||||
}
|
||||
|
||||
fn to_local_date(&self, utc: &DateZ) -> DateZ {
|
||||
utc.clone()
|
||||
}
|
||||
fn to_local_time(&self, utc: &TimeZ) -> TimeZ {
|
||||
*utc + Duration::seconds(self.local_minus_utc)
|
||||
}
|
||||
fn to_local_datetime(&self, utc: &DateTimeZ) -> DateTimeZ {
|
||||
*utc + Duration::seconds(self.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show 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) = offset.div_mod_floor(&60);
|
||||
let (hour, min) = mins.div_mod_floor(&60);
|
||||
if sec == 0 {
|
||||
write!(f, "{}{:02}:{:02}", sign, hour, min)
|
||||
} else {
|
||||
write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
109
src/time.rs
109
src/time.rs
|
@ -6,8 +6,9 @@
|
|||
* ISO 8601 time.
|
||||
*/
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, hash};
|
||||
use num::Integer;
|
||||
use offset::Offset;
|
||||
use duration::Duration;
|
||||
|
||||
/// The common set of methods for time component.
|
||||
|
@ -254,6 +255,112 @@ impl fmt::Show for TimeZ {
|
|||
}
|
||||
}
|
||||
|
||||
/// ISO 8601 time with timezone.
|
||||
#[deriving(Clone)]
|
||||
pub struct Time<Off> {
|
||||
time: TimeZ,
|
||||
offset: Off,
|
||||
}
|
||||
|
||||
impl<Off:Offset> Time<Off> {
|
||||
/// Makes a new `Time` with given *UTC* time and offset.
|
||||
/// The local time should be constructed via the `Offset` trait.
|
||||
#[inline]
|
||||
pub fn from_utc(time: TimeZ, offset: Off) -> Time<Off> {
|
||||
Time { time: time, offset: offset }
|
||||
}
|
||||
|
||||
/// Returns a view to the local time.
|
||||
fn local(&self) -> TimeZ {
|
||||
self.offset.to_local_time(&self.time)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Timelike for Time<Off> {
|
||||
#[inline] fn hour(&self) -> u32 { self.local().hour() }
|
||||
#[inline] fn minute(&self) -> u32 { self.local().minute() }
|
||||
#[inline] fn second(&self) -> u32 { self.local().second() }
|
||||
#[inline] fn nanosecond(&self) -> u32 { self.local().nanosecond() }
|
||||
|
||||
#[inline]
|
||||
fn with_hour(&self, hour: u32) -> Option<Time<Off>> {
|
||||
self.local().with_hour(hour)
|
||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_minute(&self, min: u32) -> Option<Time<Off>> {
|
||||
self.local().with_minute(min)
|
||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_second(&self, sec: u32) -> Option<Time<Off>> {
|
||||
self.local().with_second(sec)
|
||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_nanosecond(&self, nano: u32) -> Option<Time<Off>> {
|
||||
self.local().with_nanosecond(nano)
|
||||
.and_then(|time| self.offset.from_local_time(&time).single())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn num_seconds_from_midnight(&self) -> u32 { self.local().num_seconds_from_midnight() }
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialEq for Time<Off> {
|
||||
fn eq(&self, other: &Time<Off>) -> bool { self.time == other.time }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Eq for Time<Off> {
|
||||
}
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Equiv<Time<Off2>> for Time<Off> {
|
||||
fn equiv(&self, other: &Time<Off2>) -> bool { self.time == other.time }
|
||||
}
|
||||
|
||||
impl<Off:Offset> PartialOrd for Time<Off> {
|
||||
fn partial_cmp(&self, other: &Time<Off>) -> Option<Ordering> {
|
||||
self.time.partial_cmp(&other.time)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> Ord for Time<Off> {
|
||||
fn cmp(&self, other: &Time<Off>) -> Ordering { self.time.cmp(&other.time) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> hash::Hash for Time<Off> {
|
||||
fn hash(&self, state: &mut hash::sip::SipState) { self.time.hash(state) }
|
||||
}
|
||||
|
||||
impl<Off:Offset> Add<Duration,Time<Off>> for Time<Off> {
|
||||
fn add(&self, rhs: &Duration) -> Time<Off> {
|
||||
Time { time: self.time + *rhs, offset: self.offset.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Rust issue #7590, the current coherence checker can't handle multiple Add impls
|
||||
impl<Off:Offset> Add<Time<Off>,Time<Off>> for Duration {
|
||||
#[inline]
|
||||
fn add(&self, rhs: &Time<Off>) -> Time<Off> { rhs.add(self) }
|
||||
}
|
||||
*/
|
||||
|
||||
impl<Off:Offset, Off2:Offset> Sub<Time<Off2>,Duration> for Time<Off> {
|
||||
fn sub(&self, rhs: &Time<Off2>) -> Duration {
|
||||
self.time - rhs.time
|
||||
}
|
||||
}
|
||||
|
||||
impl<Off:Offset> fmt::Show for Time<Off> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.local(), self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Timelike, TimeZ};
|
||||
|
|
Loading…
Reference in New Issue