iso8601_duration_serde/
lib.rs1use iso8601_duration::Duration as IsoDuration;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use time::Duration;
4use time_core::convert::*;
5
6#[inline]
8pub fn serialize<S: Serializer>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error> {
9 let mut seconds = duration.whole_seconds();
10 let nanoseconds = duration.subsec_nanoseconds();
11
12 let days = seconds / Second::per_t::<i64>(Day);
13 seconds = seconds % Second::per_t::<i64>(Day);
14
15 let hours = seconds / Second::per_t::<i64>(Hour);
16 seconds = seconds % Second::per_t::<i64>(Hour);
17
18 let minutes = seconds / Second::per_t::<i64>(Minute);
19 seconds = seconds % Second::per_t::<i64>(Minute);
20
21 let seconds_f32 =
22 seconds as f32 + (nanoseconds as f64 / Nanosecond::per_t::<f64>(Second)) as f32;
23
24 let iso_duration = IsoDuration::new(
25 0f32,
26 0f32,
27 days as f32,
28 hours as f32,
29 minutes as f32,
30 seconds_f32,
31 );
32
33 iso_duration.serialize(serializer)
34}
35
36#[inline]
38pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<Duration, D::Error> {
39 let duration = IsoDuration::deserialize(deserializer)?;
40
41 if duration.year > 0.0 || duration.month > 0.0 {
42 return Err(serde::de::Error::custom(
43 "Duration::year and Duration::month must be zero",
44 ));
45 }
46
47 let seconds_fract = duration.day.fract() * Second::per_t::<f32>(Day)
48 + duration.hour.fract() * Second::per_t::<f32>(Hour)
49 + duration.minute.fract() * Second::per_t::<f32>(Minute)
50 + duration.second.fract();
51
52 let seconds = duration.day as i64 * Second::per_t::<i64>(Day)
53 + duration.hour as i64 * Second::per_t::<i64>(Hour)
54 + duration.minute as i64 * Second::per_t::<i64>(Minute)
55 + duration.second as i64
56 + seconds_fract as i64;
57
58 let nanoseconds = (seconds_fract.fract() * Nanosecond::per_t::<f32>(Second)) as i32;
59
60 Ok(Duration::new(seconds, nanoseconds))
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use serde::{Serialize, Deserialize};
67
68 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
69 struct TestStruct {
70 #[serde(with = "super")]
71 duration: Duration,
72 }
73
74 #[test]
75 fn test_positive_duration_with_days() {
76 let test_struct = TestStruct {
77 duration: Duration::days(5),
78 };
79
80 let json = serde_json::to_string(&test_struct).unwrap();
81 assert_eq!(json, r#"{"duration":"P5D"}"#);
82
83 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
84 assert_eq!(test_struct, deserialized);
85 }
86
87 #[test]
88 fn test_positive_duration_with_hours() {
89 let test_struct = TestStruct {
90 duration: Duration::hours(3),
91 };
92
93 let json = serde_json::to_string(&test_struct).unwrap();
94 assert_eq!(json, r#"{"duration":"PT3H"}"#);
95
96 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
97 assert_eq!(test_struct, deserialized);
98 }
99
100 #[test]
101 fn test_positive_duration_with_minutes() {
102 let test_struct = TestStruct {
103 duration: Duration::minutes(30),
104 };
105
106 let json = serde_json::to_string(&test_struct).unwrap();
107 assert_eq!(json, r#"{"duration":"PT30M"}"#);
108
109 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
110 assert_eq!(test_struct, deserialized);
111 }
112
113 #[test]
114 fn test_positive_duration_with_seconds() {
115 let test_struct = TestStruct {
116 duration: Duration::seconds(45),
117 };
118
119 let json = serde_json::to_string(&test_struct).unwrap();
120 assert_eq!(json, r#"{"duration":"PT45S"}"#);
121
122 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
123 assert_eq!(test_struct, deserialized);
124 }
125
126 #[test]
127 fn test_complex_positive_duration() {
128 let test_struct = TestStruct {
129 duration: Duration::days(2) + Duration::hours(3) + Duration::minutes(30) + Duration::seconds(15),
130 };
131
132 let json = serde_json::to_string(&test_struct).unwrap();
133 assert_eq!(json, r#"{"duration":"P2DT3H30M15S"}"#);
134
135 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
136 assert_eq!(test_struct, deserialized);
137 }
138
139 #[test]
140 fn test_duration_with_fractional_seconds() {
141 let test_struct = TestStruct {
142 duration: Duration::seconds(10) + Duration::milliseconds(500),
143 };
144
145 let json = serde_json::to_string(&test_struct).unwrap();
146 let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
149 assert_eq!(deserialized.duration.whole_seconds(), test_struct.duration.whole_seconds());
151 assert!(deserialized.duration - test_struct.duration < Duration::seconds(1));
153 assert!(test_struct.duration - deserialized.duration < Duration::seconds(1));
154 }
155
156 #[test]
157 fn test_deserialize_iso8601_variants() {
158 let json = r#"{"duration":"P7D"}"#;
162 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
163 assert_eq!(deserialized.duration, Duration::days(7));
164
165 let json = r#"{"duration":"PT5H"}"#;
167 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
168 assert_eq!(deserialized.duration, Duration::hours(5));
169
170 let json = r#"{"duration":"PT30M"}"#;
172 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
173 assert_eq!(deserialized.duration, Duration::minutes(30));
174
175 let json = r#"{"duration":"PT45S"}"#;
177 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
178 assert_eq!(deserialized.duration, Duration::seconds(45));
179
180 let json = r#"{"duration":"P1DT12H30M45S"}"#;
182 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
183 assert_eq!(deserialized.duration, Duration::days(1) + Duration::hours(12) + Duration::minutes(30) + Duration::seconds(45));
184
185 let json = r#"{"duration":"PT10.5S"}"#;
187 let deserialized: TestStruct = serde_json::from_str(json).unwrap();
188 assert!(deserialized.duration > Duration::seconds(10));
190 assert!(deserialized.duration < Duration::seconds(11));
191 }
192}