Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "libplatform/libplatform.h"
#include "support.h"
#include "unicode/locid.h"
#include "unicode/timezone.h"
#include "unicode/unistr.h"
#include "v8-callbacks.h"
#include "v8-cppgc.h"
#include "v8-fast-api-calls.h"
Expand Down Expand Up @@ -4286,6 +4288,24 @@ void icu_set_default_locale(const char* locale) {
icu::Locale::setDefault(icu::Locale(locale), status);
}

size_t icu_get_default_timezone(char* output, size_t output_len) {
icu::UnicodeString id;
std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createDefault());
tz->getID(id);
icu_77::CheckedArrayByteSink sink(output, static_cast<uint32_t>(output_len));
id.toUTF8(sink);
assert(!sink.Overflowed());
return sink.NumberOfBytesAppended();
}

void icu_set_default_timezone(const char* timezone) {
// Takes ownership of the created TimeZone and installs it as the process
// wide default, so ICU (and therefore V8's Date implementation) resolves
// the given IANA id regardless of how the host reports its time zone.
icu::TimeZone::adoptDefault(
icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(timezone)));
}

} // extern "C"

// v8::PropertyDescriptor
Expand Down
28 changes: 28 additions & 0 deletions src/icu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::ffi::CString;
unsafe extern "C" {
fn icu_get_default_locale(output: *mut char, output_len: usize) -> usize;
fn icu_set_default_locale(locale: *const char);
fn icu_get_default_timezone(output: *mut char, output_len: usize) -> usize;
fn icu_set_default_timezone(timezone: *const char);
fn udata_setCommonData_77(this: *const u8, error_code: *mut i32);
}

Expand Down Expand Up @@ -69,3 +71,29 @@ pub fn set_default_locale(locale: &str) {
icu_set_default_locale(c_str.as_ptr());
}
}

/// Returns the IANA id of ICU's current default time zone (e.g.
/// `"America/New_York"`).
pub fn get_default_time_zone() -> String {
let mut output = [0u8; 1024];
let len = unsafe {
icu_get_default_timezone(output.as_mut_ptr() as *mut char, output.len())
};
std::str::from_utf8(&output[..len]).unwrap().to_owned()
}

/// Sets ICU's default time zone from an IANA id (e.g. `"UTC"`,
/// `"Asia/Manila"`). This makes `Date` resolve the given zone on every
/// platform, including Windows, where ICU otherwise reads the host time
/// zone from the OS and ignores the `TZ` environment variable.
///
/// After calling this, notify each isolate via
/// [`crate::Isolate::date_time_configuration_change_notification`] with
/// [`crate::TimeZoneDetection::Skip`] so cached values are refreshed
/// without re-detecting (and overwriting) the zone just set here.
pub fn set_default_time_zone(timezone: &str) {
unsafe {
let c_str = CString::new(timezone).expect("Invalid timezone");
icu_set_default_timezone(c_str.as_ptr());
}
}
29 changes: 29 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9812,6 +9812,35 @@ fn icu_set_common_data_fail() {
);
}

#[test]
fn icu_default_time_zone() {
// Mutates the process-wide ICU default time zone, which also affects how
// other tests' `Date`s render, so take the exclusive lock.
let _setup_guard = setup::sequential_test();
let original = v8::icu::get_default_time_zone();

v8::icu::set_default_time_zone("America/New_York");
assert_eq!(v8::icu::get_default_time_zone(), "America/New_York");

let isolate = &mut v8::Isolate::new(Default::default());
isolate
.date_time_configuration_change_notification(v8::TimeZoneDetection::Skip);
{
v8::scope!(let scope, isolate);
let context = v8::Context::new(scope, Default::default());
let scope = &mut v8::ContextScope::new(scope, context);
// 2020-06-26T07:00:00Z is 03:00 in America/New_York (EDT, UTC-4).
let source = r#"
new Date(Date.UTC(2020, 5, 26, 7, 0, 0)).getHours();
"#;
let value = eval(scope, source).unwrap();
assert_eq!(value.number_value(scope).unwrap(), 3.0);
}

// Restore the original default so test ordering doesn't matter.
v8::icu::set_default_time_zone(&original);
}

#[test]
fn icu_format() {
let _setup_guard = setup::parallel_test();
Expand Down
Loading