use crate::tzif::{LocalTimeType, ParsedTzif};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Difference {
pub what: String,
pub ours: String,
pub theirs: String,
}
type Eff = (i32, bool, String);
fn eff(t: &LocalTimeType) -> Eff {
(t.utoff, t.is_dst, t.abbr.clone())
}
fn initial_type(p: &ParsedTzif) -> Eff {
if p.transitions.is_empty() {
return eff(&p.types[0]);
}
let idx = p.types.iter().position(|t| !t.is_dst).unwrap_or(0);
eff(&p.types[idx])
}
pub fn effective_at(p: &ParsedTzif, t: i64) -> (i32, bool, String) {
match p.transitions.iter().rev().find(|tr| tr.at <= t) {
Some(tr) => eff(&p.types[tr.type_index as usize]),
None => initial_type(p),
}
}
pub fn diff(ours: &ParsedTzif, theirs: &ParsedTzif) -> Vec<Difference> {
let mut diffs = Vec::new();
if ours.footer != theirs.footer {
diffs.push(Difference {
what: "footer (POSIX TZ)".into(),
ours: ours.footer.clone(),
theirs: theirs.footer.clone(),
});
}
let oi = initial_type(ours);
let ti = initial_type(theirs);
if oi != ti {
diffs.push(Difference {
what: "initial local-time-type".into(),
ours: format!("{oi:?}"),
theirs: format!("{ti:?}"),
});
}
if ours.transitions.len() != theirs.transitions.len() {
diffs.push(Difference {
what: "transition count".into(),
ours: ours.transitions.len().to_string(),
theirs: theirs.transitions.len().to_string(),
});
return diffs;
}
for (i, (a, b)) in ours.transitions.iter().zip(&theirs.transitions).enumerate() {
if a.at != b.at {
diffs.push(Difference {
what: format!("transition[{i}] instant"),
ours: a.at.to_string(),
theirs: b.at.to_string(),
});
}
let ea = eff(&ours.types[a.type_index as usize]);
let eb = eff(&theirs.types[b.type_index as usize]);
if ea != eb {
diffs.push(Difference {
what: format!("transition[{i}] target type"),
ours: format!("{ea:?}"),
theirs: format!("{eb:?}"),
});
}
}
diffs
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tzif::{write_bytes, TzifData};
fn parsed(d: &TzifData) -> ParsedTzif {
crate::tzif::parse(&write_bytes(d).unwrap()).unwrap()
}
#[test]
fn identical_fixed_zones_match() {
let a = parsed(&TzifData::fixed(-18000, "EST", "EST5"));
let b = parsed(&TzifData::fixed(-18000, "EST", "EST5"));
assert!(diff(&a, &b).is_empty());
}
#[test]
fn offset_mismatch_is_reported() {
let a = parsed(&TzifData::fixed(-18000, "EST", "EST5"));
let b = parsed(&TzifData::fixed(-14400, "EDT", "EDT4"));
assert!(!diff(&a, &b).is_empty());
}
}