about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-11-07 21:41:07 -0800
committerbors <bors@rust-lang.org>2013-11-07 21:41:07 -0800
commit075347b4453e4b4bc4dd2d2370e408e92041d4ce (patch)
treee28cf414eab24bbea7ecb8caf385c20862b9b078
parentd26776a7755abf2227ba7a75f657c561999ed497 (diff)
parentfe18fe0f884ab4edb2b0a986b68572b361285599 (diff)
downloadrust-075347b4453e4b4bc4dd2d2370e408e92041d4ce.tar.gz
rust-075347b4453e4b4bc4dd2d2370e408e92041d4ce.zip
auto merge of #10281 : klutzy/rust/rt-timezone, r=alexcrichton
Previously #9418 fixed utf-8 assertion issue by wcsftime,
but the function didn't work as expected: it follows the locale
set by setlocale(), not the system code page.
This patch fixes it by manual multibyte-to-unicode conversion.
-rw-r--r--src/libextra/time.rs99
-rw-r--r--src/rt/rust_builtin.cpp13
2 files changed, 68 insertions, 44 deletions
diff --git a/src/libextra/time.rs b/src/libextra/time.rs
index 2950f02ad19..eff0da96bff 100644
--- a/src/libextra/time.rs
+++ b/src/libextra/time.rs
@@ -965,8 +965,35 @@ mod tests {
     use super::*;
 
     use std::f64;
-    use std::os;
     use std::result::{Err, Ok};
+    use std::libc;
+
+    #[cfg(windows)]
+    #[fixed_stack_segment]
+    fn set_time_zone() {
+        // Windows crt doesn't see any environment variable set by
+        // `SetEnvironmentVariable`, which `os::setenv` internally uses.
+        // It is why we use `putenv` here.
+        extern {
+            fn _putenv(envstring: *libc::c_char) -> libc::c_int;
+        }
+
+        unsafe {
+            // Windows does not understand "America/Los_Angeles".
+            // PST+08 may look wrong, but not! "PST" indicates
+            // the name of timezone. "+08" means UTC = local + 08.
+            do "TZ=PST+08".with_c_str |env| {
+                _putenv(env);
+            }
+        }
+        tzset();
+    }
+    #[cfg(not(windows))]
+    fn set_time_zone() {
+        use std::os;
+        os::setenv("TZ", "America/Los_Angeles");
+        tzset();
+    }
 
     fn test_get_time() {
         static SOME_RECENT_DATE: i64 = 1325376000i64; // 2012-01-01T00:00:00Z
@@ -1007,57 +1034,54 @@ mod tests {
     }
 
     fn test_at_utc() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         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);
+        assert_eq!(utc.tm_sec, 30_i32);
+        assert_eq!(utc.tm_min, 31_i32);
+        assert_eq!(utc.tm_hour, 23_i32);
+        assert_eq!(utc.tm_mday, 13_i32);
+        assert_eq!(utc.tm_mon, 1_i32);
+        assert_eq!(utc.tm_year, 109_i32);
+        assert_eq!(utc.tm_wday, 5_i32);
+        assert_eq!(utc.tm_yday, 43_i32);
+        assert_eq!(utc.tm_isdst, 0_i32);
+        assert_eq!(utc.tm_gmtoff, 0_i32);
+        assert_eq!(utc.tm_zone, ~"UTC");
+        assert_eq!(utc.tm_nsec, 54321_i32);
     }
 
     fn test_at() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         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);
+        assert_eq!(local.tm_sec, 30_i32);
+        assert_eq!(local.tm_min, 31_i32);
+        assert_eq!(local.tm_hour, 15_i32);
+        assert_eq!(local.tm_mday, 13_i32);
+        assert_eq!(local.tm_mon, 1_i32);
+        assert_eq!(local.tm_year, 109_i32);
+        assert_eq!(local.tm_wday, 5_i32);
+        assert_eq!(local.tm_yday, 43_i32);
+        assert_eq!(local.tm_isdst, 0_i32);
+        assert_eq!(local.tm_gmtoff, -28800_i32);
 
         // FIXME (#2350): 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);
+        assert_eq!(local.tm_nsec, 54321_i32);
     }
 
     fn test_to_timespec() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         let utc = at_utc(time);
@@ -1067,8 +1091,7 @@ mod tests {
     }
 
     fn test_conversions() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         let utc = at_utc(time);
@@ -1083,8 +1106,7 @@ mod tests {
     }
 
     fn test_strptime() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         match strptime("", "") {
           Ok(ref tm) => {
@@ -1248,8 +1270,7 @@ mod tests {
     }
 
     fn test_ctime() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         let utc   = at_utc(time);
@@ -1262,8 +1283,7 @@ mod tests {
     }
 
     fn test_strftime() {
-        os::setenv("TZ", "America/Los_Angeles");
-        tzset();
+        set_time_zone();
 
         let time = Timespec::new(1234567890, 54321);
         let utc = at_utc(time);
@@ -1323,8 +1343,7 @@ mod tests {
         // abbreviation.
         let rfc822 = local.rfc822();
         let prefix = ~"Fri, 13 Feb 2009 15:31:30 ";
-        assert!(rfc822 == prefix + "PST" ||
-                     rfc822 == prefix + "Pacific Standard Time");
+        assert!(rfc822 == prefix + "PST" || rfc822 == prefix + "Pacific Standard Time");
 
         assert_eq!(local.ctime(), ~"Fri Feb 13 15:31:30 2009");
         assert_eq!(local.rfc822z(), ~"Fri, 13 Feb 2009 15:31:30 -0800");
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index e8141d37ff1..66c8407413b 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -330,11 +330,16 @@ rust_localtime(int64_t sec, int32_t nsec, rust_tm *timeptr) {
     const char* zone = NULL;
 #if defined(__WIN32__)
     int32_t gmtoff = -timezone;
-    wchar_t wbuffer[64];
-    char buffer[256];
+    wchar_t wbuffer[64] = {0};
+    char buffer[256] = {0};
     // strftime("%Z") can contain non-UTF-8 characters on non-English locale (issue #9418),
-    // so time zone should be converted from UTF-16 string set by wcsftime.
-    if (wcsftime(wbuffer, sizeof(wbuffer) / sizeof(wchar_t), L"%Z", &tm) > 0) {
+    // so time zone should be converted from UTF-16 string.
+    // Since wcsftime depends on setlocale() result,
+    // instead we convert it using MultiByteToWideChar.
+    if (strftime(buffer, sizeof(buffer) / sizeof(char), "%Z", &tm) > 0) {
+        // ANSI -> UTF-16
+        MultiByteToWideChar(CP_ACP, 0, buffer, -1, wbuffer, sizeof(wbuffer) / sizeof(wchar_t));
+        // UTF-16 -> UTF-8
         WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, buffer, sizeof(buffer), NULL, NULL);
         zone = buffer;
     }