use lemma::parsing::ast::DateTimeValue;
use lemma::Engine;
use rust_decimal::Decimal;
use std::collections::HashMap;
fn get_rule_value(engine: &Engine, spec_name: &str, rule_name: &str) -> lemma::LiteralValue {
let now = DateTimeValue::now();
let response = engine
.run(spec_name, Some(&now), HashMap::new(), false)
.unwrap();
response
.results
.values()
.find(|r| r.rule.name == rule_name)
.unwrap()
.result
.value()
.unwrap()
.clone()
}
#[test]
fn test_timezone_comparison_same_instant() {
let mut engine = Engine::new();
let code = r#"
spec test
fact time_nyc: 2024-03-15T10:00:00-05:00
fact time_london: 2024-03-15T15:00:00+00:00
rule are_equal: time_nyc is time_london
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Boolean(value),
..
} = get_rule_value(&engine, "test", "are_equal")
{
assert!(value, "Same instant in different timezones should be equal");
} else {
panic!("Expected Boolean value");
}
}
#[test]
fn test_timezone_comparison_different_instants() {
let mut engine = Engine::new();
let code = r#"
spec test
fact time_nyc: 2024-03-15T10:00:00-05:00
fact time_tokyo: 2024-03-15T10:00:00+09:00
rule nyc_is_later: time_nyc > time_tokyo
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Boolean(value),
..
} = get_rule_value(&engine, "test", "nyc_is_later")
{
assert!(value, "NYC 10am is later than Tokyo 10am (same local time)");
} else {
panic!("Expected Boolean value");
}
}
#[test]
fn test_timezone_arithmetic_preserved() {
let mut engine = Engine::new();
let code = r#"
spec test
fact start_time: 2024-03-15T10:00:00+01:00
rule later: start_time + 2 hours
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "later")
{
assert_eq!(date.hour, 12);
assert_eq!(date.minute, 0);
assert_eq!(date.second, 0);
if let Some(tz) = &date.timezone {
assert_eq!(tz.offset_hours, 1);
assert_eq!(tz.offset_minutes, 0);
} else {
panic!("Expected timezone to be preserved");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_negative_timezone_offset() {
let mut engine = Engine::new();
let code = r#"
spec test
fact west_coast: 2024-03-15T09:00:00-08:00
rule later: west_coast + 3 hours
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "later")
{
assert_eq!(date.hour, 12);
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, -8);
assert_eq!(tz.offset_minutes, 0);
} else {
panic!("Expected timezone to be preserved");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_timezone_crossing_midnight() {
let mut engine = Engine::new();
let code = r#"
spec test
fact evening: 2024-03-15T23:00:00+05:30
rule next_day: evening + 2 hours
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "next_day")
{
assert_eq!(date.year, 2024);
assert_eq!(date.month, 3);
assert_eq!(date.day, 16);
assert_eq!(date.hour, 1);
assert_eq!(date.minute, 0);
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, 5);
assert_eq!(tz.offset_minutes, 30);
} else {
panic!("Expected timezone to be preserved");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_timezone_date_difference() {
let mut engine = Engine::new();
let code = r#"
spec test
fact time1: 2024-03-15T10:00:00-05:00
fact time2: 2024-03-15T16:00:00+01:00
rule hours_diff: time2 - time1
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Duration(seconds, _),
..
} = get_rule_value(&engine, "test", "hours_diff")
{
assert_eq!(seconds, Decimal::from(0));
} else {
panic!("Expected Duration value");
}
}
#[test]
fn test_timezone_30_minute_offset() {
let mut engine = Engine::new();
let code = r#"
spec test
fact india_time: 2024-03-15T14:30:00+05:30
rule utc_equivalent: india_time
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "utc_equivalent")
{
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, 5);
assert_eq!(tz.offset_minutes, 30);
} else {
panic!("Expected timezone to be present");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_timezone_45_minute_offset() {
let mut engine = Engine::new();
let code = r#"
spec test
fact nepal_time: 2024-03-15T14:30:00+05:45
rule preserved: nepal_time
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "preserved")
{
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, 5);
assert_eq!(tz.offset_minutes, 45);
} else {
panic!("Expected timezone to be present");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_extreme_western_timezone() {
let mut engine = Engine::new();
let code = r#"
spec test
fact hawaii: 2024-03-15T12:00:00-10:00
rule later: hawaii + 1 hour
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "later")
{
assert_eq!(date.hour, 13);
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, -10);
} else {
panic!("Expected timezone to be preserved");
}
} else {
panic!("Expected Date value");
}
}
#[test]
fn test_extreme_eastern_timezone() {
let mut engine = Engine::new();
let code = r#"
spec test
fact kiribati: 2024-03-15T12:00:00+14:00
rule earlier: kiribati - 1 hour
"#;
engine
.load(code, lemma::SourceType::Labeled("test.lemma"))
.expect("Failed to parse");
if let lemma::LiteralValue {
value: lemma::ValueKind::Date(date),
..
} = get_rule_value(&engine, "test", "earlier")
{
assert_eq!(date.hour, 11);
if let Some(tz) = date.timezone {
assert_eq!(tz.offset_hours, 14);
} else {
panic!("Expected timezone to be preserved");
}
} else {
panic!("Expected Date value");
}
}