Skip to main content

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