mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Read the browser clock for NOW(), TODAY() and year-less dates on wasm (#202)
This commit is contained in:
Generated
+1
@@ -1469,6 +1469,7 @@ dependencies = [
|
||||
name = "gridcraft-core"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.21",
|
||||
|
||||
@@ -919,17 +919,11 @@ impl Calc {
|
||||
}
|
||||
}
|
||||
|
||||
/// Local date-time as a serial (1900 system). Wasm without a clock returns a fixed date.
|
||||
#[allow(clippy::disallowed_methods)] // the clock is read only off wasm
|
||||
/// Current date-time as a serial (1900 system), from the system clock (browser clock on wasm).
|
||||
/// UTC; the local offset isn't available without platform calls. Falls back to a fixed date only
|
||||
/// when no clock is available.
|
||||
pub fn now_serial() -> f64 {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
if let Ok(d) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
|
||||
// UTC; local offset isn't available without platform calls.
|
||||
return 25569.0 + d.as_secs_f64() / 86400.0;
|
||||
}
|
||||
}
|
||||
46302.5
|
||||
gridcraft_core::date::unix_now_ms().map_or(46302.5, gridcraft_core::date::serial_from_unix_ms)
|
||||
}
|
||||
|
||||
/// Evaluates a formula in the context of a cell without storing it (conditional formats, data
|
||||
|
||||
@@ -12,5 +12,8 @@ workspace = true
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -39,6 +39,26 @@ pub fn days_from_civil(y: i32, m: u32, d: u32) -> i64 {
|
||||
era * 146097 + doe - 719468
|
||||
}
|
||||
|
||||
/// Milliseconds since the Unix epoch (UTC) from the system clock; `None` when the clock is unavailable.
|
||||
/// On wasm32 `std::time::SystemTime::now` traps, so the browser clock is read through `Date.now()`.
|
||||
#[allow(clippy::disallowed_methods)] // the std clock is read only off wasm
|
||||
pub fn unix_now_ms() -> Option<f64> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let d = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).ok()?;
|
||||
Some(d.as_secs_f64() * 1000.0)
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
Some(js_sys::Date::now())
|
||||
}
|
||||
}
|
||||
|
||||
/// Date serial (1900 system, with fraction) for a Unix time in milliseconds.
|
||||
pub fn serial_from_unix_ms(ms: f64) -> f64 {
|
||||
25569.0 + ms / 86_400_000.0
|
||||
}
|
||||
|
||||
pub fn civil_from_days(z: i64) -> (i32, u32, u32) {
|
||||
let z = z + 719468;
|
||||
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
||||
@@ -160,6 +180,14 @@ pub const WEEKDAYS: [&str; 7] = ["Sunday", "Monday", "Tuesday", "Wednesday", "Th
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serial_from_unix_time() {
|
||||
assert_eq!(serial_from_unix_ms(0.0), 25569.0);
|
||||
// 2026-10-10T12:00:00Z
|
||||
assert_eq!(serial_from_unix_ms(1_791_633_600_000.0), 46305.5);
|
||||
assert!(unix_now_ms().is_some_and(|ms| serial_from_unix_ms(ms) > 46305.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_serials() {
|
||||
let s = DateSystem::D1900;
|
||||
|
||||
@@ -273,16 +273,12 @@ fn valid_ymd(sys: DateSystem, y: i64, m: i64, d: i64) -> Option<f64> {
|
||||
serial_from_ymd(sys, y, m, d)
|
||||
}
|
||||
|
||||
/// The year used for dates typed without one. Fixed per process from the system clock when
|
||||
/// available (wasm without clock falls back to 2026).
|
||||
#[allow(clippy::disallowed_methods)] // the clock is read only off wasm
|
||||
/// The year used for dates typed without one, from the system clock (browser clock on wasm).
|
||||
/// Falls back to 2026 only when no clock is available.
|
||||
pub fn current_year() -> i64 {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
if let Ok(d) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
|
||||
let days = (d.as_secs() / 86400) as i64;
|
||||
return crate::date::civil_from_days(days).0 as i64;
|
||||
}
|
||||
if let Some(ms) = crate::date::unix_now_ms() {
|
||||
let days = (ms / 86_400_000.0).floor() as i64;
|
||||
return crate::date::civil_from_days(days).0 as i64;
|
||||
}
|
||||
2026
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user