use crate::data_structures::history::event::Event;
use crate::data_types::quantity::date_time::dv_date_time::DvDateTime;
use crate::data_types::quantity::date_time::dv_duration::DvDuration;
fn duration_from_seconds(secs: f64) -> DvDuration {
let magnitude = secs.abs();
#[expect(
clippy::float_cmp,
reason = "an exact-integrality test is precisely a bit-equality question (`x.floor() == x`), not a tolerance comparison"
)]
let body = if magnitude.floor() == magnitude {
format!("PT{magnitude:.0}S")
} else {
format!("PT{magnitude}S")
};
DvDuration {
normal_status: None,
normal_range: None,
other_reference_ranges: None,
magnitude_status: None,
accuracy: None,
accuracy_is_percent: None,
value: if secs < 0.0 { format!("-{body}") } else { body },
}
}
pub(crate) fn offset_seconds(time: &DvDateTime, origin: &DvDateTime) -> Option<f64> {
Some(time.magnitude()? - origin.magnitude()?)
}
impl<T> Event<T> {
#[must_use]
pub fn time(&self) -> &DvDateTime {
match self {
Self::IntervalEvent(e) => &e.time,
Self::PointEvent(e) => &e.time,
}
}
#[must_use]
pub fn data(&self) -> &T {
match self {
Self::IntervalEvent(e) => &e.data,
Self::PointEvent(e) => &e.data,
}
}
#[must_use]
pub fn offset_from(&self, origin: &DvDateTime) -> Option<DvDuration> {
offset_seconds(self.time(), origin).map(duration_from_seconds)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data_structures::history::point_event::PointEvent;
use crate::data_types::text::dv_text::{DvText, DvTextData};
fn text(value: &str) -> DvText {
DvText::DvText(DvTextData {
value: value.to_owned(),
hyperlink: None,
formatting: None,
mappings: openehr_base::containers::present_nonempty(Vec::new()),
language: None,
encoding: None,
})
}
fn date_time(value: &str) -> DvDateTime {
DvDateTime {
normal_status: None,
normal_range: None,
other_reference_ranges: None,
magnitude_status: None,
accuracy: None,
value: value.to_owned(),
}
}
fn event(time: &str) -> Event<i32> {
Event::PointEvent(PointEvent {
name: text("event"),
archetype_node_id: "at0002".to_owned(),
uid: None,
links: openehr_base::containers::present_nonempty(Vec::new()),
archetype_details: None,
feeder_audit: None,
time: date_time(time),
state: None,
data: 1,
})
}
#[test]
fn offset_is_time_minus_origin() {
let origin = date_time("2021-01-01T00:00:00");
let e = event("2021-01-01T01:00:00");
assert_eq!(
e.offset_from(&origin).map(|d| d.value),
Some("PT3600S".to_owned())
);
let early = event("2020-12-31T23:59:00");
assert_eq!(
early.offset_from(&origin).map(|d| d.value),
Some("-PT60S".to_owned())
);
assert_eq!(
e.offset_from(&origin).and_then(|d| d.magnitude()),
Some(3600.0)
);
}
#[test]
fn offset_unavailable_for_malformed_time() {
let origin = date_time("not-a-time");
assert!(event("2021-01-01T01:00:00").offset_from(&origin).is_none());
}
}