initial implementation of `Offset` redesign (#11).
- We have splitted `Offset` into `Offset` and `OffsetState` (name changes in consideration). The former is used to construct and convert local or UTC date, and the latter is used to store the UTC offset inside constructed values. Some offsets are their own states as well. - This uses lots of associated types which implementation is still in flux. Currently it crashes with debuginfo enabled. We've temporarily disabled debuginfo from `Cargo.toml`. - This technically allows a conversion to the local time, but not yet tested.
This commit is contained in:
parent
2e7a213818
commit
e43cb62f10
135
src/date.rs
135
src/date.rs
|
@ -12,7 +12,8 @@ use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use {Weekday, Datelike};
|
use {Weekday, Datelike};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use offset::{Offset, UTC};
|
use offset::{Offset, OffsetState};
|
||||||
|
use offset::utc::UTC;
|
||||||
use naive;
|
use naive;
|
||||||
use naive::date::NaiveDate;
|
use naive::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
|
@ -21,9 +22,9 @@ use format::DelayedFormat;
|
||||||
|
|
||||||
/// ISO 8601 calendar date with timezone.
|
/// ISO 8601 calendar date with timezone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Date<Off> {
|
pub struct Date<Off: Offset> {
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
offset: Off,
|
offset: Off::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum possible `Date`.
|
/// The minimum possible `Date`.
|
||||||
|
@ -34,8 +35,10 @@ pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };
|
||||||
impl<Off: Offset> Date<Off> {
|
impl<Off: Offset> Date<Off> {
|
||||||
/// 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 `Offset` 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: Off::State) -> Date<Off> {
|
||||||
Date { date: date, offset: offset }
|
Date { date: date, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +48,8 @@ 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<Off>> {
|
||||||
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.
|
||||||
|
@ -162,85 +165,97 @@ impl<Off:Offset> Date<Off> {
|
||||||
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 state.
|
||||||
|
#[inline]
|
||||||
|
pub fn offset<'a>(&'a self) -> &'a Off::State {
|
||||||
|
&self.offset
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn timezone(&self) -> Off {
|
||||||
&self.offset
|
Offset::from_state(&self.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the associated offset.
|
/// Changes the associated offset.
|
||||||
/// 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<Off2: Offset>(&self, tz: &Off2) -> Date<Off2> {
|
||||||
Date::from_utc(self.date, offset)
|
tz.from_utc_date(&self.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<Off: Offset, F>(d: &Date<Off>, mut f: F) -> Option<Date<Off>>
|
||||||
|
where F: FnMut(NaiveDate) -> Option<NaiveDate> {
|
||||||
|
f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Off: Offset> Date<Off> where Off::State: fmt::Display {
|
||||||
/// Formats the date in the specified format string.
|
/// Formats the date in the specified format string.
|
||||||
/// See the `format` module on the supported escape sequences.
|
/// See the `format` module on the supported escape sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
||||||
DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, fmt)
|
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset> Datelike for Date<Off> {
|
impl<Off: Offset> Datelike for Date<Off> {
|
||||||
#[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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
self.local().with_ordinal0(ordinal0)
|
map_local(self, |date| date.with_ordinal0(ordinal0))
|
||||||
.and_then(|date| self.offset.from_local_date(&date).single())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,13 +303,13 @@ impl<Off:Offset> Sub<Duration> for Date<Off> {
|
||||||
|
|
||||||
impl<Off: Offset> fmt::Debug for Date<Off> {
|
impl<Off: Offset> fmt::Debug for Date<Off> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}{:?}", self.local(), self.offset)
|
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset + fmt::Display> fmt::Display for Date<Off> {
|
impl<Off: Offset> fmt::Display for Date<Off> where Off::State: 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,35 +321,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::{Offset, OffsetState, 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
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
struct OneYear;
|
||||||
|
|
||||||
impl Offset for UTC1y {
|
impl Offset for UTC1y {
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::zero() }
|
type State = OneYear;
|
||||||
|
|
||||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
|
fn from_state(_state: &OneYear) -> UTC1y { UTC1y }
|
||||||
LocalResult::Single(Date::from_utc(*local - Duration::days(365), UTC1y))
|
|
||||||
|
fn state_from_local_date(&self, _local: &NaiveDate) -> LocalResult<OneYear> {
|
||||||
|
LocalResult::Single(OneYear)
|
||||||
}
|
}
|
||||||
fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<UTC1y>> {
|
fn state_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 state_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 state_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
|
||||||
fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
|
fn state_from_utc_time(&self, _utc: &NaiveTime) -> OneYear { OneYear }
|
||||||
fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
|
fn state_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
|
||||||
*utc + Duration::days(365)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for UTC1y {
|
impl OffsetState 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") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
119
src/datetime.rs
119
src/datetime.rs
|
@ -11,7 +11,7 @@ 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;
|
use offset::{Offset, OffsetState};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use naive::datetime::NaiveDateTime;
|
use naive::datetime::NaiveDateTime;
|
||||||
use time::Time;
|
use time::Time;
|
||||||
|
@ -20,16 +20,18 @@ use format::DelayedFormat;
|
||||||
|
|
||||||
/// ISO 8601 combined date and time with timezone.
|
/// ISO 8601 combined date and time with timezone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DateTime<Off> {
|
pub struct DateTime<Off: Offset> {
|
||||||
datetime: NaiveDateTime,
|
datetime: NaiveDateTime,
|
||||||
offset: Off,
|
offset: Off::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset> DateTime<Off> {
|
impl<Off: Offset> DateTime<Off> {
|
||||||
/// 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 `Offset` 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: Off::State) -> DateTime<Off> {
|
||||||
DateTime { datetime: datetime, offset: offset }
|
DateTime { datetime: datetime, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,117 +53,125 @@ impl<Off:Offset> DateTime<Off> {
|
||||||
self.datetime.num_seconds_from_unix_epoch()
|
self.datetime.num_seconds_from_unix_epoch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves an associated offset state.
|
||||||
|
#[inline]
|
||||||
|
pub fn offset<'a>(&'a self) -> &'a Off::State {
|
||||||
|
&self.offset
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn timezone(&self) -> Off {
|
||||||
&self.offset
|
Offset::from_state(&self.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the associated offset.
|
/// Changes the associated offset.
|
||||||
/// 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<Off2: Offset>(&self, tz: &Off2) -> DateTime<Off2> {
|
||||||
DateTime::from_utc(self.datetime, offset)
|
tz.from_utc_datetime(&self.datetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset + fmt::Display> DateTime<Off> {
|
/// Maps the local datetime to other datetime with given conversion function.
|
||||||
|
fn map_local<Off: Offset, F>(dt: &DateTime<Off>, mut f: F) -> Option<DateTime<Off>>
|
||||||
|
where F: FnMut(NaiveDateTime) -> Option<NaiveDateTime> {
|
||||||
|
f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Off: Offset> DateTime<Off> where Off::State: fmt::Display {
|
||||||
/// Formats the combined date and time in the specified format string.
|
/// Formats the combined date and time in the specified format string.
|
||||||
/// See the `format` module on the supported escape sequences.
|
/// See the `format` module on the supported escape sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
||||||
let local = self.local();
|
let local = self.naive_local();
|
||||||
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, fmt)
|
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset> Datelike for DateTime<Off> {
|
impl<Off: Offset> Datelike for DateTime<Off> {
|
||||||
#[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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off: Offset> Timelike for DateTime<Off> {
|
||||||
#[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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
self.local().with_nanosecond(nano)
|
map_local(self, |datetime| datetime.with_nanosecond(nano))
|
||||||
.and_then(|datetime| self.offset.from_local_datetime(&datetime).single())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,13 +219,13 @@ impl<Off:Offset> Sub<Duration> for DateTime<Off> {
|
||||||
|
|
||||||
impl<Off: Offset> fmt::Debug for DateTime<Off> {
|
impl<Off: Offset> fmt::Debug for DateTime<Off> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}{:?}", self.local(), self.offset)
|
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset + fmt::Display> fmt::Display for DateTime<Off> {
|
impl<Off: Offset> fmt::Display for DateTime<Off> where Off::State: 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +233,10 @@ impl<Off: Offset + fmt::Display> fmt::Display for DateTime<Off> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use {Datelike};
|
use {Datelike};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use offset::{Offset, UTC, Local, FixedOffset};
|
use offset::Offset;
|
||||||
|
use offset::utc::UTC;
|
||||||
|
use offset::local::Local;
|
||||||
|
use offset::fixed::FixedOffset;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -255,7 +268,7 @@ mod tests {
|
||||||
fn test_datetime_fmt_with_local() {
|
fn test_datetime_fmt_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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::fmt;
|
||||||
|
|
||||||
use {Datelike, Timelike};
|
use {Datelike, Timelike};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use offset::Offset;
|
use offset::OffsetState;
|
||||||
use naive::date::NaiveDate;
|
use naive::date::NaiveDate;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
|
|
||||||
|
@ -198,11 +198,11 @@ impl<'a> DelayedFormat<'a> {
|
||||||
DelayedFormat { date: date, time: time, off: None, fmt: fmt }
|
DelayedFormat { date: date, time: time, off: None, fmt: fmt }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
|
/// Makes a new `DelayedFormat` value out of local date and time with offset state.
|
||||||
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
|
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
|
||||||
offset: &Off, fmt: &'a str) -> DelayedFormat<'a>
|
state: &Off, fmt: &'a str) -> DelayedFormat<'a>
|
||||||
where Off: Offset + fmt::Display {
|
where Off: OffsetState + fmt::Display {
|
||||||
let name_and_diff = (offset.to_string(), offset.local_minus_utc());
|
let name_and_diff = (state.to_string(), state.local_minus_utc());
|
||||||
DelayedFormat { date: date, time: time, off: Some(name_and_diff), fmt: fmt }
|
DelayedFormat { date: date, time: time, off: Some(name_and_diff), fmt: fmt }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -41,7 +41,7 @@ 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`
|
||||||
|
@ -53,7 +53,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")
|
||||||
|
@ -78,13 +78,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
|
||||||
|
@ -98,7 +97,7 @@ assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and includ
|
||||||
|
|
||||||
// offset accessor and manipulation
|
// offset 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.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
|
||||||
|
@ -119,7 +118,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");
|
||||||
|
@ -138,7 +137,7 @@ 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());
|
||||||
|
@ -192,8 +191,10 @@ Advanced offset handling and date/time parsing is not yet supported (but is plan
|
||||||
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::{Offset, OffsetState, 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;
|
||||||
|
|
|
@ -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::{Offset, OffsetState, LocalResult};
|
||||||
|
|
||||||
|
/// The fixed offset (and also state), 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 {
|
||||||
|
type State = FixedOffset;
|
||||||
|
|
||||||
|
fn from_state(state: &FixedOffset) -> FixedOffset { state.clone() }
|
||||||
|
|
||||||
|
fn state_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||||
|
LocalResult::Single(self.clone())
|
||||||
|
}
|
||||||
|
fn state_from_local_time(&self, _local: &NaiveTime) -> LocalResult<FixedOffset> {
|
||||||
|
LocalResult::Single(self.clone())
|
||||||
|
}
|
||||||
|
fn state_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||||
|
LocalResult::Single(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { self.clone() }
|
||||||
|
fn state_from_utc_time(&self, _utc: &NaiveTime) -> FixedOffset { self.clone() }
|
||||||
|
fn state_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffsetState 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::{Offset, 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 Offset for Local {
|
||||||
|
type State = FixedOffset;
|
||||||
|
|
||||||
|
fn from_state(_state: &FixedOffset) -> Local { Local }
|
||||||
|
|
||||||
|
// they are easier to define in terms of the finished date and time unlike other offsets
|
||||||
|
fn state_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||||
|
self.from_local_date(local).map(|&: date| *date.offset())
|
||||||
|
}
|
||||||
|
fn state_from_local_time(&self, local: &NaiveTime) -> LocalResult<FixedOffset> {
|
||||||
|
self.from_local_time(local).map(|&: time| *time.offset())
|
||||||
|
}
|
||||||
|
fn state_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||||
|
self.from_local_datetime(local).map(|&: datetime| *datetime.offset())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
||||||
|
*self.from_utc_date(utc).offset()
|
||||||
|
}
|
||||||
|
fn state_from_utc_time(&self, utc: &NaiveTime) -> FixedOffset {
|
||||||
|
*self.from_utc_time(utc).offset()
|
||||||
|
}
|
||||||
|
fn state_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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,13 +4,25 @@
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Offsets from the local time to UTC.
|
* Offsets from the local time to UTC.
|
||||||
|
*
|
||||||
|
* There are three operations provided by the `Offset` trait:
|
||||||
|
*
|
||||||
|
* 1. Converting the local `NaiveDateTime` to `DateTime<Offset>`
|
||||||
|
* 2. Converting the UTC `NaiveDateTime` to `DateTime<Offset>`
|
||||||
|
* 3. Converting `DateTime<Offset>` to the local `NaiveDateTime`
|
||||||
|
*
|
||||||
|
* 1 is used for constructors. 2 is used for the `with_offset` 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 `OffsetState` (which then passed to `Offset` for actual implementations).
|
||||||
|
* Technically speaking `Offset` has a total knowledge about given timescale,
|
||||||
|
* but `OffsetState` is used as a cache to avoid the repeated conversion
|
||||||
|
* and provides implementations for 1 and 3.
|
||||||
|
* An `Offset` instance can be reconstructed from the corresponding `OffsetState` 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;
|
||||||
|
@ -47,6 +59,15 @@ 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<Off: Offset> LocalResult<Date<Off>> {
|
||||||
|
@ -136,8 +157,16 @@ impl<T: fmt::Debug> LocalResult<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The offset state.
|
||||||
|
pub trait OffsetState: Sized + Clone + fmt::Debug {
|
||||||
|
/// Returns the offset from UTC to the local time stored in the offset state.
|
||||||
|
fn local_minus_utc(&self) -> Duration;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 {
|
||||||
|
type State: OffsetState;
|
||||||
|
|
||||||
/// Makes a new `Date` from year, month, day and the current offset.
|
/// 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.
|
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
|
||||||
///
|
///
|
||||||
|
@ -291,258 +320,68 @@ pub trait Offset: Clone + fmt::Debug {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the *current* offset from UTC to the local time.
|
/// Reconstructs the offset from the offset state.
|
||||||
fn local_minus_utc(&self) -> Duration;
|
fn from_state(state: &Self::State) -> Self;
|
||||||
|
|
||||||
|
/// Creates the offset state(s) for given local `NaiveDate` if possible.
|
||||||
|
fn state_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::State>;
|
||||||
|
|
||||||
|
/// Creates the offset state(s) for given local `NaiveTime` if possible.
|
||||||
|
fn state_from_local_time(&self, local: &NaiveTime) -> LocalResult<Self::State>;
|
||||||
|
|
||||||
|
/// Creates the offset state(s) for given local `NaiveDateTime` if possible.
|
||||||
|
fn state_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::State>;
|
||||||
|
|
||||||
/// 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.state_from_local_date(local).map(|state| {
|
||||||
|
Date::from_utc(*local - state.local_minus_utc(), state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.state_from_local_time(local).map(|state| {
|
||||||
|
Time::from_utc(*local - state.local_minus_utc(), state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.state_from_local_datetime(local).map(|state| {
|
||||||
|
DateTime::from_utc(*local - state.local_minus_utc(), state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the offset state for given UTC `NaiveDate`. This cannot fail.
|
||||||
|
fn state_from_utc_date(&self, utc: &NaiveDate) -> Self::State;
|
||||||
|
|
||||||
|
/// Creates the offset state for given UTC `NaiveTime`. This cannot fail.
|
||||||
|
fn state_from_utc_time(&self, utc: &NaiveTime) -> Self::State;
|
||||||
|
|
||||||
|
/// Creates the offset state for given UTC `NaiveDateTime`. This cannot fail.
|
||||||
|
fn state_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::State;
|
||||||
|
|
||||||
/// 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.state_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.state_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.state_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::{Offset, OffsetState, LocalResult};
|
||||||
|
|
||||||
|
/// The UTC offset. This is the most efficient offset when you don't need the local time.
|
||||||
|
/// It is also used as an offset state (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 Offset for UTC {
|
||||||
|
type State = UTC;
|
||||||
|
|
||||||
|
fn from_state(_state: &UTC) -> UTC { UTC }
|
||||||
|
|
||||||
|
fn state_from_local_date(&self, _local: &NaiveDate) -> LocalResult<UTC> {
|
||||||
|
LocalResult::Single(UTC)
|
||||||
|
}
|
||||||
|
fn state_from_local_time(&self, _local: &NaiveTime) -> LocalResult<UTC> {
|
||||||
|
LocalResult::Single(UTC)
|
||||||
|
}
|
||||||
|
fn state_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<UTC> {
|
||||||
|
LocalResult::Single(UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC }
|
||||||
|
fn state_from_utc_time(&self, _utc: &NaiveTime) -> UTC { UTC }
|
||||||
|
fn state_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffsetState 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") }
|
||||||
|
}
|
||||||
|
|
73
src/time.rs
73
src/time.rs
|
@ -11,86 +11,103 @@ use std::cmp::Ordering;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
use offset::Offset;
|
use offset::{Offset, OffsetState};
|
||||||
use duration::Duration;
|
use duration::Duration;
|
||||||
use naive::time::NaiveTime;
|
use naive::time::NaiveTime;
|
||||||
use format::DelayedFormat;
|
use format::DelayedFormat;
|
||||||
|
|
||||||
/// ISO 8601 time with timezone.
|
/// ISO 8601 time with timezone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Time<Off> {
|
pub struct Time<Off: Offset> {
|
||||||
time: NaiveTime,
|
time: NaiveTime,
|
||||||
offset: Off,
|
offset: Off::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset> Time<Off> {
|
impl<Off: Offset> Time<Off> {
|
||||||
/// 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 `Offset` 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: Off::State) -> Time<Off> {
|
||||||
Time { time: time, offset: offset }
|
Time { time: time, offset: offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an associated offset.
|
/// Retrieves an associated offset.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Off {
|
pub fn offset<'a>(&'a self) -> &'a Off::State {
|
||||||
&self.offset
|
&self.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves an associated offset.
|
||||||
|
#[inline]
|
||||||
|
pub fn timezone(&self) -> Off {
|
||||||
|
Offset::from_state(&self.offset)
|
||||||
|
}
|
||||||
|
|
||||||
/// Changes the associated offset.
|
/// Changes the associated offset.
|
||||||
/// This does not change the actual `Time` (but will change the string representation).
|
/// 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 with_timezone<Off2: Offset>(&self, tz: &Off2) -> Time<Off2> {
|
||||||
Time::from_utc(self.time, offset)
|
tz.from_utc_time(&self.time)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a view to the local time.
|
/// Returns a view to the naive UTC time.
|
||||||
fn local(&self) -> NaiveTime {
|
#[inline]
|
||||||
self.offset.to_local_time(&self.time)
|
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<Off: Offset, F>(t: &Time<Off>, mut f: F) -> Option<Time<Off>>
|
||||||
|
where F: FnMut(NaiveTime) -> Option<NaiveTime> {
|
||||||
|
f(t.naive_local()).and_then(|time| t.timezone().from_local_time(&time).single())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Off: Offset> Time<Off> where Off::State: fmt::Display {
|
||||||
/// Formats the time in the specified format string.
|
/// Formats the time in the specified format string.
|
||||||
/// See the `format` module on the supported escape sequences.
|
/// See the `format` module on the supported escape sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
|
||||||
DelayedFormat::new_with_offset(None, Some(self.local()), &self.offset, fmt)
|
DelayedFormat::new_with_offset(None, Some(self.naive_local()), &self.offset, fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset> Timelike for Time<Off> {
|
impl<Off: Offset> Timelike for Time<Off> {
|
||||||
#[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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off>> {
|
||||||
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<Off: Offset, Off2: Offset> PartialEq<Time<Off2>> for Time<Off> {
|
||||||
|
@ -137,13 +154,13 @@ impl<Off:Offset> Sub<Duration> for Time<Off> {
|
||||||
|
|
||||||
impl<Off: Offset> fmt::Debug for Time<Off> {
|
impl<Off: Offset> fmt::Debug for Time<Off> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}{:?}", self.local(), self.offset)
|
write!(f, "{:?}{:?}", self.naive_local(), self.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Off: Offset + fmt::Display> fmt::Display for Time<Off> {
|
impl<Off: Offset> fmt::Display for Time<Off> where Off::State: 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