Add DateTime type and operations

This commit introduces the `DateTime` type to the language, enabling
users to work with dates and times. It includes:

- A new `DateTime` variant in the `Value` and `StaticType` enums.
- A `date` function for parsing date strings into `DateTime` values.
- Overloads for `+` and `-` operators to support `DateTime` arithmetic.
- Comparison operators (`>`, `<`) for `DateTime` values.
- Corresponding unit tests for `DateTime` operations.
- Updates to the `gemini.md` documentation to reflect the new
  functionality.
  Add DateTime type and operations

Introduce a new `DateTime` type to the language, allowing for date and
time manipulation. This includes:

*   Parsing dates and datetimes from strings.
*   Performing arithmetic operations (addition and subtraction) between
    `DateTime` and `Int` (representing milliseconds) and between two
    `DateTime` values (resulting in an `Int` duration).
*   Enabling comparison operations (`>`, `<`) between `DateTime` values.
This commit is contained in:
Michael Schimmel
2026-02-19 13:27:44 +01:00
parent 49db73800a
commit a5957f524b
4 changed files with 54 additions and 2 deletions
+29 -1
View File
@@ -89,11 +89,31 @@ impl Environment {
fn register_stdlib(&self) {
use crate::ast::types::Signature;
use chrono::{NaiveDate, NaiveDateTime};
self.register_native("date", StaticType::Function(Box::new(Signature {
params: vec![StaticType::Text],
ret: StaticType::DateTime
})), |args| {
if let Value::Text(s) = &args[0] {
// Try parse YYYY-MM-DD
if let Ok(dt) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
let ts = dt.and_hms_opt(0, 0, 0).unwrap().and_utc().timestamp_millis();
return Value::DateTime(ts);
}
// Try parse YYYY-MM-DD HH:MM:SS
if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
return Value::DateTime(dt.and_utc().timestamp_millis());
}
}
Value::Void
});
let plus_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::Text, StaticType::Text], ret: StaticType::Text },
Signature { params: vec![StaticType::DateTime, StaticType::Int], ret: StaticType::DateTime },
]);
self.register_native("+", plus_ty, |args| {
if args.len() == 2 {
@@ -106,7 +126,8 @@ impl Environment {
let mut res = a.to_string();
res.push_str(b);
Value::Text(Rc::from(res))
}
},
(Value::DateTime(ts), Value::Int(ms)) => Value::DateTime(ts + ms),
_ => Value::Void,
}
} else {
@@ -124,6 +145,8 @@ impl Environment {
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::DateTime, StaticType::DateTime], ret: StaticType::Int },
Signature { params: vec![StaticType::DateTime, StaticType::Int], ret: StaticType::DateTime },
]);
self.register_native("-", minus_ty, |args| {
if args.is_empty() { return Value::Void; }
@@ -140,6 +163,8 @@ impl Environment {
(Value::Float(a), Value::Float(b)) => Value::Float(a - b),
(Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 - b),
(Value::Float(a), Value::Int(b)) => Value::Float(a - *b as f64),
(Value::DateTime(a), Value::DateTime(b)) => Value::Int(a - b),
(Value::DateTime(a), Value::Int(b)) => Value::DateTime(a - b),
_ => Value::Void,
};
}
@@ -192,6 +217,7 @@ impl Environment {
let cmp_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Bool },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Bool },
Signature { params: vec![StaticType::DateTime, StaticType::DateTime], ret: StaticType::Bool },
]);
self.register_native(">", cmp_ty.clone(), |args| {
if args.len() != 2 { return Value::Void; }
@@ -200,6 +226,7 @@ impl Environment {
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
(Value::Float(a), Value::Int(b)) => Value::Bool(*a > (*b as f64)),
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a > b),
_ => Value::Bool(false),
}
});
@@ -211,6 +238,7 @@ impl Environment {
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) < *b),
(Value::Float(a), Value::Int(b)) => Value::Bool(*a < (*b as f64)),
(Value::Float(a), Value::Float(b)) => Value::Bool(a < b),
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a < b),
_ => Value::Bool(false),
}
});