formualizer_eval/builtins/datetime/
today_now.rs

1//! TODAY and NOW volatile functions
2
3use super::serial::{date_to_serial_for, datetime_to_serial_for};
4use crate::function::Function;
5use crate::traits::{ArgumentHandle, FunctionContext};
6use formualizer_common::{ExcelError, LiteralValue};
7use formualizer_macros::func_caps;
8
9/// TODAY() - Returns current date as serial number (volatile)
10#[derive(Debug)]
11pub struct TodayFn;
12
13impl Function for TodayFn {
14    func_caps!(VOLATILE);
15
16    fn name(&self) -> &'static str {
17        "TODAY"
18    }
19
20    fn min_args(&self) -> usize {
21        0
22    }
23
24    fn eval_scalar<'a, 'b>(
25        &self,
26        _args: &'a [ArgumentHandle<'a, 'b>],
27        ctx: &dyn FunctionContext,
28    ) -> Result<LiteralValue, ExcelError> {
29        let today = ctx.timezone().today();
30        let serial = date_to_serial_for(ctx.date_system(), &today);
31        Ok(LiteralValue::Number(serial))
32    }
33}
34
35/// NOW() - Returns current date and time as serial number (volatile)
36#[derive(Debug)]
37pub struct NowFn;
38
39impl Function for NowFn {
40    func_caps!(VOLATILE);
41
42    fn name(&self) -> &'static str {
43        "NOW"
44    }
45
46    fn min_args(&self) -> usize {
47        0
48    }
49
50    fn eval_scalar<'a, 'b>(
51        &self,
52        _args: &'a [ArgumentHandle<'a, 'b>],
53        ctx: &dyn FunctionContext,
54    ) -> Result<LiteralValue, ExcelError> {
55        let now = ctx.timezone().now();
56        let serial = datetime_to_serial_for(ctx.date_system(), &now);
57        Ok(LiteralValue::Number(serial))
58    }
59}
60
61pub fn register_builtins() {
62    use std::sync::Arc;
63    crate::function_registry::register_function(Arc::new(TodayFn));
64    crate::function_registry::register_function(Arc::new(NowFn));
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::test_workbook::TestWorkbook;
71    use std::sync::Arc;
72
73    #[test]
74    fn test_today_volatility() {
75        let wb = TestWorkbook::new().with_function(Arc::new(TodayFn));
76        let ctx = wb.interpreter();
77        let f = ctx.context.get_function("", "TODAY").unwrap();
78
79        // Check that it returns a number
80        let result = f.dispatch(&[], &ctx.function_context(None)).unwrap();
81        match result {
82            LiteralValue::Number(n) => {
83                // Should be a reasonable date serial number (> 0)
84                assert!(n > 0.0);
85                // Should be an integer (no time component)
86                assert_eq!(n.trunc(), n);
87            }
88            _ => panic!("TODAY should return a number"),
89        }
90
91        // Volatility flag is set via func_caps!(VOLATILE) macro
92    }
93
94    #[test]
95    fn test_now_volatility() {
96        let wb = TestWorkbook::new().with_function(Arc::new(NowFn));
97        let ctx = wb.interpreter();
98        let f = ctx.context.get_function("", "NOW").unwrap();
99
100        // Check that it returns a number
101        let result = f.dispatch(&[], &ctx.function_context(None)).unwrap();
102        match result {
103            LiteralValue::Number(n) => {
104                // Should be a reasonable date serial number (> 0)
105                assert!(n > 0.0);
106                // Should have a fractional component (time)
107                // Note: There's a tiny chance this could fail if run exactly at midnight
108                // but that's extremely unlikely
109            }
110            _ => panic!("NOW should return a number"),
111        }
112
113        // Volatility flag is set via func_caps!(VOLATILE) macro
114    }
115}