lib.rs 46.3 KB
Newer Older
1
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

A
Arcterus 已提交
11 12 13 14 15
#[crate_id = "time#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];

16 17
#[allow(missing_doc)];

A
Arcterus 已提交
18 19
extern crate serialize;

A
Alex Crichton 已提交
20
use std::io::BufReader;
21
use std::libc;
22
use std::num;
23
use std::str;
24

25
static NSEC_PER_SEC: i32 = 1_000_000_000_i32;
26

27
mod rustrt {
28 29
    use super::Tm;

S
Steve Klabnik 已提交
30
    extern {
31 32 33 34 35
        pub fn rust_tzset();
        pub fn rust_gmtime(sec: i64, nsec: i32, result: &mut Tm);
        pub fn rust_localtime(sec: i64, nsec: i32, result: &mut Tm);
        pub fn rust_timegm(tm: &Tm) -> i64;
        pub fn rust_mktime(tm: &Tm) -> i64;
36
    }
37 38
}

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#[cfg(unix, not(target_os = "macos"))]
mod imp {
    use std::libc::{c_int, timespec};

    // Apparently android provides this in some other library?
    #[cfg(not(target_os = "android"))]
    #[link(name = "rt")]
    extern {}

    extern {
        pub fn clock_gettime(clk_id: c_int, tp: *mut timespec) -> c_int;
    }

}
#[cfg(target_os = "macos")]
mod imp {
    use std::libc::{timeval, timezone, c_int, mach_timebase_info};

    extern {
        pub fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int;
        pub fn mach_absolute_time() -> u64;
        pub fn mach_timebase_info(info: *mut mach_timebase_info) -> c_int;
    }
}

64
/// A record specifying a time value in seconds and nanoseconds.
65

66

67
#[deriving(Clone, Eq, Encodable, Decodable, Show)]
S
Steven Fackler 已提交
68
pub struct Timespec { sec: i64, nsec: i32 }
69 70 71 72 73 74 75 76
/*
 * Timespec assumes that pre-epoch Timespecs have negative sec and positive
 * nsec fields. Darwin's and Linux's struct timespec functions handle pre-
 * epoch timestamps using a "two steps back, one step forward" representation,
 * though the man pages do not actually document this. For example, the time
 * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64,
 * nsec: 800_000_000_i32 }`.
 */
77 78
impl Timespec {
    pub fn new(sec: i64, nsec: i32) -> Timespec {
P
Patrick Walton 已提交
79
        assert!(nsec >= 0 && nsec < NSEC_PER_SEC);
80 81 82
        Timespec { sec: sec, nsec: nsec }
    }
}
83

84
impl Ord for Timespec {
85
    fn lt(&self, other: &Timespec) -> bool {
86 87 88 89 90
        self.sec < other.sec ||
            (self.sec == other.sec && self.nsec < other.nsec)
    }
}

91 92
/**
 * Returns the current time as a `timespec` containing the seconds and
93
 * nanoseconds since 1970-01-01T00:00:00Z.
94
 */
T
Tim Chevalier 已提交
95
pub fn get_time() -> Timespec {
96
    unsafe {
97
        let (sec, nsec) = os_get_time();
98 99
        return Timespec::new(sec, nsec);
    }
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

    #[cfg(windows)]
    unsafe fn os_get_time() -> (i64, i32) {
        static NANOSECONDS_FROM_1601_TO_1970: u64 = 11644473600000000;

        let mut time = libc::FILETIME {
            dwLowDateTime: 0,
            dwHighDateTime: 0,
        };
        libc::GetSystemTimeAsFileTime(&mut time);

        // A FILETIME contains a 64-bit value representing the number of
        // hectonanosecond (100-nanosecond) intervals since 1601-01-01T00:00:00Z.
        // http://support.microsoft.com/kb/167296/en-us
        let ns_since_1601 = ((time.dwHighDateTime as u64 << 32) |
                             (time.dwLowDateTime  as u64 <<  0)) / 10;
        let ns_since_1970 = ns_since_1601 - NANOSECONDS_FROM_1601_TO_1970;

        ((ns_since_1970 / 1000000) as i64,
         ((ns_since_1970 % 1000000) * 1000) as i32)
    }

    #[cfg(target_os = "macos")]
    unsafe fn os_get_time() -> (i64, i32) {
        use std::ptr;
        let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 };
        imp::gettimeofday(&mut tv, ptr::mut_null());
        (tv.tv_sec as i64, tv.tv_usec * 1000)
    }

    #[cfg(not(target_os = "macos"), not(windows))]
    unsafe fn os_get_time() -> (i64, i32) {
        let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
        imp::clock_gettime(libc::CLOCK_REALTIME, &mut tv);
        (tv.tv_sec as i64, tv.tv_nsec as i32)
    }
T
Tim Chevalier 已提交
136 137
}

138

139 140 141 142
/**
 * Returns the current value of a high-resolution performance counter
 * in nanoseconds since an unspecified epoch.
 */
T
Tim Chevalier 已提交
143
pub fn precise_time_ns() -> u64 {
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    return os_precise_time_ns();

    #[cfg(windows)]
    fn os_precise_time_ns() -> u64 {
        let mut ticks_per_s = 0;
        assert_eq!(unsafe {
            libc::QueryPerformanceFrequency(&mut ticks_per_s)
        }, 1);
        let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
        let mut ticks = 0;
        assert_eq!(unsafe {
            libc::QueryPerformanceCounter(&mut ticks)
        }, 1);

        return (ticks as u64 * 1000000000) / (ticks_per_s as u64);
    }

    #[cfg(target_os = "macos")]
    fn os_precise_time_ns() -> u64 {
        let time = unsafe { imp::mach_absolute_time() };
        let mut info = libc::mach_timebase_info { numer: 0, denom: 0 };
        unsafe { imp::mach_timebase_info(&mut info); }
        return time * ((info.numer / info.denom) as u64);
    }

    #[cfg(not(windows), not(target_os = "macos"))]
    fn os_precise_time_ns() -> u64 {
        let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
        unsafe {
            imp::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
        }
        return (ts.tv_sec as u64) * 1000000000 + (ts.tv_nsec as u64)
176
    }
T
Tim Chevalier 已提交
177 178
}

179

180 181 182 183
/**
 * Returns the current value of a high-resolution performance counter
 * in seconds since an unspecified epoch.
 */
D
Daniel Micay 已提交
184 185
pub fn precise_time_s() -> f64 {
    return (precise_time_ns() as f64) / 1000000000.;
B
Brian Anderson 已提交
186
}
C
Chris Peterson 已提交
187

188
pub fn tzset() {
189 190 191
    unsafe {
        rustrt::rust_tzset();
    }
E
Erick Tryzelaar 已提交
192 193
}

194
#[deriving(Clone, Eq, Encodable, Decodable, Show)]
195
pub struct Tm {
S
Steven Fackler 已提交
196 197 198 199 200 201 202 203 204 205 206 207
    tm_sec: i32, // seconds after the minute ~[0-60]
    tm_min: i32, // minutes after the hour ~[0-59]
    tm_hour: i32, // hours after midnight ~[0-23]
    tm_mday: i32, // days of the month ~[1-31]
    tm_mon: i32, // months since January ~[0-11]
    tm_year: i32, // years since 1900
    tm_wday: i32, // days since Sunday ~[0-6]
    tm_yday: i32, // days since January 1 ~[0-365]
    tm_isdst: i32, // Daylight Savings Time flag
    tm_gmtoff: i32, // offset from UTC in seconds
    tm_zone: ~str, // timezone abbreviation
    tm_nsec: i32, // nanoseconds
208 209
}

210
pub fn empty_tm() -> Tm {
211 212 213
    // 64 is the max size of the timezone buffer allocated on windows
    // in rust_localtime. In glibc the max timezone size is supposedly 3.
    let zone = str::with_capacity(64);
214
    Tm {
215 216 217 218 219 220 221 222 223 224
        tm_sec: 0_i32,
        tm_min: 0_i32,
        tm_hour: 0_i32,
        tm_mday: 0_i32,
        tm_mon: 0_i32,
        tm_year: 0_i32,
        tm_wday: 0_i32,
        tm_yday: 0_i32,
        tm_isdst: 0_i32,
        tm_gmtoff: 0_i32,
225
        tm_zone: zone,
226
        tm_nsec: 0_i32,
227
    }
228 229
}

230
/// Returns the specified time in UTC
231
pub fn at_utc(clock: Timespec) -> Tm {
232
    unsafe {
233
        let Timespec { sec, nsec } = clock;
234
        let mut tm = empty_tm();
235
        rustrt::rust_gmtime(sec, nsec, &mut tm);
L
Luqman Aden 已提交
236
        tm
237
    }
238 239
}

240
/// Returns the current time in UTC
241
pub fn now_utc() -> Tm {
242 243 244
    at_utc(get_time())
}

245
/// Returns the specified time in the local timezone
246
pub fn at(clock: Timespec) -> Tm {
247
    unsafe {
248
        let Timespec { sec, nsec } = clock;
249
        let mut tm = empty_tm();
250
        rustrt::rust_localtime(sec, nsec, &mut tm);
L
Luqman Aden 已提交
251
        tm
252
    }
253 254
}

255
/// Returns the current time in the local timezone
256
pub fn now() -> Tm {
257 258 259
    at(get_time())
}

260

261
impl Tm {
262
    /// Convert time to the seconds from January 1, 1970
263
    pub fn to_timespec(&self) -> Timespec {
264
        unsafe {
265 266 267 268 269
            let sec = match self.tm_gmtoff {
                0_i32 => rustrt::rust_timegm(self),
                _     => rustrt::rust_mktime(self)
            };

270
            Timespec::new(sec, self.tm_nsec)
271 272 273 274
        }
    }

    /// Convert time to the local timezone
275
    pub fn to_local(&self) -> Tm {
276 277 278 279
        at(self.to_timespec())
    }

    /// Convert time to the UTC
280
    pub fn to_utc(&self) -> Tm {
281 282 283 284 285 286 287
        at_utc(self.to_timespec())
    }

    /**
     * Return a string of the current time in the form
     * "Thu Jan  1 00:00:00 1970".
     */
288
    pub fn ctime(&self) -> ~str { self.strftime("%c") }
289 290

    /// Formats the time according to the format string.
291
    pub fn strftime(&self, format: &str) -> ~str {
L
Luqman Aden 已提交
292
        strftime(format, self)
293
    }
294 295 296 297 298 299 300

    /**
     * Returns a time string formatted according to RFC 822.
     *
     * local: "Thu, 22 Mar 2012 07:53:18 PST"
     * utc:   "Thu, 22 Mar 2012 14:53:18 UTC"
     */
301
    pub fn rfc822(&self) -> ~str {
302
        if self.tm_gmtoff == 0_i32 {
303
            self.strftime("%a, %d %b %Y %T GMT")
304
        } else {
305
            self.strftime("%a, %d %b %Y %T %Z")
306 307 308 309 310 311 312 313 314
        }
    }

    /**
     * Returns a time string formatted according to RFC 822 with Zulu time.
     *
     * local: "Thu, 22 Mar 2012 07:53:18 -0700"
     * utc:   "Thu, 22 Mar 2012 14:53:18 -0000"
     */
315
    pub fn rfc822z(&self) -> ~str {
316
        self.strftime("%a, %d %b %Y %T %z")
317 318 319 320 321 322 323 324
    }

    /**
     * Returns a time string formatted according to ISO 8601.
     *
     * local: "2012-02-22T07:53:18-07:00"
     * utc:   "2012-02-22T14:53:18Z"
     */
325
    pub fn rfc3339(&self) -> ~str {
326
        if self.tm_gmtoff == 0_i32 {
327
            self.strftime("%Y-%m-%dT%H:%M:%SZ")
328
        } else {
329
            let s = self.strftime("%Y-%m-%dT%H:%M:%S");
330
            let sign = if self.tm_gmtoff > 0_i32 { '+' } else { '-' };
331
            let mut m = num::abs(self.tm_gmtoff) / 60_i32;
332 333
            let h = m / 60_i32;
            m -= h * 60_i32;
A
Alex Crichton 已提交
334
            s + format!("{}{:02d}:{:02d}", sign, h as int, m as int)
335 336 337 338
        }
    }
}

339 340
/// Parses the time from the string according to the format string.
pub fn strptime(s: &str, format: &str) -> Result<Tm, ~str> {
341
    fn match_str(s: &str, pos: uint, needle: &str) -> bool {
342
        let mut i = pos;
343
        for ch in needle.bytes() {
344
            if s[i] != ch {
B
Brian Anderson 已提交
345
                return false;
346 347 348
            }
            i += 1u;
        }
B
Brian Anderson 已提交
349
        return true;
350 351
    }

352
    fn match_strs(ss: &str, pos: uint, strs: &[(~str, i32)])
B
Brian Anderson 已提交
353
      -> Option<(i32, uint)> {
354
        let mut i = 0u;
355
        let len = strs.len();
356
        while i < len {
357
            match strs[i] { // can't use let due to let-pattern bugs
358 359
                (ref needle, value) => {
                    if match_str(ss, pos, *needle) {
360
                        return Some((value, pos + needle.len()));
361 362
                    }
                }
363 364 365 366
            }
            i += 1u;
        }

B
Brian Anderson 已提交
367
        None
368 369
    }

370
    fn match_digits(ss: &str, pos: uint, digits: uint, ws: bool)
B
Brian Anderson 已提交
371
      -> Option<(i32, uint)> {
372
        let mut pos = pos;
373
        let len = ss.len();
374 375 376 377
        let mut value = 0_i32;

        let mut i = 0u;
        while i < digits {
378 379 380
            if pos >= len {
                return None;
            }
381
            let range = ss.char_range_at(pos);
382
            pos = range.next;
383

384
            match range.ch {
B
Brian Anderson 已提交
385
              '0' .. '9' => {
386
                value = value * 10_i32 + (range.ch as i32 - '0' as i32);
387
              }
B
Brian Anderson 已提交
388
              ' ' if ws => (),
B
Brian Anderson 已提交
389
              _ => return None
390 391 392 393
            }
            i += 1u;
        }

B
Brian Anderson 已提交
394
        Some((value, pos))
395 396
    }

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    fn match_fractional_seconds(ss: &str, pos: uint) -> (i32, uint) {
        let len = ss.len();
        let mut value = 0_i32;
        let mut multiplier = NSEC_PER_SEC / 10;
        let mut pos = pos;

        loop {
            if pos >= len {
                break;
            }
            let range = ss.char_range_at(pos);

            match range.ch {
                '0' .. '9' => {
                    pos = range.next;
                    // This will drop digits after the nanoseconds place
                    let digit = range.ch as i32 - '0' as i32;
                    value += digit * multiplier;
                    multiplier /= 10;
                }
                _ => break
            }
        }

        (value, pos)
    }

A
Ashok Gautham 已提交
424 425
    fn match_digits_in_range(ss: &str, pos: uint, digits: uint, ws: bool,
                             min: i32, max: i32) -> Option<(i32, uint)> {
426 427 428 429 430 431 432 433
        match match_digits(ss, pos, digits, ws) {
          Some((val, pos)) if val >= min && val <= max => {
            Some((val, pos))
          }
          _ => None
        }
    }

434
    fn parse_char(s: &str, pos: uint, c: char) -> Result<uint, ~str> {
435
        let range = s.char_range_at(pos);
436

437 438
        if c == range.ch {
            Ok(range.next)
439
        } else {
A
Alex Crichton 已提交
440
            Err(format!("Expected {}, found {}",
441
                str::from_char(c),
442
                str::from_char(range.ch)))
443 444 445
        }
    }

446
    fn parse_type(s: &str, pos: uint, ch: char, tm: &mut Tm)
447
      -> Result<uint, ~str> {
448
        match ch {
449
          'A' => match match_strs(s, pos, [
B
Brian Anderson 已提交
450 451 452 453 454 455 456 457
              (~"Sunday", 0_i32),
              (~"Monday", 1_i32),
              (~"Tuesday", 2_i32),
              (~"Wednesday", 3_i32),
              (~"Thursday", 4_i32),
              (~"Friday", 5_i32),
              (~"Saturday", 6_i32)
          ]) {
458 459
            Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
            None => Err(~"Invalid day")
460
          },
461
          'a' => match match_strs(s, pos, [
B
Brian Anderson 已提交
462 463 464 465 466 467 468 469
              (~"Sun", 0_i32),
              (~"Mon", 1_i32),
              (~"Tue", 2_i32),
              (~"Wed", 3_i32),
              (~"Thu", 4_i32),
              (~"Fri", 5_i32),
              (~"Sat", 6_i32)
          ]) {
470 471
            Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
            None => Err(~"Invalid day")
472
          },
473
          'B' => match match_strs(s, pos, [
B
Brian Anderson 已提交
474 475 476 477 478 479 480 481 482 483 484 485 486
              (~"January", 0_i32),
              (~"February", 1_i32),
              (~"March", 2_i32),
              (~"April", 3_i32),
              (~"May", 4_i32),
              (~"June", 5_i32),
              (~"July", 6_i32),
              (~"August", 7_i32),
              (~"September", 8_i32),
              (~"October", 9_i32),
              (~"November", 10_i32),
              (~"December", 11_i32)
          ]) {
487 488
            Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) }
            None => Err(~"Invalid month")
489
          },
490
          'b' | 'h' => match match_strs(s, pos, [
B
Brian Anderson 已提交
491 492 493 494 495 496 497 498 499 500 501 502 503
              (~"Jan", 0_i32),
              (~"Feb", 1_i32),
              (~"Mar", 2_i32),
              (~"Apr", 3_i32),
              (~"May", 4_i32),
              (~"Jun", 5_i32),
              (~"Jul", 6_i32),
              (~"Aug", 7_i32),
              (~"Sep", 8_i32),
              (~"Oct", 9_i32),
              (~"Nov", 10_i32),
              (~"Dec", 11_i32)
          ]) {
504 505
            Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) }
            None => Err(~"Invalid month")
506
          },
A
Ashok Gautham 已提交
507 508
          'C' => match match_digits_in_range(s, pos, 2u, false, 0_i32,
                                             99_i32) {
B
Brian Anderson 已提交
509
            Some(item) => {
510
                let (v, pos) = item;
B
Brian Anderson 已提交
511
                  tm.tm_year += (v * 100_i32) - 1900_i32;
512
                  Ok(pos)
513
              }
514
            None => Err(~"Invalid year")
515
          },
B
Brian Anderson 已提交
516
          'c' => {
517
            parse_type(s, pos, 'a', &mut *tm)
518 519 520 521 522 523 524 525
                .and_then(|pos| parse_char(s, pos, ' '))
                .and_then(|pos| parse_type(s, pos, 'b', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ' '))
                .and_then(|pos| parse_type(s, pos, 'e', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ' '))
                .and_then(|pos| parse_type(s, pos, 'T', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ' '))
                .and_then(|pos| parse_type(s, pos, 'Y', &mut *tm))
526
          }
B
Brian Anderson 已提交
527
          'D' | 'x' => {
528
            parse_type(s, pos, 'm', &mut *tm)
529 530 531 532
                .and_then(|pos| parse_char(s, pos, '/'))
                .and_then(|pos| parse_type(s, pos, 'd', &mut *tm))
                .and_then(|pos| parse_char(s, pos, '/'))
                .and_then(|pos| parse_type(s, pos, 'y', &mut *tm))
533
          }
A
Ashok Gautham 已提交
534 535
          'd' => match match_digits_in_range(s, pos, 2u, false, 1_i32,
                                             31_i32) {
536 537
            Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
            None => Err(~"Invalid day of the month")
538
          },
A
Ashok Gautham 已提交
539 540
          'e' => match match_digits_in_range(s, pos, 2u, true, 1_i32,
                                             31_i32) {
541 542
            Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
            None => Err(~"Invalid day of the month")
543
          },
544 545 546 547 548
          'f' => {
            let (val, pos) = match_fractional_seconds(s, pos);
            tm.tm_nsec = val;
            Ok(pos)
          }
B
Brian Anderson 已提交
549
          'F' => {
550
            parse_type(s, pos, 'Y', &mut *tm)
551 552 553 554
                .and_then(|pos| parse_char(s, pos, '-'))
                .and_then(|pos| parse_type(s, pos, 'm', &mut *tm))
                .and_then(|pos| parse_char(s, pos, '-'))
                .and_then(|pos| parse_type(s, pos, 'd', &mut *tm))
555
          }
B
Brian Anderson 已提交
556
          'H' => {
557
            match match_digits_in_range(s, pos, 2u, false, 0_i32, 23_i32) {
558 559
              Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) }
              None => Err(~"Invalid hour")
560 561
            }
          }
B
Brian Anderson 已提交
562
          'I' => {
563
            match match_digits_in_range(s, pos, 2u, false, 1_i32, 12_i32) {
B
Brian Anderson 已提交
564
              Some(item) => {
565 566
                  let (v, pos) = item;
                  tm.tm_hour = if v == 12_i32 { 0_i32 } else { v };
567
                  Ok(pos)
568
              }
569
              None => Err(~"Invalid hour")
570 571
            }
          }
B
Brian Anderson 已提交
572
          'j' => {
573
            match match_digits_in_range(s, pos, 3u, false, 1_i32, 366_i32) {
B
Brian Anderson 已提交
574
              Some(item) => {
575 576
                let (v, pos) = item;
                tm.tm_yday = v - 1_i32;
577
                Ok(pos)
578
              }
579
              None => Err(~"Invalid day of year")
580 581
            }
          }
B
Brian Anderson 已提交
582
          'k' => {
583
            match match_digits_in_range(s, pos, 2u, true, 0_i32, 23_i32) {
584 585
              Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) }
              None => Err(~"Invalid hour")
586 587
            }
          }
B
Brian Anderson 已提交
588
          'l' => {
589
            match match_digits_in_range(s, pos, 2u, true, 1_i32, 12_i32) {
B
Brian Anderson 已提交
590
              Some(item) => {
591 592
                  let (v, pos) = item;
                  tm.tm_hour = if v == 12_i32 { 0_i32 } else { v };
593
                  Ok(pos)
594
              }
595
              None => Err(~"Invalid hour")
596 597
            }
          }
B
Brian Anderson 已提交
598
          'M' => {
599
            match match_digits_in_range(s, pos, 2u, false, 0_i32, 59_i32) {
600 601
              Some(item) => { let (v, pos) = item; tm.tm_min = v; Ok(pos) }
              None => Err(~"Invalid minute")
602 603
            }
          }
B
Brian Anderson 已提交
604
          'm' => {
605
            match match_digits_in_range(s, pos, 2u, false, 1_i32, 12_i32) {
B
Brian Anderson 已提交
606
              Some(item) => {
607 608
                let (v, pos) = item;
                tm.tm_mon = v - 1_i32;
609
                Ok(pos)
610
              }
611
              None => Err(~"Invalid month")
612 613
            }
          }
B
Brian Anderson 已提交
614
          'n' => parse_char(s, pos, '\n'),
615
          'P' => match match_strs(s, pos,
616
                                  [(~"am", 0_i32), (~"pm", 12_i32)]) {
617

618 619
            Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) }
            None => Err(~"Invalid hour")
620
          },
621
          'p' => match match_strs(s, pos,
622
                                  [(~"AM", 0_i32), (~"PM", 12_i32)]) {
623

624 625
            Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) }
            None => Err(~"Invalid hour")
626
          },
B
Brian Anderson 已提交
627
          'R' => {
628
            parse_type(s, pos, 'H', &mut *tm)
629 630
                .and_then(|pos| parse_char(s, pos, ':'))
                .and_then(|pos| parse_type(s, pos, 'M', &mut *tm))
631
          }
B
Brian Anderson 已提交
632
          'r' => {
633
            parse_type(s, pos, 'I', &mut *tm)
634 635 636 637 638 639
                .and_then(|pos| parse_char(s, pos, ':'))
                .and_then(|pos| parse_type(s, pos, 'M', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ':'))
                .and_then(|pos| parse_type(s, pos, 'S', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ' '))
                .and_then(|pos| parse_type(s, pos, 'p', &mut *tm))
640
          }
B
Brian Anderson 已提交
641
          'S' => {
642
            match match_digits_in_range(s, pos, 2u, false, 0_i32, 60_i32) {
B
Brian Anderson 已提交
643
              Some(item) => {
644 645
                let (v, pos) = item;
                tm.tm_sec = v;
646
                Ok(pos)
647
              }
648
              None => Err(~"Invalid second")
649 650 651
            }
          }
          //'s' {}
B
Brian Anderson 已提交
652
          'T' | 'X' => {
653
            parse_type(s, pos, 'H', &mut *tm)
654 655 656 657
                .and_then(|pos| parse_char(s, pos, ':'))
                .and_then(|pos| parse_type(s, pos, 'M', &mut *tm))
                .and_then(|pos| parse_char(s, pos, ':'))
                .and_then(|pos| parse_type(s, pos, 'S', &mut *tm))
658
          }
B
Brian Anderson 已提交
659 660
          't' => parse_char(s, pos, '\t'),
          'u' => {
661
            match match_digits_in_range(s, pos, 1u, false, 1_i32, 7_i32) {
B
Brian Anderson 已提交
662
              Some(item) => {
663
                let (v, pos) = item;
664
                tm.tm_wday = if v == 7 { 0 } else { v };
665
                Ok(pos)
666
              }
667
              None => Err(~"Invalid day of week")
668 669
            }
          }
B
Brian Anderson 已提交
670
          'v' => {
671
            parse_type(s, pos, 'e', &mut *tm)
672 673 674 675
                .and_then(|pos|  parse_char(s, pos, '-'))
                .and_then(|pos| parse_type(s, pos, 'b', &mut *tm))
                .and_then(|pos| parse_char(s, pos, '-'))
                .and_then(|pos| parse_type(s, pos, 'Y', &mut *tm))
676 677
          }
          //'W' {}
B
Brian Anderson 已提交
678
          'w' => {
679
            match match_digits_in_range(s, pos, 1u, false, 0_i32, 6_i32) {
680
              Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
681
              None => Err(~"Invalid day of week")
682 683
            }
          }
B
Brian Anderson 已提交
684
          'Y' => {
685
            match match_digits(s, pos, 4u, false) {
B
Brian Anderson 已提交
686
              Some(item) => {
687 688
                let (v, pos) = item;
                tm.tm_year = v - 1900_i32;
689
                Ok(pos)
690
              }
691
              None => Err(~"Invalid year")
692 693
            }
          }
B
Brian Anderson 已提交
694
          'y' => {
695
            match match_digits_in_range(s, pos, 2u, false, 0_i32, 99_i32) {
B
Brian Anderson 已提交
696
              Some(item) => {
697
                let (v, pos) = item;
698
                tm.tm_year = v;
699
                Ok(pos)
700
              }
701
              None => Err(~"Invalid year")
702 703
            }
          }
B
Brian Anderson 已提交
704
          'Z' => {
705
            if match_str(s, pos, "UTC") || match_str(s, pos, "GMT") {
706
                tm.tm_gmtoff = 0_i32;
707
                tm.tm_zone = ~"UTC";
708
                Ok(pos + 3u)
709 710 711 712
            } else {
                // It's odd, but to maintain compatibility with c's
                // strptime we ignore the timezone.
                let mut pos = pos;
713
                let len = s.len();
714
                while pos < len {
715
                    let range = s.char_range_at(pos);
716 717
                    pos = range.next;
                    if range.ch == ' ' { break; }
718 719
                }

720
                Ok(pos)
721 722
            }
          }
B
Brian Anderson 已提交
723
          'z' => {
724
            let range = s.char_range_at(pos);
725

726 727
            if range.ch == '+' || range.ch == '-' {
                match match_digits(s, range.next, 4u, false) {
B
Brian Anderson 已提交
728
                  Some(item) => {
729 730 731
                    let (v, pos) = item;
                    if v == 0_i32 {
                        tm.tm_gmtoff = 0_i32;
732
                        tm.tm_zone = ~"UTC";
733 734
                    }

735
                    Ok(pos)
736
                  }
737
                  None => Err(~"Invalid zone offset")
738 739
                }
            } else {
740
                Err(~"Invalid zone offset")
741 742
            }
          }
B
Brian Anderson 已提交
743 744
          '%' => parse_char(s, pos, '%'),
          ch => {
A
Alex Crichton 已提交
745
            Err(format!("unknown formatting type: {}", str::from_char(ch)))
746 747 748 749
          }
        }
    }

A
Alex Crichton 已提交
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
    let mut rdr = BufReader::new(format.as_bytes());
    let mut tm = Tm {
        tm_sec: 0_i32,
        tm_min: 0_i32,
        tm_hour: 0_i32,
        tm_mday: 0_i32,
        tm_mon: 0_i32,
        tm_year: 0_i32,
        tm_wday: 0_i32,
        tm_yday: 0_i32,
        tm_isdst: 0_i32,
        tm_gmtoff: 0_i32,
        tm_zone: ~"",
        tm_nsec: 0_i32,
    };
    let mut pos = 0u;
    let len = s.len();
    let mut result = Err(~"Invalid time");
768

A
Alex Crichton 已提交
769 770 771 772 773 774 775
    while pos < len {
        let range = s.char_range_at(pos);
        let ch = range.ch;
        let next = range.next;

        let mut buf = [0];
        let c = match rdr.read(buf) {
A
Alex Crichton 已提交
776 777
            Ok(..) => buf[0] as char,
            Err(..) => break
A
Alex Crichton 已提交
778 779 780 781
        };
        match c {
            '%' => {
                let ch = match rdr.read(buf) {
A
Alex Crichton 已提交
782 783
                    Ok(..) => buf[0] as char,
                    Err(..) => break
A
Alex Crichton 已提交
784 785 786 787
                };
                match parse_type(s, pos, ch, &mut tm) {
                    Ok(next) => pos = next,
                    Err(e) => { result = Err(e); break; }
788
                }
A
Alex Crichton 已提交
789 790 791 792
            },
            c => {
                if c != ch { break }
                pos = next;
793 794 795
            }
        }
    }
A
Alex Crichton 已提交
796

A
Alex Crichton 已提交
797
    if pos == len && rdr.tell().unwrap() == format.len() as u64 {
A
Alex Crichton 已提交
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
        Ok(Tm {
            tm_sec: tm.tm_sec,
            tm_min: tm.tm_min,
            tm_hour: tm.tm_hour,
            tm_mday: tm.tm_mday,
            tm_mon: tm.tm_mon,
            tm_year: tm.tm_year,
            tm_wday: tm.tm_wday,
            tm_yday: tm.tm_yday,
            tm_isdst: tm.tm_isdst,
            tm_gmtoff: tm.tm_gmtoff,
            tm_zone: tm.tm_zone.clone(),
            tm_nsec: tm.tm_nsec,
        })
    } else { result }
813 814
}

815 816
/// Formats the time according to the format string.
pub fn strftime(format: &str, tm: &Tm) -> ~str {
817
    fn days_in_year(year: int) -> i32 {
H
Huon Wilson 已提交
818
        if (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) {
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
            366    /* Days in a leap year */
        } else {
            365    /* Days in a non-leap year */
        }
    }

    fn iso_week_days(yday: i32, wday: i32) -> int {
        /* The number of days from the first day of the first ISO week of this
        * year to the year day YDAY with week day WDAY.
        * ISO weeks start on Monday. The first ISO week has the year's first
        * Thursday.
        * YDAY may be as small as yday_minimum.
        */
        let yday: int = yday as int;
        let wday: int = wday as int;
        let iso_week_start_wday: int = 1;                     /* Monday */
        let iso_week1_wday: int = 4;                          /* Thursday */
        let yday_minimum: int = 366;
        /* Add enough to the first operand of % to make it nonnegative. */
        let big_enough_multiple_of_7: int = (yday_minimum / 7 + 2) * 7;

        yday - (yday - wday + iso_week1_wday + big_enough_multiple_of_7) % 7
            + iso_week1_wday - iso_week_start_wday
    }

    fn iso_week(ch:char, tm: &Tm) -> ~str {
        let mut year: int = tm.tm_year as int + 1900;
        let mut days: int = iso_week_days (tm.tm_yday, tm.tm_wday);

H
Huon Wilson 已提交
848
        if days < 0 {
849 850 851 852 853 854
            /* This ISO week belongs to the previous year. */
            year -= 1;
            days = iso_week_days (tm.tm_yday + (days_in_year(year)), tm.tm_wday);
        } else {
            let d: int = iso_week_days (tm.tm_yday - (days_in_year(year)),
                                        tm.tm_wday);
H
Huon Wilson 已提交
855
            if 0 <= d {
856 857 858 859 860 861 862 863 864 865 866 867 868 869
                /* This ISO week belongs to the next year. */
                year += 1;
                days = d;
            }
        }

        match ch {
            'G' => format!("{}", year),
            'g' => format!("{:02d}", (year % 100 + 100) % 100),
            'V' => format!("{:02d}", days / 7 + 1),
            _ => ~""
        }
    }

B
Brian Anderson 已提交
870
    fn parse_type(ch: char, tm: &Tm) -> ~str {
A
Alex Crichton 已提交
871
      let die = || format!("strftime: can't understand this format {} ", ch);
T
Tim Chevalier 已提交
872 873
        match ch {
          'A' => match tm.tm_wday as int {
B
Brian Anderson 已提交
874 875 876 877 878 879
            0 => ~"Sunday",
            1 => ~"Monday",
            2 => ~"Tuesday",
            3 => ~"Wednesday",
            4 => ~"Thursday",
            5 => ~"Friday",
T
Tim Chevalier 已提交
880 881
            6 => ~"Saturday",
            _ => die()
882
          },
T
Tim Chevalier 已提交
883
         'a' => match tm.tm_wday as int {
B
Brian Anderson 已提交
884 885 886 887 888 889
            0 => ~"Sun",
            1 => ~"Mon",
            2 => ~"Tue",
            3 => ~"Wed",
            4 => ~"Thu",
            5 => ~"Fri",
T
Tim Chevalier 已提交
890 891
            6 => ~"Sat",
            _ => die()
892
          },
T
Tim Chevalier 已提交
893
          'B' => match tm.tm_mon as int {
B
Brian Anderson 已提交
894 895 896 897 898 899 900 901 902 903 904
            0 => ~"January",
            1 => ~"February",
            2 => ~"March",
            3 => ~"April",
            4 => ~"May",
            5 => ~"June",
            6 => ~"July",
            7 => ~"August",
            8 => ~"September",
            9 => ~"October",
            10 => ~"November",
T
Tim Chevalier 已提交
905 906
            11 => ~"December",
            _ => die()
907
          },
T
Tim Chevalier 已提交
908
          'b' | 'h' => match tm.tm_mon as int {
B
Brian Anderson 已提交
909 910 911 912 913 914 915 916 917 918 919 920
            0 => ~"Jan",
            1 => ~"Feb",
            2 => ~"Mar",
            3 => ~"Apr",
            4 => ~"May",
            5 => ~"Jun",
            6 => ~"Jul",
            7 => ~"Aug",
            8 => ~"Sep",
            9 => ~"Oct",
            10 => ~"Nov",
            11 => ~"Dec",
T
Tim Chevalier 已提交
921
            _  => die()
922
          },
A
Alex Crichton 已提交
923
          'C' => format!("{:02d}", (tm.tm_year as int + 1900) / 100),
B
Brian Anderson 已提交
924
          'c' => {
A
Alex Crichton 已提交
925
            format!("{} {} {} {} {}",
926 927 928 929
                parse_type('a', tm),
                parse_type('b', tm),
                parse_type('e', tm),
                parse_type('T', tm),
P
Paul Stansifer 已提交
930
                parse_type('Y', tm))
931
          }
B
Brian Anderson 已提交
932
          'D' | 'x' => {
A
Alex Crichton 已提交
933
            format!("{}/{}/{}",
934 935
                parse_type('m', tm),
                parse_type('d', tm),
P
Paul Stansifer 已提交
936
                parse_type('y', tm))
937
          }
A
Alex Crichton 已提交
938 939 940
          'd' => format!("{:02d}", tm.tm_mday),
          'e' => format!("{:2d}", tm.tm_mday),
          'f' => format!("{:09d}", tm.tm_nsec),
B
Brian Anderson 已提交
941
          'F' => {
A
Alex Crichton 已提交
942
            format!("{}-{}-{}",
943 944
                parse_type('Y', tm),
                parse_type('m', tm),
P
Paul Stansifer 已提交
945
                parse_type('d', tm))
946
          }
947 948
          'G' => iso_week('G', tm),
          'g' => iso_week('g', tm),
A
Alex Crichton 已提交
949
          'H' => format!("{:02d}", tm.tm_hour),
B
Brian Anderson 已提交
950
          'I' => {
A
Alex Crichton 已提交
951
            let mut h = tm.tm_hour;
952 953
            if h == 0 { h = 12 }
            if h > 12 { h -= 12 }
A
Alex Crichton 已提交
954
            format!("{:02d}", h)
955
          }
A
Alex Crichton 已提交
956 957
          'j' => format!("{:03d}", tm.tm_yday + 1),
          'k' => format!("{:2d}", tm.tm_hour),
B
Brian Anderson 已提交
958
          'l' => {
A
Alex Crichton 已提交
959
            let mut h = tm.tm_hour;
960 961
            if h == 0 { h = 12 }
            if h > 12 { h -= 12 }
A
Alex Crichton 已提交
962
            format!("{:2d}", h)
963
          }
A
Alex Crichton 已提交
964 965
          'M' => format!("{:02d}", tm.tm_min),
          'm' => format!("{:02d}", tm.tm_mon + 1),
B
Brian Anderson 已提交
966
          'n' => ~"\n",
967 968
          'P' => if (tm.tm_hour as int) < 12 { ~"am" } else { ~"pm" },
          'p' => if (tm.tm_hour as int) < 12 { ~"AM" } else { ~"PM" },
B
Brian Anderson 已提交
969
          'R' => {
A
Alex Crichton 已提交
970
            format!("{}:{}",
971
                parse_type('H', tm),
P
Paul Stansifer 已提交
972
                parse_type('M', tm))
973
          }
B
Brian Anderson 已提交
974
          'r' => {
A
Alex Crichton 已提交
975
            format!("{}:{}:{} {}",
976 977 978
                parse_type('I', tm),
                parse_type('M', tm),
                parse_type('S', tm),
P
Paul Stansifer 已提交
979
                parse_type('p', tm))
980
          }
A
Alex Crichton 已提交
981 982
          'S' => format!("{:02d}", tm.tm_sec),
          's' => format!("{}", tm.to_timespec().sec),
B
Brian Anderson 已提交
983
          'T' | 'X' => {
A
Alex Crichton 已提交
984
            format!("{}:{}:{}",
985 986
                parse_type('H', tm),
                parse_type('M', tm),
P
Paul Stansifer 已提交
987
                parse_type('S', tm))
988
          }
B
Brian Anderson 已提交
989
          't' => ~"\t",
990
          'U' => format!("{:02d}", (tm.tm_yday - tm.tm_wday + 7) / 7),
B
Brian Anderson 已提交
991
          'u' => {
992
            let i = tm.tm_wday as int;
993
            (if i == 0 { 7 } else { i }).to_str()
994
          }
995
          'V' => iso_week('V', tm),
B
Brian Anderson 已提交
996
          'v' => {
A
Alex Crichton 已提交
997
            format!("{}-{}-{}",
998 999
                parse_type('e', tm),
                parse_type('b', tm),
P
Paul Stansifer 已提交
1000
                parse_type('Y', tm))
1001
          }
1002 1003
          'W' => format!("{:02d}", (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7)
                         / 7),
1004 1005
          'w' => (tm.tm_wday as int).to_str(),
          'Y' => (tm.tm_year as int + 1900).to_str(),
A
Alex Crichton 已提交
1006
          'y' => format!("{:02d}", (tm.tm_year as int + 1900) % 100),
1007
          'Z' => tm.tm_zone.clone(),
B
Brian Anderson 已提交
1008
          'z' => {
1009
            let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' };
1010
            let mut m = num::abs(tm.tm_gmtoff) / 60_i32;
1011 1012
            let h = m / 60_i32;
            m -= h * 60_i32;
A
Alex Crichton 已提交
1013
            format!("{}{:02d}{:02d}", sign, h, m)
1014
          }
1015
          '+' => tm.rfc3339(),
T
Tim Chevalier 已提交
1016 1017
          '%' => ~"%",
          _   => die()
1018 1019 1020
        }
    }

A
Alex Crichton 已提交
1021
    let mut buf = ~[];
1022

A
Alex Crichton 已提交
1023 1024 1025 1026
    let mut rdr = BufReader::new(format.as_bytes());
    loop {
        let mut b = [0];
        let ch = match rdr.read(b) {
A
Alex Crichton 已提交
1027 1028
            Ok(..) => b[0],
            Err(..) => break,
A
Alex Crichton 已提交
1029 1030 1031
        };
        match ch as char {
            '%' => {
A
Alex Crichton 已提交
1032
                rdr.read(b).unwrap();
A
Alex Crichton 已提交
1033 1034
                let s = parse_type(b[0] as char, tm);
                buf.push_all(s.as_bytes());
1035
            }
A
Alex Crichton 已提交
1036
            ch => buf.push(ch as u8)
1037 1038 1039
        }
    }

1040
    str::from_utf8_owned(buf).unwrap()
1041 1042
}

C
Chris Peterson 已提交
1043 1044
#[cfg(test)]
mod tests {
A
Arcterus 已提交
1045 1046
    use super::{Timespec, get_time, precise_time_ns, precise_time_s, tzset,
                at_utc, at, strptime};
1047

D
Daniel Micay 已提交
1048
    use std::f64;
1049
    use std::result::{Err, Ok};
K
klutzy 已提交
1050 1051 1052

    #[cfg(windows)]
    fn set_time_zone() {
1053
        use std::libc;
K
klutzy 已提交
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
        // Windows crt doesn't see any environment variable set by
        // `SetEnvironmentVariable`, which `os::setenv` internally uses.
        // It is why we use `putenv` here.
        extern {
            fn _putenv(envstring: *libc::c_char) -> libc::c_int;
        }

        unsafe {
            // Windows does not understand "America/Los_Angeles".
            // PST+08 may look wrong, but not! "PST" indicates
            // the name of timezone. "+08" means UTC = local + 08.
P
Patrick Walton 已提交
1065
            "TZ=PST+08".with_c_str(|env| {
K
klutzy 已提交
1066
                _putenv(env);
P
Patrick Walton 已提交
1067
            })
K
klutzy 已提交
1068 1069 1070 1071 1072 1073 1074 1075 1076
        }
        tzset();
    }
    #[cfg(not(windows))]
    fn set_time_zone() {
        use std::os;
        os::setenv("TZ", "America/Los_Angeles");
        tzset();
    }
1077

1078
    fn test_get_time() {
1079 1080
        static SOME_RECENT_DATE: i64 = 1325376000i64; // 2012-01-01T00:00:00Z
        static SOME_FUTURE_DATE: i64 = 1577836800i64; // 2020-01-01T00:00:00Z
C
Chris Peterson 已提交
1081 1082

        let tv1 = get_time();
1083
        debug!("tv1={:?} sec + {:?} nsec", tv1.sec as uint, tv1.nsec as uint);
C
Chris Peterson 已提交
1084

1085
        assert!(tv1.sec > SOME_RECENT_DATE);
P
Patrick Walton 已提交
1086
        assert!(tv1.nsec < 1000000000i32);
C
Chris Peterson 已提交
1087 1088

        let tv2 = get_time();
1089
        debug!("tv2={:?} sec + {:?} nsec", tv2.sec as uint, tv2.nsec as uint);
C
Chris Peterson 已提交
1090

P
Patrick Walton 已提交
1091
        assert!(tv2.sec >= tv1.sec);
1092
        assert!(tv2.sec < SOME_FUTURE_DATE);
P
Patrick Walton 已提交
1093
        assert!(tv2.nsec < 1000000000i32);
C
Chris Peterson 已提交
1094
        if tv2.sec == tv1.sec {
P
Patrick Walton 已提交
1095
            assert!(tv2.nsec >= tv1.nsec);
C
Chris Peterson 已提交
1096 1097 1098
        }
    }

1099
    fn test_precise_time() {
C
Chris Peterson 已提交
1100
        let s0 = precise_time_s();
1101
        debug!("s0={} sec", f64::to_str_digits(s0, 9u));
P
Patrick Walton 已提交
1102
        assert!(s0 > 0.);
C
Chris Peterson 已提交
1103

A
Alex Crichton 已提交
1104 1105 1106 1107
        let ns0 = precise_time_ns();
        let ns1 = precise_time_ns();
        debug!("ns0={:?} ns", ns0);
        debug!("ns1={:?} ns", ns1);
P
Patrick Walton 已提交
1108
        assert!(ns1 >= ns0);
C
Chris Peterson 已提交
1109 1110

        let ns2 = precise_time_ns();
A
Alex Crichton 已提交
1111
        debug!("ns2={:?} ns", ns2);
P
Patrick Walton 已提交
1112
        assert!(ns2 >= ns1);
C
Chris Peterson 已提交
1113
    }
1114

1115
    fn test_at_utc() {
K
klutzy 已提交
1116
        set_time_zone();
1117

1118
        let time = Timespec::new(1234567890, 54321);
1119 1120
        let utc = at_utc(time);

K
klutzy 已提交
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
        assert_eq!(utc.tm_sec, 30_i32);
        assert_eq!(utc.tm_min, 31_i32);
        assert_eq!(utc.tm_hour, 23_i32);
        assert_eq!(utc.tm_mday, 13_i32);
        assert_eq!(utc.tm_mon, 1_i32);
        assert_eq!(utc.tm_year, 109_i32);
        assert_eq!(utc.tm_wday, 5_i32);
        assert_eq!(utc.tm_yday, 43_i32);
        assert_eq!(utc.tm_isdst, 0_i32);
        assert_eq!(utc.tm_gmtoff, 0_i32);
        assert_eq!(utc.tm_zone, ~"UTC");
        assert_eq!(utc.tm_nsec, 54321_i32);
1133 1134
    }

1135
    fn test_at() {
K
klutzy 已提交
1136
        set_time_zone();
1137

1138
        let time = Timespec::new(1234567890, 54321);
1139 1140
        let local = at(time);

1141
        debug!("time_at: {:?}", local);
E
Erick Tryzelaar 已提交
1142

K
klutzy 已提交
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
        assert_eq!(local.tm_sec, 30_i32);
        assert_eq!(local.tm_min, 31_i32);
        assert_eq!(local.tm_hour, 15_i32);
        assert_eq!(local.tm_mday, 13_i32);
        assert_eq!(local.tm_mon, 1_i32);
        assert_eq!(local.tm_year, 109_i32);
        assert_eq!(local.tm_wday, 5_i32);
        assert_eq!(local.tm_yday, 43_i32);
        assert_eq!(local.tm_isdst, 0_i32);
        assert_eq!(local.tm_gmtoff, -28800_i32);
1153

1154
        // FIXME (#2350): We should probably standardize on the timezone
1155
        // abbreviation.
1156
        let zone = &local.tm_zone;
P
Patrick Walton 已提交
1157
        assert!(*zone == ~"PST" || *zone == ~"Pacific Standard Time");
1158

K
klutzy 已提交
1159
        assert_eq!(local.tm_nsec, 54321_i32);
1160 1161
    }

1162
    fn test_to_timespec() {
K
klutzy 已提交
1163
        set_time_zone();
1164

1165
        let time = Timespec::new(1234567890, 54321);
1166 1167
        let utc = at_utc(time);

1168 1169
        assert_eq!(utc.to_timespec(), time);
        assert_eq!(utc.to_local().to_timespec(), time);
1170 1171
    }

1172
    fn test_conversions() {
K
klutzy 已提交
1173
        set_time_zone();
1174

1175
        let time = Timespec::new(1234567890, 54321);
1176 1177 1178
        let utc = at_utc(time);
        let local = at(time);

P
Patrick Walton 已提交
1179 1180 1181 1182 1183 1184
        assert!(local.to_local() == local);
        assert!(local.to_utc() == utc);
        assert!(local.to_utc().to_local() == local);
        assert!(utc.to_utc() == utc);
        assert!(utc.to_local() == local);
        assert!(utc.to_local().to_utc() == utc);
1185
    }
1186

1187
    fn test_strptime() {
K
klutzy 已提交
1188
        set_time_zone();
1189

E
Erick Tryzelaar 已提交
1190
        match strptime("", "") {
B
Brian Anderson 已提交
1191
          Ok(ref tm) => {
P
Patrick Walton 已提交
1192 1193 1194 1195 1196 1197 1198
            assert!(tm.tm_sec == 0_i32);
            assert!(tm.tm_min == 0_i32);
            assert!(tm.tm_hour == 0_i32);
            assert!(tm.tm_mday == 0_i32);
            assert!(tm.tm_mon == 0_i32);
            assert!(tm.tm_year == 0_i32);
            assert!(tm.tm_wday == 0_i32);
1199
            assert!(tm.tm_isdst == 0_i32);
P
Patrick Walton 已提交
1200 1201 1202
            assert!(tm.tm_gmtoff == 0_i32);
            assert!(tm.tm_zone == ~"");
            assert!(tm.tm_nsec == 0_i32);
1203
          }
1204
          Err(_) => ()
1205 1206
        }

1207
        let format = "%a %b %e %T.%f %Y";
E
Erick Tryzelaar 已提交
1208 1209
        assert_eq!(strptime("", format), Err(~"Invalid time"));
        assert!(strptime("Fri Feb 13 15:31:30", format)
1210
            == Err(~"Invalid time"));
1211

1212
        match strptime("Fri Feb 13 15:31:30.01234 2009", format) {
1213
          Err(e) => fail!(e),
B
Brian Anderson 已提交
1214
          Ok(ref tm) => {
P
Patrick Walton 已提交
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
            assert!(tm.tm_sec == 30_i32);
            assert!(tm.tm_min == 31_i32);
            assert!(tm.tm_hour == 15_i32);
            assert!(tm.tm_mday == 13_i32);
            assert!(tm.tm_mon == 1_i32);
            assert!(tm.tm_year == 109_i32);
            assert!(tm.tm_wday == 5_i32);
            assert!(tm.tm_yday == 0_i32);
            assert!(tm.tm_isdst == 0_i32);
            assert!(tm.tm_gmtoff == 0_i32);
            assert!(tm.tm_zone == ~"");
1226
            assert!(tm.tm_nsec == 12340000_i32);
1227 1228 1229
          }
        }

1230
        fn test(s: &str, format: &str) -> bool {
1231
            match strptime(s, format) {
1232
              Ok(ref tm) => tm.strftime(format) == s.to_owned(),
1233
              Err(e) => fail!(e)
1234 1235 1236
            }
        }

1237
        let days = [
1238 1239 1240 1241 1242 1243 1244
            ~"Sunday",
            ~"Monday",
            ~"Tuesday",
            ~"Wednesday",
            ~"Thursday",
            ~"Friday",
            ~"Saturday"
1245
        ];
D
Daniel Micay 已提交
1246
        for day in days.iter() {
E
Erick Tryzelaar 已提交
1247
            assert!(test(*day, "%A"));
1248
        }
1249

1250
        let days = [
1251 1252 1253 1254 1255 1256 1257
            ~"Sun",
            ~"Mon",
            ~"Tue",
            ~"Wed",
            ~"Thu",
            ~"Fri",
            ~"Sat"
1258
        ];
D
Daniel Micay 已提交
1259
        for day in days.iter() {
E
Erick Tryzelaar 已提交
1260
            assert!(test(*day, "%a"));
1261
        }
1262

1263
        let months = [
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
            ~"January",
            ~"February",
            ~"March",
            ~"April",
            ~"May",
            ~"June",
            ~"July",
            ~"August",
            ~"September",
            ~"October",
            ~"November",
            ~"December"
1276
        ];
D
Daniel Micay 已提交
1277
        for day in months.iter() {
E
Erick Tryzelaar 已提交
1278
            assert!(test(*day, "%B"));
1279
        }
1280

1281
        let months = [
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
            ~"Jan",
            ~"Feb",
            ~"Mar",
            ~"Apr",
            ~"May",
            ~"Jun",
            ~"Jul",
            ~"Aug",
            ~"Sep",
            ~"Oct",
            ~"Nov",
            ~"Dec"
1294
        ];
D
Daniel Micay 已提交
1295
        for day in months.iter() {
E
Erick Tryzelaar 已提交
1296
            assert!(test(*day, "%b"));
1297
        }
1298

E
Erick Tryzelaar 已提交
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
        assert!(test("19", "%C"));
        assert!(test("Fri Feb 13 23:31:30 2009", "%c"));
        assert!(test("02/13/09", "%D"));
        assert!(test("03", "%d"));
        assert!(test("13", "%d"));
        assert!(test(" 3", "%e"));
        assert!(test("13", "%e"));
        assert!(test("2009-02-13", "%F"));
        assert!(test("03", "%H"));
        assert!(test("13", "%H"));
        assert!(test("03", "%I")); // FIXME (#2350): flesh out
        assert!(test("11", "%I")); // FIXME (#2350): flesh out
        assert!(test("044", "%j"));
        assert!(test(" 3", "%k"));
        assert!(test("13", "%k"));
        assert!(test(" 1", "%l"));
        assert!(test("11", "%l"));
        assert!(test("03", "%M"));
        assert!(test("13", "%M"));
        assert!(test("\n", "%n"));
        assert!(test("am", "%P"));
        assert!(test("pm", "%P"));
        assert!(test("AM", "%p"));
        assert!(test("PM", "%p"));
        assert!(test("23:31", "%R"));
        assert!(test("11:31:30 AM", "%r"));
        assert!(test("11:31:30 PM", "%r"));
        assert!(test("03", "%S"));
        assert!(test("13", "%S"));
        assert!(test("15:31:30", "%T"));
        assert!(test("\t", "%t"));
        assert!(test("1", "%u"));
        assert!(test("7", "%u"));
        assert!(test("13-Feb-2009", "%v"));
        assert!(test("0", "%w"));
        assert!(test("6", "%w"));
        assert!(test("2009", "%Y"));
        assert!(test("09", "%y"));
E
Erick Tryzelaar 已提交
1337
        assert!(strptime("UTC", "%Z").unwrap().tm_zone ==
P
Patrick Walton 已提交
1338
            ~"UTC");
E
Erick Tryzelaar 已提交
1339
        assert!(strptime("PST", "%Z").unwrap().tm_zone ==
P
Patrick Walton 已提交
1340
            ~"");
E
Erick Tryzelaar 已提交
1341
        assert!(strptime("-0000", "%z").unwrap().tm_gmtoff ==
P
Patrick Walton 已提交
1342
            0);
E
Erick Tryzelaar 已提交
1343
        assert!(strptime("-0800", "%z").unwrap().tm_gmtoff ==
P
Patrick Walton 已提交
1344
            0);
E
Erick Tryzelaar 已提交
1345
        assert!(test("%", "%%"));
H
Huon Wilson 已提交
1346 1347 1348

        // Test for #7256
        assert_eq!(strptime("360", "%Y-%m-%d"), Err(~"Invalid year"))
1349 1350
    }

1351
    fn test_ctime() {
K
klutzy 已提交
1352
        set_time_zone();
1353

1354
        let time = Timespec::new(1234567890, 54321);
1355 1356 1357
        let utc   = at_utc(time);
        let local = at(time);

1358
        debug!("test_ctime: {:?} {:?}", utc.ctime(), local.ctime());
E
Erick Tryzelaar 已提交
1359

1360 1361
        assert_eq!(utc.ctime(), ~"Fri Feb 13 23:31:30 2009");
        assert_eq!(local.ctime(), ~"Fri Feb 13 15:31:30 2009");
1362 1363
    }

1364
    fn test_strftime() {
K
klutzy 已提交
1365
        set_time_zone();
1366

1367
        let time = Timespec::new(1234567890, 54321);
1368 1369 1370
        let utc = at_utc(time);
        let local = at(time);

E
Erick Tryzelaar 已提交
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
        assert_eq!(local.strftime(""), ~"");
        assert_eq!(local.strftime("%A"), ~"Friday");
        assert_eq!(local.strftime("%a"), ~"Fri");
        assert_eq!(local.strftime("%B"), ~"February");
        assert_eq!(local.strftime("%b"), ~"Feb");
        assert_eq!(local.strftime("%C"), ~"20");
        assert_eq!(local.strftime("%c"), ~"Fri Feb 13 15:31:30 2009");
        assert_eq!(local.strftime("%D"), ~"02/13/09");
        assert_eq!(local.strftime("%d"), ~"13");
        assert_eq!(local.strftime("%e"), ~"13");
1381
        assert_eq!(local.strftime("%f"), ~"000054321");
E
Erick Tryzelaar 已提交
1382
        assert_eq!(local.strftime("%F"), ~"2009-02-13");
1383 1384
        assert_eq!(local.strftime("%G"), ~"2009");
        assert_eq!(local.strftime("%g"), ~"09");
E
Erick Tryzelaar 已提交
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
        assert_eq!(local.strftime("%H"), ~"15");
        assert_eq!(local.strftime("%I"), ~"03");
        assert_eq!(local.strftime("%j"), ~"044");
        assert_eq!(local.strftime("%k"), ~"15");
        assert_eq!(local.strftime("%l"), ~" 3");
        assert_eq!(local.strftime("%M"), ~"31");
        assert_eq!(local.strftime("%m"), ~"02");
        assert_eq!(local.strftime("%n"), ~"\n");
        assert_eq!(local.strftime("%P"), ~"pm");
        assert_eq!(local.strftime("%p"), ~"PM");
        assert_eq!(local.strftime("%R"), ~"15:31");
        assert_eq!(local.strftime("%r"), ~"03:31:30 PM");
        assert_eq!(local.strftime("%S"), ~"30");
        assert_eq!(local.strftime("%s"), ~"1234567890");
        assert_eq!(local.strftime("%T"), ~"15:31:30");
        assert_eq!(local.strftime("%t"), ~"\t");
1401
        assert_eq!(local.strftime("%U"), ~"06");
E
Erick Tryzelaar 已提交
1402
        assert_eq!(local.strftime("%u"), ~"5");
1403
        assert_eq!(local.strftime("%V"), ~"07");
E
Erick Tryzelaar 已提交
1404
        assert_eq!(local.strftime("%v"), ~"13-Feb-2009");
1405
        assert_eq!(local.strftime("%W"), ~"06");
E
Erick Tryzelaar 已提交
1406
        assert_eq!(local.strftime("%w"), ~"5");
1407 1408
        assert_eq!(local.strftime("%X"), ~"15:31:30"); // FIXME (#2350): support locale
        assert_eq!(local.strftime("%x"), ~"02/13/09"); // FIXME (#2350): support locale
E
Erick Tryzelaar 已提交
1409 1410
        assert_eq!(local.strftime("%Y"), ~"2009");
        assert_eq!(local.strftime("%y"), ~"09");
1411
        assert_eq!(local.strftime("%+"), ~"2009-02-13T15:31:30-08:00");
1412

1413
        // FIXME (#2350): We should probably standardize on the timezone
1414
        // abbreviation.
E
Erick Tryzelaar 已提交
1415
        let zone = local.strftime("%Z");
P
Patrick Walton 已提交
1416
        assert!(zone == ~"PST" || zone == ~"Pacific Standard Time");
1417

E
Erick Tryzelaar 已提交
1418 1419
        assert_eq!(local.strftime("%z"), ~"-0800");
        assert_eq!(local.strftime("%%"), ~"%");
1420

1421
        // FIXME (#2350): We should probably standardize on the timezone
1422 1423
        // abbreviation.
        let rfc822 = local.rfc822();
1424
        let prefix = ~"Fri, 13 Feb 2009 15:31:30 ";
K
klutzy 已提交
1425
        assert!(rfc822 == prefix + "PST" || rfc822 == prefix + "Pacific Standard Time");
1426

1427 1428 1429
        assert_eq!(local.ctime(), ~"Fri Feb 13 15:31:30 2009");
        assert_eq!(local.rfc822z(), ~"Fri, 13 Feb 2009 15:31:30 -0800");
        assert_eq!(local.rfc3339(), ~"2009-02-13T15:31:30-08:00");
1430

1431 1432 1433 1434
        assert_eq!(utc.ctime(), ~"Fri Feb 13 23:31:30 2009");
        assert_eq!(utc.rfc822(), ~"Fri, 13 Feb 2009 23:31:30 GMT");
        assert_eq!(utc.rfc822z(), ~"Fri, 13 Feb 2009 23:31:30 -0000");
        assert_eq!(utc.rfc3339(), ~"2009-02-13T23:31:30Z");
1435
    }
1436

1437
    fn test_timespec_eq_ord() {
1438 1439 1440 1441 1442 1443
        let a = &Timespec::new(-2, 1);
        let b = &Timespec::new(-1, 2);
        let c = &Timespec::new(1, 2);
        let d = &Timespec::new(2, 1);
        let e = &Timespec::new(2, 1);

1444 1445
        assert!(d.eq(e));
        assert!(c.ne(e));
1446

1447 1448 1449
        assert!(a.lt(b));
        assert!(b.lt(c));
        assert!(c.lt(d));
1450

1451 1452 1453 1454 1455
        assert!(a.le(b));
        assert!(b.le(c));
        assert!(c.le(d));
        assert!(d.le(e));
        assert!(e.le(d));
1456

1457 1458 1459 1460 1461
        assert!(b.ge(a));
        assert!(c.ge(b));
        assert!(d.ge(c));
        assert!(e.ge(d));
        assert!(d.ge(e));
1462

1463 1464 1465
        assert!(b.gt(a));
        assert!(c.gt(b));
        assert!(d.gt(c));
1466
    }
1467 1468

    #[test]
1469
    #[ignore(cfg(target_os = "android"))] // FIXME #10958
1470
    fn run_tests() {
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
        // The tests race on tzset. So instead of having many independent
        // tests, we will just call the functions now.
        test_get_time();
        test_precise_time();
        test_at_utc();
        test_at();
        test_to_timespec();
        test_conversions();
        test_strptime();
        test_ctime();
        test_strftime();
        test_timespec_eq_ord();
    }
C
Chris Peterson 已提交
1484
}