diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 3957bd31a1092..64385fca9bf1d 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -108,7 +108,15 @@ fn chain_err( } } -impl methods for result { +impl extensions for result { + fn get() -> T { get(self) } + + fn get_err() -> E { get_err(self) } + + fn success() -> bool { success(self) } + + fn failure() -> bool { failure(self) } + fn chain(op: fn(T) -> result) -> result { chain(self, op) } diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 1e48c307ac17a..cb130c4b4072d 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -1,19 +1,43 @@ +import libc::{c_char, c_int, c_long, size_t, time_t}; +import io::{reader, reader_util}; +import result::{result, ok, err, extensions}; + +export + timeval, + get_time, + precise_time_ns, + precise_time_s, + tm, + empty_tm, + now, + at, + now_utc, + at_utc, + parse; + #[abi = "cdecl"] native mod rustrt { - fn get_time(&sec: u32, &usec: u32); + fn get_time(&sec: i64, &usec: i32); fn precise_time_ns(&ns: u64); + + // FIXME: The i64 values can be passed by-val when #2064 is fixed. + fn rust_gmtime(&&sec: i64, &&usec: i32, &&result: tm); + fn rust_localtime(&&sec: i64, &&usec: i32, &result: tm); + fn rust_timezone(&&gmtoff: c_long) -> str; + 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 timeval = {sec: u32, usec: u32}; +type timeval = {sec: i64, usec: i32}; #[doc = " Returns the current time as a `timeval` containing the seconds and microseconds since 1970-01-01T00:00:00Z. "] fn get_time() -> timeval { - let mut sec = 0u32; - let mut usec = 0u32; + let mut sec = 0i64; + let mut usec = 0i32; rustrt::get_time(sec, usec); ret {sec: sec, usec: usec}; } @@ -36,21 +60,799 @@ fn precise_time_s() -> float { ret (precise_time_ns() as float) / 1000000000.; } +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_usec: i32, // microseconds +}; + +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_usec: 0_i32, + } +} + +#[doc = "Returns the specified time in UTC"] +fn at_utc(clock: timeval) -> tm { + let mut {sec, usec} = clock; + let mut tm = empty_tm(); + rustrt::rust_gmtime(sec, usec, 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: timeval) -> tm { + let mut {sec, usec} = clock; + let mut tm = empty_tm(); + rustrt::rust_localtime(sec, usec, tm); + tm +} + +#[doc = "Returns the current time in the local timezone"] +fn now() -> tm { + at(get_time()) +} + +fn timezone() -> (i32, str) { + let mut gmtoff = 0 as c_long; + let zone = rustrt::rust_timezone(gmtoff); + (gmtoff as i32, zone) +} + +fn timegm(tm: tm) -> timeval { + let mut sec = 0i64; + rustrt::rust_timegm(tm, sec); + { + sec: sec, + usec: tm.tm_usec + } +} + +fn mktime(tm: tm) -> timeval { + let mut sec = 0i64; + rustrt::rust_mktime(tm, sec); + { + sec: sec, + usec: tm.tm_usec + } +} + +#[doc = "Parses the time from the string according to the format string."] +fn parse(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_usec: 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. + 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_usec: 0_i32, + }; + let mut pos = 0u; + let len = str::len(s); + let mut result = err("Invalid format"); + + 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_usec: tm.tm_usec, + }) + } 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_timeval().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_timeval() -> timeval { + if self.tm_gmtoff == 0_i32 { timegm(self) } else { mktime(self) } + } + + #[doc = "Convert time to the local timezone"] + fn to_local() -> tm { + at(self.to_timeval()) + } + + #[doc = "Convert time to the UTC"] + fn to_utc() -> tm { + at_utc(self.to_timeval()) + } + + #[doc = " + Return a string of the current time in the form + \"Thu Jan 1 00:00:00 1970\". + "] + fn ctime() -> str { self.format("%c") } + + #[doc = "Formats the time according to the format string."] + fn format(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.format("%a, %d %b %Y %T GMT") + } else { + self.format("%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.format("%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.format("%Y-%m-%dT%H:%M:%SZ") + } else { + let s = self.format("%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: u32 = 1325376000u32; // 2012-01-01T00:00:00Z - const some_future_date: u32 = 1577836800u32; // 2020-01-01T00:00:00Z + 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.usec as uint) + " usec"); assert tv1.sec > some_recent_date; - assert tv1.usec < 1000000u32; + assert tv1.usec < 1000000i32; let tv2 = get_time(); log(debug, "tv2=" + uint::str(tv2.sec as uint) + " sec + " @@ -58,7 +860,7 @@ mod tests { assert tv2.sec >= tv1.sec; assert tv2.sec < some_future_date; - assert tv2.usec < 1000000u32; + assert tv2.usec < 1000000i32; if tv2.sec == tv1.sec { assert tv2.usec >= tv1.usec; } @@ -81,4 +883,313 @@ mod tests { log(debug, "ns2=" + u64::str(ns2) + " ns"); assert ns2 >= ns1; } + + #[test] + fn test_at_utc() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 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_usec == 54321_i32; + } + + #[test] + fn test_at() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 54321_i32 }; + let local = at(time); + + 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; + #error("test_at local.tm_zone: %?", local.tm_zone); + //assert local.tm_zone == "PST"; + assert local.tm_usec == 54321_i32; + } + + #[test] + fn test_to_timeval() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 54321_i32 }; + let utc = at_utc(time); + + assert utc.to_timeval() == time; + assert utc.to_local().to_timeval() == time; + } + + #[test] + fn test_conversions() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 54321_i32 }; + let utc = at_utc(time); + let local = at(time); + + #error("test_conversions %? %?", + sys::size_of::(), + sys::align_of::()); + + #error("local.to_timeval %?", local.to_timeval()); + + #error("local: %?", local); + #error("local.to_local(): %?", local.to_local()); + #error("local.to_utc(): %?", local.to_utc()); + #error("local.to_utc().to_local(): %?", local.to_utc().to_local()); + #error("utc: %?", utc); + #error("utc.to_utc(): %?", utc.to_utc()); + #error("utc.to_local(): %?", utc.to_local()); + #error("utc.to_local().to_utc(): %?", utc.to_local().to_utc()); + + 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_parse() { + os::setenv("TZ", "America/Los_Angeles"); + + alt parse("", "") { + 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_usec as i32 == 0_i32; + } + err(_) {} + } + + let format = "%a %b %e %T %Y"; + assert parse("", format) == err("Invalid format"); + assert parse("Fri Feb 13 15:31:30", format) == err("Invalid format"); + + alt parse("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; + + // FIXME: some platforms don't set all the values. + //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_usec == 0_i32; + } + } + + fn test(s: str, format: str) -> bool { + alt parse(s, format) { + ok(tm) { tm.format(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 parse("UTC", "%Z").get().tm_zone == "UTC"; + #error("test_parse parse: %?", parse("PST", "%Z")); + //assert parse("PST", "%Z").get().tm_zone == ""; + assert parse("-0000", "%z").get().tm_gmtoff == 0_i32; + assert parse("-0800", "%z").get().tm_gmtoff == 0_i32; + assert test("%", "%%"); + } + + #[test] + fn test_ctime() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 54321_i32 }; + let utc = at_utc(time); + let local = at(time); + + assert utc.ctime() == "Fri Feb 13 23:31:30 2009"; + assert local.ctime() == "Fri Feb 13 15:31:30 2009"; + } + + fn test_format() { + os::setenv("TZ", "America/Los_Angeles"); + + let time = { sec: 1234567890_i64, usec: 54321_i32 }; + let utc = at_utc(time); + let local = at(time); + + assert local.format("") == ""; + assert local.format("%A") == "Friday"; + assert local.format("%a") == "Fri"; + assert local.format("%B") == "February"; + assert local.format("%b") == "Feb"; + assert local.format("%C") == "20"; + assert local.format("%c") == "Fri Feb 13 15:31:30 2009"; + assert local.format("%D") == "02/13/09"; + assert local.format("%d") == "13"; + assert local.format("%e") == "13"; + assert local.format("%F") == "2009-02-13"; + // assert local.format("%G") == "2009"; + // assert local.format("%g") == "09"; + assert local.format("%H") == "15"; + assert local.format("%I") == "03"; + assert local.format("%j") == "044"; + assert local.format("%k") == "15"; + assert local.format("%l") == " 3"; + assert local.format("%M") == "31"; + assert local.format("%m") == "02"; + assert local.format("%n") == "\n"; + assert local.format("%P") == "pm"; + assert local.format("%p") == "PM"; + assert local.format("%R") == "15:31"; + assert local.format("%r") == "03:31:30 PM"; + assert local.format("%S") == "30"; + assert local.format("%s") == "1234567890"; + assert local.format("%T") == "15:31:30"; + assert local.format("%t") == "\t"; + // assert local.format("%U") == "06"; + assert local.format("%u") == "5"; + // assert local.format("%V") == "07"; + assert local.format("%v") == "13-Feb-2009"; + // assert local.format("%W") == "06"; + assert local.format("%w") == "5"; + // handle "%X" + // handle "%x" + assert local.format("%Y") == "2009"; + assert local.format("%y") == "09"; + #error("test_format local.format: %?", local.format("%Z")); + //assert local.format("%Z") == "PST"; + assert local.format("%z") == "-0700"; + assert local.format("%%") == "%"; + + assert local.ctime() == "Fri Feb 13 15:31:30 2009"; + #error("test_format local.rfc822: %?", local.rfc822()); + //assert local.rfc822() == "Fri, 13 Feb 2009 15:31:30 PST"; + 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"; + } } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 08707d7f38dc0..b677fe9146001 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -8,6 +8,8 @@ #include "sync/timer.h" #include "rust_abi.h" +#include + #ifdef __APPLE__ #include #endif @@ -79,7 +81,7 @@ rust_getcwd() { return NULL; } - return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(getcwd"); + return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(getcwd)"); } #if defined(__WIN32__) @@ -408,7 +410,7 @@ rust_ptr_eq(type_desc *t, rust_box *a, rust_box *b) { #if defined(__WIN32__) extern "C" CDECL void -get_time(uint32_t *sec, uint32_t *usec) { +get_time(int64_t *sec, int32_t *usec) { FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); @@ -427,7 +429,7 @@ get_time(uint32_t *sec, uint32_t *usec) { } #else extern "C" CDECL void -get_time(uint32_t *sec, uint32_t *usec) { +get_time(uint64_t *sec, int32_t *usec) { struct timeval tv; gettimeofday(&tv, NULL); *sec = tv.tv_sec; @@ -441,6 +443,159 @@ precise_time_ns(uint64_t *ns) { *ns = t.time_ns(); } +struct rust_tm { + int32_t tm_sec; + int32_t tm_min; + int32_t tm_hour; + int32_t tm_mday; + int32_t tm_mon; + int32_t tm_year; + int32_t tm_wday; + int32_t tm_yday; + int32_t tm_isdst; + int32_t tm_gmtoff; + rust_str *tm_zone; + int32_t tm_usec; +}; + +void rust_tm_to_tm(rust_tm* in_tm, tm* out_tm) { + memset(out_tm, 0, sizeof(tm)); + out_tm->tm_sec = in_tm->tm_sec; + out_tm->tm_min = in_tm->tm_min; + out_tm->tm_hour = in_tm->tm_hour; + out_tm->tm_mday = in_tm->tm_mday; + out_tm->tm_mon = in_tm->tm_mon; + out_tm->tm_year = in_tm->tm_year; + out_tm->tm_wday = in_tm->tm_wday; + out_tm->tm_yday = in_tm->tm_yday; + out_tm->tm_isdst = in_tm->tm_isdst; +} + +void tm_to_rust_tm(tm* in_tm, rust_tm* out_tm) { + out_tm->tm_sec = in_tm->tm_sec; + out_tm->tm_min = in_tm->tm_min; + out_tm->tm_hour = in_tm->tm_hour; + out_tm->tm_mday = in_tm->tm_mday; + out_tm->tm_mon = in_tm->tm_mon; + out_tm->tm_year = in_tm->tm_year; + out_tm->tm_wday = in_tm->tm_wday; + out_tm->tm_yday = in_tm->tm_yday; + out_tm->tm_isdst = in_tm->tm_isdst; +} + +#if defined(__WIN32__) +#define TZSET() _tzset() +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define GMTIME(clock, result) gmtime_s((result), (clock)) +#define LOCALTIME(clock, result) localtime_s((result), (clock)) +#define TIMEGM(result) _mkgmtime64(result) +#else +struct tm* GMTIME(const time_t *clock, tm *result) { + struct tm* t = gmtime(clock); + if (t == NULL || result == NULL) { return NULL; } + *result = *t; + return result; +} +struct tm* LOCALTIME(const time_t *clock, tm *result) { + struct tm* t = localtime(clock); + if (t == NULL || result == NULL) { return NULL; } + *result = *t; + return result; +} +#define TIMEGM(result) mktime((result)) - _timezone +#endif +#else +#define TZSET() tzset() +#define GMTIME(clock, result) gmtime_r((clock), (result)) +#define LOCALTIME(clock, result) localtime_r((clock), (result)) +#define TIMEGM(result) timegm(result) +#endif + +extern "C" CDECL void +rust_gmtime(int64_t *sec, int32_t *usec, rust_tm *timeptr) { + tm tm; + time_t s = *sec; + GMTIME(&s, &tm); + + tm_to_rust_tm(&tm, timeptr); + timeptr->tm_gmtoff = 0; + timeptr->tm_usec = *usec; + + const char *zone = "UTC"; + size_t size = strlen(zone); + str_reserve_shared(&timeptr->tm_zone, size); + memcpy(timeptr->tm_zone->data, zone, size); + timeptr->tm_zone->fill = size + 1; + timeptr->tm_zone->data[size] = '\0'; +} + +extern "C" CDECL void +rust_localtime(int64_t *sec, int32_t *usec, rust_tm *timeptr) { + tm tm; + TZSET(); + time_t s = *sec; + LOCALTIME(&s, &tm); + + tm_to_rust_tm(&tm, timeptr); + +#if defined(__WIN32__) + timeptr->tm_gmtoff = -timezone; + char zone[64]; + strftime(zone, sizeof(zone), "%Z", &tm); +#else + timeptr->tm_gmtoff = tm.tm_gmtoff; + const char *zone = tm.tm_zone; +#endif + + timeptr->tm_usec = *usec; + + if (zone != NULL) { + size_t size = strlen(zone); + str_reserve_shared(&timeptr->tm_zone, size); + memcpy(timeptr->tm_zone->data, zone, size); + timeptr->tm_zone->fill = size + 1; + timeptr->tm_zone->data[size] = '\0'; + } +} + +extern "C" CDECL rust_str * +rust_timezone(long *gmtoff) { + rust_task *task = rust_sched_loop::get_task(); + tm tm; + TZSET(); + time_t t = time(NULL); + LOCALTIME(&t, &tm); + +#if defined(__WIN32__) + *gmtoff = -timezone; + char zone[64]; + strftime(zone, sizeof(zone), "%Z", &tm); +#else + *gmtoff = tm.tm_gmtoff; + const char *zone = tm.tm_zone; + + if (zone == NULL) { + zone = ""; + } +#endif + + return make_str(task->kernel, zone, strlen(zone), "rust_timezone"); +} + +extern "C" CDECL void +rust_timegm(rust_tm* timeptr, int64_t *out) { + tm t; + rust_tm_to_tm(timeptr, &t); + *out = TIMEGM(&t); +} + +extern "C" CDECL void +rust_mktime(rust_tm* timeptr, int64_t *out) { + tm t; + rust_tm_to_tm(timeptr, &t); + *out = mktime(&t); +} + extern "C" CDECL rust_sched_id rust_get_sched_id() { rust_task *task = rust_get_current_task(); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index c9f46a45177f6..5671bfca2409f 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -12,6 +12,11 @@ debug_abi_2 get_port_id get_task_id get_time +rust_gmtime +rust_localtime +rust_timezone +rust_timegm +rust_mktime last_os_error new_port new_task diff --git a/src/rustc/middle/infer.rs b/src/rustc/middle/infer.rs index f0e2bcf40fe1b..6d52dc62d7afd 100644 --- a/src/rustc/middle/infer.rs +++ b/src/rustc/middle/infer.rs @@ -5,7 +5,7 @@ import middle::ty; import syntax::ast; import syntax::ast::{ret_style}; import util::ppaux::{ty_to_str, mt_to_str}; -import result::{result, methods, chain, chain_err, ok, err, map, map2, iter2}; +import result::{result, extensions, ok, err, map, map2, iter2}; import ty::type_is_bot; export infer_ctxt; @@ -85,7 +85,7 @@ fn fixup_vars(cx: infer_ctxt, a: ty::t) -> fres { impl methods for ures { fn then(f: fn() -> result) -> result { - chain(self) {|_i| f() } + self.chain() {|_i| f() } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 0a3e20793e19d..95681dfb1fa8f 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -4050,7 +4050,7 @@ fn trans_fn(ccx: @crate_ctxt, id: ast::node_id) { let do_time = ccx.sess.opts.stats; let start = if do_time { time::get_time() } - else { {sec: 0u32, usec: 0u32} }; + else { {sec: 0i64, usec: 0i32} }; let _icx = ccx.insn_ctxt("trans_fn"); trans_closure(ccx, path, decl, body, llfndecl, ty_self, param_substs, id, {|fcx|