diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c97c0b160..a485fbe99 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -117,7 +117,6 @@ jobs: os: [ubuntu-latest] target: [ - wasm32-unknown-unknown, wasm32-wasi, wasm32-unknown-emscripten, aarch64-apple-ios, @@ -130,19 +129,11 @@ jobs: with: targets: ${{ matrix.target }} - uses: Swatinem/rust-cache@v2 - - uses: actions/setup-node@v3 with: node-version: "12" - - run: | - set -euxo pipefail - export RUST_BACKTRACE=1 - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf \ - | bash --noprofile --norc - wasm-pack --version - shell: bash - run: cargo build --target ${{ matrix.target }} --color=always - features_check_wasm: + test_wasm: strategy: matrix: os: [ubuntu-latest] @@ -154,10 +145,11 @@ jobs: targets: wasm32-unknown-unknown - uses: taiki-e/install-action@cargo-hack - uses: Swatinem/rust-cache@v2 - - run: | - cargo hack check --feature-powerset --optional-deps serde,rkyv \ - --skip default --skip __internal_bench --skip __doctest \ - --skip iana-time-zone --skip pure-rust-locales + - uses: actions/setup-node@v3 + - uses: jetli/wasm-pack-action@v0.4.0 + # The `TZ` and `NOW` variables are used to compare the results inside the WASM environment + # with the host system. + - run: TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind cross-targets: strategy: diff --git a/src/offset/local/mod.rs b/src/offset/local/mod.rs index 372fd4976..9e7d30335 100644 --- a/src/offset/local/mod.rs +++ b/src/offset/local/mod.rs @@ -50,15 +50,34 @@ mod inner { not(any(target_os = "emscripten", target_os = "wasi")) ))] mod inner { - use crate::{FixedOffset, LocalResult, NaiveDateTime}; + use crate::{Datelike, FixedOffset, LocalResult, NaiveDateTime, Timelike}; - pub(super) fn offset_from_utc_datetime(_utc: &NaiveDateTime) -> LocalResult { - let offset = js_sys::Date::new_0().get_timezone_offset(); + pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> LocalResult { + let offset = js_sys::Date::from(utc.and_utc()).get_timezone_offset(); LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap()) } pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> LocalResult { - offset_from_utc_datetime(local) + let mut year = local.year(); + if year < 100 { + // The API in `js_sys` does not let us create a `Date` with negative years. + // And values for years from `0` to `99` map to the years `1900` to `1999`. + // Shift the value by a multiple of 400 years until it is `>= 100`. + let shift_cycles = (year - 100).div_euclid(400); + year -= shift_cycles * 400; + } + let js_date = js_sys::Date::new_with_year_month_day_hr_min_sec( + year as u32, + local.month0() as i32, + local.day() as i32, + local.hour() as i32, + local.minute() as i32, + local.second() as i32, + // ignore milliseconds, our representation of leap seconds may be problematic + ); + let offset = js_date.get_timezone_offset(); + // We always get a result, even if this time does not exist or is ambiguous. + LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap()) } } @@ -95,33 +114,9 @@ impl Local { } /// Returns a `DateTime` which corresponds to the current date and time. - #[cfg(not(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) - )))] - #[must_use] pub fn now() -> DateTime { Utc::now().with_timezone(&Local) } - - /// Returns a `DateTime` which corresponds to the current date and time. - #[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) - ))] - #[must_use] - pub fn now() -> DateTime { - use super::Utc; - let now: DateTime = super::Utc::now(); - - // Workaround missing timezone logic in `time` crate - let offset = - FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60) - .unwrap(); - DateTime::from_utc(now.naive_utc(), offset) - } } impl TimeZone for Local { diff --git a/tests/wasm.rs b/tests/wasm.rs index f003d4db9..3cc3dc039 100644 --- a/tests/wasm.rs +++ b/tests/wasm.rs @@ -1,11 +1,18 @@ +//! Run this test with: +//! `env TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind` +//! +//! The `TZ` and `NOW` variables are used to compare the results inside the WASM environment with +//! the host system. +//! The check will fail if the local timezone does not match one of the timezones defined below. + #![cfg(all( target_arch = "wasm32", feature = "wasmbind", not(any(target_os = "emscripten", target_os = "wasi")) ))] -use self::chrono::prelude::*; -use self::wasm_bindgen_test::*; +use chrono::prelude::*; +use wasm_bindgen_test::*; #[wasm_bindgen_test] fn now() { @@ -52,7 +59,7 @@ fn from_is_exact() { let dt = DateTime::::from(now.clone()); - assert_eq!(now.get_time() as i64, dt.timestamp_millis_opt().unwrap()); + assert_eq!(now.get_time() as i64, dt.timestamp_millis()); } #[wasm_bindgen_test] @@ -72,7 +79,7 @@ fn convert_all_parts_with_milliseconds() { let js_date = js_sys::Date::from(time); assert_eq!(js_date.get_utc_full_year(), 2020); - assert_eq!(js_date.get_utc_month(), 12); + assert_eq!(js_date.get_utc_month(), 11); // months are numbered 0..=11 assert_eq!(js_date.get_utc_date(), 1); assert_eq!(js_date.get_utc_hours(), 3); assert_eq!(js_date.get_utc_minutes(), 1);