bo4e_core/com/
time_period.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
25#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
26#[cfg_attr(feature = "json-schema", schemars(rename = "Zeitraum"))]
27#[serde(rename_all = "camelCase")]
28pub struct TimePeriod {
29 #[serde(flatten)]
31 pub meta: Bo4eMeta,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 #[cfg_attr(feature = "json-schema", schemars(rename = "startdatum"))]
36 pub start: Option<DateTime<Utc>>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 #[cfg_attr(feature = "json-schema", schemars(rename = "enddatum"))]
41 pub end: Option<DateTime<Utc>>,
42}
43
44impl Bo4eObject for TimePeriod {
45 fn type_name_german() -> &'static str {
46 "Zeitraum"
47 }
48
49 fn type_name_english() -> &'static str {
50 "TimePeriod"
51 }
52
53 fn meta(&self) -> &Bo4eMeta {
54 &self.meta
55 }
56
57 fn meta_mut(&mut self) -> &mut Bo4eMeta {
58 &mut self.meta
59 }
60}
61
62impl TimePeriod {
63 pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
65 Self {
66 start: Some(start),
67 end: Some(end),
68 ..Default::default()
69 }
70 }
71
72 pub fn starting_from(start: DateTime<Utc>) -> Self {
74 Self {
75 start: Some(start),
76 end: None,
77 ..Default::default()
78 }
79 }
80
81 pub fn contains(&self, timestamp: DateTime<Utc>) -> bool {
83 let after_start = self.start.map_or(true, |s| timestamp >= s);
84 let before_end = self.end.map_or(true, |e| timestamp < e);
85 after_start && before_end
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use chrono::TimeZone;
93
94 #[test]
95 fn test_time_period_creation() {
96 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
97 let end = Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap();
98
99 let period = TimePeriod::new(start, end);
100 assert_eq!(period.start, Some(start));
101 assert_eq!(period.end, Some(end));
102 }
103
104 #[test]
105 fn test_contains() {
106 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
107 let end = Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap();
108 let period = TimePeriod::new(start, end);
109
110 let mid = Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap();
111 assert!(period.contains(mid));
112
113 let before = Utc.with_ymd_and_hms(2023, 12, 31, 0, 0, 0).unwrap();
114 assert!(!period.contains(before));
115 }
116
117 #[test]
118 fn test_serialize_iso8601() {
119 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
120 let period = TimePeriod::starting_from(start);
121
122 let json = serde_json::to_string(&period).unwrap();
123 assert!(json.contains("2024-01-01"));
124 }
125
126 #[test]
127 fn test_roundtrip() {
128 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
129 let end = Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap();
130 let period = TimePeriod::new(start, end);
131
132 let json = serde_json::to_string(&period).unwrap();
133 let parsed: TimePeriod = serde_json::from_str(&json).unwrap();
134 assert_eq!(period, parsed);
135 }
136
137 #[test]
138 fn test_bo4e_object_impl() {
139 assert_eq!(TimePeriod::type_name_german(), "Zeitraum");
140 assert_eq!(TimePeriod::type_name_english(), "TimePeriod");
141 }
142}