Repository: time-rs/time (language: rust)

Issue:
Doesn't build anymore for webassembly target
I'm using the time package as a transitive dependency. The latest build seems incompatible with webassembly. Building my project with "cargo web build" yields the following error message:

       Compiling time v0.2.7
    error[E0308]: mismatched types
       --> /home/hc/.cargo/registry/src/github.com-1ecc6299db9ec823/time-0.2.7/src/utc_offset.rs:294:53
        |
    294 | fn try_local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
        |    -------------------                              ^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found `()`
        |    |
        |    implicitly returns `()` as its body has no tail or `return` expression
        |
        = note:   expected enum `std::option::Option<utc_offset::UtcOffset>`
                found unit type `()`




Discussion / hints:
Looking a bit at the source code, I'd say this is caused by the conditional compilation statements and for webassembly, no implementation exists yet
I'll try to fix this myself and create a pull request
Yeah, that sounds about right. I'd be fine just automatically returning `None` there for now.

Actually, having a catch-all for anything else would probably be best.

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index b382fffb7..f266e6428 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ readme = "README.md"
 license = "MIT OR Apache-2.0"
 # TODO(upstream) Add GitHub Actions badge once rust-lang/crates.io#1838 is merged.
 description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]."
+build = "build.rs"
 
 [package.metadata.docs.rs]
 all-features = true
@@ -18,7 +19,7 @@ all-features = true
 default = ["deprecated", "std"]
 deprecated = []
 panicking-api = []
-std = ["libc", "winapi"]
+std = ["libc", "winapi", "stdweb"]
 
 # Internal usage. This is used when building for docs.rs and time-rs.github.io.
 # This feature should never be used by external users. It will likely be
@@ -26,6 +27,7 @@ std = ["libc", "winapi"]
 __doc = []
 
 [dependencies]
+cfg-if = "0.1"
 rand = { version = "0.7", optional = true, default-features = false }
 rustversion = "1"
 serde = { version = "1", optional = true, default-features = false, features = ["derive"] }
@@ -39,3 +41,6 @@ libc = { version = "0.2", optional = true }
 
 [target.'cfg(windows)'.dependencies]
 winapi = { version = "0.3", features = ["minwinbase", "minwindef", "timezoneapi"], optional = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+stdweb = { version = "0.4", default-features = false, optional = true }
diff --git a/build.rs b/build.rs
new file mode 100644
index 000000000..1813e4293
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,7 @@
+use std::env;
+
+fn main() {
+    if env::var("COMPILING_UNDER_CARGO_WEB") == Ok("1".into()) {
+        println!("cargo:rustc-cfg=cargo_web")
+    }
+}
diff --git a/src/utc_offset.rs b/src/utc_offset.rs
index 01cf6e58d..af29d5fde 100644
--- a/src/utc_offset.rs
+++ b/src/utc_offset.rs
@@ -7,6 +7,8 @@ use crate::{
     DeferredFormat, Duration,
 };
 use core::fmt::{self, Display};
+#[cfg(cargo_web)]
+use stdweb::js;
 
 /// An offset from UTC.
 ///
@@ -290,165 +292,181 @@ impl Display for UtcOffset {
 /// Attempt to obtain the system's UTC offset. If the offset cannot be
 /// determined, `None` is returned.
 #[cfg(feature = "std")]
-#[allow(clippy::too_many_lines)] //
+#[allow(clippy::too_many_lines)]
 fn try_local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
-    use core::{convert::TryInto, mem};
-
-    #[cfg(target_family = "unix")]
-    {
-        /// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on
-        /// any error.
-        fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
-            extern "C" {
-                fn tzset();
-            }
+    cfg_if::cfg_if! {
+        if #[cfg(target_family = "unix")] {
+            use core::{convert::TryInto, mem};
+
+            /// Convert the given Unix timestamp to a `libc::tm`. Returns `None`
+            /// on any error.
+            fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
+                extern "C" {
+                    fn tzset();
+                }
 
-            let timestamp = timestamp.try_into().ok()?;
+                let timestamp = timestamp.try_into().ok()?;
 
-            // Safety: Plain old data.
-            #[allow(unsafe_code)]
-            let mut tm = unsafe { mem::zeroed() };
+                // Safety: Plain old data.
+                #[allow(unsafe_code)]
+                    let mut tm = unsafe { mem::zeroed() };
 
-            // Update timezone information from system. `localtime_r` does not
-            // do this for us.
-            //
-            // Safety: tzset is thread-safe.
-            #[allow(unsafe_code)]
-            unsafe {
-                tzset();
-            }
+                // Update timezone information from system. `localtime_r` does
+                // not do this for us.
+                //
+                // Safety: tzset is thread-safe.
+                #[allow(unsafe_code)]
+                    unsafe {
+                    tzset();
+                }
 
-            // Safety: We are calling a system API, which mutates the `tm`
-            // variable. If a null pointer is returned, an error occurred.
-            #[allow(unsafe_code)]
-            let tm_ptr = unsafe { libc::localtime_r(&timestamp, &mut tm) };
+                // Safety: We are calling a system API, which mutates the `tm`
+                // variable. If a null pointer is returned, an error occurred.
+                #[allow(unsafe_code)]
+                    let tm_ptr = unsafe { libc::localtime_r(&timestamp, &mut tm) };
 
-            if tm_ptr.is_null() {
-                None
-            } else {
-                Some(tm)
+                if tm_ptr.is_null() {
+                    None
+                } else {
+                    Some(tm)
+                }
             }
-        }
 
-        let tm = timestamp_to_tm(datetime.timestamp())?;
+            let tm = timestamp_to_tm(datetime.timestamp())?;
 
-        // `tm_gmtoff` extension
-        #[cfg(not(target_os = "solaris"))]
-        {
-            tm.tm_gmtoff.try_into().ok().map(UtcOffset::seconds)
-        }
+            // `tm_gmtoff` extension
+            #[cfg(not(target_os = "solaris"))]
+                {
+                    tm.tm_gmtoff.try_into().ok().map(UtcOffset::seconds)
+                }
+
+            // No `tm_gmtoff` extension
+            #[cfg(target_os = "solaris")]
+                {
+                    use crate::Date;
+                    use core::convert::TryFrom;
+
+                    let mut tm = tm;
+                    if tm.tm_sec == 60 {
+                        // Leap seconds are not currently supported.
+                        tm.tm_sec = 59;
+                    }
+
+                    let local_timestamp =
+                        Date::try_from_yo(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
+                            .ok()?
+                            .try_with_hms(
+                                tm.tm_hour.try_into().ok()?,
+                                tm.tm_min.try_into().ok()?,
+                                tm.tm_sec.try_into().ok()?,
+                            )
+                            .ok()?
+                            .assume_utc()
+                            .timestamp();
+
+                    (local_timestamp - datetime.timestamp())
+                        .try_into()
+                        .ok()
+                        .map(UtcOffset::seconds)
+                }
+        } else if #[cfg(target_family = "windows")] {
+            use core::{convert::TryInto, mem};
+            use crate::offset;
+            use winapi::{
+                shared::minwindef::FILETIME,
+                um::{
+                    minwinbase::SYSTEMTIME,
+                    timezoneapi::{SystemTimeToFileTime, SystemTimeToTzSpecificLocalTime},
+                },
+            };
+
+            /// Convert a `SYSTEMTIME` to a `FILETIME`. Returns `None` if any
+            /// error occurred.
+            fn systemtime_to_filetime(systime: &SYSTEMTIME) -> Option<FILETIME> {
+                // Safety: We only read `ft` if it is properly initialized.
+                #[allow(unsafe_code, deprecated)]
+                    let mut ft = unsafe { mem::uninitialized() };
+
+                // Safety: `SystemTimeToFileTime` is thread-safe.
+                #[allow(unsafe_code)]
+                    {
+                        if 0 == unsafe { SystemTimeToFileTime(systime, &mut ft) } {
+                            // failed
+                            None
+                        } else {
+                            Some(ft)
+                        }
+                    }
+            }
 
-        // No `tm_gmtoff` extension
-        #[cfg(target_os = "solaris")]
-        {
-            use crate::Date;
-            use core::convert::TryFrom;
+            /// Convert a `FILETIME` to an `i64`, representing a number of
+            /// seconds.
+            fn filetime_to_secs(filetime: &FILETIME) -> i64 {
+                /// FILETIME represents 100-nanosecond intervals
+                const FT_TO_SECS: i64 = 10_000_000;
+                ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) /
+                    FT_TO_SECS
+            }
 
-            let mut tm = tm;
-            if tm.tm_sec == 60 {
-                // Leap seconds are not currently supported.
-                tm.tm_sec = 59;
+            /// Convert an `OffsetDateTime` to a `SYSTEMTIME`.
+            fn offset_to_systemtime(datetime: OffsetDateTime) -> SYSTEMTIME {
+                let (month, day_of_month) = datetime.to_offset(offset!(UTC)).month_day();
+                SYSTEMTIME {
+                    wYear: datetime.year() as u16,
+                    wMonth: month as u16,
+                    wDay: day_of_month as u16,
+                    wDayOfWeek: 0, // ignored
+                    wHour: datetime.hour() as u16,
+                    wMinute: datetime.minute() as u16,
+                    wSecond: datetime.second() as u16,
+                    wMilliseconds: datetime.millisecond(),
+                }
             }
 
-            let local_timestamp =
-                Date::try_from_yo(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
-                    .ok()?
-                    .try_with_hms(
-                        tm.tm_hour.try_into().ok()?,
-                        tm.tm_min.try_into().ok()?,
-                        tm.tm_sec.try_into().ok()?,
-                    )
-                    .ok()?
-                    .assume_utc()
-                    .timestamp();
-
-            (local_timestamp - datetime.timestamp())
-                .try_into()
-                .ok()
-                .map(UtcOffset::seconds)
-        }
-    }
+            // This function falls back to UTC if any system call fails.
+            let systime_utc = offset_to_systemtime(datetime.to_offset(offset!(UTC)));
 
-    #[cfg(target_family = "windows")]
-    {
-        use crate::offset;
-        use winapi::{
-            shared::minwindef::FILETIME,
-            um::{
-                minwinbase::SYSTEMTIME,
-                timezoneapi::{SystemTimeToFileTime, SystemTimeToTzSpecificLocalTime},
-            },
-        };
-
-        /// Convert a `SYSTEMTIME` to a `FILETIME`. Returns `None` if any error
-        /// occurred.
-        fn systemtime_to_filetime(systime: &SYSTEMTIME) -> Option<FILETIME> {
-            // Safety: We only read `ft` if it is properly initialized.
-            #[allow(unsafe_code, deprecated)]
-            let mut ft = unsafe { mem::uninitialized() };
-
-            // Safety: `SystemTimeToFileTime` is thread-safe.
+            // Safety: `local_time` is only read if it is properly initialized, and
+            // `SystemTimeToTzSpecificLocalTime` is thread-safe.
             #[allow(unsafe_code)]
-            {
-                if 0 == unsafe { SystemTimeToFileTime(systime, &mut ft) } {
-                    // failed
-                    None
+                let systime_local = unsafe {
+                #[allow(deprecated)]
+                    let mut local_time = mem::uninitialized();
+                if 0 == SystemTimeToTzSpecificLocalTime(
+                    core::ptr::null(), // use system's current timezone
+                    &systime_utc,
+                    &mut local_time,
+                ) {
+                    // call failed
+                    return None;
                 } else {
-                    Some(ft)
+                    local_time
                 }
-            }
-        }
+            };
 
-        /// Convert a `FILETIME` to an `i64`, representing a number of seconds.
-        fn filetime_to_secs(filetime: &FILETIME) -> i64 {
-            /// FILETIME represents 100-nanosecond intervals
-            const FT_TO_SECS: i64 = 10_000_000;
-            ((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS
-        }
+            // Convert SYSTEMTIMEs to FILETIMEs so we can perform arithmetic on
+            // them.
+            let ft_system = systemtime_to_filetime(&systime_utc)?;
+            let ft_local = systemtime_to_filetime(&systime_local)?;
 
-        /// Convert an `OffsetDateTime` to a `SYSTEMTIME`.
-        fn offset_to_systemtime(datetime: OffsetDateTime) -> SYSTEMTIME {
-            let (month, day_of_month) = datetime.to_offset(offset!(UTC)).month_day();
-            SYSTEMTIME {
-                wYear: datetime.year() as u16,
-                wMonth: month as u16,
-                wDay: day_of_month as u16,
-                wDayOfWeek: 0, // ignored
-                wHour: datetime.hour() as u16,
-                wMinute: datetime.minute() as u16,
-                wSecond: datetime.second() as u16,
-                wMilliseconds: datetime.millisecond(),
-            }
-        }
+            let diff_secs = filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system);
 
-        // This function falls back to UTC if any system call fails.
-        let systime_utc = offset_to_systemtime(datetime.to_offset(offset!(UTC)));
-
-        // Safety: `local_time` is only read if it is properly initialized, and
-        // `SystemTimeToTzSpecificLocalTime` is thread-safe.
-        #[allow(unsafe_code)]
-        let systime_local = unsafe {
-            #[allow(deprecated)]
-            let mut local_time = mem::uninitialized();
-            if 0 == SystemTimeToTzSpecificLocalTime(
-                core::ptr::null(), // use system's current timezone
-                &systime_utc,
-                &mut local_time,
-            ) {
-                // call failed
-                return None;
-            } else {
-                local_time
-            }
-        };
+            diff_secs.try_into().ok().map(UtcOffset::seconds)
+        } else if #[cfg(cargo_web)] {
+            use stdweb::unstable::TryInto;
 
-        // Convert SYSTEMTIMEs to FILETIMEs so we can perform arithmetic on them.
-        let ft_system = systemtime_to_filetime(&systime_utc)?;
-        let ft_local = systemtime_to_filetime(&systime_local)?;
+            let timestamp_utc = datetime.timestamp();
+            let low_bits = (timestamp_utc & 0xFF_FF_FF_FF) as i32;
+            let high_bits = (timestamp_utc >> 32) as i32;
 
-        let diff_secs = filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system);
+            let timezone_offset = js! {
+                return new Date(@{high_bits} << 32 + @{low_bits}).getTimezoneOffset() * -60;
+            };
 
-        diff_secs.try_into().ok().map(UtcOffset::seconds)
+            timezone_offset.try_into().ok().map(UtcOffset::seconds)
+        } else {
+            None
+        }
     }
 }
 


Test patch: