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..b4c37a5d44df4 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -1,21 +1,44 @@ +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, + tm, + empty_tm, + now, + at, + now_utc, + at_utc, + strptime; + #[abi = "cdecl"] native mod rustrt { - fn get_time(&sec: u32, &usec: u32); + fn get_time(&sec: i64, &nsec: 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, &&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 timeval = {sec: u32, usec: u32}; +type timespec = {sec: i64, nsec: i32}; #[doc = " -Returns the current time as a `timeval` containing the seconds and +Returns the current time as a `timespec` containing the seconds and microseconds since 1970-01-01T00:00:00Z. "] -fn get_time() -> timeval { - let mut sec = 0u32; - let mut usec = 0u32; - rustrt::get_time(sec, usec); - ret {sec: sec, usec: usec}; +fn get_time() -> timespec { + let mut sec = 0i64; + let mut nsec = 0i32; + rustrt::get_time(sec, nsec); + ret {sec: sec, nsec: nsec}; } #[doc = " @@ -36,31 +59,791 @@ 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_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. + 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: 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"); + + uint::str(tv1.nsec as uint) + " nsec"); assert tv1.sec > some_recent_date; - assert tv1.usec < 1000000u32; + assert tv1.nsec < 1000000000i32; let tv2 = get_time(); log(debug, "tv2=" + uint::str(tv2.sec as uint) + " sec + " - + uint::str(tv2.usec as uint) + " usec"); + + uint::str(tv2.nsec as uint) + " nsec"); assert tv2.sec >= tv1.sec; assert tv2.sec < some_future_date; - assert tv2.usec < 1000000u32; + assert tv2.nsec < 1000000000i32; if tv2.sec == tv1.sec { - assert tv2.usec >= tv1.usec; + assert tv2.nsec >= tv1.nsec; } } @@ -81,4 +864,308 @@ 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, 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"); + + let time = { sec: 1234567890_i64, nsec: 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; + + // 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"); + + 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"); + + 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"); + + 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"); + + let time = { sec: 1234567890_i64, nsec: 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"; + } + + #[test] + fn test_strftime() { + os::setenv("TZ", "America/Los_Angeles"); + + 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"; + } } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 0d1b67674a156..9c2e2c3276c16 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -8,6 +8,8 @@ #include "rust_abi.h" #include "rust_port.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 *nsec) { FILETIME fileTime; GetSystemTimeAsFileTime(&fileTime); @@ -423,15 +425,22 @@ get_time(uint32_t *sec, uint32_t *usec) { const uint64_t NANOSECONDS_FROM_1601_TO_1970 = 11644473600000000u; uint64_t ns_since_1970 = ns_since_1601 - NANOSECONDS_FROM_1601_TO_1970; *sec = ns_since_1970 / 1000000; - *usec = ns_since_1970 % 1000000; + *nsec = (ns_since_1970 % 1000000) * 1000; } #else extern "C" CDECL void -get_time(uint32_t *sec, uint32_t *usec) { +get_time(int64_t *sec, int32_t *nsec) { +#ifdef __APPLE__ struct timeval tv; gettimeofday(&tv, NULL); *sec = tv.tv_sec; - *usec = tv.tv_usec; + *nsec = tv.tv_usec * 1000; +#else + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + *sec = ts.tv_sec; + *nsec = ts.tv_nsec; +#endif } #endif @@ -441,6 +450,127 @@ 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_nsec; +}; + +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, int32_t gmtoff, + const char *zone, int32_t nsec) { + 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; + out_tm->tm_gmtoff = gmtoff; + out_tm->tm_nsec = nsec; + + if (zone != NULL) { + size_t size = strlen(zone); + str_reserve_shared(&out_tm->tm_zone, size); + memcpy(out_tm->tm_zone->data, zone, size); + out_tm->tm_zone->fill = size + 1; + out_tm->tm_zone->data[size] = '\0'; + } +} + +#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 *nsec, rust_tm *timeptr) { + tm tm; + time_t s = *sec; + GMTIME(&s, &tm); + + tm_to_rust_tm(&tm, timeptr, 0, "UTC", *nsec); +} + +extern "C" CDECL void +rust_localtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) { + tm tm; + TZSET(); + time_t s = *sec; + LOCALTIME(&s, &tm); + +#if defined(__WIN32__) + int32_t gmtoff = -timezone; + char zone[64]; + strftime(zone, sizeof(zone), "%Z", &tm); +#else + int32_t gmtoff = tm.tm_gmtoff; + const char *zone = tm.tm_zone; +#endif + + tm_to_rust_tm(&tm, timeptr, gmtoff, zone, *nsec); +} + +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 cdfb5ff67a7d8..c392b7142e15a 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -12,6 +12,10 @@ debug_abi_2 get_port_id get_task_id get_time +rust_gmtime +rust_localtime +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 ec7af5ff36529..157141235b38f 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 38af204a4e74f..495622b4e522a 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -155,10 +155,10 @@ fn get_dest_addr(dest: dest) -> ValueRef { } } -fn log_fn_time(ccx: @crate_ctxt, name: str, start: time::timeval, - end: time::timeval) { +fn log_fn_time(ccx: @crate_ctxt, name: str, start: time::timespec, + end: time::timespec) { let elapsed = 1000 * ((end.sec - start.sec) as int) + - ((end.usec as int) - (start.usec as int)) / 1000; + ((end.nsec as int) - (start.nsec as int)) / 1000000; *ccx.stats.fn_times += [{ident: name, time: elapsed}]; } @@ -4056,7 +4056,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, nsec: 0i32} }; let _icx = ccx.insn_ctxt("trans_fn"); trans_closure(ccx, path, decl, body, llfndecl, ty_self, param_substs, id, {|fcx|