1use crate::error::StdlibError;
7use crate::module::StdlibModule;
8use crate::value::Value;
9
10const MS_PER_DAY: f64 = 86_400_000.0;
12const MS_PER_SECOND: f64 = 1_000.0;
14
15pub struct TimeModule;
17
18impl TimeModule {
19 pub fn new() -> Self {
20 Self
21 }
22}
23
24impl Default for TimeModule {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl StdlibModule for TimeModule {
31 fn name(&self) -> &'static str {
32 "time"
33 }
34
35 fn has_function(&self, function: &str) -> bool {
36 matches!(
37 function,
38 "now" | "format" | "diff" | "day_of_week" | "start_of_day"
39 )
40 }
41
42 fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
43 match function {
44 "now" => self.now(args),
45 "format" => self.format(args),
46 "diff" => self.diff(args),
47 "day_of_week" => self.day_of_week(args),
48 "start_of_day" => self.start_of_day(args),
49 _ => Err(StdlibError::unknown_function("time", function)),
50 }
51 }
52}
53
54impl TimeModule {
55 fn now(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
59 if !args.is_empty() {
60 return Err(StdlibError::wrong_args("time.now", 0, args.len()));
61 }
62 Ok(Value::Number(0.0))
64 }
65
66 fn format(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
70 if args.len() != 2 {
71 return Err(StdlibError::wrong_args("time.format", 2, args.len()));
72 }
73 let ts = extract_number("time.format", &args[0], 1)?;
74 let pattern = extract_string("time.format", &args[1], 2)?;
75
76 let (year, month, day, hour, min, sec) = timestamp_to_parts(ts);
77
78 let result = pattern
79 .replace("YYYY", &format!("{:04}", year))
80 .replace("MM", &format!("{:02}", month))
81 .replace("DD", &format!("{:02}", day))
82 .replace("HH", &format!("{:02}", hour))
83 .replace("mm", &format!("{:02}", min))
84 .replace("ss", &format!("{:02}", sec));
85
86 Ok(Value::String(result))
87 }
88
89 fn diff(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
92 if args.len() != 2 {
93 return Err(StdlibError::wrong_args("time.diff", 2, args.len()));
94 }
95 let a = extract_number("time.diff", &args[0], 1)?;
96 let b = extract_number("time.diff", &args[1], 2)?;
97 Ok(Value::Number(a - b))
98 }
99
100 fn day_of_week(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
104 if args.len() != 1 {
105 return Err(StdlibError::wrong_args("time.day_of_week", 1, args.len()));
106 }
107 let ts = extract_number("time.day_of_week", &args[0], 1)?;
108 let days = (ts / MS_PER_DAY).floor() as i64;
110 let dow = ((days % 7 + 4) % 7 + 7) % 7;
112 Ok(Value::Number(dow as f64))
113 }
114
115 fn start_of_day(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
118 if args.len() != 1 {
119 return Err(StdlibError::wrong_args("time.start_of_day", 1, args.len()));
120 }
121 let ts = extract_number("time.start_of_day", &args[0], 1)?;
122 let day_start = (ts / MS_PER_DAY).floor() * MS_PER_DAY;
123 Ok(Value::Number(day_start))
124 }
125}
126
127fn timestamp_to_parts(ts: f64) -> (i64, u32, u32, u32, u32, u32) {
132 let total_ms = ts as i64;
133 let total_sec = total_ms.div_euclid(MS_PER_SECOND as i64);
134 let sec = total_sec.rem_euclid(60) as u32;
135 let total_min = total_sec.div_euclid(60);
136 let min = total_min.rem_euclid(60) as u32;
137 let total_hour = total_min.div_euclid(60);
138 let hour = total_hour.rem_euclid(24) as u32;
139
140 let days = total_hour.div_euclid(24);
141 let (year, month, day) = days_to_civil(days);
142 (year, month, day, hour, min, sec)
143}
144
145fn days_to_civil(days: i64) -> (i64, u32, u32) {
148 let z = days + 719468;
149 let era = if z >= 0 { z } else { z - 146096 } / 146097;
150 let doe = (z - era * 146097) as u32; let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; let y = yoe as i64 + era * 400;
153 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = doy - (153 * mp + 2) / 5 + 1;
156 let m = if mp < 10 { mp + 3 } else { mp - 9 };
157 let y = if m <= 2 { y + 1 } else { y };
158 (y, m, d)
159}
160
161fn extract_number(func: &str, val: &Value, pos: usize) -> Result<f64, StdlibError> {
164 match val {
165 Value::Number(n) => Ok(*n),
166 _ => Err(StdlibError::type_mismatch(
167 func,
168 pos,
169 "number",
170 val.type_name(),
171 )),
172 }
173}
174
175fn extract_string<'a>(func: &str, val: &'a Value, pos: usize) -> Result<&'a str, StdlibError> {
176 match val {
177 Value::String(s) => Ok(s),
178 _ => Err(StdlibError::type_mismatch(
179 func,
180 pos,
181 "string",
182 val.type_name(),
183 )),
184 }
185}