Skip to main content

windjammer_runtime/
time.rs

1//! Date and time operations
2//!
3//! Windjammer's `std::time` module maps to these functions.
4
5use chrono::{DateTime, Local, Utc};
6use std::time::Instant as StdInstant;
7
8/// Wall-clock timestamps (`std::time::SystemTime` in Windjammer sources).
9pub use std::time::SystemTime;
10
11/// Re-export Duration type for use in Windjammer code
12pub use chrono::Duration;
13
14/// Instant wrapper for time measurements (re-exported for Windjammer)
15#[derive(Debug, Clone, Copy)]
16pub struct Instant {
17    inner: StdInstant,
18}
19
20impl Instant {
21    /// Get duration since another instant
22    pub fn duration_since(&self, earlier: &Instant) -> Duration {
23        let elapsed = self.inner.duration_since(earlier.inner);
24        Duration::from_std(elapsed).unwrap_or_else(|_| Duration::zero())
25    }
26}
27
28/// Get current instant for time measurements
29pub fn now() -> Instant {
30    Instant {
31        inner: StdInstant::now(),
32    }
33}
34
35/// Get current timestamp (seconds since epoch)
36pub fn timestamp() -> i64 {
37    Utc::now().timestamp()
38}
39
40/// Get current timestamp (milliseconds since epoch)
41pub fn now_millis() -> i64 {
42    Utc::now().timestamp_millis()
43}
44
45/// Get current date/time as string (ISO 8601)
46pub fn now_string() -> String {
47    Utc::now().to_rfc3339()
48}
49
50/// Get local date/time as string
51pub fn now_local() -> String {
52    Local::now().to_rfc3339()
53}
54
55/// Parse ISO 8601 date string  
56pub fn parse(s: &str) -> Result<i64, String> {
57    s.parse::<DateTime<Utc>>()
58        .map(|dt| dt.timestamp())
59        .map_err(|e| e.to_string())
60}
61
62/// Parse date string with custom format
63pub fn parse_format(s: &str, fmt: &str) -> Result<i64, String> {
64    DateTime::parse_from_str(s, fmt)
65        .map(|dt| dt.timestamp())
66        .map_err(|e| e.to_string())
67}
68
69/// Format timestamp as string
70pub fn format(timestamp: i64, format: &str) -> String {
71    DateTime::from_timestamp(timestamp, 0)
72        .unwrap_or_else(Utc::now)
73        .format(format)
74        .to_string()
75}
76
77/// Sleep for duration (seconds)
78pub fn sleep(secs: u64) {
79    std::thread::sleep(std::time::Duration::from_secs(secs));
80}
81
82/// Sleep for duration (milliseconds)
83pub fn sleep_millis(millis: u64) {
84    std::thread::sleep(std::time::Duration::from_millis(millis));
85}
86
87/// Add seconds to timestamp
88pub fn add_seconds(timestamp: i64, seconds: i64) -> i64 {
89    DateTime::from_timestamp(timestamp, 0)
90        .and_then(|dt| dt.checked_add_signed(Duration::seconds(seconds)))
91        .map(|dt| dt.timestamp())
92        .unwrap_or(timestamp)
93}
94
95/// Add days to timestamp
96pub fn add_days(timestamp: i64, days: i64) -> i64 {
97    DateTime::from_timestamp(timestamp, 0)
98        .and_then(|dt| dt.checked_add_signed(Duration::days(days)))
99        .map(|dt| dt.timestamp())
100        .unwrap_or(timestamp)
101}
102
103/// Calculate duration in seconds between two timestamps
104pub fn duration_secs(start: i64, end: i64) -> i64 {
105    end - start
106}
107
108/// Calculate duration in milliseconds between two timestamps
109pub fn duration_millis(start: i64, end: i64) -> i64 {
110    (end - start) * 1000
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_now() {
119        let ts = timestamp();
120        assert!(ts > 1_600_000_000); // After 2020
121    }
122
123    #[test]
124    fn test_now_string() {
125        let s = now_string();
126        assert!(s.contains('T')); // ISO 8601 format has T separator
127                                  // RFC3339 format may have +00:00 instead of Z
128        assert!(s.contains('Z') || s.contains('+'));
129    }
130
131    #[test]
132    fn test_parse() {
133        let result = parse("2024-01-01T00:00:00Z");
134        assert!(result.is_ok());
135    }
136
137    #[test]
138    fn test_add_seconds() {
139        let ts = 1_700_000_000i64;
140        let new_ts = add_seconds(ts, 3600);
141        assert_eq!(new_ts, ts + 3600);
142    }
143
144    #[test]
145    fn test_add_days() {
146        let ts = 1_700_000_000i64;
147        let new_ts = add_days(ts, 1);
148        assert_eq!(new_ts, ts + 86400);
149    }
150}