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