import libc::{c_char, c_int, c_long, size_t, time_t}; import io::{reader, reader_util}; import result::{result, ok, err, extensions}; export timespec, get_time, precise_time_ns, precise_time_s, tzset, tm, empty_tm, now, at, now_utc, at_utc, strptime; #[abi = "cdecl"] native mod rustrt { fn get_time(&sec: i64, &nsec: i32); fn precise_time_ns(&ns: u64); fn rust_tzset(); // FIXME: The i64 values can be passed by-val when #2064 is fixed. fn rust_gmtime(&&sec: i64, &&nsec: i32, &&result: tm); fn rust_localtime(&&sec: i64, &&nsec: i32, &&result: tm); fn rust_timegm(&&tm: tm, &sec: i64); fn rust_mktime(&&tm: tm, &sec: i64); } #[doc = "A record specifying a time value in seconds and microseconds."] type timespec = {sec: i64, nsec: i32}; #[doc = " Returns the current time as a `timespec` containing the seconds and microseconds since 1970-01-01T00:00:00Z. "] fn get_time() -> timespec { let mut sec = 0i64; let mut nsec = 0i32; rustrt::get_time(sec, nsec); ret {sec: sec, nsec: nsec}; } #[doc = " Returns the current value of a high-resolution performance counter in nanoseconds since an unspecified epoch. "] fn precise_time_ns() -> u64 { let mut ns = 0u64; rustrt::precise_time_ns(ns); ns } #[doc = " Returns the current value of a high-resolution performance counter in seconds since an unspecified epoch. "] fn precise_time_s() -> float { ret (precise_time_ns() as float) / 1000000000.; } fn tzset() { rustrt::rust_tzset(); } type tm = { 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 }; fn empty_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, } } #[doc = "Returns the specified time in UTC"] fn at_utc(clock: timespec) -> tm { let mut {sec, nsec} = clock; let mut tm = empty_tm(); rustrt::rust_gmtime(sec, nsec, tm); tm } #[doc = "Returns the current time in UTC"] fn now_utc() -> tm { at_utc(get_time()) } #[doc = "Returns the specified time in the local timezone"] fn at(clock: timespec) -> tm { let mut {sec, nsec} = clock; let mut tm = empty_tm(); rustrt::rust_localtime(sec, nsec, tm); tm } #[doc = "Returns the current time in the local timezone"] fn now() -> tm { at(get_time()) } #[doc = "Parses the time from the string according to the format string."] fn strptime(s: str, format: str) -> result { type tm_mut = { mut tm_sec: i32, mut tm_min: i32, mut tm_hour: i32, mut tm_mday: i32, mut tm_mon: i32, mut tm_year: i32, mut tm_wday: i32, mut tm_yday: i32, mut tm_isdst: i32, mut tm_gmtoff: i32, mut tm_zone: str, mut tm_nsec: i32, }; fn match_str(s: str, pos: uint, needle: str) -> bool { let mut i = pos; for str::each(needle) {|ch| if s[i] != ch { ret false; } i += 1u; } ret true; } fn match_strs(s: str, pos: uint, strs: [(str, i32)]) -> option<(i32, uint)> { let mut i = 0u; let len = vec::len(strs); while i < len { let (needle, value) = strs[i]; if match_str(s, pos, needle) { ret some((value, pos + str::len(needle))); } i += 1u; } none } fn match_digits(s: str, pos: uint, digits: uint, ws: bool) -> option<(i32, uint)> { let mut pos = pos; let mut value = 0_i32; let mut i = 0u; while i < digits { let {ch, next} = str::char_range_at(s, pos); pos = next; alt ch { '0' to '9' { value = value * 10_i32 + (ch as i32 - '0' as i32); } ' ' if ws { } _ { ret none; } } i += 1u; } some((value, pos)) } fn parse_char(s: str, pos: uint, c: char) -> result { let {ch, next} = str::char_range_at(s, pos); if c == ch { ok(next) } else { err(#fmt("Expected %?, found %?", str::from_char(c), str::from_char(ch))) } } fn parse_type(s: str, pos: uint, ch: char, tm: tm_mut) -> result { alt ch { 'A' { alt match_strs(s, pos, [ ("Sunday", 0_i32), ("Monday", 1_i32), ("Tuesday", 2_i32), ("Wednesday", 3_i32), ("Thursday", 4_i32), ("Friday", 5_i32), ("Saturday", 6_i32) ]) { some(item) { let (v, pos) = item; tm.tm_wday = v; ok(pos) } none { err("Invalid day") } } } 'a' { alt match_strs(s, pos, [ ("Sun", 0_i32), ("Mon", 1_i32), ("Tue", 2_i32), ("Wed", 3_i32), ("Thu", 4_i32), ("Fri", 5_i32), ("Sat", 6_i32) ]) { some(item) { let (v, pos) = item; tm.tm_wday = v; ok(pos) } none { err("Invalid day") } } } 'B' { alt match_strs(s, pos, [ ("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) ]) { some(item) { let (v, pos) = item; tm.tm_mon = v; ok(pos) } none { err("Invalid month") } } } 'b' | 'h' { alt match_strs(s, pos, [ ("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) ]) { some(item) { let (v, pos) = item; tm.tm_mon = v; ok(pos) } none { err("Invalid month") } } } 'C' { alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_year += (v * 100_i32) - 1900_i32; ok(pos) } none { err("Invalid year") } } } 'c' { parse_type(s, pos, 'a', tm) .chain { |pos| parse_char(s, pos, ' ') } .chain { |pos| parse_type(s, pos, 'b', tm) } .chain { |pos| parse_char(s, pos, ' ') } .chain { |pos| parse_type(s, pos, 'e', tm) } .chain { |pos| parse_char(s, pos, ' ') } .chain { |pos| parse_type(s, pos, 'T', tm) } .chain { |pos| parse_char(s, pos, ' ') } .chain { |pos| parse_type(s, pos, 'Y', tm) } } 'D' | 'x' { parse_type(s, pos, 'm', tm) .chain { |pos| parse_char(s, pos, '/') } .chain { |pos| parse_type(s, pos, 'd', tm) } .chain { |pos| parse_char(s, pos, '/') } .chain { |pos| parse_type(s, pos, 'y', tm) } } 'd' { alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_mday = v; ok(pos) } none { err("Invalid day of the month") } } } 'e' { alt match_digits(s, pos, 2u, true) { some(item) { let (v, pos) = item; tm.tm_mday = v; ok(pos) } none { err("Invalid day of the month") } } } 'F' { parse_type(s, pos, 'Y', tm) .chain { |pos| parse_char(s, pos, '-') } .chain { |pos| parse_type(s, pos, 'm', tm) } .chain { |pos| parse_char(s, pos, '-') } .chain { |pos| parse_type(s, pos, 'd', tm) } } 'H' { // FIXME: range check. (#2350 -- same issue for all FIXMEs in this // file.) alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_hour = v; ok(pos) } none { err("Invalid hour") } } } 'I' { // FIXME: range check. alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_hour = if v == 12_i32 { 0_i32 } else { v }; ok(pos) } none { err("Invalid hour") } } } 'j' { // FIXME: range check. alt match_digits(s, pos, 3u, false) { some(item) { let (v, pos) = item; tm.tm_yday = v - 1_i32; ok(pos) } none { err("Invalid year") } } } 'k' { // FIXME: range check. alt match_digits(s, pos, 2u, true) { some(item) { let (v, pos) = item; tm.tm_hour = v; ok(pos) } none { err("Invalid hour") } } } 'l' { // FIXME: range check. alt match_digits(s, pos, 2u, true) { some(item) { let (v, pos) = item; tm.tm_hour = if v == 12_i32 { 0_i32 } else { v }; ok(pos) } none { err("Invalid hour") } } } 'M' { // FIXME: range check. alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_min = v; ok(pos) } none { err("Invalid minute") } } } 'm' { // FIXME: range check. alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_mon = v - 1_i32; ok(pos) } none { err("Invalid month") } } } 'n' { parse_char(s, pos, '\n') } 'P' { alt match_strs(s, pos, [("am", 0_i32), ("pm", 12_i32)]) { some(item) { let (v, pos) = item; tm.tm_hour += v; ok(pos) } none { err("Invalid hour") } } } 'p' { alt match_strs(s, pos, [("AM", 0_i32), ("PM", 12_i32)]) { some(item) { let (v, pos) = item; tm.tm_hour += v; ok(pos) } none { err("Invalid hour") } } } 'R' { parse_type(s, pos, 'H', tm) .chain { |pos| parse_char(s, pos, ':') } .chain { |pos| parse_type(s, pos, 'M', tm) } } 'r' { parse_type(s, pos, 'I', tm) .chain { |pos| parse_char(s, pos, ':') } .chain { |pos| parse_type(s, pos, 'M', tm) } .chain { |pos| parse_char(s, pos, ':') } .chain { |pos| parse_type(s, pos, 'S', tm) } .chain { |pos| parse_char(s, pos, ' ') } .chain { |pos| parse_type(s, pos, 'p', tm) } } 'S' { // FIXME: range check. alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_sec = v; ok(pos) } none { err("Invalid second") } } } //'s' {} 'T' | 'X' { parse_type(s, pos, 'H', tm) .chain { |pos| parse_char(s, pos, ':') } .chain { |pos| parse_type(s, pos, 'M', tm) } .chain { |pos| parse_char(s, pos, ':') } .chain { |pos| parse_type(s, pos, 'S', tm) } } 't' { parse_char(s, pos, '\t') } 'u' { // FIXME: range check. alt match_digits(s, pos, 1u, false) { some(item) { let (v, pos) = item; tm.tm_wday = v; ok(pos) } none { err("Invalid weekday") } } } 'v' { parse_type(s, pos, 'e', tm) .chain { |pos| parse_char(s, pos, '-') } .chain { |pos| parse_type(s, pos, 'b', tm) } .chain { |pos| parse_char(s, pos, '-') } .chain { |pos| parse_type(s, pos, 'Y', tm) } } //'W' {} 'w' { // FIXME: range check. alt match_digits(s, pos, 1u, false) { some(item) { let (v, pos) = item; tm.tm_wday = v; ok(pos) } none { err("Invalid weekday") } } } //'X' {} //'x' {} 'Y' { // FIXME: range check. alt match_digits(s, pos, 4u, false) { some(item) { let (v, pos) = item; tm.tm_year = v - 1900_i32; ok(pos) } none { err("Invalid weekday") } } } 'y' { // FIXME: range check. alt match_digits(s, pos, 2u, false) { some(item) { let (v, pos) = item; tm.tm_year = v - 1900_i32; ok(pos) } none { err("Invalid weekday") } } } 'Z' { if match_str(s, pos, "UTC") || match_str(s, pos, "GMT") { tm.tm_gmtoff = 0_i32; tm.tm_zone = "UTC"; ok(pos + 3u) } else { // It's odd, but to maintain compatibility with c's // strptime we ignore the timezone. let mut pos = pos; let len = str::len(s); while pos < len { let {ch, next} = str::char_range_at(s, pos); pos = next; if ch == ' ' { break; } } ok(pos) } } 'z' { let {ch, next} = str::char_range_at(s, pos); if ch == '+' || ch == '-' { alt match_digits(s, next, 4u, false) { some(item) { let (v, pos) = item; if v == 0_i32 { tm.tm_gmtoff = 0_i32; tm.tm_zone = "UTC"; } ok(pos) } none { err("Invalid zone offset") } } } else { err("Invalid zone offset") } } '%' { parse_char(s, pos, '%') } ch { err(#fmt("unknown formatting type: %?", str::from_char(ch))) } } } io::with_str_reader(format) { |rdr| let tm = { mut tm_sec: 0_i32, mut tm_min: 0_i32, mut tm_hour: 0_i32, mut tm_mday: 0_i32, mut tm_mon: 0_i32, mut tm_year: 0_i32, mut tm_wday: 0_i32, mut tm_yday: 0_i32, mut tm_isdst: 0_i32, mut tm_gmtoff: 0_i32, mut tm_zone: "", mut tm_nsec: 0_i32, }; let mut pos = 0u; let len = str::len(s); let mut result = err("Invalid time"); while !rdr.eof() && pos < len { let {ch, next} = str::char_range_at(s, pos); alt rdr.read_char() { '%' { alt parse_type(s, pos, rdr.read_char(), tm) { ok(next) { pos = next; } err(e) { result = err(e); break; } } } c { if c != ch { break } pos = next; } } } if pos == len && rdr.eof() { ok({ 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, tm_nsec: tm.tm_nsec, }) } else { result } } } fn strftime(format: str, tm: tm) -> str { fn parse_type(ch: char, tm: tm) -> str { //FIXME: Implement missing types. alt check ch { 'A' { alt check tm.tm_wday as int { 0 { "Sunday" } 1 { "Monday" } 2 { "Tuesday" } 3 { "Wednesday" } 4 { "Thursday" } 5 { "Friday" } 6 { "Saturday" } } } 'a' { alt check tm.tm_wday as int { 0 { "Sun" } 1 { "Mon" } 2 { "Tue" } 3 { "Wed" } 4 { "Thu" } 5 { "Fri" } 6 { "Sat" } } } 'B' { alt check tm.tm_mon as int { 0 { "January" } 1 { "February" } 2 { "March" } 3 { "April" } 4 { "May" } 5 { "June" } 6 { "July" } 7 { "August" } 8 { "September" } 9 { "October" } 10 { "November" } 11 { "December" } } } 'b' | 'h' { alt check tm.tm_mon as int { 0 { "Jan" } 1 { "Feb" } 2 { "Mar" } 3 { "Apr" } 4 { "May" } 5 { "Jun" } 6 { "Jul" } 7 { "Aug" } 8 { "Sep" } 9 { "Oct" } 10 { "Nov" } 11 { "Dec" } } } 'C' { #fmt("%02d", (tm.tm_year as int + 1900) / 100) } 'c' { #fmt("%s %s %s %s %s", parse_type('a', tm), parse_type('b', tm), parse_type('e', tm), parse_type('T', tm), parse_type('Y', tm)) } 'D' | 'x' { #fmt("%s/%s/%s", parse_type('m', tm), parse_type('d', tm), parse_type('y', tm)) } 'd' { #fmt("%02d", tm.tm_mday as int) } 'e' { #fmt("%2d", tm.tm_mday as int) } 'F' { #fmt("%s-%s-%s", parse_type('Y', tm), parse_type('m', tm), parse_type('d', tm)) } //'G' {} //'g' {} 'H' { #fmt("%02d", tm.tm_hour as int) } 'I' { let mut h = tm.tm_hour as int; if h == 0 { h = 12 } if h > 12 { h -= 12 } #fmt("%02d", h) } 'j' { #fmt("%03d", tm.tm_yday as int + 1) } 'k' { #fmt("%2d", tm.tm_hour as int) } 'l' { let mut h = tm.tm_hour as int; if h == 0 { h = 12 } if h > 12 { h -= 12 } #fmt("%2d", h) } 'M' { #fmt("%02d", tm.tm_min as int) } 'm' { #fmt("%02d", tm.tm_mon as int + 1) } 'n' { "\n" } 'P' { if tm.tm_hour as int < 12 { "am" } else { "pm" } } 'p' { if tm.tm_hour as int < 12 { "AM" } else { "PM" } } 'R' { #fmt("%s:%s", parse_type('H', tm), parse_type('M', tm)) } 'r' { #fmt("%s:%s:%s %s", parse_type('I', tm), parse_type('M', tm), parse_type('S', tm), parse_type('p', tm)) } 'S' { #fmt("%02d", tm.tm_sec as int) } 's' { #fmt("%d", tm.to_timespec().sec as int) } 'T' | 'X' { #fmt("%s:%s:%s", parse_type('H', tm), parse_type('M', tm), parse_type('S', tm)) } 't' { "\t" } //'U' {} 'u' { let i = tm.tm_wday as int; int::str(if i == 0 { 7 } else { i }) } //'V' {} 'v' { #fmt("%s-%s-%s", parse_type('e', tm), parse_type('b', tm), parse_type('Y', tm)) } //'W' {} 'w' { int::str(tm.tm_wday as int) } //'X' {} //'x' {} 'Y' { int::str(tm.tm_year as int + 1900) } 'y' { #fmt("%02d", (tm.tm_year as int + 1900) % 100) } 'Z' { tm.tm_zone } 'z' { let sign = if tm.tm_gmtoff > 0_i32 { '+' } else { '-' }; let mut m = i32::abs(tm.tm_gmtoff) / 60_i32; let h = m / 60_i32; m -= h * 60_i32; #fmt("%c%02d%02d", sign, h as int, m as int) } //'+' {} '%' { "%" } } } let mut buf = ""; io::with_str_reader(format) { |rdr| while !rdr.eof() { alt rdr.read_char() { '%' { buf += parse_type(rdr.read_char(), tm); } ch { str::push_char(buf, ch); } } } } buf } impl tm for tm { #[doc = "Convert time to the seconds from January 1, 1970"] fn to_timespec() -> timespec { let mut sec = 0i64; if self.tm_gmtoff == 0_i32 { rustrt::rust_timegm(self, sec); } else { rustrt::rust_mktime(self, sec); } { sec: sec, nsec: self.tm_nsec } } #[doc = "Convert time to the local timezone"] fn to_local() -> tm { at(self.to_timespec()) } #[doc = "Convert time to the UTC"] fn to_utc() -> tm { at_utc(self.to_timespec()) } #[doc = " Return a string of the current time in the form \"Thu Jan 1 00:00:00 1970\". "] fn ctime() -> str { self.strftime("%c") } #[doc = "Formats the time according to the format string."] fn strftime(format: str) -> str { strftime(format, self) } #[doc = " 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\" "] fn rfc822() -> str { if self.tm_gmtoff == 0_i32 { self.strftime("%a, %d %b %Y %T GMT") } else { self.strftime("%a, %d %b %Y %T %Z") } } #[doc = " 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\" "] fn rfc822z() -> str { self.strftime("%a, %d %b %Y %T %z") } #[doc = " Returns a time string formatted according to ISO 8601. local: \"2012-02-22T07:53:18-07:00\" utc: \"2012-02-22T14:53:18Z\" "] fn rfc3339() -> str { if self.tm_gmtoff == 0_i32 { self.strftime("%Y-%m-%dT%H:%M:%SZ") } else { let s = self.strftime("%Y-%m-%dT%H:%M:%S"); let sign = if self.tm_gmtoff > 0_i32 { '+' } else { '-' }; let mut m = i32::abs(self.tm_gmtoff) / 60_i32; let h = m / 60_i32; m -= h * 60_i32; s + #fmt("%c%02d:%02d", sign, h as int, m as int) } } } #[cfg(test)] mod tests { import task; #[test] fn test_get_time() { const some_recent_date: i64 = 1325376000i64; // 2012-01-01T00:00:00Z const some_future_date: i64 = 1577836800i64; // 2020-01-01T00:00:00Z let tv1 = get_time(); log(debug, "tv1=" + uint::str(tv1.sec as uint) + " sec + " + uint::str(tv1.nsec as uint) + " nsec"); assert tv1.sec > some_recent_date; assert tv1.nsec < 1000000000i32; let tv2 = get_time(); log(debug, "tv2=" + uint::str(tv2.sec as uint) + " sec + " + uint::str(tv2.nsec as uint) + " nsec"); assert tv2.sec >= tv1.sec; assert tv2.sec < some_future_date; assert tv2.nsec < 1000000000i32; if tv2.sec == tv1.sec { assert tv2.nsec >= tv1.nsec; } } #[test] fn test_precise_time() { let s0 = precise_time_s(); let ns1 = precise_time_ns(); log(debug, "s0=" + float::to_str(s0, 9u) + " sec"); assert s0 > 0.; let ns0 = (s0 * 1000000000.) as u64; log(debug, "ns0=" + u64::str(ns0) + " ns"); log(debug, "ns1=" + u64::str(ns1) + " ns"); assert ns1 >= ns0; let ns2 = precise_time_ns(); log(debug, "ns2=" + u64::str(ns2) + " ns"); assert ns2 >= ns1; } #[test] fn test_at_utc() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let utc = at_utc(time); assert utc.tm_sec == 30_i32; assert utc.tm_min == 31_i32; assert utc.tm_hour == 23_i32; assert utc.tm_mday == 13_i32; assert utc.tm_mon == 1_i32; assert utc.tm_year == 109_i32; assert utc.tm_wday == 5_i32; assert utc.tm_yday == 43_i32; assert utc.tm_isdst == 0_i32; assert utc.tm_gmtoff == 0_i32; assert utc.tm_zone == "UTC"; assert utc.tm_nsec == 54321_i32; } #[test] fn test_at() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let local = at(time); #error("time_at: %?", local); assert local.tm_sec == 30_i32; assert local.tm_min == 31_i32; assert local.tm_hour == 15_i32; assert local.tm_mday == 13_i32; assert local.tm_mon == 1_i32; assert local.tm_year == 109_i32; assert local.tm_wday == 5_i32; assert local.tm_yday == 43_i32; assert local.tm_isdst == 0_i32; assert local.tm_gmtoff == -28800_i32; // FIXME: We should probably standardize on the timezone // abbreviation. let zone = local.tm_zone; assert zone == "PST" || zone == "Pacific Standard Time"; assert local.tm_nsec == 54321_i32; } #[test] fn test_to_timespec() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let utc = at_utc(time); assert utc.to_timespec() == time; assert utc.to_local().to_timespec() == time; } #[test] fn test_conversions() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let utc = at_utc(time); let local = at(time); 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; } #[test] fn test_strptime() { os::setenv("TZ", "America/Los_Angeles"); tzset(); alt strptime("", "") { ok(tm) { 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; assert tm.tm_isdst== 0_i32; assert tm.tm_gmtoff == 0_i32; assert tm.tm_zone == ""; assert tm.tm_nsec == 0_i32; } err(_) {} } let format = "%a %b %e %T %Y"; assert strptime("", format) == err("Invalid time"); assert strptime("Fri Feb 13 15:31:30", format) == err("Invalid time"); alt strptime("Fri Feb 13 15:31:30 2009", format) { err(e) { fail e } ok(tm) { 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 == ""; assert tm.tm_nsec == 0_i32; } } fn test(s: str, format: str) -> bool { alt strptime(s, format) { ok(tm) { tm.strftime(format) == s } err(e) { fail e } } } [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ].iter { |day| assert test(day, "%A"); } [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ].iter { |day| assert test(day, "%a"); } [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ].iter { |day| assert test(day, "%B"); } [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ].iter { |day| assert test(day, "%b"); } 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: flesh out assert test("11", "%I"); // FIXME: 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"); assert strptime("UTC", "%Z").get().tm_zone == "UTC"; assert strptime("PST", "%Z").get().tm_zone == ""; assert strptime("-0000", "%z").get().tm_gmtoff == 0_i32; assert strptime("-0800", "%z").get().tm_gmtoff == 0_i32; assert test("%", "%%"); } #[test] fn test_ctime() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let utc = at_utc(time); let local = at(time); #error("test_ctime: %? %?", utc.ctime(), local.ctime()); assert utc.ctime() == "Fri Feb 13 23:31:30 2009"; assert local.ctime() == "Fri Feb 13 15:31:30 2009"; } #[test] fn test_strftime() { os::setenv("TZ", "America/Los_Angeles"); tzset(); let time = { sec: 1234567890_i64, nsec: 54321_i32 }; let utc = at_utc(time); let local = at(time); assert local.strftime("") == ""; assert local.strftime("%A") == "Friday"; assert local.strftime("%a") == "Fri"; assert local.strftime("%B") == "February"; assert local.strftime("%b") == "Feb"; assert local.strftime("%C") == "20"; assert local.strftime("%c") == "Fri Feb 13 15:31:30 2009"; assert local.strftime("%D") == "02/13/09"; assert local.strftime("%d") == "13"; assert local.strftime("%e") == "13"; assert local.strftime("%F") == "2009-02-13"; // assert local.strftime("%G") == "2009"; // assert local.strftime("%g") == "09"; assert local.strftime("%H") == "15"; assert local.strftime("%I") == "03"; assert local.strftime("%j") == "044"; assert local.strftime("%k") == "15"; assert local.strftime("%l") == " 3"; assert local.strftime("%M") == "31"; assert local.strftime("%m") == "02"; assert local.strftime("%n") == "\n"; assert local.strftime("%P") == "pm"; assert local.strftime("%p") == "PM"; assert local.strftime("%R") == "15:31"; assert local.strftime("%r") == "03:31:30 PM"; assert local.strftime("%S") == "30"; assert local.strftime("%s") == "1234567890"; assert local.strftime("%T") == "15:31:30"; assert local.strftime("%t") == "\t"; // assert local.strftime("%U") == "06"; assert local.strftime("%u") == "5"; // assert local.strftime("%V") == "07"; assert local.strftime("%v") == "13-Feb-2009"; // assert local.strftime("%W") == "06"; assert local.strftime("%w") == "5"; // handle "%X" // handle "%x" assert local.strftime("%Y") == "2009"; assert local.strftime("%y") == "09"; // FIXME: We should probably standardize on the timezone // abbreviation. let zone = local.strftime("%Z"); assert zone == "PST" || zone == "Pacific Standard Time"; assert local.strftime("%z") == "-0800"; assert local.strftime("%%") == "%"; // FIXME: We should probably standardize on the timezone // abbreviation. let rfc822 = local.rfc822(); let prefix = "Fri, 13 Feb 2009 15:31:30 "; assert rfc822 == prefix + "PST" || rfc822 == prefix + "Pacific Standard Time"; assert local.ctime() == "Fri Feb 13 15:31:30 2009"; assert local.rfc822z() == "Fri, 13 Feb 2009 15:31:30 -0800"; assert local.rfc3339() == "2009-02-13T15:31:30-08:00"; assert utc.ctime() == "Fri Feb 13 23:31:30 2009"; assert utc.rfc822() == "Fri, 13 Feb 2009 23:31:30 GMT"; assert utc.rfc822z() == "Fri, 13 Feb 2009 23:31:30 -0000"; assert utc.rfc3339() == "2009-02-13T23:31:30Z"; } }