]>
Commit | Line | Data |
---|---|---|
ff7c6d11 XL |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Simple time handling. | |
12 | //! | |
13 | //! # Usage | |
14 | //! | |
15 | //! This crate is [on crates.io](https://crates.io/crates/time) and can be | |
16 | //! used by adding `time` to the dependencies in your project's `Cargo.toml`. | |
17 | //! | |
18 | //! ```toml | |
19 | //! [dependencies] | |
20 | //! time = "0.1" | |
21 | //! ``` | |
22 | //! | |
23 | //! And this in your crate root: | |
24 | //! | |
25 | //! ```rust | |
26 | //! extern crate time; | |
27 | //! ``` | |
28 | //! | |
29 | //! This crate uses the same syntax for format strings as the | |
30 | //! [`strftime()`](http://man7.org/linux/man-pages/man3/strftime.3.html) | |
31 | //! function from the C standard library. | |
32 | ||
33 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", | |
34 | html_favicon_url = "https://www.rust-lang.org/favicon.ico", | |
35 | html_root_url = "https://doc.rust-lang.org/time/")] | |
f035d41b XL |
36 | #![allow(unknown_lints)] |
37 | #![allow(ellipsis_inclusive_range_patterns)] // `..=` requires Rust 1.26 | |
ff7c6d11 XL |
38 | #![allow(trivial_numeric_casts)] |
39 | #![cfg_attr(test, deny(warnings))] | |
40 | ||
ff7c6d11 XL |
41 | #[cfg(unix)] extern crate libc; |
42 | #[cfg(windows)] extern crate winapi; | |
43 | #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; | |
44 | ||
1b1a35ee XL |
45 | #[cfg(target_os = "wasi")] extern crate wasi; |
46 | ||
ff7c6d11 XL |
47 | #[cfg(test)] #[macro_use] extern crate log; |
48 | ||
49 | use std::cmp::Ordering; | |
50 | use std::error::Error; | |
51 | use std::fmt; | |
52 | use std::ops::{Add, Sub}; | |
53 | ||
54 | pub use duration::{Duration, OutOfRangeError}; | |
55 | ||
56 | use self::ParseError::{InvalidDay, InvalidDayOfMonth, InvalidDayOfWeek, | |
57 | InvalidDayOfYear, InvalidFormatSpecifier, InvalidHour, | |
58 | InvalidMinute, InvalidMonth, InvalidSecond, InvalidTime, | |
59 | InvalidYear, InvalidZoneOffset, InvalidSecondsSinceEpoch, | |
60 | MissingFormatConverter, UnexpectedCharacter}; | |
61 | ||
62 | pub use parse::strptime; | |
63 | ||
64 | mod display; | |
65 | mod duration; | |
66 | mod parse; | |
67 | mod sys; | |
68 | ||
69 | static NSEC_PER_SEC: i32 = 1_000_000_000; | |
70 | ||
71 | /// A record specifying a time value in seconds and nanoseconds, where | |
72 | /// nanoseconds represent the offset from the given second. | |
73 | /// | |
74 | /// For example a timespec of 1.2 seconds after the beginning of the epoch would | |
75 | /// be represented as {sec: 1, nsec: 200000000}. | |
76 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] | |
77 | #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] | |
78 | pub struct Timespec { pub sec: i64, pub nsec: i32 } | |
79 | /* | |
80 | * Timespec assumes that pre-epoch Timespecs have negative sec and positive | |
81 | * nsec fields. Darwin's and Linux's struct timespec functions handle pre- | |
82 | * epoch timestamps using a "two steps back, one step forward" representation, | |
83 | * though the man pages do not actually document this. For example, the time | |
84 | * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64, | |
85 | * nsec: 800_000_000 }`. | |
86 | */ | |
87 | impl Timespec { | |
88 | pub fn new(sec: i64, nsec: i32) -> Timespec { | |
89 | assert!(nsec >= 0 && nsec < NSEC_PER_SEC); | |
90 | Timespec { sec: sec, nsec: nsec } | |
91 | } | |
92 | } | |
93 | ||
94 | impl Add<Duration> for Timespec { | |
95 | type Output = Timespec; | |
96 | ||
97 | fn add(self, other: Duration) -> Timespec { | |
98 | let d_sec = other.num_seconds(); | |
99 | // It is safe to unwrap the nanoseconds, because there cannot be | |
100 | // more than one second left, which fits in i64 and in i32. | |
101 | let d_nsec = (other - Duration::seconds(d_sec)) | |
102 | .num_nanoseconds().unwrap() as i32; | |
103 | let mut sec = self.sec + d_sec; | |
104 | let mut nsec = self.nsec + d_nsec; | |
105 | if nsec >= NSEC_PER_SEC { | |
106 | nsec -= NSEC_PER_SEC; | |
107 | sec += 1; | |
108 | } else if nsec < 0 { | |
109 | nsec += NSEC_PER_SEC; | |
110 | sec -= 1; | |
111 | } | |
112 | Timespec::new(sec, nsec) | |
113 | } | |
114 | } | |
115 | ||
116 | impl Sub<Duration> for Timespec { | |
117 | type Output = Timespec; | |
118 | ||
119 | fn sub(self, other: Duration) -> Timespec { | |
120 | let d_sec = other.num_seconds(); | |
121 | // It is safe to unwrap the nanoseconds, because there cannot be | |
122 | // more than one second left, which fits in i64 and in i32. | |
123 | let d_nsec = (other - Duration::seconds(d_sec)) | |
124 | .num_nanoseconds().unwrap() as i32; | |
125 | let mut sec = self.sec - d_sec; | |
126 | let mut nsec = self.nsec - d_nsec; | |
127 | if nsec >= NSEC_PER_SEC { | |
128 | nsec -= NSEC_PER_SEC; | |
129 | sec += 1; | |
130 | } else if nsec < 0 { | |
131 | nsec += NSEC_PER_SEC; | |
132 | sec -= 1; | |
133 | } | |
134 | Timespec::new(sec, nsec) | |
135 | } | |
136 | } | |
137 | ||
138 | impl Sub<Timespec> for Timespec { | |
139 | type Output = Duration; | |
140 | ||
141 | fn sub(self, other: Timespec) -> Duration { | |
142 | let sec = self.sec - other.sec; | |
143 | let nsec = self.nsec - other.nsec; | |
144 | Duration::seconds(sec) + Duration::nanoseconds(nsec as i64) | |
145 | } | |
146 | } | |
147 | ||
148 | /** | |
149 | * Returns the current time as a `timespec` containing the seconds and | |
150 | * nanoseconds since 1970-01-01T00:00:00Z. | |
151 | */ | |
152 | pub fn get_time() -> Timespec { | |
153 | let (sec, nsec) = sys::get_time(); | |
154 | Timespec::new(sec, nsec) | |
155 | } | |
156 | ||
157 | ||
158 | /** | |
159 | * Returns the current value of a high-resolution performance counter | |
160 | * in nanoseconds since an unspecified epoch. | |
161 | */ | |
8faf50e0 | 162 | #[inline] |
ff7c6d11 XL |
163 | pub fn precise_time_ns() -> u64 { |
164 | sys::get_precise_ns() | |
165 | } | |
166 | ||
167 | ||
168 | /** | |
169 | * Returns the current value of a high-resolution performance counter | |
170 | * in seconds since an unspecified epoch. | |
171 | */ | |
172 | pub fn precise_time_s() -> f64 { | |
173 | return (precise_time_ns() as f64) / 1000000000.; | |
174 | } | |
175 | ||
176 | /// An opaque structure representing a moment in time. | |
177 | /// | |
178 | /// The only operation that can be performed on a `PreciseTime` is the | |
179 | /// calculation of the `Duration` of time that lies between them. | |
180 | /// | |
181 | /// # Examples | |
182 | /// | |
183 | /// Repeatedly call a function for 1 second: | |
184 | /// | |
185 | /// ```rust | |
186 | /// use time::{Duration, PreciseTime}; | |
187 | /// # fn do_some_work() {} | |
188 | /// | |
189 | /// let start = PreciseTime::now(); | |
190 | /// | |
191 | /// while start.to(PreciseTime::now()) < Duration::seconds(1) { | |
192 | /// do_some_work(); | |
193 | /// } | |
194 | /// ``` | |
195 | #[derive(Copy, Clone)] | |
196 | pub struct PreciseTime(u64); | |
197 | ||
198 | impl PreciseTime { | |
199 | /// Returns a `PreciseTime` representing the current moment in time. | |
200 | pub fn now() -> PreciseTime { | |
201 | PreciseTime(precise_time_ns()) | |
202 | } | |
203 | ||
204 | /// Returns a `Duration` representing the span of time from the value of | |
205 | /// `self` to the value of `later`. | |
206 | /// | |
207 | /// # Notes | |
208 | /// | |
209 | /// If `later` represents a time before `self`, the result of this method | |
210 | /// is unspecified. | |
211 | /// | |
212 | /// If `later` represents a time more than 293 years after `self`, the | |
213 | /// result of this method is unspecified. | |
214 | #[inline] | |
215 | pub fn to(&self, later: PreciseTime) -> Duration { | |
216 | // NB: even if later is less than self due to overflow, this will work | |
217 | // since the subtraction will underflow properly as well. | |
218 | // | |
219 | // We could deal with the overflow when casting to an i64, but all that | |
220 | // gets us is the ability to handle intervals of up to 584 years, which | |
221 | // seems not very useful :) | |
222 | Duration::nanoseconds((later.0 - self.0) as i64) | |
223 | } | |
224 | } | |
225 | ||
226 | /// A structure representing a moment in time. | |
227 | /// | |
228 | /// `SteadyTime`s are generated by a "steady" clock, that is, a clock which | |
229 | /// never experiences discontinuous jumps and for which time always flows at | |
230 | /// the same rate. | |
231 | /// | |
232 | /// # Examples | |
233 | /// | |
234 | /// Repeatedly call a function for 1 second: | |
235 | /// | |
236 | /// ```rust | |
237 | /// # use time::{Duration, SteadyTime}; | |
238 | /// # fn do_some_work() {} | |
239 | /// let start = SteadyTime::now(); | |
240 | /// | |
241 | /// while SteadyTime::now() - start < Duration::seconds(1) { | |
242 | /// do_some_work(); | |
243 | /// } | |
244 | /// ``` | |
245 | #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)] | |
246 | pub struct SteadyTime(sys::SteadyTime); | |
247 | ||
248 | impl SteadyTime { | |
249 | /// Returns a `SteadyTime` representing the current moment in time. | |
250 | pub fn now() -> SteadyTime { | |
251 | SteadyTime(sys::SteadyTime::now()) | |
252 | } | |
253 | } | |
254 | ||
255 | impl fmt::Display for SteadyTime { | |
256 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
257 | // TODO: needs a display customization | |
258 | fmt::Debug::fmt(self, fmt) | |
259 | } | |
260 | } | |
261 | ||
262 | impl Sub for SteadyTime { | |
263 | type Output = Duration; | |
264 | ||
265 | fn sub(self, other: SteadyTime) -> Duration { | |
266 | self.0 - other.0 | |
267 | } | |
268 | } | |
269 | ||
270 | impl Sub<Duration> for SteadyTime { | |
271 | type Output = SteadyTime; | |
272 | ||
273 | fn sub(self, other: Duration) -> SteadyTime { | |
274 | SteadyTime(self.0 - other) | |
275 | } | |
276 | } | |
277 | ||
278 | impl Add<Duration> for SteadyTime { | |
279 | type Output = SteadyTime; | |
280 | ||
281 | fn add(self, other: Duration) -> SteadyTime { | |
282 | SteadyTime(self.0 + other) | |
283 | } | |
284 | } | |
285 | ||
60c5eb7d | 286 | #[cfg(not(any(windows, target_env = "sgx")))] |
ff7c6d11 XL |
287 | pub fn tzset() { |
288 | extern { fn tzset(); } | |
289 | unsafe { tzset() } | |
290 | } | |
291 | ||
292 | ||
60c5eb7d | 293 | #[cfg(any(windows, target_env = "sgx"))] |
ff7c6d11 XL |
294 | pub fn tzset() {} |
295 | ||
296 | /// Holds a calendar date and time broken down into its components (year, month, | |
297 | /// day, and so on), also called a broken-down time value. | |
298 | // FIXME: use c_int instead of i32? | |
299 | #[repr(C)] | |
300 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] | |
301 | #[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] | |
302 | pub struct Tm { | |
303 | /// Seconds after the minute - [0, 60] | |
304 | pub tm_sec: i32, | |
305 | ||
306 | /// Minutes after the hour - [0, 59] | |
307 | pub tm_min: i32, | |
308 | ||
309 | /// Hours after midnight - [0, 23] | |
310 | pub tm_hour: i32, | |
311 | ||
312 | /// Day of the month - [1, 31] | |
313 | pub tm_mday: i32, | |
314 | ||
315 | /// Months since January - [0, 11] | |
316 | pub tm_mon: i32, | |
317 | ||
318 | /// Years since 1900 | |
319 | pub tm_year: i32, | |
320 | ||
321 | /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. | |
322 | pub tm_wday: i32, | |
323 | ||
324 | /// Days since January 1 - [0, 365] | |
325 | pub tm_yday: i32, | |
326 | ||
327 | /// Daylight Saving Time flag. | |
328 | /// | |
329 | /// This value is positive if Daylight Saving Time is in effect, zero if | |
330 | /// Daylight Saving Time is not in effect, and negative if this information | |
331 | /// is not available. | |
332 | pub tm_isdst: i32, | |
333 | ||
334 | /// Identifies the time zone that was used to compute this broken-down time | |
335 | /// value, including any adjustment for Daylight Saving Time. This is the | |
336 | /// number of seconds east of UTC. For example, for U.S. Pacific Daylight | |
337 | /// Time, the value is `-7*60*60 = -25200`. | |
338 | pub tm_utcoff: i32, | |
339 | ||
340 | /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1] | |
341 | pub tm_nsec: i32, | |
342 | } | |
343 | ||
344 | impl Add<Duration> for Tm { | |
345 | type Output = Tm; | |
346 | ||
347 | /// The resulting Tm is in UTC. | |
348 | // FIXME: The resulting Tm should have the same timezone as `self`; | |
349 | // however, we need a function such as `at_tm(clock: Timespec, offset: i32)` | |
350 | // for this. | |
351 | fn add(self, other: Duration) -> Tm { | |
352 | at_utc(self.to_timespec() + other) | |
353 | } | |
354 | } | |
355 | ||
356 | impl Sub<Duration> for Tm { | |
357 | type Output = Tm; | |
358 | ||
359 | /// The resulting Tm is in UTC. | |
360 | // FIXME: The resulting Tm should have the same timezone as `self`; | |
361 | // however, we need a function such as `at_tm(clock: Timespec, offset: i32)` | |
362 | // for this. | |
363 | fn sub(self, other: Duration) -> Tm { | |
364 | at_utc(self.to_timespec() - other) | |
365 | } | |
366 | } | |
367 | ||
368 | impl Sub<Tm> for Tm { | |
369 | type Output = Duration; | |
370 | ||
371 | fn sub(self, other: Tm) -> Duration { | |
372 | self.to_timespec() - other.to_timespec() | |
373 | } | |
374 | } | |
375 | ||
376 | impl PartialOrd for Tm { | |
377 | fn partial_cmp(&self, other: &Tm) -> Option<Ordering> { | |
378 | self.to_timespec().partial_cmp(&other.to_timespec()) | |
379 | } | |
380 | } | |
381 | ||
382 | impl Ord for Tm { | |
383 | fn cmp(&self, other: &Tm) -> Ordering { | |
384 | self.to_timespec().cmp(&other.to_timespec()) | |
385 | } | |
386 | } | |
387 | ||
388 | pub fn empty_tm() -> Tm { | |
389 | Tm { | |
390 | tm_sec: 0, | |
391 | tm_min: 0, | |
392 | tm_hour: 0, | |
393 | tm_mday: 0, | |
394 | tm_mon: 0, | |
395 | tm_year: 0, | |
396 | tm_wday: 0, | |
397 | tm_yday: 0, | |
398 | tm_isdst: 0, | |
399 | tm_utcoff: 0, | |
400 | tm_nsec: 0, | |
401 | } | |
402 | } | |
403 | ||
404 | /// Returns the specified time in UTC | |
405 | pub fn at_utc(clock: Timespec) -> Tm { | |
406 | let Timespec { sec, nsec } = clock; | |
407 | let mut tm = empty_tm(); | |
408 | sys::time_to_utc_tm(sec, &mut tm); | |
409 | tm.tm_nsec = nsec; | |
410 | tm | |
411 | } | |
412 | ||
413 | /// Returns the current time in UTC | |
414 | pub fn now_utc() -> Tm { | |
415 | at_utc(get_time()) | |
416 | } | |
417 | ||
418 | /// Returns the specified time in the local timezone | |
419 | pub fn at(clock: Timespec) -> Tm { | |
420 | let Timespec { sec, nsec } = clock; | |
421 | let mut tm = empty_tm(); | |
422 | sys::time_to_local_tm(sec, &mut tm); | |
423 | tm.tm_nsec = nsec; | |
424 | tm | |
425 | } | |
426 | ||
427 | /// Returns the current time in the local timezone | |
428 | pub fn now() -> Tm { | |
429 | at(get_time()) | |
430 | } | |
431 | ||
432 | impl Tm { | |
433 | /// Convert time to the seconds from January 1, 1970 | |
434 | pub fn to_timespec(&self) -> Timespec { | |
435 | let sec = match self.tm_utcoff { | |
436 | 0 => sys::utc_tm_to_time(self), | |
437 | _ => sys::local_tm_to_time(self) | |
438 | }; | |
439 | ||
440 | Timespec::new(sec, self.tm_nsec) | |
441 | } | |
442 | ||
443 | /// Convert time to the local timezone | |
444 | pub fn to_local(&self) -> Tm { | |
445 | at(self.to_timespec()) | |
446 | } | |
447 | ||
448 | /// Convert time to the UTC | |
449 | pub fn to_utc(&self) -> Tm { | |
450 | match self.tm_utcoff { | |
451 | 0 => *self, | |
452 | _ => at_utc(self.to_timespec()) | |
453 | } | |
454 | } | |
455 | ||
456 | /** | |
457 | * Returns a TmFmt that outputs according to the `asctime` format in ISO | |
458 | * C, in the local timezone. | |
459 | * | |
460 | * Example: "Thu Jan 1 00:00:00 1970" | |
461 | */ | |
462 | pub fn ctime(&self) -> TmFmt { | |
463 | TmFmt { | |
464 | tm: self, | |
465 | format: Fmt::Ctime, | |
466 | } | |
467 | } | |
468 | ||
469 | /** | |
470 | * Returns a TmFmt that outputs according to the `asctime` format in ISO | |
471 | * C. | |
472 | * | |
473 | * Example: "Thu Jan 1 00:00:00 1970" | |
474 | */ | |
475 | pub fn asctime(&self) -> TmFmt { | |
476 | TmFmt { | |
477 | tm: self, | |
478 | format: Fmt::Str("%c"), | |
479 | } | |
480 | } | |
481 | ||
482 | /// Formats the time according to the format string. | |
483 | pub fn strftime<'a>(&'a self, format: &'a str) -> Result<TmFmt<'a>, ParseError> { | |
484 | validate_format(TmFmt { | |
485 | tm: self, | |
486 | format: Fmt::Str(format), | |
487 | }) | |
488 | } | |
489 | ||
490 | /** | |
491 | * Returns a TmFmt that outputs according to RFC 822. | |
492 | * | |
493 | * local: "Thu, 22 Mar 2012 07:53:18 PST" | |
494 | * utc: "Thu, 22 Mar 2012 14:53:18 GMT" | |
495 | */ | |
496 | pub fn rfc822(&self) -> TmFmt { | |
497 | let fmt = if self.tm_utcoff == 0 { | |
498 | "%a, %d %b %Y %T GMT" | |
499 | } else { | |
500 | "%a, %d %b %Y %T %Z" | |
501 | }; | |
502 | TmFmt { | |
503 | tm: self, | |
504 | format: Fmt::Str(fmt), | |
505 | } | |
506 | } | |
507 | ||
508 | /** | |
509 | * Returns a TmFmt that outputs according to RFC 822 with Zulu time. | |
510 | * | |
511 | * local: "Thu, 22 Mar 2012 07:53:18 -0700" | |
512 | * utc: "Thu, 22 Mar 2012 14:53:18 -0000" | |
513 | */ | |
514 | pub fn rfc822z(&self) -> TmFmt { | |
515 | TmFmt { | |
516 | tm: self, | |
517 | format: Fmt::Str("%a, %d %b %Y %T %z"), | |
518 | } | |
519 | } | |
520 | ||
521 | /** | |
522 | * Returns a TmFmt that outputs according to RFC 3339. RFC 3339 is | |
523 | * compatible with ISO 8601. | |
524 | * | |
525 | * local: "2012-02-22T07:53:18-07:00" | |
526 | * utc: "2012-02-22T14:53:18Z" | |
527 | */ | |
528 | pub fn rfc3339<'a>(&'a self) -> TmFmt { | |
529 | TmFmt { | |
530 | tm: self, | |
531 | format: Fmt::Rfc3339, | |
532 | } | |
533 | } | |
534 | } | |
535 | ||
536 | #[derive(Copy, PartialEq, Debug, Clone)] | |
537 | pub enum ParseError { | |
538 | InvalidSecond, | |
539 | InvalidMinute, | |
540 | InvalidHour, | |
541 | InvalidDay, | |
542 | InvalidMonth, | |
543 | InvalidYear, | |
544 | InvalidDayOfWeek, | |
545 | InvalidDayOfMonth, | |
546 | InvalidDayOfYear, | |
547 | InvalidZoneOffset, | |
548 | InvalidTime, | |
549 | InvalidSecondsSinceEpoch, | |
550 | MissingFormatConverter, | |
551 | InvalidFormatSpecifier(char), | |
552 | UnexpectedCharacter(char, char), | |
553 | } | |
554 | ||
555 | impl fmt::Display for ParseError { | |
556 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
f035d41b | 557 | #[allow(deprecated)] |
ff7c6d11 XL |
558 | match *self { |
559 | InvalidFormatSpecifier(ch) => { | |
560 | write!(f, "{}: %{}", self.description(), ch) | |
561 | } | |
562 | UnexpectedCharacter(a, b) => { | |
563 | write!(f, "expected: `{}`, found: `{}`", a, b) | |
564 | } | |
565 | _ => write!(f, "{}", self.description()) | |
566 | } | |
567 | } | |
568 | } | |
569 | ||
570 | impl Error for ParseError { | |
571 | fn description(&self) -> &str { | |
572 | match *self { | |
573 | InvalidSecond => "Invalid second.", | |
574 | InvalidMinute => "Invalid minute.", | |
575 | InvalidHour => "Invalid hour.", | |
576 | InvalidDay => "Invalid day.", | |
577 | InvalidMonth => "Invalid month.", | |
578 | InvalidYear => "Invalid year.", | |
579 | InvalidDayOfWeek => "Invalid day of the week.", | |
580 | InvalidDayOfMonth => "Invalid day of the month.", | |
581 | InvalidDayOfYear => "Invalid day of the year.", | |
582 | InvalidZoneOffset => "Invalid zone offset.", | |
583 | InvalidTime => "Invalid time.", | |
584 | InvalidSecondsSinceEpoch => "Invalid seconds since epoch.", | |
585 | MissingFormatConverter => "missing format converter after `%`", | |
586 | InvalidFormatSpecifier(..) => "invalid format specifier", | |
587 | UnexpectedCharacter(..) => "Unexpected character.", | |
588 | } | |
589 | } | |
590 | } | |
591 | ||
592 | /// A wrapper around a `Tm` and format string that implements Display. | |
593 | #[derive(Debug)] | |
594 | pub struct TmFmt<'a> { | |
595 | tm: &'a Tm, | |
596 | format: Fmt<'a> | |
597 | } | |
598 | ||
599 | #[derive(Debug)] | |
600 | enum Fmt<'a> { | |
601 | Str(&'a str), | |
602 | Rfc3339, | |
603 | Ctime, | |
604 | } | |
605 | ||
606 | fn validate_format<'a>(fmt: TmFmt<'a>) -> Result<TmFmt<'a>, ParseError> { | |
607 | ||
608 | match (fmt.tm.tm_wday, fmt.tm.tm_mon) { | |
609 | (0...6, 0...11) => (), | |
610 | (_wday, 0...11) => return Err(InvalidDayOfWeek), | |
611 | (0...6, _mon) => return Err(InvalidMonth), | |
612 | _ => return Err(InvalidDay) | |
613 | } | |
614 | match fmt.format { | |
615 | Fmt::Str(ref s) => { | |
616 | let mut chars = s.chars(); | |
617 | loop { | |
618 | match chars.next() { | |
619 | Some('%') => { | |
620 | match chars.next() { | |
621 | Some('A') | Some('a') | Some('B') | Some('b') | | |
622 | Some('C') | Some('c') | Some('D') | Some('d') | | |
623 | Some('e') | Some('F') | Some('f') | Some('G') | | |
624 | Some('g') | Some('H') | Some('h') | Some('I') | | |
625 | Some('j') | Some('k') | Some('l') | Some('M') | | |
626 | Some('m') | Some('n') | Some('P') | Some('p') | | |
627 | Some('R') | Some('r') | Some('S') | Some('s') | | |
628 | Some('T') | Some('t') | Some('U') | Some('u') | | |
629 | Some('V') | Some('v') | Some('W') | Some('w') | | |
630 | Some('X') | Some('x') | Some('Y') | Some('y') | | |
631 | Some('Z') | Some('z') | Some('+') | Some('%') => (), | |
632 | ||
633 | Some(c) => return Err(InvalidFormatSpecifier(c)), | |
634 | None => return Err(MissingFormatConverter), | |
635 | } | |
636 | }, | |
637 | None => break, | |
638 | _ => () | |
639 | } | |
640 | } | |
641 | }, | |
642 | _ => () | |
643 | } | |
644 | Ok(fmt) | |
645 | } | |
646 | ||
647 | /// Formats the time according to the format string. | |
648 | pub fn strftime(format: &str, tm: &Tm) -> Result<String, ParseError> { | |
649 | tm.strftime(format).map(|fmt| fmt.to_string()) | |
650 | } | |
651 | ||
652 | #[cfg(test)] | |
653 | mod tests { | |
654 | use super::{Timespec, get_time, precise_time_ns, precise_time_s, | |
655 | at_utc, at, strptime, PreciseTime, SteadyTime, ParseError, Duration}; | |
656 | use super::ParseError::{InvalidTime, InvalidYear, MissingFormatConverter, | |
657 | InvalidFormatSpecifier}; | |
658 | ||
f035d41b XL |
659 | #[allow(deprecated)] // `Once::new` is const starting in Rust 1.32 |
660 | use std::sync::ONCE_INIT; | |
661 | use std::sync::{Once, Mutex, MutexGuard, LockResult}; | |
662 | use std::i32; | |
ff7c6d11 XL |
663 | use std::mem; |
664 | ||
665 | struct TzReset { | |
666 | _tzreset: ::sys::TzReset, | |
667 | _lock: LockResult<MutexGuard<'static, ()>>, | |
668 | } | |
669 | ||
670 | fn set_time_zone_la_or_london(london: bool) -> TzReset { | |
671 | // Lock manages current timezone because some tests require LA some | |
672 | // London | |
673 | static mut LOCK: *mut Mutex<()> = 0 as *mut _; | |
f035d41b | 674 | #[allow(deprecated)] // `Once::new` is const starting in Rust 1.32 |
ff7c6d11 XL |
675 | static INIT: Once = ONCE_INIT; |
676 | ||
677 | unsafe { | |
678 | INIT.call_once(|| { | |
679 | LOCK = mem::transmute(Box::new(Mutex::new(()))); | |
680 | }); | |
681 | ||
682 | let timezone_lock = (*LOCK).lock(); | |
683 | let reset_func = if london { | |
684 | ::sys::set_london_with_dst_time_zone() | |
685 | } else { | |
686 | ::sys::set_los_angeles_time_zone() | |
687 | }; | |
688 | TzReset { | |
689 | _lock: timezone_lock, | |
690 | _tzreset: reset_func, | |
691 | } | |
692 | } | |
693 | } | |
694 | ||
695 | fn set_time_zone() -> TzReset { | |
696 | set_time_zone_la_or_london(false) | |
697 | } | |
698 | ||
699 | fn set_time_zone_london_dst() -> TzReset { | |
700 | set_time_zone_la_or_london(true) | |
701 | } | |
702 | ||
703 | #[test] | |
704 | fn test_get_time() { | |
f035d41b XL |
705 | static SOME_RECENT_DATE: i64 = 1577836800i64; // 2020-01-01T00:00:00Z |
706 | static SOME_FUTURE_DATE: i64 = i32::MAX as i64; // Y2038 | |
ff7c6d11 XL |
707 | |
708 | let tv1 = get_time(); | |
709 | debug!("tv1={} sec + {} nsec", tv1.sec, tv1.nsec); | |
710 | ||
711 | assert!(tv1.sec > SOME_RECENT_DATE); | |
712 | assert!(tv1.nsec < 1000000000i32); | |
713 | ||
714 | let tv2 = get_time(); | |
715 | debug!("tv2={} sec + {} nsec", tv2.sec, tv2.nsec); | |
716 | ||
717 | assert!(tv2.sec >= tv1.sec); | |
718 | assert!(tv2.sec < SOME_FUTURE_DATE); | |
719 | assert!(tv2.nsec < 1000000000i32); | |
720 | if tv2.sec == tv1.sec { | |
721 | assert!(tv2.nsec >= tv1.nsec); | |
722 | } | |
723 | } | |
724 | ||
725 | #[test] | |
726 | fn test_precise_time() { | |
727 | let s0 = precise_time_s(); | |
728 | debug!("s0={} sec", s0); | |
729 | assert!(s0 > 0.); | |
730 | ||
731 | let ns0 = precise_time_ns(); | |
732 | let ns1 = precise_time_ns(); | |
733 | debug!("ns0={} ns", ns0); | |
734 | debug!("ns1={} ns", ns1); | |
735 | assert!(ns1 >= ns0); | |
736 | ||
737 | let ns2 = precise_time_ns(); | |
738 | debug!("ns2={} ns", ns2); | |
739 | assert!(ns2 >= ns1); | |
740 | } | |
741 | ||
742 | #[test] | |
743 | fn test_precise_time_to() { | |
744 | let t0 = PreciseTime(1000); | |
745 | let t1 = PreciseTime(1023); | |
746 | assert_eq!(Duration::nanoseconds(23), t0.to(t1)); | |
747 | } | |
748 | ||
749 | #[test] | |
750 | fn test_at_utc() { | |
751 | let _reset = set_time_zone(); | |
752 | ||
753 | let time = Timespec::new(1234567890, 54321); | |
754 | let utc = at_utc(time); | |
755 | ||
756 | assert_eq!(utc.tm_sec, 30); | |
757 | assert_eq!(utc.tm_min, 31); | |
758 | assert_eq!(utc.tm_hour, 23); | |
759 | assert_eq!(utc.tm_mday, 13); | |
760 | assert_eq!(utc.tm_mon, 1); | |
761 | assert_eq!(utc.tm_year, 109); | |
762 | assert_eq!(utc.tm_wday, 5); | |
763 | assert_eq!(utc.tm_yday, 43); | |
764 | assert_eq!(utc.tm_isdst, 0); | |
765 | assert_eq!(utc.tm_utcoff, 0); | |
766 | assert_eq!(utc.tm_nsec, 54321); | |
767 | } | |
768 | ||
769 | #[test] | |
770 | fn test_at() { | |
771 | let _reset = set_time_zone(); | |
772 | ||
773 | let time = Timespec::new(1234567890, 54321); | |
774 | let local = at(time); | |
775 | ||
776 | debug!("time_at: {:?}", local); | |
777 | ||
778 | assert_eq!(local.tm_sec, 30); | |
779 | assert_eq!(local.tm_min, 31); | |
780 | assert_eq!(local.tm_hour, 15); | |
781 | assert_eq!(local.tm_mday, 13); | |
782 | assert_eq!(local.tm_mon, 1); | |
783 | assert_eq!(local.tm_year, 109); | |
784 | assert_eq!(local.tm_wday, 5); | |
785 | assert_eq!(local.tm_yday, 43); | |
786 | assert_eq!(local.tm_isdst, 0); | |
787 | assert_eq!(local.tm_utcoff, -28800); | |
788 | assert_eq!(local.tm_nsec, 54321); | |
789 | } | |
790 | ||
791 | #[test] | |
792 | fn test_to_timespec() { | |
793 | let _reset = set_time_zone(); | |
794 | ||
795 | let time = Timespec::new(1234567890, 54321); | |
796 | let utc = at_utc(time); | |
797 | ||
798 | assert_eq!(utc.to_timespec(), time); | |
799 | assert_eq!(utc.to_local().to_timespec(), time); | |
800 | } | |
801 | ||
802 | #[test] | |
803 | fn test_conversions() { | |
804 | let _reset = set_time_zone(); | |
805 | ||
806 | let time = Timespec::new(1234567890, 54321); | |
807 | let utc = at_utc(time); | |
808 | let local = at(time); | |
809 | ||
810 | assert!(local.to_local() == local); | |
811 | assert!(local.to_utc() == utc); | |
812 | assert!(local.to_utc().to_local() == local); | |
813 | assert!(utc.to_utc() == utc); | |
814 | assert!(utc.to_local() == local); | |
815 | assert!(utc.to_local().to_utc() == utc); | |
816 | } | |
817 | ||
818 | #[test] | |
819 | fn test_strptime() { | |
820 | let _reset = set_time_zone(); | |
821 | ||
822 | match strptime("", "") { | |
823 | Ok(ref tm) => { | |
824 | assert!(tm.tm_sec == 0); | |
825 | assert!(tm.tm_min == 0); | |
826 | assert!(tm.tm_hour == 0); | |
827 | assert!(tm.tm_mday == 0); | |
828 | assert!(tm.tm_mon == 0); | |
829 | assert!(tm.tm_year == 0); | |
830 | assert!(tm.tm_wday == 0); | |
831 | assert!(tm.tm_isdst == 0); | |
832 | assert!(tm.tm_utcoff == 0); | |
833 | assert!(tm.tm_nsec == 0); | |
834 | } | |
835 | Err(_) => () | |
836 | } | |
837 | ||
838 | let format = "%a %b %e %T.%f %Y"; | |
839 | assert_eq!(strptime("", format), Err(ParseError::InvalidDay)); | |
840 | assert_eq!(strptime("Fri Feb 13 15:31:30", format), | |
841 | Err(InvalidTime)); | |
842 | ||
843 | match strptime("Fri Feb 13 15:31:30.01234 2009", format) { | |
844 | Err(e) => panic!("{}", e), | |
845 | Ok(ref tm) => { | |
846 | assert_eq!(tm.tm_sec, 30); | |
847 | assert_eq!(tm.tm_min, 31); | |
848 | assert_eq!(tm.tm_hour, 15); | |
849 | assert_eq!(tm.tm_mday, 13); | |
850 | assert_eq!(tm.tm_mon, 1); | |
851 | assert_eq!(tm.tm_year, 109); | |
852 | assert_eq!(tm.tm_wday, 5); | |
853 | assert_eq!(tm.tm_yday, 0); | |
854 | assert_eq!(tm.tm_isdst, 0); | |
855 | assert_eq!(tm.tm_utcoff, 0); | |
856 | assert_eq!(tm.tm_nsec, 12340000); | |
857 | } | |
858 | } | |
859 | ||
860 | fn test(s: &str, format: &str) -> bool { | |
861 | match strptime(s, format) { | |
862 | Ok(tm) => { | |
863 | tm.strftime(format).unwrap().to_string() == s.to_string() | |
864 | }, | |
865 | Err(e) => panic!("{:?}, s={:?}, format={:?}", e, s, format) | |
866 | } | |
867 | } | |
868 | ||
869 | fn test_oneway(s : &str, format : &str) -> bool { | |
870 | match strptime(s, format) { | |
871 | Ok(_) => { | |
872 | // oneway tests are used when reformatting the parsed Tm | |
873 | // back into a string can generate a different string | |
874 | // from the original (i.e. leading zeroes) | |
875 | true | |
876 | }, | |
877 | Err(e) => panic!("{:?}, s={:?}, format={:?}", e, s, format) | |
878 | } | |
879 | } | |
880 | ||
881 | let days = [ | |
882 | "Sunday".to_string(), | |
883 | "Monday".to_string(), | |
884 | "Tuesday".to_string(), | |
885 | "Wednesday".to_string(), | |
886 | "Thursday".to_string(), | |
887 | "Friday".to_string(), | |
888 | "Saturday".to_string() | |
889 | ]; | |
890 | for day in days.iter() { | |
891 | assert!(test(&day, "%A")); | |
892 | } | |
893 | ||
894 | let days = [ | |
895 | "Sun".to_string(), | |
896 | "Mon".to_string(), | |
897 | "Tue".to_string(), | |
898 | "Wed".to_string(), | |
899 | "Thu".to_string(), | |
900 | "Fri".to_string(), | |
901 | "Sat".to_string() | |
902 | ]; | |
903 | for day in days.iter() { | |
904 | assert!(test(&day, "%a")); | |
905 | } | |
906 | ||
907 | let months = [ | |
908 | "January".to_string(), | |
909 | "February".to_string(), | |
910 | "March".to_string(), | |
911 | "April".to_string(), | |
912 | "May".to_string(), | |
913 | "June".to_string(), | |
914 | "July".to_string(), | |
915 | "August".to_string(), | |
916 | "September".to_string(), | |
917 | "October".to_string(), | |
918 | "November".to_string(), | |
919 | "December".to_string() | |
920 | ]; | |
921 | for day in months.iter() { | |
922 | assert!(test(&day, "%B")); | |
923 | } | |
924 | ||
925 | let months = [ | |
926 | "Jan".to_string(), | |
927 | "Feb".to_string(), | |
928 | "Mar".to_string(), | |
929 | "Apr".to_string(), | |
930 | "May".to_string(), | |
931 | "Jun".to_string(), | |
932 | "Jul".to_string(), | |
933 | "Aug".to_string(), | |
934 | "Sep".to_string(), | |
935 | "Oct".to_string(), | |
936 | "Nov".to_string(), | |
937 | "Dec".to_string() | |
938 | ]; | |
939 | for day in months.iter() { | |
940 | assert!(test(&day, "%b")); | |
941 | } | |
942 | ||
943 | assert!(test("19", "%C")); | |
944 | assert!(test("Fri Feb 3 23:31:30 2009", "%c")); | |
945 | assert!(test("Fri Feb 13 23:31:30 2009", "%c")); | |
946 | assert!(test("02/13/09", "%D")); | |
947 | assert!(test("03", "%d")); | |
948 | assert!(test("13", "%d")); | |
949 | assert!(test(" 3", "%e")); | |
950 | assert!(test("13", "%e")); | |
951 | assert!(test("2009-02-13", "%F")); | |
952 | assert!(test("03", "%H")); | |
953 | assert!(test("13", "%H")); | |
954 | assert!(test("03", "%I")); // FIXME (#2350): flesh out | |
955 | assert!(test("11", "%I")); // FIXME (#2350): flesh out | |
956 | assert!(test("044", "%j")); | |
957 | assert!(test(" 3", "%k")); | |
958 | assert!(test("13", "%k")); | |
959 | assert!(test(" 1", "%l")); | |
960 | assert!(test("11", "%l")); | |
961 | assert!(test("03", "%M")); | |
962 | assert!(test("13", "%M")); | |
963 | assert!(test("\n", "%n")); | |
964 | assert!(test("am", "%P")); | |
965 | assert!(test("pm", "%P")); | |
966 | assert!(test("AM", "%p")); | |
967 | assert!(test("PM", "%p")); | |
968 | assert!(test("23:31", "%R")); | |
969 | assert!(test("11:31:30 AM", "%r")); | |
970 | assert!(test("11:31:30 PM", "%r")); | |
971 | assert!(test("03", "%S")); | |
972 | assert!(test("13", "%S")); | |
973 | assert!(test("15:31:30", "%T")); | |
974 | assert!(test("\t", "%t")); | |
975 | assert!(test("1", "%u")); | |
976 | assert!(test("7", "%u")); | |
977 | assert!(test("13-Feb-2009", "%v")); | |
978 | assert!(test("0", "%w")); | |
979 | assert!(test("6", "%w")); | |
980 | assert!(test("2009", "%Y")); | |
981 | assert!(test("09", "%y")); | |
982 | ||
983 | assert!(test_oneway("3", "%d")); | |
984 | assert!(test_oneway("3", "%H")); | |
985 | assert!(test_oneway("3", "%e")); | |
986 | assert!(test_oneway("3", "%M")); | |
987 | assert!(test_oneway("3", "%S")); | |
988 | ||
989 | assert!(strptime("-0000", "%z").unwrap().tm_utcoff == 0); | |
990 | assert!(strptime("-00:00", "%z").unwrap().tm_utcoff == 0); | |
991 | assert!(strptime("Z", "%z").unwrap().tm_utcoff == 0); | |
992 | assert_eq!(-28800, strptime("-0800", "%z").unwrap().tm_utcoff); | |
993 | assert_eq!(-28800, strptime("-08:00", "%z").unwrap().tm_utcoff); | |
994 | assert_eq!(28800, strptime("+0800", "%z").unwrap().tm_utcoff); | |
995 | assert_eq!(28800, strptime("+08:00", "%z").unwrap().tm_utcoff); | |
996 | assert_eq!(5400, strptime("+0130", "%z").unwrap().tm_utcoff); | |
997 | assert_eq!(5400, strptime("+01:30", "%z").unwrap().tm_utcoff); | |
998 | assert!(test("%", "%%")); | |
999 | ||
1000 | // Test for #7256 | |
1001 | assert_eq!(strptime("360", "%Y-%m-%d"), Err(InvalidYear)); | |
1002 | ||
1003 | // Test for epoch seconds parsing | |
1004 | { | |
1005 | assert!(test("1428035610", "%s")); | |
1006 | let tm = strptime("1428035610", "%s").unwrap(); | |
1007 | assert_eq!(tm.tm_utcoff, 0); | |
1008 | assert_eq!(tm.tm_isdst, 0); | |
1009 | assert_eq!(tm.tm_yday, 92); | |
1010 | assert_eq!(tm.tm_wday, 5); | |
1011 | assert_eq!(tm.tm_year, 115); | |
1012 | assert_eq!(tm.tm_mon, 3); | |
1013 | assert_eq!(tm.tm_mday, 3); | |
1014 | assert_eq!(tm.tm_hour, 4); | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | #[test] | |
1019 | fn test_asctime() { | |
1020 | let _reset = set_time_zone(); | |
1021 | ||
1022 | let time = Timespec::new(1234567890, 54321); | |
1023 | let utc = at_utc(time); | |
1024 | let local = at(time); | |
1025 | ||
1026 | debug!("test_ctime: {} {}", utc.asctime(), local.asctime()); | |
1027 | ||
1028 | assert_eq!(utc.asctime().to_string(), "Fri Feb 13 23:31:30 2009".to_string()); | |
1029 | assert_eq!(local.asctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1030 | } | |
1031 | ||
1032 | #[test] | |
1033 | fn test_ctime() { | |
1034 | let _reset = set_time_zone(); | |
1035 | ||
1036 | let time = Timespec::new(1234567890, 54321); | |
1037 | let utc = at_utc(time); | |
1038 | let local = at(time); | |
1039 | ||
1040 | debug!("test_ctime: {} {}", utc.ctime(), local.ctime()); | |
1041 | ||
1042 | assert_eq!(utc.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1043 | assert_eq!(local.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1044 | } | |
1045 | ||
1046 | #[test] | |
1047 | fn test_strftime() { | |
1048 | let _reset = set_time_zone(); | |
1049 | ||
1050 | let time = Timespec::new(1234567890, 54321); | |
1051 | let utc = at_utc(time); | |
1052 | let local = at(time); | |
1053 | ||
1054 | assert_eq!(local.strftime("").unwrap().to_string(), "".to_string()); | |
1055 | assert_eq!(local.strftime("%A").unwrap().to_string(), "Friday".to_string()); | |
1056 | assert_eq!(local.strftime("%a").unwrap().to_string(), "Fri".to_string()); | |
1057 | assert_eq!(local.strftime("%B").unwrap().to_string(), "February".to_string()); | |
1058 | assert_eq!(local.strftime("%b").unwrap().to_string(), "Feb".to_string()); | |
1059 | assert_eq!(local.strftime("%C").unwrap().to_string(), "20".to_string()); | |
1060 | assert_eq!(local.strftime("%c").unwrap().to_string(), | |
1061 | "Fri Feb 13 15:31:30 2009".to_string()); | |
1062 | assert_eq!(local.strftime("%D").unwrap().to_string(), "02/13/09".to_string()); | |
1063 | assert_eq!(local.strftime("%d").unwrap().to_string(), "13".to_string()); | |
1064 | assert_eq!(local.strftime("%e").unwrap().to_string(), "13".to_string()); | |
1065 | assert_eq!(local.strftime("%F").unwrap().to_string(), "2009-02-13".to_string()); | |
1066 | assert_eq!(local.strftime("%f").unwrap().to_string(), "000054321".to_string()); | |
1067 | assert_eq!(local.strftime("%G").unwrap().to_string(), "2009".to_string()); | |
1068 | assert_eq!(local.strftime("%g").unwrap().to_string(), "09".to_string()); | |
1069 | assert_eq!(local.strftime("%H").unwrap().to_string(), "15".to_string()); | |
1070 | assert_eq!(local.strftime("%h").unwrap().to_string(), "Feb".to_string()); | |
1071 | assert_eq!(local.strftime("%I").unwrap().to_string(), "03".to_string()); | |
1072 | assert_eq!(local.strftime("%j").unwrap().to_string(), "044".to_string()); | |
1073 | assert_eq!(local.strftime("%k").unwrap().to_string(), "15".to_string()); | |
1074 | assert_eq!(local.strftime("%l").unwrap().to_string(), " 3".to_string()); | |
1075 | assert_eq!(local.strftime("%M").unwrap().to_string(), "31".to_string()); | |
1076 | assert_eq!(local.strftime("%m").unwrap().to_string(), "02".to_string()); | |
1077 | assert_eq!(local.strftime("%n").unwrap().to_string(), "\n".to_string()); | |
1078 | assert_eq!(local.strftime("%P").unwrap().to_string(), "pm".to_string()); | |
1079 | assert_eq!(local.strftime("%p").unwrap().to_string(), "PM".to_string()); | |
1080 | assert_eq!(local.strftime("%R").unwrap().to_string(), "15:31".to_string()); | |
1081 | assert_eq!(local.strftime("%r").unwrap().to_string(), "03:31:30 PM".to_string()); | |
1082 | assert_eq!(local.strftime("%S").unwrap().to_string(), "30".to_string()); | |
1083 | assert_eq!(local.strftime("%s").unwrap().to_string(), "1234567890".to_string()); | |
1084 | assert_eq!(local.strftime("%T").unwrap().to_string(), "15:31:30".to_string()); | |
1085 | assert_eq!(local.strftime("%t").unwrap().to_string(), "\t".to_string()); | |
1086 | assert_eq!(local.strftime("%U").unwrap().to_string(), "06".to_string()); | |
1087 | assert_eq!(local.strftime("%u").unwrap().to_string(), "5".to_string()); | |
1088 | assert_eq!(local.strftime("%V").unwrap().to_string(), "07".to_string()); | |
1089 | assert_eq!(local.strftime("%v").unwrap().to_string(), "13-Feb-2009".to_string()); | |
1090 | assert_eq!(local.strftime("%W").unwrap().to_string(), "06".to_string()); | |
1091 | assert_eq!(local.strftime("%w").unwrap().to_string(), "5".to_string()); | |
1092 | // FIXME (#2350): support locale | |
1093 | assert_eq!(local.strftime("%X").unwrap().to_string(), "15:31:30".to_string()); | |
1094 | // FIXME (#2350): support locale | |
1095 | assert_eq!(local.strftime("%x").unwrap().to_string(), "02/13/09".to_string()); | |
1096 | assert_eq!(local.strftime("%Y").unwrap().to_string(), "2009".to_string()); | |
1097 | assert_eq!(local.strftime("%y").unwrap().to_string(), "09".to_string()); | |
1098 | // FIXME (#2350): support locale | |
1099 | assert_eq!(local.strftime("%Z").unwrap().to_string(), "".to_string()); | |
1100 | assert_eq!(local.strftime("%z").unwrap().to_string(), "-0800".to_string()); | |
1101 | assert_eq!(local.strftime("%+").unwrap().to_string(), | |
1102 | "2009-02-13T15:31:30-08:00".to_string()); | |
1103 | assert_eq!(local.strftime("%%").unwrap().to_string(), "%".to_string()); | |
1104 | ||
1105 | let invalid_specifiers = ["%E", "%J", "%K", "%L", "%N", "%O", "%o", "%Q", "%q"]; | |
1106 | for &sp in invalid_specifiers.iter() { | |
1107 | assert_eq!(local.strftime(sp).unwrap_err(), | |
1108 | InvalidFormatSpecifier(sp[1..].chars().next().unwrap())); | |
1109 | } | |
1110 | assert_eq!(local.strftime("%").unwrap_err(), MissingFormatConverter); | |
1111 | assert_eq!(local.strftime("%A %").unwrap_err(), MissingFormatConverter); | |
1112 | ||
1113 | assert_eq!(local.asctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1114 | assert_eq!(local.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1115 | assert_eq!(local.rfc822z().to_string(), "Fri, 13 Feb 2009 15:31:30 -0800".to_string()); | |
1116 | assert_eq!(local.rfc3339().to_string(), "2009-02-13T15:31:30-08:00".to_string()); | |
1117 | ||
1118 | assert_eq!(utc.asctime().to_string(), "Fri Feb 13 23:31:30 2009".to_string()); | |
1119 | assert_eq!(utc.ctime().to_string(), "Fri Feb 13 15:31:30 2009".to_string()); | |
1120 | assert_eq!(utc.rfc822().to_string(), "Fri, 13 Feb 2009 23:31:30 GMT".to_string()); | |
1121 | assert_eq!(utc.rfc822z().to_string(), "Fri, 13 Feb 2009 23:31:30 -0000".to_string()); | |
1122 | assert_eq!(utc.rfc3339().to_string(), "2009-02-13T23:31:30Z".to_string()); | |
1123 | } | |
1124 | ||
1125 | #[test] | |
1126 | fn test_timespec_eq_ord() { | |
1127 | let a = &Timespec::new(-2, 1); | |
1128 | let b = &Timespec::new(-1, 2); | |
1129 | let c = &Timespec::new(1, 2); | |
1130 | let d = &Timespec::new(2, 1); | |
1131 | let e = &Timespec::new(2, 1); | |
1132 | ||
1133 | assert!(d.eq(e)); | |
1134 | assert!(c.ne(e)); | |
1135 | ||
1136 | assert!(a.lt(b)); | |
1137 | assert!(b.lt(c)); | |
1138 | assert!(c.lt(d)); | |
1139 | ||
1140 | assert!(a.le(b)); | |
1141 | assert!(b.le(c)); | |
1142 | assert!(c.le(d)); | |
1143 | assert!(d.le(e)); | |
1144 | assert!(e.le(d)); | |
1145 | ||
1146 | assert!(b.ge(a)); | |
1147 | assert!(c.ge(b)); | |
1148 | assert!(d.ge(c)); | |
1149 | assert!(e.ge(d)); | |
1150 | assert!(d.ge(e)); | |
1151 | ||
1152 | assert!(b.gt(a)); | |
1153 | assert!(c.gt(b)); | |
1154 | assert!(d.gt(c)); | |
1155 | } | |
1156 | ||
1157 | #[test] | |
1158 | #[allow(deprecated)] | |
1159 | fn test_timespec_hash() { | |
1160 | use std::hash::{Hash, Hasher}; | |
1161 | ||
1162 | let c = &Timespec::new(3, 2); | |
1163 | let d = &Timespec::new(2, 1); | |
1164 | let e = &Timespec::new(2, 1); | |
1165 | ||
1166 | let mut hasher = ::std::hash::SipHasher::new(); | |
1167 | ||
1168 | let d_hash:u64 = { | |
1169 | d.hash(&mut hasher); | |
1170 | hasher.finish() | |
1171 | }; | |
1172 | ||
1173 | hasher = ::std::hash::SipHasher::new(); | |
1174 | ||
1175 | let e_hash:u64 = { | |
1176 | e.hash(&mut hasher); | |
1177 | hasher.finish() | |
1178 | }; | |
1179 | ||
1180 | hasher = ::std::hash::SipHasher::new(); | |
1181 | ||
1182 | let c_hash:u64 = { | |
1183 | c.hash(&mut hasher); | |
1184 | hasher.finish() | |
1185 | }; | |
1186 | ||
1187 | assert_eq!(d_hash, e_hash); | |
1188 | assert!(c_hash != e_hash); | |
1189 | } | |
1190 | ||
1191 | #[test] | |
1192 | fn test_timespec_add() { | |
1193 | let a = Timespec::new(1, 2); | |
1194 | let b = Duration::seconds(2) + Duration::nanoseconds(3); | |
1195 | let c = a + b; | |
1196 | assert_eq!(c.sec, 3); | |
1197 | assert_eq!(c.nsec, 5); | |
1198 | ||
1199 | let p = Timespec::new(1, super::NSEC_PER_SEC - 2); | |
1200 | let q = Duration::seconds(2) + Duration::nanoseconds(2); | |
1201 | let r = p + q; | |
1202 | assert_eq!(r.sec, 4); | |
1203 | assert_eq!(r.nsec, 0); | |
1204 | ||
1205 | let u = Timespec::new(1, super::NSEC_PER_SEC - 2); | |
1206 | let v = Duration::seconds(2) + Duration::nanoseconds(3); | |
1207 | let w = u + v; | |
1208 | assert_eq!(w.sec, 4); | |
1209 | assert_eq!(w.nsec, 1); | |
1210 | ||
1211 | let k = Timespec::new(1, 0); | |
1212 | let l = Duration::nanoseconds(-1); | |
1213 | let m = k + l; | |
1214 | assert_eq!(m.sec, 0); | |
1215 | assert_eq!(m.nsec, 999_999_999); | |
1216 | } | |
1217 | ||
1218 | #[test] | |
1219 | fn test_timespec_sub() { | |
1220 | let a = Timespec::new(2, 3); | |
1221 | let b = Timespec::new(1, 2); | |
1222 | let c = a - b; | |
1223 | assert_eq!(c.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 + 1)); | |
1224 | ||
1225 | let p = Timespec::new(2, 0); | |
1226 | let q = Timespec::new(1, 2); | |
1227 | let r = p - q; | |
1228 | assert_eq!(r.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 - 2)); | |
1229 | ||
1230 | let u = Timespec::new(1, 2); | |
1231 | let v = Timespec::new(2, 3); | |
1232 | let w = u - v; | |
1233 | assert_eq!(w.num_nanoseconds(), Some(-super::NSEC_PER_SEC as i64 - 1)); | |
1234 | } | |
1235 | ||
1236 | #[test] | |
1237 | fn test_time_sub() { | |
1238 | let a = ::now(); | |
1239 | let b = at(a.to_timespec() + Duration::seconds(5)); | |
1240 | let c = b - a; | |
1241 | assert_eq!(c.num_nanoseconds(), Some(super::NSEC_PER_SEC as i64 * 5)); | |
1242 | } | |
1243 | ||
1244 | #[test] | |
1245 | fn test_steadytime_sub() { | |
1246 | let a = SteadyTime::now(); | |
1247 | let b = a + Duration::seconds(1); | |
1248 | assert_eq!(b - a, Duration::seconds(1)); | |
1249 | assert_eq!(a - b, Duration::seconds(-1)); | |
1250 | } | |
1251 | ||
1252 | #[test] | |
1253 | fn test_date_before_1970() { | |
1254 | let early = strptime("1901-01-06", "%F").unwrap(); | |
1255 | let late = strptime("2000-01-01", "%F").unwrap(); | |
1256 | assert!(early < late); | |
1257 | } | |
1258 | ||
1259 | #[test] | |
1260 | fn test_dst() { | |
1261 | let _reset = set_time_zone_london_dst(); | |
1262 | let utc_in_feb = strptime("2015-02-01Z", "%F%z").unwrap(); | |
1263 | let utc_in_jun = strptime("2015-06-01Z", "%F%z").unwrap(); | |
1264 | let utc_in_nov = strptime("2015-11-01Z", "%F%z").unwrap(); | |
1265 | let local_in_feb = utc_in_feb.to_local(); | |
1266 | let local_in_jun = utc_in_jun.to_local(); | |
1267 | let local_in_nov = utc_in_nov.to_local(); | |
1268 | ||
1269 | assert_eq!(local_in_feb.tm_mon, 1); | |
1270 | assert_eq!(local_in_feb.tm_hour, 0); | |
1271 | assert_eq!(local_in_feb.tm_utcoff, 0); | |
1272 | assert_eq!(local_in_feb.tm_isdst, 0); | |
1273 | ||
1274 | assert_eq!(local_in_jun.tm_mon, 5); | |
1275 | assert_eq!(local_in_jun.tm_hour, 1); | |
1276 | assert_eq!(local_in_jun.tm_utcoff, 3600); | |
1277 | assert_eq!(local_in_jun.tm_isdst, 1); | |
1278 | ||
1279 | assert_eq!(local_in_nov.tm_mon, 10); | |
1280 | assert_eq!(local_in_nov.tm_hour, 0); | |
1281 | assert_eq!(local_in_nov.tm_utcoff, 0); | |
1282 | assert_eq!(local_in_nov.tm_isdst, 0) | |
1283 | } | |
1284 | } |