Skip to main content

flaron_sdk/
time.rs

1//! Edge-side timestamps in a variety of formats.
2//!
3//! All timestamps are produced by the host so they reflect the wall clock of
4//! the edge node serving the request, not the (non-existent) Wasm clock.
5
6use crate::{ffi, mem};
7
8/// Supported timestamp formats accepted by [`now`] and [`format`].
9pub mod format {
10    /// Seconds since the Unix epoch as a decimal string.
11    pub const UNIX: &str = "unix";
12    /// Milliseconds since the Unix epoch as a decimal string.
13    pub const MILLIS: &str = "ms";
14    /// Nanoseconds since the Unix epoch as a decimal string.
15    pub const NANOS: &str = "ns";
16    /// RFC 3339 (`2026-04-07T11:12:13Z`).
17    pub const RFC3339: &str = "rfc3339";
18    /// HTTP date header format (`Tue, 07 Apr 2026 11:12:13 GMT`).
19    pub const HTTP: &str = "http";
20    /// ISO 8601 (alias of RFC 3339).
21    pub const ISO8601: &str = "iso8601";
22}
23
24/// Get the current edge time formatted according to `fmt`.
25///
26/// Use one of the constants in [`format`] for `fmt`. Unknown formats fall
27/// back to RFC 3339 host-side. Returns an empty string if the host returned
28/// no result (which should never happen).
29pub fn now(fmt: &str) -> String {
30    let args = serde_json::json!({ "format": fmt });
31    let args_str = args.to_string();
32    let (args_ptr, args_len) = mem::host_arg_str(&args_str);
33    let result = unsafe { ffi::timestamp(args_ptr, args_len) };
34    // SAFETY: host writes a valid UTF-8 timestamp into the bump arena.
35    unsafe { mem::read_packed_string(result) }.unwrap_or_default()
36}
37
38/// Convenience: current time as Unix milliseconds.
39///
40/// Returns `0` if the host response could not be parsed (which should never
41/// happen - the host always returns a decimal integer).
42pub fn now_ms() -> u64 {
43    now(format::MILLIS).parse().unwrap_or(0)
44}
45
46/// Convenience: current time as Unix seconds.
47pub fn now_unix() -> u64 {
48    now(format::UNIX).parse().unwrap_or(0)
49}
50
51/// Convenience: current time as an RFC 3339 string.
52pub fn now_rfc3339() -> String {
53    now(format::RFC3339)
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::ffi::test_host;
60
61    #[test]
62    fn now_passes_format_in_args() {
63        test_host::reset();
64        test_host::with_mock(|m| m.timestamp_response = Some("2026-04-07T11:12:13Z".into()));
65        let s = now(format::RFC3339);
66        assert_eq!(s, "2026-04-07T11:12:13Z");
67
68        let captured = test_host::read_mock(|m| m.last_timestamp_args.clone()).unwrap();
69        let parsed: serde_json::Value = serde_json::from_str(&captured).unwrap();
70        assert_eq!(parsed["format"], "rfc3339");
71    }
72
73    #[test]
74    fn now_ms_parses_decimal_response() {
75        test_host::reset();
76        test_host::with_mock(|m| m.timestamp_response = Some("1759839133000".into()));
77        assert_eq!(now_ms(), 1_759_839_133_000);
78    }
79
80    #[test]
81    fn now_unix_parses_decimal_response() {
82        test_host::reset();
83        test_host::with_mock(|m| m.timestamp_response = Some("1759839133".into()));
84        assert_eq!(now_unix(), 1_759_839_133);
85    }
86
87    #[test]
88    fn now_unparseable_response_zero() {
89        test_host::reset();
90        test_host::with_mock(|m| m.timestamp_response = Some("not-a-number".into()));
91        assert_eq!(now_ms(), 0);
92        assert_eq!(now_unix(), 0);
93    }
94
95    #[test]
96    fn now_rfc3339_uses_rfc3339_format() {
97        test_host::reset();
98        test_host::with_mock(|m| m.timestamp_response = Some("2026-04-07T00:00:00Z".into()));
99        let s = now_rfc3339();
100        assert_eq!(s, "2026-04-07T00:00:00Z");
101        let captured = test_host::read_mock(|m| m.last_timestamp_args.clone()).unwrap();
102        assert!(captured.contains("rfc3339"));
103    }
104
105    #[test]
106    fn format_constants_match_expected_strings() {
107        assert_eq!(format::UNIX, "unix");
108        assert_eq!(format::MILLIS, "ms");
109        assert_eq!(format::NANOS, "ns");
110        assert_eq!(format::RFC3339, "rfc3339");
111        assert_eq!(format::HTTP, "http");
112        assert_eq!(format::ISO8601, "iso8601");
113    }
114}