├── .circleci └── config.yml ├── .gitignore ├── AUTHORS ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── lib.rs ├── native.rs └── wasm.rs └── tests └── wasm.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | rust-executor: 5 | docker: 6 | - image: rust:latest 7 | 8 | jobs: 9 | build: 10 | executor: rust-executor 11 | steps: 12 | - checkout 13 | - run: 14 | name: install cargo-web 15 | command: cargo install -f cargo-web; 16 | - run: 17 | name: build 18 | command: cargo build --verbose; 19 | - run: 20 | name: build --features stdweb 21 | command: cargo web build --verbose --target wasm32-unknown-unknown --features "stdweb"; 22 | - run: 23 | name: build --features wasm-bindgen 24 | command: cargo build --verbose --target wasm32-unknown-unknown --features "wasm-bindgen"; 25 | - run: 26 | name: build --features now 27 | command: cargo build --verbose --features now; 28 | - run: 29 | name: build --features now stdweb 30 | command: cargo web build --verbose --target wasm32-unknown-unknown --features "now stdweb"; 31 | - run: 32 | name: build --features now wasm-bindgen 33 | command: cargo build --verbose --target wasm32-unknown-unknown --features "now wasm-bindgen"; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.so 4 | *.rlib 5 | *.dSYM 6 | *.dylib 7 | cargo.lock 8 | Cargo.lock 9 | target 10 | .idea 11 | wasm-pack.log 12 | bin 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Main developer: 2 | * Sébastien Crozet -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.1.12 2 | ## Added 3 | - Add `SystemTime` which works in both native and WASM environments. 4 | 5 | ## Modified 6 | - The `now` function is always available now: there is no need to enable the `now` feature any more. The `now` feature 7 | still exists (but doesn’t do anything) for backwards compatibility. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "instant" 3 | version = "0.1.13" 4 | authors = ["sebcrozet "] 5 | description = "Unmaintained, consider using web-time instead - A partial replacement for std::time::Instant that works on WASM to." 6 | repository = "https://github.com/sebcrozet/instant" 7 | readme = "README.md" 8 | license = "BSD-3-Clause" 9 | keywords = [ "time", "wasm" ] 10 | edition = "2018" 11 | 12 | [badges] 13 | maintenance = { status = "looking-for-maintainer" } 14 | 15 | [features] 16 | wasm-bindgen = ["js-sys", "wasm-bindgen_rs", "web-sys"] 17 | inaccurate = [] 18 | now = [] 19 | 20 | [dependencies] 21 | cfg-if = "1.0" 22 | 23 | [target.wasm32-unknown-unknown.dependencies] 24 | js-sys = { version = "0.3", optional = true } 25 | stdweb = { version = "0.4", optional = true } 26 | wasm-bindgen_rs = { package = "wasm-bindgen", version = "0.2", optional = true } 27 | web-sys = { version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming'] } 28 | 29 | [target.wasm32-unknown-emscripten.dependencies] 30 | js-sys = { version = "0.3", optional = true } 31 | stdweb = { version = "0.4", optional = true } 32 | wasm-bindgen_rs = { package = "wasm-bindgen", version = "0.2", optional = true } 33 | web-sys = { version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming'] } 34 | 35 | [target.asmjs-unknown-emscripten.dependencies] 36 | js-sys = { version = "0.3", optional = true } 37 | stdweb = { version = "0.4", optional = true } 38 | wasm-bindgen_rs = { package = "wasm-bindgen", version = "0.2", optional = true } 39 | web-sys = { version = "0.3", optional = true, features = ['Window', 'Performance', 'PerformanceTiming'] } 40 | 41 | [dev-dependencies] 42 | wasm-bindgen-test = "0.3" 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Sébastien Crozet 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instant 2 | 3 | **This crate is no longer maintained. Please consider creating a fork or using `web-time` instead. Or reach out if 4 | you are interested in taking over its maintenance.** 5 | 6 | If you call `std::time::Instant::now()` on a WASM platform, it will panic. This crate provides a partial 7 | replacement for `std::time::Instant` that works on WASM too. This defines the type `instant::Instant` which is: 8 | 9 | * A struct emulating the behavior of **std::time::Instant** if you are targeting `wasm32-unknown-unknown` or `wasm32-unknown-asmjs` 10 | **and** you enabled either the `stdweb` or the `wasm-bindgen` feature. This emulation is based on the javascript `performance.now()` function. 11 | * A type alias for `std::time::Instant` otherwise. 12 | 13 | 14 | 15 | Note that even if the **stdweb** or **wasm-bindgen** feature is enabled, this crate will continue to rely on `std::time::Instant` 16 | as long as you are not targeting wasm32. This allows for portable code that will work on both native and WASM platforms. 17 | 18 | This crate also exports the function `instant::now()` which returns a representation of the current time as an `f64`, expressed in milliseconds, in a platform-agnostic way. `instant::now()` will either: 19 | 20 | * Call `performance.now()` when compiling for a WASM platform with the features **stdweb** or **wasm-bindgen** enabled, or using a custom javascript function. 21 | * Return the time elapsed since the *Unix Epoch* on *native*, *non-WASM* platforms. 22 | 23 | *Note*: The old feature, `now`, has been deprecated. `instant::now()` is always exported and the `now` feature flag no longer has any effect. It remains listed in `Cargo.toml` to avoid introducing breaking changes and may be removed in future versions. 24 | 25 | ## Examples 26 | ### Using `instant` for a native platform. 27 | _Cargo.toml_: 28 | ```toml 29 | [dependencies] 30 | instant = "0.1" 31 | ``` 32 | 33 | _main.rs_: 34 | ```rust 35 | fn main() { 36 | // Will be the same as `std::time::Instant`. 37 | let now = instant::Instant::now(); 38 | } 39 | ``` 40 | 41 | ----- 42 | 43 | ### Using `instant` for a WASM platform. 44 | This example shows the use of the `stdweb` feature. It would be similar with `wasm-bindgen`. 45 | 46 | _Cargo.toml_: 47 | ```toml 48 | [dependencies] 49 | instant = { version = "0.1", features = [ "stdweb" ] } 50 | ``` 51 | 52 | _main.rs_: 53 | ```rust 54 | fn main() { 55 | // Will emulate `std::time::Instant` based on `performance.now()`. 56 | let now = instant::Instant::now(); 57 | } 58 | ``` 59 | 60 | ----- 61 | 62 | ### Using `instant` for a WASM platform where `performance.now()` is not available. 63 | This example shows the use of the `inaccurate` feature. 64 | 65 | _Cargo.toml_: 66 | ```toml 67 | [dependencies] 68 | instant = { version = "0.1", features = [ "wasm-bindgen", "inaccurate" ] } 69 | ``` 70 | 71 | _main.rs_: 72 | ```rust 73 | fn main() { 74 | // Will emulate `std::time::Instant` based on `Date.now()`. 75 | let now = instant::Instant::now(); 76 | } 77 | ``` 78 | 79 | 80 | ----- 81 | 82 | ### Using `instant` for any platform enabling a feature transitively. 83 | _Cargo.toml_: 84 | ```toml 85 | [features] 86 | stdweb = [ "instant/stdweb" ] 87 | wasm-bindgen = [ "instant/wasm-bindgen" ] 88 | 89 | [dependencies] 90 | instant = "0.1" 91 | ``` 92 | 93 | _lib.rs_: 94 | ```rust 95 | fn my_function() { 96 | // Will select the proper implementation depending on the 97 | // feature selected by the user. 98 | let now = instant::Instant::now(); 99 | } 100 | ``` 101 | 102 | ----- 103 | 104 | ### Using `instant::now()` 105 | _Cargo.toml_: 106 | ```toml 107 | [features] 108 | stdweb = [ "instant/stdweb" ] 109 | wasm-bindgen = [ "instant/wasm-bindgen" ] 110 | 111 | [dependencies] 112 | instant = "0.1" 113 | ``` 114 | 115 | _lib.rs_: 116 | ```rust 117 | fn my_function() { 118 | // Will select the proper implementation depending on the 119 | // feature selected by the user. 120 | let now_instant = instant::Instant::now(); 121 | let now_milliseconds = instant::now(); // In milliseconds. 122 | } 123 | ``` 124 | 125 | ### Using the feature `now` without `stdweb` or `wasm-bindgen`. 126 | _Cargo.toml_: 127 | ```toml 128 | [dependencies] 129 | instant = "0.1" 130 | ``` 131 | 132 | _lib.rs_: 133 | ```rust 134 | fn my_function() { 135 | // Will use the 'now' javascript implementation. 136 | let now_instant = instant::Instant::now(); 137 | let now_milliseconds = instant::now(); // In milliseconds. 138 | } 139 | ``` 140 | 141 | _javascript WASM bindings file_: 142 | ```js 143 | function now() { 144 | return Date.now() / 1000.0; 145 | } 146 | ``` 147 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(any( 3 | all(target_arch = "wasm32", not(target_os = "wasi")), 4 | target_arch = "asmjs" 5 | ))] { 6 | #[cfg(all(feature = "stdweb", not(feature = "wasm-bindgen")))] 7 | #[macro_use] 8 | extern crate stdweb; 9 | 10 | mod wasm; 11 | pub use wasm::Instant; 12 | pub use crate::wasm::now; 13 | pub use wasm::SystemTime; 14 | } else { 15 | mod native; 16 | pub use native::Instant; 17 | pub use native::now; 18 | pub use native::SystemTime; 19 | } 20 | } 21 | 22 | pub use std::time::Duration; 23 | -------------------------------------------------------------------------------- /src/native.rs: -------------------------------------------------------------------------------- 1 | pub type Instant = std::time::Instant; 2 | pub type SystemTime = std::time::SystemTime; 3 | 4 | /// The current time, expressed in milliseconds since the Unix Epoch. 5 | pub fn now() -> f64 { 6 | std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) 7 | .expect("System clock was before 1970.") 8 | .as_secs_f64() * 1000.0 9 | } 10 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::ops::{Add, AddAssign, Sub, SubAssign}; 3 | use std::time::Duration; 4 | 5 | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)] 6 | pub struct Instant(Duration); 7 | 8 | impl Ord for Instant { 9 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 10 | self.partial_cmp(other) 11 | .expect("an instant should never be NaN or Inf.") 12 | } 13 | } 14 | impl Eq for Instant {} 15 | 16 | impl Instant { 17 | #[inline] 18 | pub fn now() -> Self { 19 | Instant(duration_from_f64(now())) 20 | } 21 | 22 | #[inline] 23 | pub fn duration_since(&self, earlier: Instant) -> Duration { 24 | assert!( 25 | earlier.0 <= self.0, 26 | "`earlier` cannot be later than `self`." 27 | ); 28 | self.0 - earlier.0 29 | } 30 | 31 | #[inline] 32 | pub fn elapsed(&self) -> Duration { 33 | Self::now().duration_since(*self) 34 | } 35 | 36 | /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as 37 | /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` 38 | /// otherwise. 39 | #[inline] 40 | pub fn checked_add(&self, duration: Duration) -> Option { 41 | self.0.checked_add(duration).map(Instant) 42 | } 43 | 44 | /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as 45 | /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` 46 | /// otherwise. 47 | #[inline] 48 | pub fn checked_sub(&self, duration: Duration) -> Option { 49 | self.0.checked_sub(duration).map(Instant) 50 | } 51 | 52 | /// Returns the amount of time elapsed from another instant to this one, or None if that 53 | /// instant is later than this one. 54 | #[inline] 55 | pub fn checked_duration_since(&self, earlier: Instant) -> Option { 56 | if earlier.0 > self.0 { 57 | None 58 | } else { 59 | Some(self.0 - earlier.0) 60 | } 61 | } 62 | 63 | /// Returns the amount of time elapsed from another instant to this one, or zero duration if 64 | /// that instant is later than this one. 65 | #[inline] 66 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { 67 | self.checked_duration_since(earlier).unwrap_or_default() 68 | } 69 | } 70 | 71 | impl Add for Instant { 72 | type Output = Self; 73 | 74 | #[inline] 75 | fn add(self, rhs: Duration) -> Self { 76 | Instant(self.0 + rhs) 77 | } 78 | } 79 | 80 | impl AddAssign for Instant { 81 | #[inline] 82 | fn add_assign(&mut self, rhs: Duration) { 83 | self.0 += rhs 84 | } 85 | } 86 | 87 | impl Sub for Instant { 88 | type Output = Self; 89 | 90 | #[inline] 91 | fn sub(self, rhs: Duration) -> Self { 92 | Instant(self.0 - rhs) 93 | } 94 | } 95 | 96 | impl Sub for Instant { 97 | type Output = Duration; 98 | 99 | #[inline] 100 | fn sub(self, rhs: Instant) -> Duration { 101 | self.duration_since(rhs) 102 | } 103 | } 104 | 105 | impl SubAssign for Instant { 106 | #[inline] 107 | fn sub_assign(&mut self, rhs: Duration) { 108 | self.0 -= rhs 109 | } 110 | } 111 | 112 | fn duration_from_f64(millis: f64) -> Duration { 113 | Duration::from_millis(millis.trunc() as u64) 114 | + Duration::from_nanos((millis.fract() * 1.0e6) as u64) 115 | } 116 | 117 | #[cfg(all(feature = "stdweb", not(feature = "wasm-bindgen")))] 118 | #[allow(unused_results)] // Needed because the js macro triggers it. 119 | pub fn now() -> f64 { 120 | use stdweb::unstable::TryInto; 121 | 122 | // https://developer.mozilla.org/en-US/docs/Web/API/Performance/now 123 | #[cfg(not(feature = "inaccurate"))] 124 | let v = js! { return performance.now(); }; 125 | #[cfg(feature = "inaccurate")] 126 | let v = js! { return Date.now(); }; 127 | v.try_into().unwrap() 128 | } 129 | 130 | #[cfg(feature = "wasm-bindgen")] 131 | pub fn now() -> f64 { 132 | #[cfg(not(feature = "inaccurate"))] 133 | let now = { 134 | use wasm_bindgen_rs::prelude::*; 135 | use wasm_bindgen_rs::JsCast; 136 | js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("performance")) 137 | .expect("failed to get performance from global object") 138 | .unchecked_into::() 139 | .now() 140 | }; 141 | #[cfg(feature = "inaccurate")] 142 | let now = js_sys::Date::now(); 143 | now 144 | } 145 | 146 | // The JS now function is in a module so it won't have to be renamed 147 | #[cfg(not(any(feature = "wasm-bindgen", feature = "stdweb")))] 148 | mod js { 149 | extern "C" { 150 | #[cfg(not(target_os = "emscripten"))] 151 | pub fn now() -> f64; 152 | #[cfg(target_os = "emscripten")] 153 | pub fn _emscripten_get_now() -> f64; 154 | } 155 | } 156 | // Make the unsafe extern function "safe" so it can be called like the other 'now' functions 157 | #[cfg(not(any(feature = "wasm-bindgen", feature = "stdweb")))] 158 | pub fn now() -> f64 { 159 | #[cfg(not(target_os = "emscripten"))] 160 | return unsafe { js::now() }; 161 | #[cfg(target_os = "emscripten")] 162 | return unsafe { js::_emscripten_get_now() }; 163 | } 164 | 165 | /// Returns the number of millisecods elapsed since January 1, 1970 00:00:00 UTC. 166 | #[cfg(any(feature = "wasm-bindgen", feature = "stdweb"))] 167 | fn get_time() -> f64 { 168 | #[cfg(feature = "wasm-bindgen")] 169 | return js_sys::Date::now(); 170 | #[cfg(all(feature = "stdweb", not(feature = "wasm-bindgen")))] 171 | { 172 | let v = js! { return Date.now(); }; 173 | return v.try_into().unwrap(); 174 | } 175 | } 176 | 177 | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] 178 | pub struct SystemTime(f64); 179 | 180 | impl SystemTime { 181 | pub const UNIX_EPOCH: SystemTime = SystemTime(0.0); 182 | 183 | pub fn now() -> SystemTime { 184 | cfg_if::cfg_if! { 185 | if #[cfg(any(feature = "wasm-bindgen", feature = "stdweb"))] { 186 | SystemTime(get_time()) 187 | } else { 188 | SystemTime(now()) 189 | } 190 | } 191 | } 192 | 193 | pub fn duration_since(&self, earlier: SystemTime) -> Result { 194 | let dur_ms = self.0 - earlier.0; 195 | if dur_ms < 0.0 { 196 | return Err(()); 197 | } 198 | Ok(Duration::from_millis(dur_ms as u64)) 199 | } 200 | 201 | pub fn elapsed(&self) -> Result { 202 | Self::now().duration_since(*self) 203 | } 204 | 205 | pub fn checked_add(&self, duration: Duration) -> Option { 206 | Some(*self + duration) 207 | } 208 | 209 | pub fn checked_sub(&self, duration: Duration) -> Option { 210 | Some(*self - duration) 211 | } 212 | } 213 | 214 | impl Add for SystemTime { 215 | type Output = SystemTime; 216 | 217 | fn add(self, other: Duration) -> SystemTime { 218 | SystemTime(self.0 + other.as_millis() as f64) 219 | } 220 | } 221 | 222 | impl Sub for SystemTime { 223 | type Output = SystemTime; 224 | 225 | fn sub(self, other: Duration) -> SystemTime { 226 | SystemTime(self.0 - other.as_millis() as f64) 227 | } 228 | } 229 | 230 | impl AddAssign for SystemTime { 231 | fn add_assign(&mut self, rhs: Duration) { 232 | *self = *self + rhs; 233 | } 234 | } 235 | 236 | impl SubAssign for SystemTime { 237 | fn sub_assign(&mut self, rhs: Duration) { 238 | *self = *self - rhs; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /tests/wasm.rs: -------------------------------------------------------------------------------- 1 | extern crate wasm_bindgen_test; 2 | 3 | use instant::{Instant, SystemTime}; 4 | use std::time::Duration; 5 | use wasm_bindgen_test::*; 6 | 7 | wasm_bindgen_test_configure!(run_in_browser); 8 | // run these tests using: wasm-pack test --chrome --headless -- --features wasm-bindgen 9 | 10 | #[wasm_bindgen_test] 11 | fn test_instant_now() { 12 | let now = Instant::now(); 13 | #[cfg(feature = "inaccurate")] 14 | while now.elapsed().as_millis() == 0 {} 15 | #[cfg(not(feature = "inaccurate"))] 16 | assert!(now.elapsed().as_nanos() > 0); 17 | } 18 | 19 | #[wasm_bindgen_test] 20 | fn test_duration() { 21 | let now = Instant::now(); 22 | let one_sec = Duration::from_secs(1); 23 | assert!(now.elapsed() < one_sec); 24 | } 25 | 26 | // Duration::new will overflow when you have u64::MAX seconds and one billion nanoseconds. 27 | // 28 | const ONE_BILLION: u32 = 1_000_000_000; 29 | 30 | #[wasm_bindgen_test] 31 | fn test_checked_add() { 32 | let now = Instant::now(); 33 | 34 | assert!(now.checked_add(Duration::from_millis(1)).is_some()); 35 | assert_eq!( 36 | None, 37 | now.checked_add(Duration::new(u64::MAX, ONE_BILLION - 1)) 38 | ); 39 | } 40 | 41 | #[wasm_bindgen_test] 42 | fn test_checked_sub() { 43 | let now = Instant::now(); 44 | 45 | assert!(now.checked_sub(Duration::from_millis(1)).is_some()); 46 | assert!(now 47 | .checked_sub(Duration::new(u64::MAX, ONE_BILLION - 1)) 48 | .is_none()); 49 | } 50 | 51 | #[wasm_bindgen_test] 52 | fn test_system_time() { 53 | assert!(SystemTime::UNIX_EPOCH 54 | .duration_since(SystemTime::now()) 55 | .is_err()); 56 | } 57 | 58 | --------------------------------------------------------------------------------