Skip to main content

uqa_sql/expr/
current_time.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! SQL current date/time values read from the owning execution context.
8
9use super::{age_between, coerce_temporal, EvalContext, Result, SQLError, TemporalValue, Value};
10
11/// Read the platform wall clock as Unix microseconds.
12#[must_use]
13pub fn clock_timestamp_micros() -> i64 {
14    chrono::Utc::now().timestamp_micros()
15}
16
17pub(super) fn eval_current_time(
18    name: &str,
19    args: &[Value],
20    context: Option<&EvalContext<'_>>,
21) -> Option<Result<Value>> {
22    if !(matches!(
23        name,
24        "now"
25            | "transaction_timestamp"
26            | "statement_timestamp"
27            | "current_timestamp"
28            | "current_date"
29            | "current_time"
30            | "localtime"
31            | "localtimestamp"
32    ) || name == "age" && args.len() == 1)
33    {
34        return None;
35    }
36    Some((|| {
37        if name == "age" && matches!(args, [Value::Null]) {
38            return Ok(Value::Null);
39        }
40        if name != "age" && !args.is_empty() {
41            return Err(SQLError::BadArity {
42                name: name.into(),
43                expected: "0".into(),
44                actual: args.len(),
45            });
46        }
47        let micros = context
48            .and_then(|context| context.engine)
49            .and_then(|engine| {
50                if name == "statement_timestamp" {
51                    engine.statement_timestamp_micros()
52                } else {
53                    engine.transaction_timestamp_micros()
54                }
55            })
56            .unwrap_or_else(clock_timestamp_micros);
57        const MICROS_PER_DAY: i64 = 86_400_000_000;
58        let value = match name {
59            "current_date" => TemporalValue::Date {
60                days: i32::try_from(micros.div_euclid(MICROS_PER_DAY)).map_err(|_| {
61                    SQLError::Internal("current date exceeds its day carrier".into())
62                })?,
63            },
64            "current_time" => TemporalValue::TimeTz {
65                micros: micros.rem_euclid(MICROS_PER_DAY),
66                offset_minutes: 0,
67            },
68            "localtime" => TemporalValue::Time {
69                micros: micros.rem_euclid(MICROS_PER_DAY),
70            },
71            "localtimestamp" => TemporalValue::Timestamp { micros },
72            "age" => {
73                return age_between(
74                    &TemporalValue::Timestamp {
75                        micros: micros.div_euclid(MICROS_PER_DAY) * MICROS_PER_DAY,
76                    },
77                    &coerce_temporal(&args[0])?,
78                );
79            }
80            _ => TemporalValue::TimestampTz { micros },
81        };
82        Ok(Value::Temporal(value))
83    })())
84}