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