]> git.lizzy.rs Git - rust.git/blob - src/libstd/time.rs
Make borrowck's notion of scopes consistent with trans's notion of scopes
[rust.git] / src / libstd / time.rs
1 // Copyright 2012 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 #[forbid(deprecated_mode)];
12
13 use core::cmp::{Eq, Ord};
14 use core::int;
15 use core::libc::{c_char, c_int, c_long, size_t, time_t};
16 use core::i32;
17 use core::io::{Reader, ReaderUtil};
18 use core::io;
19 use core::prelude::*;
20 use core::result::{Result, Ok, Err};
21 use core::str;
22
23 const NSEC_PER_SEC: i32 = 1_000_000_000_i32;
24
25 #[abi = "cdecl"]
26 extern mod rustrt {
27     #[legacy_exports]
28     unsafe fn get_time(sec: &mut i64, nsec: &mut i32);
29
30     unsafe fn precise_time_ns(ns: &mut u64);
31
32     unsafe fn rust_tzset();
33     // FIXME: The i64 values can be passed by-val when #2064 is fixed.
34     unsafe fn rust_gmtime(&&sec: i64, &&nsec: i32, &&result: Tm);
35     unsafe fn rust_localtime(&&sec: i64, &&nsec: i32, &&result: Tm);
36     unsafe fn rust_timegm(&&tm: Tm, sec: &mut i64);
37     unsafe fn rust_mktime(&&tm: Tm, sec: &mut i64);
38 }
39
40 /// A record specifying a time value in seconds and nanoseconds.
41 #[auto_encode]
42 #[auto_decode]
43 pub struct Timespec { sec: i64, nsec: i32 }
44
45 /*
46  * Timespec assumes that pre-epoch Timespecs have negative sec and positive
47  * nsec fields. Darwin's and Linux's struct timespec functions handle pre-
48  * epoch timestamps using a "two steps back, one step forward" representation,
49  * though the man pages do not actually document this. For example, the time
50  * -1.2 seconds before the epoch is represented by `Timespec { sec: -2_i64,
51  * nsec: 800_000_000_i32 }`.
52  */
53 impl Timespec {
54     static pure fn new(sec: i64, nsec: i32) -> Timespec {
55         assert nsec >= 0 && nsec < NSEC_PER_SEC;
56         Timespec { sec: sec, nsec: nsec }
57     }
58 }
59
60 impl Timespec : Eq {
61     pure fn eq(&self, other: &Timespec) -> bool {
62         self.sec == other.sec && self.nsec == other.nsec
63     }
64     pure fn ne(&self, other: &Timespec) -> bool { !self.eq(other) }
65 }
66
67 impl Timespec : Ord {
68     pure fn lt(&self, other: &Timespec) -> bool {
69         self.sec < other.sec ||
70             (self.sec == other.sec && self.nsec < other.nsec)
71     }
72     pure fn le(&self, other: &Timespec) -> bool { !other.lt(self) }
73     pure fn ge(&self, other: &Timespec) -> bool { !self.lt(other) }
74     pure fn gt(&self, other: &Timespec) -> bool { !self.le(other) }
75 }
76
77 /**
78  * Returns the current time as a `timespec` containing the seconds and
79  * nanoseconds since 1970-01-01T00:00:00Z.
80  */
81 pub fn get_time() -> Timespec {
82     unsafe {
83         let mut sec = 0i64;
84         let mut nsec = 0i32;
85         rustrt::get_time(&mut sec, &mut nsec);
86         return Timespec::new(sec, nsec);
87     }
88 }
89
90
91 /**
92  * Returns the current value of a high-resolution performance counter
93  * in nanoseconds since an unspecified epoch.
94  */
95 pub fn precise_time_ns() -> u64 {
96     unsafe {
97         let mut ns = 0u64;
98         rustrt::precise_time_ns(&mut ns);
99         ns
100     }
101 }
102
103
104 /**
105  * Returns the current value of a high-resolution performance counter
106  * in seconds since an unspecified epoch.
107  */
108 pub fn precise_time_s() -> float {
109     return (precise_time_ns() as float) / 1000000000.;
110 }
111
112 pub fn tzset() {
113     unsafe {
114         rustrt::rust_tzset();
115     }
116 }
117
118 #[auto_encode]
119 #[auto_decode]
120 pub struct Tm {
121     tm_sec: i32, // seconds after the minute ~[0-60]
122     tm_min: i32, // minutes after the hour ~[0-59]
123     tm_hour: i32, // hours after midnight ~[0-23]
124     tm_mday: i32, // days of the month ~[1-31]
125     tm_mon: i32, // months since January ~[0-11]
126     tm_year: i32, // years since 1900
127     tm_wday: i32, // days since Sunday ~[0-6]
128     tm_yday: i32, // days since January 1 ~[0-365]
129     tm_isdst: i32, // Daylight Savings Time flag
130     tm_gmtoff: i32, // offset from UTC in seconds
131     tm_zone: ~str, // timezone abbreviation
132     tm_nsec: i32, // nanoseconds
133 }
134
135 impl Tm : Eq {
136     pure fn eq(&self, other: &Tm) -> bool {
137         self.tm_sec == (*other).tm_sec &&
138         self.tm_min == (*other).tm_min &&
139         self.tm_hour == (*other).tm_hour &&
140         self.tm_mday == (*other).tm_mday &&
141         self.tm_mon == (*other).tm_mon &&
142         self.tm_year == (*other).tm_year &&
143         self.tm_wday == (*other).tm_wday &&
144         self.tm_yday == (*other).tm_yday &&
145         self.tm_isdst == (*other).tm_isdst &&
146         self.tm_gmtoff == (*other).tm_gmtoff &&
147         self.tm_zone == (*other).tm_zone &&
148         self.tm_nsec == (*other).tm_nsec
149     }
150     pure fn ne(&self, other: &Tm) -> bool { !self.eq(other) }
151 }
152
153 pub pure fn empty_tm() -> Tm {
154     Tm {
155         tm_sec: 0_i32,
156         tm_min: 0_i32,
157         tm_hour: 0_i32,
158         tm_mday: 0_i32,
159         tm_mon: 0_i32,
160         tm_year: 0_i32,
161         tm_wday: 0_i32,
162         tm_yday: 0_i32,
163         tm_isdst: 0_i32,
164         tm_gmtoff: 0_i32,
165         tm_zone: ~"",
166         tm_nsec: 0_i32,
167     }
168 }
169
170 /// Returns the specified time in UTC
171 pub fn at_utc(clock: Timespec) -> Tm {
172     unsafe {
173         let mut Timespec { sec, nsec } = clock;
174         let mut tm = empty_tm();
175         rustrt::rust_gmtime(sec, nsec, tm);
176         move tm
177     }
178 }
179
180 /// Returns the current time in UTC
181 pub fn now_utc() -> Tm {
182     at_utc(get_time())
183 }
184
185 /// Returns the specified time in the local timezone
186 pub fn at(clock: Timespec) -> Tm {
187     unsafe {
188         let mut Timespec { sec, nsec } = clock;
189         let mut tm = empty_tm();
190         rustrt::rust_localtime(sec, nsec, tm);
191         move tm
192     }
193 }
194
195 /// Returns the current time in the local timezone
196 pub fn now() -> Tm {
197     at(get_time())
198 }
199
200 /// Parses the time from the string according to the format string.
201 pub pure fn strptime(s: &str, format: &str) -> Result<Tm, ~str> {
202     // unsafe only because do_strptime is annoying to make pure
203     // (it does IO with a str_reader)
204     unsafe {do_strptime(s, format)}
205 }
206
207 /// Formats the time according to the format string.
208 pub pure fn strftime(format: &str, tm: &Tm) -> ~str {
209     // unsafe only because do_strftime is annoying to make pure
210     // (it does IO with a str_reader)
211     move unsafe { do_strftime(format, tm) }
212 }
213
214 impl Tm {
215     /// Convert time to the seconds from January 1, 1970
216     fn to_timespec() -> Timespec {
217         unsafe {
218             let mut sec = 0i64;
219             if self.tm_gmtoff == 0_i32 {
220                 rustrt::rust_timegm(self, &mut sec);
221             } else {
222                 rustrt::rust_mktime(self, &mut sec);
223             }
224             Timespec::new(sec, self.tm_nsec)
225         }
226     }
227
228     /// Convert time to the local timezone
229     fn to_local() -> Tm {
230         at(self.to_timespec())
231     }
232
233     /// Convert time to the UTC
234     fn to_utc() -> Tm {
235         at_utc(self.to_timespec())
236     }
237
238     /**
239      * Return a string of the current time in the form
240      * "Thu Jan  1 00:00:00 1970".
241      */
242     pure fn ctime() -> ~str { self.strftime(~"%c") }
243
244     /// Formats the time according to the format string.
245     pure fn strftime(&self, format: &str) -> ~str {
246         move strftime(format, self)
247     }
248
249     /**
250      * Returns a time string formatted according to RFC 822.
251      *
252      * local: "Thu, 22 Mar 2012 07:53:18 PST"
253      * utc:   "Thu, 22 Mar 2012 14:53:18 UTC"
254      */
255     pure fn rfc822() -> ~str {
256         if self.tm_gmtoff == 0_i32 {
257             self.strftime(~"%a, %d %b %Y %T GMT")
258         } else {
259             self.strftime(~"%a, %d %b %Y %T %Z")
260         }
261     }
262
263     /**
264      * Returns a time string formatted according to RFC 822 with Zulu time.
265      *
266      * local: "Thu, 22 Mar 2012 07:53:18 -0700"
267      * utc:   "Thu, 22 Mar 2012 14:53:18 -0000"
268      */
269     pure fn rfc822z() -> ~str {
270         self.strftime(~"%a, %d %b %Y %T %z")
271     }
272
273     /**
274      * Returns a time string formatted according to ISO 8601.
275      *
276      * local: "2012-02-22T07:53:18-07:00"
277      * utc:   "2012-02-22T14:53:18Z"
278      */
279     pure fn rfc3339() -> ~str {
280         if self.tm_gmtoff == 0_i32 {
281             self.strftime(~"%Y-%m-%dT%H:%M:%SZ")
282         } else {
283             let s = self.strftime(~"%Y-%m-%dT%H:%M:%S");
284             let sign = if self.tm_gmtoff > 0_i32 { '+' } else { '-' };
285             let mut m = i32::abs(self.tm_gmtoff) / 60_i32;
286             let h = m / 60_i32;
287             m -= h * 60_i32;
288             s + fmt!("%c%02d:%02d", sign, h as int, m as int)
289         }
290     }
291 }
292
293 priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
294     fn match_str(s: &str, pos: uint, needle: &str) -> bool {
295         let mut i = pos;
296         for str::each(needle) |ch| {
297             if s[i] != ch {
298                 return false;
299             }
300             i += 1u;
301         }
302         return true;
303     }
304
305     fn match_strs(ss: &str, pos: uint, strs: &[(~str, i32)])
306       -> Option<(i32, uint)> {
307         let mut i = 0u;
308         let len = strs.len();
309         while i < len {
310             let &(needle, value) = &strs[i];
311
312             if match_str(ss, pos, needle) {
313                 return Some((value, pos + str::len(needle)));
314             }
315             i += 1u;
316         }
317
318         None
319     }
320
321     fn match_digits(ss: &str, pos: uint, digits: uint, ws: bool)
322       -> Option<(i32, uint)> {
323         let mut pos = pos;
324         let mut value = 0_i32;
325
326         let mut i = 0u;
327         while i < digits {
328             let range = str::char_range_at(str::from_slice(ss), pos);
329             pos = range.next;
330
331             match range.ch {
332               '0' .. '9' => {
333                 value = value * 10_i32 + (range.ch as i32 - '0' as i32);
334               }
335               ' ' if ws => (),
336               _ => return None
337             }
338             i += 1u;
339         }
340
341         Some((value, pos))
342     }
343
344     fn parse_char(s: &str, pos: uint, c: char) -> Result<uint, ~str> {
345         let range = str::char_range_at(s, pos);
346
347         if c == range.ch {
348             Ok(range.next)
349         } else {
350             Err(fmt!("Expected %?, found %?",
351                 str::from_char(c),
352                 str::from_char(range.ch)))
353         }
354     }
355
356     fn parse_type(s: &str, pos: uint, ch: char, tm: &mut Tm)
357       -> Result<uint, ~str> {
358         match ch {
359           'A' => match match_strs(s, pos, ~[
360               (~"Sunday", 0_i32),
361               (~"Monday", 1_i32),
362               (~"Tuesday", 2_i32),
363               (~"Wednesday", 3_i32),
364               (~"Thursday", 4_i32),
365               (~"Friday", 5_i32),
366               (~"Saturday", 6_i32)
367           ]) {
368             Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
369             None => Err(~"Invalid day")
370           },
371           'a' => match match_strs(s, pos, ~[
372               (~"Sun", 0_i32),
373               (~"Mon", 1_i32),
374               (~"Tue", 2_i32),
375               (~"Wed", 3_i32),
376               (~"Thu", 4_i32),
377               (~"Fri", 5_i32),
378               (~"Sat", 6_i32)
379           ]) {
380             Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
381             None => Err(~"Invalid day")
382           },
383           'B' => match match_strs(s, pos, ~[
384               (~"January", 0_i32),
385               (~"February", 1_i32),
386               (~"March", 2_i32),
387               (~"April", 3_i32),
388               (~"May", 4_i32),
389               (~"June", 5_i32),
390               (~"July", 6_i32),
391               (~"August", 7_i32),
392               (~"September", 8_i32),
393               (~"October", 9_i32),
394               (~"November", 10_i32),
395               (~"December", 11_i32)
396           ]) {
397             Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) }
398             None => Err(~"Invalid month")
399           },
400           'b' | 'h' => match match_strs(s, pos, ~[
401               (~"Jan", 0_i32),
402               (~"Feb", 1_i32),
403               (~"Mar", 2_i32),
404               (~"Apr", 3_i32),
405               (~"May", 4_i32),
406               (~"Jun", 5_i32),
407               (~"Jul", 6_i32),
408               (~"Aug", 7_i32),
409               (~"Sep", 8_i32),
410               (~"Oct", 9_i32),
411               (~"Nov", 10_i32),
412               (~"Dec", 11_i32)
413           ]) {
414             Some(item) => { let (v, pos) = item; tm.tm_mon = v; Ok(pos) }
415             None => Err(~"Invalid month")
416           },
417           'C' => match match_digits(s, pos, 2u, false) {
418             Some(item) => {
419                 let (v, pos) = item;
420                   tm.tm_year += (v * 100_i32) - 1900_i32;
421                   Ok(pos)
422               }
423             None => Err(~"Invalid year")
424           },
425           'c' => {
426             parse_type(s, pos, 'a', &mut *tm)
427                 .chain(|pos| parse_char(s, pos, ' '))
428                 .chain(|pos| parse_type(s, pos, 'b', &mut *tm))
429                 .chain(|pos| parse_char(s, pos, ' '))
430                 .chain(|pos| parse_type(s, pos, 'e', &mut *tm))
431                 .chain(|pos| parse_char(s, pos, ' '))
432                 .chain(|pos| parse_type(s, pos, 'T', &mut *tm))
433                 .chain(|pos| parse_char(s, pos, ' '))
434                 .chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
435           }
436           'D' | 'x' => {
437             parse_type(s, pos, 'm', &mut *tm)
438                 .chain(|pos| parse_char(s, pos, '/'))
439                 .chain(|pos| parse_type(s, pos, 'd', &mut *tm))
440                 .chain(|pos| parse_char(s, pos, '/'))
441                 .chain(|pos| parse_type(s, pos, 'y', &mut *tm))
442           }
443           'd' => match match_digits(s, pos, 2u, false) {
444             Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
445             None => Err(~"Invalid day of the month")
446           },
447           'e' => match match_digits(s, pos, 2u, true) {
448             Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
449             None => Err(~"Invalid day of the month")
450           },
451           'F' => {
452             parse_type(s, pos, 'Y', &mut *tm)
453                 .chain(|pos| parse_char(s, pos, '-'))
454                 .chain(|pos| parse_type(s, pos, 'm', &mut *tm))
455                 .chain(|pos| parse_char(s, pos, '-'))
456                 .chain(|pos| parse_type(s, pos, 'd', &mut *tm))
457           }
458           'H' => {
459             // FIXME (#2350): range check.
460             match match_digits(s, pos, 2u, false) {
461               Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) }
462               None => Err(~"Invalid hour")
463             }
464           }
465           'I' => {
466             // FIXME (#2350): range check.
467             match match_digits(s, pos, 2u, false) {
468               Some(item) => {
469                   let (v, pos) = item;
470                   tm.tm_hour = if v == 12_i32 { 0_i32 } else { v };
471                   Ok(pos)
472               }
473               None => Err(~"Invalid hour")
474             }
475           }
476           'j' => {
477             // FIXME (#2350): range check.
478             match match_digits(s, pos, 3u, false) {
479               Some(item) => {
480                 let (v, pos) = item;
481                 tm.tm_yday = v - 1_i32;
482                 Ok(pos)
483               }
484               None => Err(~"Invalid year")
485             }
486           }
487           'k' => {
488             // FIXME (#2350): range check.
489             match match_digits(s, pos, 2u, true) {
490               Some(item) => { let (v, pos) = item; tm.tm_hour = v; Ok(pos) }
491               None => Err(~"Invalid hour")
492             }
493           }
494           'l' => {
495             // FIXME (#2350): range check.
496             match match_digits(s, pos, 2u, true) {
497               Some(item) => {
498                   let (v, pos) = item;
499                   tm.tm_hour = if v == 12_i32 { 0_i32 } else { v };
500                   Ok(pos)
501               }
502               None => Err(~"Invalid hour")
503             }
504           }
505           'M' => {
506             // FIXME (#2350): range check.
507             match match_digits(s, pos, 2u, false) {
508               Some(item) => { let (v, pos) = item; tm.tm_min = v; Ok(pos) }
509               None => Err(~"Invalid minute")
510             }
511           }
512           'm' => {
513             // FIXME (#2350): range check.
514             match match_digits(s, pos, 2u, false) {
515               Some(item) => {
516                 let (v, pos) = item;
517                 tm.tm_mon = v - 1_i32;
518                 Ok(pos)
519               }
520               None => Err(~"Invalid month")
521             }
522           }
523           'n' => parse_char(s, pos, '\n'),
524           'P' => match match_strs(s, pos,
525                                   ~[(~"am", 0_i32), (~"pm", 12_i32)]) {
526
527             Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) }
528             None => Err(~"Invalid hour")
529           },
530           'p' => match match_strs(s, pos,
531                                   ~[(~"AM", 0_i32), (~"PM", 12_i32)]) {
532
533             Some(item) => { let (v, pos) = item; tm.tm_hour += v; Ok(pos) }
534             None => Err(~"Invalid hour")
535           },
536           'R' => {
537             parse_type(s, pos, 'H', &mut *tm)
538                 .chain(|pos| parse_char(s, pos, ':'))
539                 .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
540           }
541           'r' => {
542             parse_type(s, pos, 'I', &mut *tm)
543                 .chain(|pos| parse_char(s, pos, ':'))
544                 .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
545                 .chain(|pos| parse_char(s, pos, ':'))
546                 .chain(|pos| parse_type(s, pos, 'S', &mut *tm))
547                 .chain(|pos| parse_char(s, pos, ' '))
548                 .chain(|pos| parse_type(s, pos, 'p', &mut *tm))
549           }
550           'S' => {
551             // FIXME (#2350): range check.
552             match match_digits(s, pos, 2u, false) {
553               Some(item) => {
554                 let (v, pos) = item;
555                 tm.tm_sec = v;
556                 Ok(pos)
557               }
558               None => Err(~"Invalid second")
559             }
560           }
561           //'s' {}
562           'T' | 'X' => {
563             parse_type(s, pos, 'H', &mut *tm)
564                 .chain(|pos| parse_char(s, pos, ':'))
565                 .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
566                 .chain(|pos| parse_char(s, pos, ':'))
567                 .chain(|pos| parse_type(s, pos, 'S', &mut *tm))
568           }
569           't' => parse_char(s, pos, '\t'),
570           'u' => {
571             // FIXME (#2350): range check.
572             match match_digits(s, pos, 1u, false) {
573               Some(item) => {
574                 let (v, pos) = item;
575                 tm.tm_wday = v;
576                 Ok(pos)
577               }
578               None => Err(~"Invalid weekday")
579             }
580           }
581           'v' => {
582             parse_type(s, pos, 'e', &mut *tm)
583                 .chain(|pos|  parse_char(s, pos, '-'))
584                 .chain(|pos| parse_type(s, pos, 'b', &mut *tm))
585                 .chain(|pos| parse_char(s, pos, '-'))
586                 .chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
587           }
588           //'W' {}
589           'w' => {
590             // FIXME (#2350): range check.
591             match match_digits(s, pos, 1u, false) {
592               Some(item) => { let (v, pos) = item; tm.tm_wday = v; Ok(pos) }
593               None => Err(~"Invalid weekday")
594             }
595           }
596           //'X' {}
597           //'x' {}
598           'Y' => {
599             // FIXME (#2350): range check.
600             match match_digits(s, pos, 4u, false) {
601               Some(item) => {
602                 let (v, pos) = item;
603                 tm.tm_year = v - 1900_i32;
604                 Ok(pos)
605               }
606               None => Err(~"Invalid weekday")
607             }
608           }
609           'y' => {
610             // FIXME (#2350): range check.
611             match match_digits(s, pos, 2u, false) {
612               Some(item) => {
613                 let (v, pos) = item;
614                 tm.tm_year = v - 1900_i32;
615                 Ok(pos)
616               }
617               None => Err(~"Invalid weekday")
618             }
619           }
620           'Z' => {
621             if match_str(s, pos, ~"UTC") || match_str(s, pos, ~"GMT") {
622                 tm.tm_gmtoff = 0_i32;
623                 tm.tm_zone = ~"UTC";
624                 Ok(pos + 3u)
625             } else {
626                 // It's odd, but to maintain compatibility with c's
627                 // strptime we ignore the timezone.
628                 let mut pos = pos;
629                 let len = str::len(s);
630                 while pos < len {
631                     let range = str::char_range_at(s, pos);
632                     pos = range.next;
633                     if range.ch == ' ' { break; }
634                 }
635
636                 Ok(pos)
637             }
638           }
639           'z' => {
640             let range = str::char_range_at(s, pos);
641
642             if range.ch == '+' || range.ch == '-' {
643                 match match_digits(s, range.next, 4u, false) {
644                   Some(item) => {
645                     let (v, pos) = item;
646                     if v == 0_i32 {
647                         tm.tm_gmtoff = 0_i32;
648                         tm.tm_zone = ~"UTC";
649                     }
650
651                     Ok(pos)
652                   }
653                   None => Err(~"Invalid zone offset")
654                 }
655             } else {
656                 Err(~"Invalid zone offset")
657             }
658           }
659           '%' => parse_char(s, pos, '%'),
660           ch => {
661             Err(fmt!("unknown formatting type: %?", str::from_char(ch)))
662           }
663         }
664     }
665
666     do io::with_str_reader(str::from_slice(format)) |rdr| {
667         let mut tm = Tm {
668             tm_sec: 0_i32,
669             tm_min: 0_i32,
670             tm_hour: 0_i32,
671             tm_mday: 0_i32,
672             tm_mon: 0_i32,
673             tm_year: 0_i32,
674             tm_wday: 0_i32,
675             tm_yday: 0_i32,
676             tm_isdst: 0_i32,
677             tm_gmtoff: 0_i32,
678             tm_zone: ~"",
679             tm_nsec: 0_i32,
680         };
681         let mut pos = 0u;
682         let len = str::len(s);
683         let mut result = Err(~"Invalid time");
684
685         while !rdr.eof() && pos < len {
686             let range = str::char_range_at(s, pos);
687             let ch = range.ch;
688             let next = range.next;
689
690             match rdr.read_char() {
691                 '%' => {
692                     match parse_type(s, pos, rdr.read_char(), &mut tm) {
693                         Ok(next) => pos = next,
694                         Err(move e) => { result = Err(move e); break; }
695                     }
696                 },
697                 c => {
698                     if c != ch { break }
699                     pos = next;
700                 }
701             }
702         }
703
704         if pos == len && rdr.eof() {
705             Ok(Tm {
706                 tm_sec: tm.tm_sec,
707                 tm_min: tm.tm_min,
708                 tm_hour: tm.tm_hour,
709                 tm_mday: tm.tm_mday,
710                 tm_mon: tm.tm_mon,
711                 tm_year: tm.tm_year,
712                 tm_wday: tm.tm_wday,
713                 tm_yday: tm.tm_yday,
714                 tm_isdst: tm.tm_isdst,
715                 tm_gmtoff: tm.tm_gmtoff,
716                 tm_zone: copy tm.tm_zone,
717                 tm_nsec: tm.tm_nsec,
718             })
719         } else { move result }
720     }
721 }
722
723 priv fn do_strftime(format: &str, tm: &Tm) -> ~str {
724     fn parse_type(ch: char, tm: &Tm) -> ~str {
725         //FIXME (#2350): Implement missing types.
726       let die = || fmt!("strftime: can't understand this format %c ", ch);
727         match ch {
728           'A' => match tm.tm_wday as int {
729             0 => ~"Sunday",
730             1 => ~"Monday",
731             2 => ~"Tuesday",
732             3 => ~"Wednesday",
733             4 => ~"Thursday",
734             5 => ~"Friday",
735             6 => ~"Saturday",
736             _ => die()
737           },
738          'a' => match tm.tm_wday as int {
739             0 => ~"Sun",
740             1 => ~"Mon",
741             2 => ~"Tue",
742             3 => ~"Wed",
743             4 => ~"Thu",
744             5 => ~"Fri",
745             6 => ~"Sat",
746             _ => die()
747           },
748           'B' => match tm.tm_mon as int {
749             0 => ~"January",
750             1 => ~"February",
751             2 => ~"March",
752             3 => ~"April",
753             4 => ~"May",
754             5 => ~"June",
755             6 => ~"July",
756             7 => ~"August",
757             8 => ~"September",
758             9 => ~"October",
759             10 => ~"November",
760             11 => ~"December",
761             _ => die()
762           },
763           'b' | 'h' => match tm.tm_mon as int {
764             0 => ~"Jan",
765             1 => ~"Feb",
766             2 => ~"Mar",
767             3 => ~"Apr",
768             4 => ~"May",
769             5 => ~"Jun",
770             6 => ~"Jul",
771             7 => ~"Aug",
772             8 => ~"Sep",
773             9 => ~"Oct",
774             10 => ~"Nov",
775             11 => ~"Dec",
776             _  => die()
777           },
778           'C' => fmt!("%02d", (tm.tm_year as int + 1900) / 100),
779           'c' => {
780             fmt!("%s %s %s %s %s",
781                 parse_type('a', tm),
782                 parse_type('b', tm),
783                 parse_type('e', tm),
784                 parse_type('T', tm),
785                 parse_type('Y', tm))
786           }
787           'D' | 'x' => {
788             fmt!("%s/%s/%s",
789                 parse_type('m', tm),
790                 parse_type('d', tm),
791                 parse_type('y', tm))
792           }
793           'd' => fmt!("%02d", tm.tm_mday as int),
794           'e' => fmt!("%2d", tm.tm_mday as int),
795           'F' => {
796             fmt!("%s-%s-%s",
797                 parse_type('Y', tm),
798                 parse_type('m', tm),
799                 parse_type('d', tm))
800           }
801           //'G' {}
802           //'g' {}
803           'H' => fmt!("%02d", tm.tm_hour as int),
804           'I' => {
805             let mut h = tm.tm_hour as int;
806             if h == 0 { h = 12 }
807             if h > 12 { h -= 12 }
808             fmt!("%02d", h)
809           }
810           'j' => fmt!("%03d", tm.tm_yday as int + 1),
811           'k' => fmt!("%2d", tm.tm_hour as int),
812           'l' => {
813             let mut h = tm.tm_hour as int;
814             if h == 0 { h = 12 }
815             if h > 12 { h -= 12 }
816             fmt!("%2d", h)
817           }
818           'M' => fmt!("%02d", tm.tm_min as int),
819           'm' => fmt!("%02d", tm.tm_mon as int + 1),
820           'n' => ~"\n",
821           'P' => if tm.tm_hour as int < 12 { ~"am" } else { ~"pm" },
822           'p' => if tm.tm_hour as int < 12 { ~"AM" } else { ~"PM" },
823           'R' => {
824             fmt!("%s:%s",
825                 parse_type('H', tm),
826                 parse_type('M', tm))
827           }
828           'r' => {
829             fmt!("%s:%s:%s %s",
830                 parse_type('I', tm),
831                 parse_type('M', tm),
832                 parse_type('S', tm),
833                 parse_type('p', tm))
834           }
835           'S' => fmt!("%02d", tm.tm_sec as int),
836           's' => fmt!("%d", tm.to_timespec().sec as int),
837           'T' | 'X' => {
838             fmt!("%s:%s:%s",
839                 parse_type('H', tm),
840                 parse_type('M', tm),
841                 parse_type('S', tm))
842           }
843           't' => ~"\t",
844           //'U' {}
845           'u' => {
846             let i = tm.tm_wday as int;
847             int::str(if i == 0 { 7 } else { i })
848           }
849           //'V' {}
850           'v' => {
851             fmt!("%s-%s-%s",
852                 parse_type('e', tm),
853                 parse_type('b', tm),
854                 parse_type('Y', tm))
855           }
856           //'W' {}
857           'w' => int::str(tm.tm_wday as int),
858           //'X' {}
859           //'x' {}
860           'Y' => int::str(tm.tm_year as int + 1900),
861           'y' => fmt!("%02d", (tm.tm_year as int + 1900) % 100),
862           'Z' => copy tm.tm_zone,
863           'z' => {
864             let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' };
865             let mut m = i32::abs(tm.tm_gmtoff) / 60_i32;
866             let h = m / 60_i32;
867             m -= h * 60_i32;
868             fmt!("%c%02d%02d", sign, h as int, m as int)
869           }
870           //'+' {}
871           '%' => ~"%",
872           _   => die()
873         }
874     }
875
876     let mut buf = ~"";
877
878     do io::with_str_reader(str::from_slice(format)) |rdr| {
879         while !rdr.eof() {
880             match rdr.read_char() {
881                 '%' => buf += parse_type(rdr.read_char(), tm),
882                 ch => str::push_char(&mut buf, ch)
883             }
884         }
885     }
886
887     move buf
888 }
889
890 #[cfg(test)]
891 mod tests {
892     #[legacy_exports];
893
894     use time::*;
895
896     use core::float;
897     use core::os;
898     use core::result;
899     use core::str;
900     use core::u64;
901     use core::uint;
902     use core::vec;
903
904     #[test]
905     fn test_get_time() {
906         const some_recent_date: i64 = 1325376000i64; // 2012-01-01T00:00:00Z
907         const some_future_date: i64 = 1577836800i64; // 2020-01-01T00:00:00Z
908
909         let tv1 = get_time();
910         log(debug, ~"tv1=" + uint::str(tv1.sec as uint) + ~" sec + "
911                    + uint::str(tv1.nsec as uint) + ~" nsec");
912
913         assert tv1.sec > some_recent_date;
914         assert tv1.nsec < 1000000000i32;
915
916         let tv2 = get_time();
917         log(debug, ~"tv2=" + uint::str(tv2.sec as uint) + ~" sec + "
918                    + uint::str(tv2.nsec as uint) + ~" nsec");
919
920         assert tv2.sec >= tv1.sec;
921         assert tv2.sec < some_future_date;
922         assert tv2.nsec < 1000000000i32;
923         if tv2.sec == tv1.sec {
924             assert tv2.nsec >= tv1.nsec;
925         }
926     }
927
928     #[test]
929     fn test_precise_time() {
930         let s0 = precise_time_s();
931         let ns1 = precise_time_ns();
932
933         log(debug, ~"s0=" + float::to_str(s0, 9u) + ~" sec");
934         assert s0 > 0.;
935         let ns0 = (s0 * 1000000000.) as u64;
936         log(debug, ~"ns0=" + u64::str(ns0) + ~" ns");
937
938         log(debug, ~"ns1=" + u64::str(ns1) + ~" ns");
939         assert ns1 >= ns0;
940
941         let ns2 = precise_time_ns();
942         log(debug, ~"ns2=" + u64::str(ns2) + ~" ns");
943         assert ns2 >= ns1;
944     }
945
946     #[test]
947     fn test_at_utc() {
948         os::setenv(~"TZ", ~"America/Los_Angeles");
949         tzset();
950
951         let time = ::time::Timespec::new(1234567890, 54321);
952         let utc = at_utc(time);
953
954         assert utc.tm_sec == 30_i32;
955         assert utc.tm_min == 31_i32;
956         assert utc.tm_hour == 23_i32;
957         assert utc.tm_mday == 13_i32;
958         assert utc.tm_mon == 1_i32;
959         assert utc.tm_year == 109_i32;
960         assert utc.tm_wday == 5_i32;
961         assert utc.tm_yday == 43_i32;
962         assert utc.tm_isdst == 0_i32;
963         assert utc.tm_gmtoff == 0_i32;
964         assert utc.tm_zone == ~"UTC";
965         assert utc.tm_nsec == 54321_i32;
966     }
967
968     #[test]
969     fn test_at() {
970         os::setenv(~"TZ", ~"America/Los_Angeles");
971         tzset();
972
973         let time = ::time::Timespec::new(1234567890, 54321);
974         let local = at(time);
975
976         error!("time_at: %?", local);
977
978         assert local.tm_sec == 30_i32;
979         assert local.tm_min == 31_i32;
980         assert local.tm_hour == 15_i32;
981         assert local.tm_mday == 13_i32;
982         assert local.tm_mon == 1_i32;
983         assert local.tm_year == 109_i32;
984         assert local.tm_wday == 5_i32;
985         assert local.tm_yday == 43_i32;
986         assert local.tm_isdst == 0_i32;
987         assert local.tm_gmtoff == -28800_i32;
988
989         // FIXME (#2350): We should probably standardize on the timezone
990         // abbreviation.
991         let zone = &local.tm_zone;
992         assert *zone == ~"PST" || *zone == ~"Pacific Standard Time";
993
994         assert local.tm_nsec == 54321_i32;
995     }
996
997     #[test]
998     fn test_to_timespec() {
999         os::setenv(~"TZ", ~"America/Los_Angeles");
1000         tzset();
1001
1002         let time = ::time::Timespec::new(1234567890, 54321);
1003         let utc = at_utc(time);
1004
1005         assert utc.to_timespec() == time;
1006         assert utc.to_local().to_timespec() == time;
1007     }
1008
1009     #[test]
1010     fn test_conversions() {
1011         os::setenv(~"TZ", ~"America/Los_Angeles");
1012         tzset();
1013
1014         let time = ::time::Timespec::new(1234567890, 54321);
1015         let utc = at_utc(time);
1016         let local = at(time);
1017
1018         assert local.to_local() == local;
1019         assert local.to_utc() == utc;
1020         assert local.to_utc().to_local() == local;
1021         assert utc.to_utc() == utc;
1022         assert utc.to_local() == local;
1023         assert utc.to_local().to_utc() == utc;
1024     }
1025
1026     #[test]
1027     fn test_strptime() {
1028         os::setenv(~"TZ", ~"America/Los_Angeles");
1029         tzset();
1030
1031         match strptime(~"", ~"") {
1032           Ok(ref tm) => {
1033             assert tm.tm_sec == 0_i32;
1034             assert tm.tm_min == 0_i32;
1035             assert tm.tm_hour == 0_i32;
1036             assert tm.tm_mday == 0_i32;
1037             assert tm.tm_mon == 0_i32;
1038             assert tm.tm_year == 0_i32;
1039             assert tm.tm_wday == 0_i32;
1040             assert tm.tm_isdst== 0_i32;
1041             assert tm.tm_gmtoff == 0_i32;
1042             assert tm.tm_zone == ~"";
1043             assert tm.tm_nsec == 0_i32;
1044           }
1045           Err(_) => ()
1046         }
1047
1048         let format = ~"%a %b %e %T %Y";
1049         assert strptime(~"", format) == Err(~"Invalid time");
1050         assert strptime(~"Fri Feb 13 15:31:30", format)
1051             == Err(~"Invalid time");
1052
1053         match strptime(~"Fri Feb 13 15:31:30 2009", format) {
1054           Err(copy e) => fail e,
1055           Ok(ref tm) => {
1056             assert tm.tm_sec == 30_i32;
1057             assert tm.tm_min == 31_i32;
1058             assert tm.tm_hour == 15_i32;
1059             assert tm.tm_mday == 13_i32;
1060             assert tm.tm_mon == 1_i32;
1061             assert tm.tm_year == 109_i32;
1062             assert tm.tm_wday == 5_i32;
1063             assert tm.tm_yday == 0_i32;
1064             assert tm.tm_isdst == 0_i32;
1065             assert tm.tm_gmtoff == 0_i32;
1066             assert tm.tm_zone == ~"";
1067             assert tm.tm_nsec == 0_i32;
1068           }
1069         }
1070
1071         fn test(s: &str, format: &str) -> bool {
1072             match strptime(s, format) {
1073               Ok(ref tm) => tm.strftime(format) == str::from_slice(s),
1074               Err(copy e) => fail e
1075             }
1076         }
1077
1078         for vec::each([
1079             ~"Sunday",
1080             ~"Monday",
1081             ~"Tuesday",
1082             ~"Wednesday",
1083             ~"Thursday",
1084             ~"Friday",
1085             ~"Saturday"
1086         ]) |day| {
1087             assert test(*day, ~"%A");
1088         }
1089
1090         for vec::each([
1091             ~"Sun",
1092             ~"Mon",
1093             ~"Tue",
1094             ~"Wed",
1095             ~"Thu",
1096             ~"Fri",
1097             ~"Sat"
1098         ]) |day| {
1099             assert test(*day, ~"%a");
1100         }
1101
1102         for vec::each([
1103             ~"January",
1104             ~"February",
1105             ~"March",
1106             ~"April",
1107             ~"May",
1108             ~"June",
1109             ~"July",
1110             ~"August",
1111             ~"September",
1112             ~"October",
1113             ~"November",
1114             ~"December"
1115         ]) |day| {
1116             assert test(*day, ~"%B");
1117         }
1118
1119         for vec::each([
1120             ~"Jan",
1121             ~"Feb",
1122             ~"Mar",
1123             ~"Apr",
1124             ~"May",
1125             ~"Jun",
1126             ~"Jul",
1127             ~"Aug",
1128             ~"Sep",
1129             ~"Oct",
1130             ~"Nov",
1131             ~"Dec"
1132         ]) |day| {
1133             assert test(*day, ~"%b");
1134         }
1135
1136         assert test(~"19", ~"%C");
1137         assert test(~"Fri Feb 13 23:31:30 2009", ~"%c");
1138         assert test(~"02/13/09", ~"%D");
1139         assert test(~"03", ~"%d");
1140         assert test(~"13", ~"%d");
1141         assert test(~" 3", ~"%e");
1142         assert test(~"13", ~"%e");
1143         assert test(~"2009-02-13", ~"%F");
1144         assert test(~"03", ~"%H");
1145         assert test(~"13", ~"%H");
1146         assert test(~"03", ~"%I"); // FIXME (#2350): flesh out
1147         assert test(~"11", ~"%I"); // FIXME (#2350): flesh out
1148         assert test(~"044", ~"%j");
1149         assert test(~" 3", ~"%k");
1150         assert test(~"13", ~"%k");
1151         assert test(~" 1", ~"%l");
1152         assert test(~"11", ~"%l");
1153         assert test(~"03", ~"%M");
1154         assert test(~"13", ~"%M");
1155         assert test(~"\n", ~"%n");
1156         assert test(~"am", ~"%P");
1157         assert test(~"pm", ~"%P");
1158         assert test(~"AM", ~"%p");
1159         assert test(~"PM", ~"%p");
1160         assert test(~"23:31", ~"%R");
1161         assert test(~"11:31:30 AM", ~"%r");
1162         assert test(~"11:31:30 PM", ~"%r");
1163         assert test(~"03", ~"%S");
1164         assert test(~"13", ~"%S");
1165         assert test(~"15:31:30", ~"%T");
1166         assert test(~"\t", ~"%t");
1167         assert test(~"1", ~"%u");
1168         assert test(~"7", ~"%u");
1169         assert test(~"13-Feb-2009", ~"%v");
1170         assert test(~"0", ~"%w");
1171         assert test(~"6", ~"%w");
1172         assert test(~"2009", ~"%Y");
1173         assert test(~"09", ~"%y");
1174         assert result::unwrap(strptime(~"UTC", ~"%Z")).tm_zone == ~"UTC";
1175         assert result::unwrap(strptime(~"PST", ~"%Z")).tm_zone == ~"";
1176         assert result::unwrap(strptime(~"-0000", ~"%z")).tm_gmtoff == 0;
1177         assert result::unwrap(strptime(~"-0800", ~"%z")).tm_gmtoff == 0;
1178         assert test(~"%", ~"%%");
1179     }
1180
1181     #[test]
1182     #[ignore(reason = "randomred")]
1183     fn test_ctime() {
1184         os::setenv(~"TZ", ~"America/Los_Angeles");
1185         tzset();
1186
1187         let time = ::time::Timespec::new(1234567890, 54321);
1188         let utc   = at_utc(time);
1189         let local = at(time);
1190
1191         error!("test_ctime: %? %?", utc.ctime(), local.ctime());
1192
1193         assert utc.ctime()   == ~"Fri Feb 13 23:31:30 2009";
1194         assert local.ctime() == ~"Fri Feb 13 15:31:30 2009";
1195     }
1196
1197     #[test]
1198     #[ignore(reason = "randomred")]
1199     fn test_strftime() {
1200         os::setenv(~"TZ", ~"America/Los_Angeles");
1201         tzset();
1202
1203         let time = ::time::Timespec::new(1234567890, 54321);
1204         let utc = at_utc(time);
1205         let local = at(time);
1206
1207         assert local.strftime(~"") == ~"";
1208         assert local.strftime(~"%A") == ~"Friday";
1209         assert local.strftime(~"%a") == ~"Fri";
1210         assert local.strftime(~"%B") == ~"February";
1211         assert local.strftime(~"%b") == ~"Feb";
1212         assert local.strftime(~"%C") == ~"20";
1213         assert local.strftime(~"%c") == ~"Fri Feb 13 15:31:30 2009";
1214         assert local.strftime(~"%D") == ~"02/13/09";
1215         assert local.strftime(~"%d") == ~"13";
1216         assert local.strftime(~"%e") == ~"13";
1217         assert local.strftime(~"%F") == ~"2009-02-13";
1218         // assert local.strftime("%G") == "2009";
1219         // assert local.strftime("%g") == "09";
1220         assert local.strftime(~"%H") == ~"15";
1221         assert local.strftime(~"%I") == ~"03";
1222         assert local.strftime(~"%j") == ~"044";
1223         assert local.strftime(~"%k") == ~"15";
1224         assert local.strftime(~"%l") == ~" 3";
1225         assert local.strftime(~"%M") == ~"31";
1226         assert local.strftime(~"%m") == ~"02";
1227         assert local.strftime(~"%n") == ~"\n";
1228         assert local.strftime(~"%P") == ~"pm";
1229         assert local.strftime(~"%p") == ~"PM";
1230         assert local.strftime(~"%R") == ~"15:31";
1231         assert local.strftime(~"%r") == ~"03:31:30 PM";
1232         assert local.strftime(~"%S") == ~"30";
1233         assert local.strftime(~"%s") == ~"1234567890";
1234         assert local.strftime(~"%T") == ~"15:31:30";
1235         assert local.strftime(~"%t") == ~"\t";
1236         // assert local.strftime("%U") == "06";
1237         assert local.strftime(~"%u") == ~"5";
1238         // assert local.strftime("%V") == "07";
1239         assert local.strftime(~"%v") == ~"13-Feb-2009";
1240         // assert local.strftime("%W") == "06";
1241         assert local.strftime(~"%w") == ~"5";
1242         // handle "%X"
1243         // handle "%x"
1244         assert local.strftime(~"%Y") == ~"2009";
1245         assert local.strftime(~"%y") == ~"09";
1246
1247         // FIXME (#2350): We should probably standardize on the timezone
1248         // abbreviation.
1249         let zone = local.strftime(~"%Z");
1250         assert zone == ~"PST" || zone == ~"Pacific Standard Time";
1251
1252         assert local.strftime(~"%z") == ~"-0800";
1253         assert local.strftime(~"%%") == ~"%";
1254
1255         // FIXME (#2350): We should probably standardize on the timezone
1256         // abbreviation.
1257         let rfc822 = local.rfc822();
1258         let prefix = ~"Fri, 13 Feb 2009 15:31:30 ";
1259         assert rfc822 == prefix + ~"PST" ||
1260                rfc822 == prefix + ~"Pacific Standard Time";
1261
1262         assert local.ctime() == ~"Fri Feb 13 15:31:30 2009";
1263         assert local.rfc822z() == ~"Fri, 13 Feb 2009 15:31:30 -0800";
1264         assert local.rfc3339() == ~"2009-02-13T15:31:30-08:00";
1265
1266         assert utc.ctime() == ~"Fri Feb 13 23:31:30 2009";
1267         assert utc.rfc822() == ~"Fri, 13 Feb 2009 23:31:30 GMT";
1268         assert utc.rfc822z() == ~"Fri, 13 Feb 2009 23:31:30 -0000";
1269         assert utc.rfc3339() == ~"2009-02-13T23:31:30Z";
1270     }
1271
1272     #[test]
1273     fn test_timespec_eq_ord() {
1274         use core::cmp::{eq, ge, gt, le, lt, ne};
1275
1276         let a = &Timespec::new(-2, 1);
1277         let b = &Timespec::new(-1, 2);
1278         let c = &Timespec::new(1, 2);
1279         let d = &Timespec::new(2, 1);
1280         let e = &Timespec::new(2, 1);
1281
1282         assert eq(d, e);
1283         assert ne(c, e);
1284
1285         assert lt(a, b);
1286         assert lt(b, c);
1287         assert lt(c, d);
1288
1289         assert le(a, b);
1290         assert le(b, c);
1291         assert le(c, d);
1292         assert le(d, e);
1293         assert le(e, d);
1294
1295         assert ge(b, a);
1296         assert ge(c, b);
1297         assert ge(d, c);
1298         assert ge(e, d);
1299         assert ge(d, e);
1300
1301         assert gt(b, a);
1302         assert gt(c, b);
1303         assert gt(d, c);
1304     }
1305 }