use chrono::{Datelike, NaiveDate};
use super::evaluator::ConditionResult;
pub fn is_mesz_utc(dtm_value: &str) -> ConditionResult {
let s = dtm_value.trim();
if s.len() < 8 {
return ConditionResult::Unknown;
}
let year: i32 = match s[0..4].parse() {
Ok(v) => v,
Err(_) => return ConditionResult::Unknown,
};
let month: u32 = match s[4..6].parse() {
Ok(v) => v,
Err(_) => return ConditionResult::Unknown,
};
let day: u32 = match s[6..8].parse() {
Ok(v) => v,
Err(_) => return ConditionResult::Unknown,
};
let hour: u32 = if s.len() >= 10 {
match s[8..10].parse() {
Ok(v) => v,
Err(_) => return ConditionResult::Unknown,
}
} else {
0
};
let minute: u32 = if s.len() >= 12 {
match s[10..12].parse() {
Ok(v) => v,
Err(_) => return ConditionResult::Unknown,
}
} else {
0
};
let Some(dt) =
NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, minute, 0))
else {
return ConditionResult::Unknown;
};
let mesz_start = last_sunday_of_month(year, 3).and_hms_opt(1, 0, 0).unwrap();
let mesz_end = last_sunday_of_month(year, 10).and_hms_opt(1, 0, 0).unwrap();
ConditionResult::from(dt >= mesz_start && dt < mesz_end)
}
pub fn is_mez_utc(dtm_value: &str) -> ConditionResult {
match is_mesz_utc(dtm_value) {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
fn last_sunday_of_month(year: i32, month: u32) -> NaiveDate {
let last_day = NaiveDate::from_ymd_opt(year, month + 1, 1)
.unwrap_or_else(|| NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap())
.pred_opt()
.unwrap();
let days_since_sunday = last_day.weekday().num_days_from_sunday();
last_day - chrono::Duration::days(days_since_sunday as i64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_known_mesz_date() {
assert_eq!(is_mesz_utc("202607151200"), ConditionResult::True);
}
#[test]
fn test_known_mez_date() {
assert_eq!(is_mesz_utc("202601151200"), ConditionResult::False);
}
#[test]
fn test_march_transition_2026() {
assert_eq!(is_mesz_utc("202603290059"), ConditionResult::False);
assert_eq!(is_mesz_utc("202603290100"), ConditionResult::True);
}
#[test]
fn test_october_transition_2026() {
assert_eq!(is_mesz_utc("202610250059"), ConditionResult::True);
assert_eq!(is_mesz_utc("202610250100"), ConditionResult::False);
}
#[test]
fn test_short_input() {
assert_eq!(is_mesz_utc("2026"), ConditionResult::Unknown);
assert_eq!(is_mesz_utc(""), ConditionResult::Unknown);
assert_eq!(is_mesz_utc("20260715"), ConditionResult::True);
assert_eq!(is_mesz_utc("20260115"), ConditionResult::False);
}
#[test]
fn test_invalid_input_returns_unknown() {
assert_eq!(is_mesz_utc("abcdefghijkl"), ConditionResult::Unknown);
assert_eq!(is_mesz_utc("202613151200"), ConditionResult::Unknown);
assert_eq!(is_mesz_utc("202601321200"), ConditionResult::Unknown);
}
#[test]
fn test_is_mez_complements_is_mesz() {
assert_eq!(is_mez_utc("202607151200"), ConditionResult::False);
assert_eq!(is_mez_utc("202601151200"), ConditionResult::True);
assert_eq!(is_mez_utc("short"), ConditionResult::Unknown);
}
#[test]
fn test_value_with_timezone_suffix() {
assert_eq!(is_mesz_utc("202607151200UTC"), ConditionResult::True);
assert_eq!(is_mesz_utc("202601151200303"), ConditionResult::False);
}
#[test]
fn test_last_sunday_of_march_2026() {
assert_eq!(
last_sunday_of_month(2026, 3),
NaiveDate::from_ymd_opt(2026, 3, 29).unwrap()
);
}
#[test]
fn test_last_sunday_of_october_2026() {
assert_eq!(
last_sunday_of_month(2026, 10),
NaiveDate::from_ymd_opt(2026, 10, 25).unwrap()
);
}
#[test]
fn test_different_years() {
assert_eq!(
last_sunday_of_month(2025, 3),
NaiveDate::from_ymd_opt(2025, 3, 30).unwrap()
);
assert_eq!(
last_sunday_of_month(2025, 10),
NaiveDate::from_ymd_opt(2025, 10, 26).unwrap()
);
assert_eq!(
last_sunday_of_month(2024, 3),
NaiveDate::from_ymd_opt(2024, 3, 31).unwrap()
);
assert_eq!(
last_sunday_of_month(2024, 10),
NaiveDate::from_ymd_opt(2024, 10, 27).unwrap()
);
}
}