Skip to main content

mailang_stdlib/stdlib/
time.rs

1//! Time built-in functions
2
3use mailang_bytecode::Value;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6pub fn builtin_time_now(_args: &[Value]) -> Result<Value, String> {
7    let duration = SystemTime::now()
8        .duration_since(UNIX_EPOCH)
9        .map_err(|e| e.to_string())?;
10    Ok(Value::Int(duration.as_millis() as i64))
11}
12
13pub fn builtin_time_now_secs(_args: &[Value]) -> Result<Value, String> {
14    let duration = SystemTime::now()
15        .duration_since(UNIX_EPOCH)
16        .map_err(|e| e.to_string())?;
17    Ok(Value::Int(duration.as_secs() as i64))
18}
19
20pub fn builtin_time_year(_args: &[Value]) -> Result<Value, String> {
21    let secs = get_epoch_secs()?;
22    let (year, _, _) = epoch_to_ymd(secs);
23    Ok(Value::Int(year))
24}
25
26pub fn builtin_time_month(_args: &[Value]) -> Result<Value, String> {
27    let secs = get_epoch_secs()?;
28    let (_, month, _) = epoch_to_ymd(secs);
29    Ok(Value::Int(month))
30}
31
32pub fn builtin_time_day(_args: &[Value]) -> Result<Value, String> {
33    let secs = get_epoch_secs()?;
34    let (_, _, day) = epoch_to_ymd(secs);
35    Ok(Value::Int(day))
36}
37
38pub fn builtin_time_hour(_args: &[Value]) -> Result<Value, String> {
39    let secs = get_epoch_secs()?;
40    let (_, h, _, _) = epoch_to_hms(secs);
41    Ok(Value::Int(h))
42}
43
44pub fn builtin_time_minute(_args: &[Value]) -> Result<Value, String> {
45    let secs = get_epoch_secs()?;
46    let (_, _, m, _) = epoch_to_hms(secs);
47    Ok(Value::Int(m))
48}
49
50pub fn builtin_time_second(_args: &[Value]) -> Result<Value, String> {
51    let secs = get_epoch_secs()?;
52    let (_, _, _, s) = epoch_to_hms(secs);
53    Ok(Value::Int(s))
54}
55
56pub fn builtin_time_date(_args: &[Value]) -> Result<Value, String> {
57    let secs = get_epoch_secs()?;
58    let (year, month, day) = epoch_to_ymd(secs);
59    Ok(Value::Str(
60        format!("{:04}-{:02}-{:02}", year, month, day).into(),
61    ))
62}
63
64pub fn builtin_time_datetime(_args: &[Value]) -> Result<Value, String> {
65    let secs = get_epoch_secs()?;
66    let (year, month, day) = epoch_to_ymd(secs);
67    let (_, hour, minute, second) = epoch_to_hms(secs);
68    Ok(Value::Str(
69        format!(
70            "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
71            year, month, day, hour, minute, second
72        )
73        .into(),
74    ))
75}
76
77pub fn builtin_time_elapsed(args: &[Value]) -> Result<Value, String> {
78    let start = match &args[0] {
79        Value::Int(n) => *n,
80        _ => return Err("elapsed expects an integer timestamp".to_string()),
81    };
82    let now = builtin_time_now(&[])?;
83    match now {
84        Value::Int(now_ms) => Ok(Value::Int(now_ms - start)),
85        _ => Err("Failed to get current time".to_string()),
86    }
87}
88
89pub fn builtin_time_sleep(args: &[Value]) -> Result<Value, String> {
90    let ms = match &args[0] {
91        Value::Int(n) => *n as u64,
92        Value::Float(n) => *n as u64,
93        _ => return Err("sleep expects a number".to_string()),
94    };
95    std::thread::sleep(std::time::Duration::from_millis(ms));
96    Ok(Value::Null)
97}
98
99fn get_epoch_secs() -> Result<i64, String> {
100    let duration = SystemTime::now()
101        .duration_since(UNIX_EPOCH)
102        .map_err(|e| e.to_string())?;
103    Ok(duration.as_secs() as i64)
104}
105
106/// Convert epoch seconds to (year, month, day) - UTC
107fn epoch_to_ymd(epoch: i64) -> (i64, i64, i64) {
108    let days = epoch / 86400;
109    let mut y = 1970;
110    let mut remaining_days = days;
111
112    loop {
113        let days_in_year = if is_leap_year(y) { 366 } else { 365 };
114        if remaining_days < days_in_year {
115            break;
116        }
117        remaining_days -= days_in_year;
118        y += 1;
119    }
120
121    let leap = is_leap_year(y);
122    let month_days = [
123        31,
124        if leap { 29 } else { 28 },
125        31,
126        30,
127        31,
128        30,
129        31,
130        31,
131        30,
132        31,
133        30,
134        31,
135    ];
136    let mut m = 1;
137    for &days_in_month in &month_days {
138        if remaining_days < days_in_month {
139            break;
140        }
141        remaining_days -= days_in_month;
142        m += 1;
143    }
144
145    (y, m, remaining_days + 1)
146}
147
148/// Convert epoch seconds to (_, hour, minute, second) - UTC
149fn epoch_to_hms(epoch: i64) -> (i64, i64, i64, i64) {
150    let secs_in_day = epoch % 86400;
151    let hour = secs_in_day / 3600;
152    let minute = (secs_in_day % 3600) / 60;
153    let second = secs_in_day % 60;
154    (0, hour, minute, second)
155}
156
157fn is_leap_year(year: i64) -> bool {
158    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
159}