Skip to content

Commit

Permalink
Add timezone host functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
cdmurph32 committed Jun 14, 2024
1 parent 1512a95 commit 0ea0f2e
Show file tree
Hide file tree
Showing 25 changed files with 300 additions and 19 deletions.
97 changes: 97 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ humantime = "2.0.0"
postcard = { version = "1.0.8", default-features = false, features = ['alloc'] }
criterion = { version = "0.5.0", default-features = false, features = ["html_reports", "rayon"] }
rustc-hash = "1.1.0"
chrono = "0.4.26"
chrono-tz = "0.8.3"

# =============================================================================
#
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/cli/imports.wit
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package wasi:cli@0.2.0;

world imports {
include wasi:clocks/imports@0.2.0;
include wasi:clocks/imports@0.2.1;
include wasi:filesystem/imports@0.2.0;
include wasi:sockets/imports@0.2.0;
include wasi:random/imports@0.2.0;
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/clocks/monotonic-clock.wit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package wasi:clocks@0.2.0;
package wasi:clocks@0.2.1;
/// WASI Monotonic Clock is a clock API intended to let users measure elapsed
/// time.
///
Expand Down
50 changes: 50 additions & 0 deletions crates/wasi-http/wit/deps/clocks/timezone.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package wasi:clocks@0.2.1;

interface timezone {
use wall-clock.{datetime};

/// Return information needed to display the given `datetime`. This includes
/// the UTC offset, the time zone name, and a flag indicating whether
/// daylight saving time is active.
///
/// If the timezone cannot be determined for the given `datetime`, return a
/// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight
/// saving time.
display: func(when: datetime) -> timezone-display;

/// The same as `display`, but only return the UTC offset.
utc-offset: func(when: datetime) -> s32;

/// Information useful for displaying the timezone of a specific `datetime`.
///
/// This information may vary within a single `timezone` to reflect daylight
/// saving time adjustments.
record timezone-display {
/// The number of seconds difference between UTC time and the local
/// time of the timezone.
///
/// The returned value will always be less than 86400 which is the
/// number of seconds in a day (24*60*60).
///
/// In implementations that do not expose an actual time zone, this
/// should return 0.
utc-offset: s32,

/// The abbreviated name of the timezone to display to a user. The name
/// `UTC` indicates Coordinated Universal Time. Otherwise, this should
/// reference local standards for the name of the time zone.
///
/// In implementations that do not expose an actual time zone, this
/// should be the string `UTC`.
///
/// In time zones that do not have an applicable name, a formatted
/// representation of the UTC offset may be returned, such as `-04:00`.
name: string,

/// Whether daylight saving time is active.
///
/// In implementations that do not expose an actual time zone, this
/// should return false.
in-daylight-saving-time: bool,
}
}
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/clocks/wall-clock.wit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package wasi:clocks@0.2.0;
package wasi:clocks@0.2.1;
/// WASI Wall Clock is a clock API intended to let users query the current
/// time. The name "wall" makes an analogy to a "clock on the wall", which
/// is not necessarily monotonic as it may be reset.
Expand Down
3 changes: 2 additions & 1 deletion crates/wasi-http/wit/deps/clocks/world.wit
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package wasi:clocks@0.2.0;
package wasi:clocks@0.2.1;

world imports {
import monotonic-clock;
import wall-clock;
import timezone;
}
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/filesystem/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ package wasi:[email protected];
/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
interface types {
use wasi:io/streams@0.2.0.{input-stream, output-stream, error};
use wasi:clocks/wall-clock@0.2.0.{datetime};
use wasi:clocks/wall-clock@0.2.1.{datetime};

/// File size or length of a region within a file.
type filesize = u64;
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/http/proxy.wit
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package wasi:[email protected];
/// outgoing HTTP requests.
world proxy {
/// HTTP proxies have access to time and randomness.
include wasi:clocks/imports@0.2.0;
include wasi:clocks/imports@0.2.1;
import wasi:random/random@0.2.0;

/// Proxies have standard output and error streams which are expected to
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/http/types.wit
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// HTTP Requests and Responses, both incoming and outgoing, as well as
/// their headers, trailers, and bodies.
interface types {
use wasi:clocks/monotonic-clock@0.2.0.{duration};
use wasi:clocks/monotonic-clock@0.2.1.{duration};
use wasi:io/streams@0.2.0.{input-stream, output-stream};
use wasi:io/error@0.2.0.{error as io-error};
use wasi:io/poll@0.2.0.{pollable};
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-http/wit/deps/sockets/tcp.wit
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
interface tcp {
use wasi:io/streams@0.2.0.{input-stream, output-stream};
use wasi:io/poll@0.2.0.{pollable};
use wasi:clocks/monotonic-clock@0.2.0.{duration};
use wasi:clocks/monotonic-clock@0.2.1.{duration};
use network.{network, error-code, ip-socket-address, ip-address-family};

enum shutdown-type {
Expand Down
2 changes: 2 additions & 0 deletions crates/wasi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ cap-rand = { workspace = true }
cap-fs-ext = { workspace = true }
cap-net-ext = { workspace = true }
cap-time-ext = { workspace = true }
chrono = { workspace = true }
chrono-tz = { workspace = true }
io-lifetimes = { workspace = true }
fs-set-times = { workspace = true }
bitflags = { workspace = true }
Expand Down
6 changes: 6 additions & 0 deletions crates/wasi/src/clocks.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod host;
use crate::bindings::clocks::timezone::TimezoneDisplay;
use cap_std::time::Duration;

pub trait HostWallClock: Send {
Expand All @@ -10,3 +11,8 @@ pub trait HostMonotonicClock: Send {
fn resolution(&self) -> u64;
fn now(&self) -> u64;
}

pub trait HostTimezone: Send {
fn display(&self, datetime: Duration) -> TimezoneDisplay;
fn utc_offset(&self, datetime: Duration) -> i32;
}
56 changes: 55 additions & 1 deletion crates/wasi/src/clocks/host.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use super::{HostMonotonicClock, HostWallClock};
use super::{HostMonotonicClock, HostTimezone, HostWallClock};
use crate::bindings::clocks::timezone::TimezoneDisplay;
use cap_std::time::{Duration, Instant, SystemClock};
use cap_std::{ambient_authority, AmbientAuthority};
use cap_time_ext::{MonotonicClockExt, SystemClockExt};
use chrono::{NaiveDateTime, TimeZone};
use chrono_tz::{OffsetComponents, Tz, TZ_VARIANTS};

pub struct WallClock {
/// The underlying system clock.
Expand Down Expand Up @@ -64,10 +67,61 @@ impl HostMonotonicClock for MonotonicClock {
}
}

pub struct Timezone {
// The underlying system timezone.
timezone: cap_time_ext::Timezone,
}

impl Timezone {
pub fn new(ambient_authority: AmbientAuthority) -> Self {
Self {
timezone: cap_time_ext::Timezone::new(ambient_authority),
}
}

fn timezone_from_duration(&self, datetime: Duration) -> Option<TimezoneDisplay> {
let name = self.timezone.timezone_name().ok()?;
let tz: Tz = TZ_VARIANTS.into_iter().find(|tz| tz.to_string() == name)?;
let naive_datetime = NaiveDateTime::from_timestamp_opt(datetime.as_secs() as i64, 0)?;
let tz_offset = tz.offset_from_local_datetime(&naive_datetime).single()?;
let utc_offset = tz_offset.base_utc_offset().num_hours() as i32;
let in_daylight_saving_time = !tz_offset.dst_offset().is_zero();
Some(TimezoneDisplay {
utc_offset,
name,
in_daylight_saving_time,
})
}
}

impl HostTimezone for Timezone {
fn display(&self, datetime: Duration) -> TimezoneDisplay {
match self.timezone_from_duration(datetime) {
None => TimezoneDisplay {
utc_offset: 0,
name: "UTC".to_string(),
in_daylight_saving_time: false,
},
Some(timezone_display) => timezone_display,
}
}

fn utc_offset(&self, datetime: Duration) -> i32 {
match self.timezone_from_duration(datetime) {
None => 0,
Some(timezone_display) => timezone_display.utc_offset,
}
}
}

pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
Box::new(MonotonicClock::new(ambient_authority()))
}

pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
Box::new(WallClock::new(ambient_authority()))
}

pub fn timezone() -> Box<dyn HostTimezone + Send> {
Box::new(Timezone::new(ambient_authority()))
}
Loading

0 comments on commit 0ea0f2e

Please sign in to comment.