1 // This is a part of rust-chrono.
2 // Copyright (c) 2014-2015, Kang Seonghoon.
3 // See README.md and LICENSE.txt for details.
4 
5 /*!
6  * The time zone, which calculates offsets from the local time to UTC.
7  *
8  * There are three operations provided by the `TimeZone` trait:
9  *
10  * 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
11  * 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
12  * 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
13  *
14  * 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
15  * 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
16  * which implements `Offset` (which then passed to `TimeZone` for actual implementations).
17  * Technically speaking `TimeZone` has a total knowledge about given timescale,
18  * but `Offset` is used as a cache to avoid the repeated conversion
19  * and provides implementations for 1 and 3.
20  * An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
21  */
22 
23 use std::fmt;
24 
25 use Weekday;
26 use duration::Duration;
27 use naive::date::NaiveDate;
28 use naive::time::NaiveTime;
29 use naive::datetime::NaiveDateTime;
30 use date::Date;
31 use datetime::DateTime;
32 use format::{parse, Parsed, ParseResult, StrftimeItems};
33 
34 /// The conversion result from the local time to the timezone-aware datetime types.
35 #[derive(Clone, PartialEq, Debug)]
36 pub enum LocalResult<T> {
37     /// Given local time representation is invalid.
38     /// This can occur when, for example, the positive timezone transition.
39     None,
40     /// Given local time representation has a single unique result.
41     Single(T),
42     /// Given local time representation has multiple results and thus ambiguous.
43     /// This can occur when, for example, the negative timezone transition.
44     Ambiguous(T /*min*/, T /*max*/),
45 }
46 
47 impl<T> LocalResult<T> {
48     /// Returns `Some` only when the conversion result is unique, or `None` otherwise.
single(self) -> Option<T>49     pub fn single(self) -> Option<T> {
50         match self { LocalResult::Single(t) => Some(t), _ => None }
51     }
52 
53     /// Returns `Some` for the earliest possible conversion result, or `None` if none.
earliest(self) -> Option<T>54     pub fn earliest(self) -> Option<T> {
55         match self { LocalResult::Single(t) | LocalResult::Ambiguous(t,_) => Some(t), _ => None }
56     }
57 
58     /// Returns `Some` for the latest possible conversion result, or `None` if none.
latest(self) -> Option<T>59     pub fn latest(self) -> Option<T> {
60         match self { LocalResult::Single(t) | LocalResult::Ambiguous(_,t) => Some(t), _ => None }
61     }
62 
63     /// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U>64     pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
65         match self {
66             LocalResult::None => LocalResult::None,
67             LocalResult::Single(v) => LocalResult::Single(f(v)),
68             LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
69         }
70     }
71 }
72 
73 impl<Tz: TimeZone> LocalResult<Date<Tz>> {
74     /// Makes a new `DateTime` from the current date and given `NaiveTime`.
75     /// The offset in the current date is preserved.
76     ///
77     /// Propagates any error. Ambiguous result would be discarded.
78     #[inline]
and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>>79     pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
80         match self {
81             LocalResult::Single(d) => d.and_time(time)
82                                        .map_or(LocalResult::None, LocalResult::Single),
83             _ => LocalResult::None,
84         }
85     }
86 
87     /// Makes a new `DateTime` from the current date, hour, minute and second.
88     /// The offset in the current date is preserved.
89     ///
90     /// Propagates any error. Ambiguous result would be discarded.
91     #[inline]
and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>>92     pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
93         match self {
94             LocalResult::Single(d) => d.and_hms_opt(hour, min, sec)
95                                        .map_or(LocalResult::None, LocalResult::Single),
96             _ => LocalResult::None,
97         }
98     }
99 
100     /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
101     /// The millisecond part can exceed 1,000 in order to represent the leap second.
102     /// The offset in the current date is preserved.
103     ///
104     /// Propagates any error. Ambiguous result would be discarded.
105     #[inline]
and_hms_milli_opt(self, hour: u32, min: u32, sec: u32, milli: u32) -> LocalResult<DateTime<Tz>>106     pub fn and_hms_milli_opt(self, hour: u32, min: u32, sec: u32,
107                              milli: u32) -> LocalResult<DateTime<Tz>> {
108         match self {
109             LocalResult::Single(d) => d.and_hms_milli_opt(hour, min, sec, milli)
110                                        .map_or(LocalResult::None, LocalResult::Single),
111             _ => LocalResult::None,
112         }
113     }
114 
115     /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
116     /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
117     /// The offset in the current date is preserved.
118     ///
119     /// Propagates any error. Ambiguous result would be discarded.
120     #[inline]
and_hms_micro_opt(self, hour: u32, min: u32, sec: u32, micro: u32) -> LocalResult<DateTime<Tz>>121     pub fn and_hms_micro_opt(self, hour: u32, min: u32, sec: u32,
122                              micro: u32) -> LocalResult<DateTime<Tz>> {
123         match self {
124             LocalResult::Single(d) => d.and_hms_micro_opt(hour, min, sec, micro)
125                                        .map_or(LocalResult::None, LocalResult::Single),
126             _ => LocalResult::None,
127         }
128     }
129 
130     /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
131     /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
132     /// The offset in the current date is preserved.
133     ///
134     /// Propagates any error. Ambiguous result would be discarded.
135     #[inline]
and_hms_nano_opt(self, hour: u32, min: u32, sec: u32, nano: u32) -> LocalResult<DateTime<Tz>>136     pub fn and_hms_nano_opt(self, hour: u32, min: u32, sec: u32,
137                             nano: u32) -> LocalResult<DateTime<Tz>> {
138         match self {
139             LocalResult::Single(d) => d.and_hms_nano_opt(hour, min, sec, nano)
140                                        .map_or(LocalResult::None, LocalResult::Single),
141             _ => LocalResult::None,
142         }
143     }
144 
145 }
146 
147 impl<T: fmt::Debug> LocalResult<T> {
148     /// Returns the single unique conversion result, or panics accordingly.
unwrap(self) -> T149     pub fn unwrap(self) -> T {
150         match self {
151             LocalResult::None => panic!("No such local time"),
152             LocalResult::Single(t) => t,
153             LocalResult::Ambiguous(t1,t2) => {
154                 panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
155             }
156         }
157     }
158 }
159 
160 /// The offset from the local time to UTC.
161 pub trait Offset: Sized + Clone + fmt::Debug {
162     /// Returns the offset from UTC to the local time stored.
local_minus_utc(&self) -> Duration163     fn local_minus_utc(&self) -> Duration;
164 }
165 
166 /// The time zone.
167 pub trait TimeZone: Sized + Clone {
168     /// An associated offset type.
169     /// This type is used to store the actual offset in date and time types.
170     /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
171     type Offset: Offset;
172 
173     /// Makes a new `Date` from year, month, day and the current time zone.
174     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
175     ///
176     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
177     /// but it will propagate to the `DateTime` values constructed via this date.
178     ///
179     /// Panics on the out-of-range date, invalid month and/or day.
ymd(&self, year: i32, month: u32, day: u32) -> Date<Self>180     fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
181         self.ymd_opt(year, month, day).unwrap()
182     }
183 
184     /// Makes a new `Date` from year, month, day and the current time zone.
185     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
186     ///
187     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
188     /// but it will propagate to the `DateTime` values constructed via this date.
189     ///
190     /// Returns `None` on the out-of-range date, invalid month and/or day.
ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>>191     fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
192         match NaiveDate::from_ymd_opt(year, month, day) {
193             Some(d) => self.from_local_date(&d),
194             None => LocalResult::None,
195         }
196     }
197 
198     /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
199     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
200     ///
201     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
202     /// but it will propagate to the `DateTime` values constructed via this date.
203     ///
204     /// Panics on the out-of-range date and/or invalid DOY.
yo(&self, year: i32, ordinal: u32) -> Date<Self>205     fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
206         self.yo_opt(year, ordinal).unwrap()
207     }
208 
209     /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
210     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
211     ///
212     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
213     /// but it will propagate to the `DateTime` values constructed via this date.
214     ///
215     /// Returns `None` on the out-of-range date and/or invalid DOY.
yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>>216     fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
217         match NaiveDate::from_yo_opt(year, ordinal) {
218             Some(d) => self.from_local_date(&d),
219             None => LocalResult::None,
220         }
221     }
222 
223     /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
224     /// the current time zone.
225     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
226     /// The resulting `Date` may have a different year from the input year.
227     ///
228     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
229     /// but it will propagate to the `DateTime` values constructed via this date.
230     ///
231     /// Panics on the out-of-range date and/or invalid week number.
isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self>232     fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
233         self.isoywd_opt(year, week, weekday).unwrap()
234     }
235 
236     /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
237     /// the current time zone.
238     /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
239     /// The resulting `Date` may have a different year from the input year.
240     ///
241     /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
242     /// but it will propagate to the `DateTime` values constructed via this date.
243     ///
244     /// Returns `None` on the out-of-range date and/or invalid week number.
isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>>245     fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
246         match NaiveDate::from_isoywd_opt(year, week, weekday) {
247             Some(d) => self.from_local_date(&d),
248             None => LocalResult::None,
249         }
250     }
251 
252     /// Makes a new `DateTime` from the number of non-leap seconds
253     /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
254     /// and the number of nanoseconds since the last whole non-leap second.
255     ///
256     /// Panics on the out-of-range number of seconds and/or invalid nanosecond.
timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self>257     fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
258         self.timestamp_opt(secs, nsecs).unwrap()
259     }
260 
261     /// Makes a new `DateTime` from the number of non-leap seconds
262     /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
263     /// and the number of nanoseconds since the last whole non-leap second.
264     ///
265     /// Returns `None` on the out-of-range number of seconds and/or invalid nanosecond.
timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>>266     fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
267         match NaiveDateTime::from_timestamp_opt(secs, nsecs) {
268             Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
269             None => LocalResult::None,
270         }
271     }
272 
273     /// Parses a string with the specified format string and
274     /// returns a `DateTime` with the current offset.
275     /// See the [`format::strftime` module](../../format/strftime/index.html)
276     /// on the supported escape sequences.
277     ///
278     /// If the format does not include offsets, the current offset is assumed;
279     /// otherwise the input should have a matching UTC offset.
280     ///
281     /// See also `DateTime::parse_from_str` which gives a local `DateTime`
282     /// with parsed `FixedOffset`.
datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>>283     fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
284         let mut parsed = Parsed::new();
285         try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
286         parsed.to_datetime_with_timezone(self)
287     }
288 
289     /// Reconstructs the time zone from the offset.
from_offset(offset: &Self::Offset) -> Self290     fn from_offset(offset: &Self::Offset) -> Self;
291 
292     /// Creates the offset(s) for given local `NaiveDate` if possible.
offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>293     fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
294 
295     /// Creates the offset(s) for given local `NaiveDateTime` if possible.
offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>296     fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>;
297 
298     /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>>299     fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
300         self.offset_from_local_date(local).map(|offset| {
301             Date::from_utc(*local - offset.local_minus_utc(), offset)
302         })
303     }
304 
305     /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>>306     fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
307         self.offset_from_local_datetime(local).map(|offset| {
308             DateTime::from_utc(*local - offset.local_minus_utc(), offset)
309         })
310     }
311 
312     /// Creates the offset for given UTC `NaiveDate`. This cannot fail.
offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset313     fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
314 
315     /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset316     fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
317 
318     /// Converts the UTC `NaiveDate` to the local time.
319     /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
from_utc_date(&self, utc: &NaiveDate) -> Date<Self>320     fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
321         Date::from_utc(utc.clone(), self.offset_from_utc_date(utc))
322     }
323 
324     /// Converts the UTC `NaiveDateTime` to the local time.
325     /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self>326     fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
327         DateTime::from_utc(utc.clone(), self.offset_from_utc_datetime(utc))
328     }
329 }
330 
331 pub mod utc;
332 pub mod fixed;
333 pub mod local;
334 
335