fusabi_stdlib_ext/
time.rs

1//! Time module.
2//!
3//! Provides time and duration utilities.
4
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7use fusabi_host::ExecutionContext;
8use fusabi_host::Value;
9
10/// Get current Unix timestamp in seconds.
11pub fn now(
12    _args: &[Value],
13    _ctx: &ExecutionContext,
14) -> fusabi_host::Result<Value> {
15    let timestamp = SystemTime::now()
16        .duration_since(UNIX_EPOCH)
17        .map(|d| d.as_secs())
18        .unwrap_or(0);
19
20    Ok(Value::Int(timestamp as i64))
21}
22
23/// Get current Unix timestamp in milliseconds.
24pub fn now_millis(
25    _args: &[Value],
26    _ctx: &ExecutionContext,
27) -> fusabi_host::Result<Value> {
28    let timestamp = SystemTime::now()
29        .duration_since(UNIX_EPOCH)
30        .map(|d| d.as_millis())
31        .unwrap_or(0);
32
33    Ok(Value::Int(timestamp as i64))
34}
35
36/// Sleep for a duration in milliseconds.
37pub fn sleep(
38    args: &[Value],
39    _ctx: &ExecutionContext,
40) -> fusabi_host::Result<Value> {
41    let millis = args
42        .first()
43        .and_then(|v| v.as_int())
44        .ok_or_else(|| fusabi_host::Error::host_function("time.sleep: missing milliseconds argument"))?;
45
46    if millis < 0 {
47        return Err(fusabi_host::Error::host_function("time.sleep: milliseconds must be non-negative"));
48    }
49
50    std::thread::sleep(Duration::from_millis(millis as u64));
51    Ok(Value::Null)
52}
53
54/// Format a Unix timestamp.
55pub fn format_time(
56    args: &[Value],
57    _ctx: &ExecutionContext,
58) -> fusabi_host::Result<Value> {
59    let timestamp = args
60        .first()
61        .and_then(|v| v.as_int())
62        .ok_or_else(|| fusabi_host::Error::host_function("time.format: missing timestamp argument"))?;
63
64    let format_str = args
65        .get(1)
66        .and_then(|v| v.as_str())
67        .unwrap_or("%Y-%m-%d %H:%M:%S");
68
69    // Simple formatting - in real implementation would use chrono
70    let formatted = format_timestamp(timestamp, format_str);
71    Ok(Value::String(formatted))
72}
73
74/// Parse a time string to Unix timestamp.
75pub fn parse_time(
76    args: &[Value],
77    _ctx: &ExecutionContext,
78) -> fusabi_host::Result<Value> {
79    let time_str = args
80        .first()
81        .and_then(|v| v.as_str())
82        .ok_or_else(|| fusabi_host::Error::host_function("time.parse: missing time string argument"))?;
83
84    let _format_str = args
85        .get(1)
86        .and_then(|v| v.as_str())
87        .unwrap_or("%Y-%m-%d %H:%M:%S");
88
89    // Simple parsing - in real implementation would use chrono
90    // For now, just return an error indicating format not supported
91    Err(fusabi_host::Error::host_function(format!(
92        "time.parse: parsing '{}' not yet implemented",
93        time_str
94    )))
95}
96
97// Helper function for simple timestamp formatting
98fn format_timestamp(timestamp: i64, _format: &str) -> String {
99    // Very simple formatting - real implementation would use chrono
100    let secs = timestamp as u64;
101    let days = secs / 86400;
102    let hours = (secs % 86400) / 3600;
103    let minutes = (secs % 3600) / 60;
104    let seconds = secs % 60;
105
106    // Calculate approximate date (very simplified, ignoring leap years)
107    let years = 1970 + (days / 365);
108    let remaining_days = days % 365;
109    let month = remaining_days / 30 + 1;
110    let day = remaining_days % 30 + 1;
111
112    format!(
113        "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
114        years, month, day, hours, minutes, seconds
115    )
116}
117
118/// Duration helper functions
119pub mod duration {
120    /// Convert seconds to milliseconds.
121    pub fn seconds_to_millis(secs: i64) -> i64 {
122        secs * 1000
123    }
124
125    /// Convert milliseconds to seconds.
126    pub fn millis_to_seconds(millis: i64) -> i64 {
127        millis / 1000
128    }
129
130    /// Convert minutes to seconds.
131    pub fn minutes_to_seconds(mins: i64) -> i64 {
132        mins * 60
133    }
134
135    /// Convert hours to seconds.
136    pub fn hours_to_seconds(hours: i64) -> i64 {
137        hours * 3600
138    }
139
140    /// Convert days to seconds.
141    pub fn days_to_seconds(days: i64) -> i64 {
142        days * 86400
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use fusabi_host::Capabilities;
150    use fusabi_host::{Sandbox, SandboxConfig};
151    use fusabi_host::Limits;
152
153    fn create_test_ctx() -> ExecutionContext {
154        let sandbox = Sandbox::new(SandboxConfig::default()).unwrap();
155        ExecutionContext::new(1, Capabilities::none(), Limits::default(), sandbox)
156    }
157
158    #[test]
159    fn test_now() {
160        let ctx = create_test_ctx();
161        let result = now(&[], &ctx).unwrap();
162
163        let timestamp = result.as_int().unwrap();
164        assert!(timestamp > 0);
165        assert!(timestamp > 1700000000); // After Nov 2023
166    }
167
168    #[test]
169    fn test_now_millis() {
170        let ctx = create_test_ctx();
171        let result = now_millis(&[], &ctx).unwrap();
172
173        let timestamp = result.as_int().unwrap();
174        assert!(timestamp > 0);
175        assert!(timestamp > 1700000000000); // After Nov 2023 in millis
176    }
177
178    #[test]
179    fn test_format_time() {
180        let ctx = create_test_ctx();
181
182        // Test with a known timestamp (Jan 1, 2024 00:00:00 UTC)
183        let result = format_time(&[Value::Int(1704067200)], &ctx).unwrap();
184        let formatted = result.as_str().unwrap();
185
186        assert!(formatted.contains("2024"));
187    }
188
189    #[test]
190    fn test_sleep_validation() {
191        let ctx = create_test_ctx();
192
193        // Negative sleep should fail
194        let result = sleep(&[Value::Int(-100)], &ctx);
195        assert!(result.is_err());
196    }
197
198    #[test]
199    fn test_duration_helpers() {
200        assert_eq!(duration::seconds_to_millis(5), 5000);
201        assert_eq!(duration::millis_to_seconds(5000), 5);
202        assert_eq!(duration::minutes_to_seconds(2), 120);
203        assert_eq!(duration::hours_to_seconds(1), 3600);
204        assert_eq!(duration::days_to_seconds(1), 86400);
205    }
206}