1#![warn(clippy::as_conversions)]
7
8use crate::{
9 formats::{Flexible, Format, Strict, Strictness},
10 prelude::*,
11};
12
13#[derive(Copy, Clone, PartialEq, Eq)]
14#[cfg_attr(test, derive(Debug))]
15pub(crate) enum Sign {
16 Positive,
17 Negative,
18}
19
20impl Sign {
21 #[allow(dead_code)]
22 pub(crate) fn is_positive(&self) -> bool {
23 *self == Sign::Positive
24 }
25
26 #[allow(dead_code)]
27 pub(crate) fn is_negative(&self) -> bool {
28 *self == Sign::Negative
29 }
30
31 pub(crate) fn apply_f64(&self, value: f64) -> f64 {
32 match *self {
33 Sign::Positive => value,
34 Sign::Negative => -value,
35 }
36 }
37
38 pub(crate) fn apply_i64(&self, value: i64) -> Option<i64> {
39 match *self {
40 Sign::Positive => Some(value),
41 Sign::Negative => value.checked_neg(),
42 }
43 }
44}
45
46#[derive(Copy, Clone)]
47pub(crate) struct DurationSigned {
48 pub(crate) sign: Sign,
49 pub(crate) duration: Duration,
50}
51
52impl DurationSigned {
53 pub(crate) fn new(sign: Sign, secs: u64, nanosecs: u32) -> Self {
54 Self {
55 sign,
56 duration: Duration::new(secs, nanosecs),
57 }
58 }
59
60 pub(crate) fn checked_mul(mut self, rhs: u32) -> Option<Self> {
61 self.duration = self.duration.checked_mul(rhs)?;
62 Some(self)
63 }
64
65 pub(crate) fn checked_div(mut self, rhs: u32) -> Option<Self> {
66 self.duration = self.duration.checked_div(rhs)?;
67 Some(self)
68 }
69
70 #[cfg(any(feature = "chrono_0_4", feature = "time_0_3"))]
71 pub(crate) fn with_duration(sign: Sign, duration: Duration) -> Self {
72 Self { sign, duration }
73 }
74
75 #[cfg(feature = "std")]
76 pub(crate) fn to_system_time<'de, D>(self) -> Result<SystemTime, D::Error>
77 where
78 D: Deserializer<'de>,
79 {
80 match self.sign {
81 Sign::Positive => SystemTime::UNIX_EPOCH.checked_add(self.duration),
82 Sign::Negative => SystemTime::UNIX_EPOCH.checked_sub(self.duration),
83 }
84 .ok_or_else(|| DeError::custom("timestamp is outside the range for std::time::SystemTime"))
85 }
86
87 pub(crate) fn to_std_duration<'de, D>(self) -> Result<Duration, D::Error>
88 where
89 D: Deserializer<'de>,
90 {
91 match self.sign {
92 Sign::Positive => Ok(self.duration),
93 Sign::Negative => Err(DeError::custom("std::time::Duration cannot be negative")),
94 }
95 }
96}
97
98impl From<&Duration> for DurationSigned {
99 fn from(&duration: &Duration) -> Self {
100 Self {
101 sign: Sign::Positive,
102 duration,
103 }
104 }
105}
106
107#[cfg(feature = "std")]
108impl From<&SystemTime> for DurationSigned {
109 fn from(time: &SystemTime) -> Self {
110 match time.duration_since(SystemTime::UNIX_EPOCH) {
111 Ok(dur) => DurationSigned {
112 sign: Sign::Positive,
113 duration: dur,
114 },
115 Err(err) => DurationSigned {
116 sign: Sign::Negative,
117 duration: err.duration(),
118 },
119 }
120 }
121}
122
123impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<u64, STRICTNESS>
124where
125 STRICTNESS: Strictness,
126{
127 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: Serializer,
130 {
131 if source.sign.is_negative() {
132 return Err(SerError::custom(
133 "cannot serialize a negative Duration as u64",
134 ));
135 }
136
137 let mut secs = source.duration.as_secs();
138
139 if source.duration.subsec_millis() >= 500 {
141 if source.sign.is_positive() {
142 secs += 1;
143 } else {
144 secs -= 1;
145 }
146 }
147 secs.serialize(serializer)
148 }
149}
150
151impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<i64, STRICTNESS>
152where
153 STRICTNESS: Strictness,
154{
155 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
156 where
157 S: Serializer,
158 {
159 let mut secs = source
160 .sign
161 .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| {
162 SerError::custom("The Duration of Timestamp is outside the supported range.")
163 })?)
164 .ok_or_else(|| {
165 S::Error::custom("The Duration of Timestamp is outside the supported range.")
166 })?;
167
168 if source.duration.subsec_millis() >= 500 {
171 if source.sign.is_positive() {
172 secs += 1;
173 } else {
174 secs -= 1;
175 }
176 }
177 secs.serialize(serializer)
178 }
179}
180
181impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<f64, STRICTNESS>
182where
183 STRICTNESS: Strictness,
184{
185 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
186 where
187 S: Serializer,
188 {
189 #[allow(clippy::as_conversions)]
191 let mut secs = source.sign.apply_f64(source.duration.as_secs() as f64);
192
193 if source.duration.subsec_millis() >= 500 {
195 if source.sign.is_positive() {
196 secs += 1.;
197 } else {
198 secs -= 1.;
199 }
200 }
201 secs.serialize(serializer)
202 }
203}
204
205#[cfg(feature = "alloc")]
206impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSeconds<String, STRICTNESS>
207where
208 STRICTNESS: Strictness,
209{
210 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
211 where
212 S: Serializer,
213 {
214 let mut secs = source
215 .sign
216 .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| {
217 SerError::custom("The Duration of Timestamp is outside the supported range.")
218 })?)
219 .ok_or_else(|| {
220 S::Error::custom("The Duration of Timestamp is outside the supported range.")
221 })?;
222
223 if source.duration.subsec_millis() >= 500 {
225 if source.sign.is_positive() {
226 secs += 1;
227 } else {
228 secs -= 1;
229 }
230 }
231 secs.to_string().serialize(serializer)
232 }
233}
234
235impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<f64, STRICTNESS>
236where
237 STRICTNESS: Strictness,
238{
239 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
240 where
241 S: Serializer,
242 {
243 source
244 .sign
245 .apply_f64(source.duration.as_secs_f64())
246 .serialize(serializer)
247 }
248}
249
250#[cfg(feature = "alloc")]
251impl<STRICTNESS> SerializeAs<DurationSigned> for DurationSecondsWithFrac<String, STRICTNESS>
252where
253 STRICTNESS: Strictness,
254{
255 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: Serializer,
258 {
259 source
260 .sign
261 .apply_f64(source.duration.as_secs_f64())
262 .to_string()
263 .serialize(serializer)
264 }
265}
266
267macro_rules! duration_impls {
268 ($($inner:ident { $($factor:literal => $outer:ident,)+ })+) => {
269 $($(
270
271 impl<FORMAT, STRICTNESS> SerializeAs<DurationSigned> for $outer<FORMAT, STRICTNESS>
272 where
273 FORMAT: Format,
274 STRICTNESS: Strictness,
275 $inner<FORMAT, STRICTNESS>: SerializeAs<DurationSigned>
276 {
277 fn serialize_as<S>(source: &DurationSigned, serializer: S) -> Result<S::Ok, S::Error>
278 where
279 S: Serializer,
280 {
281 let value = source.checked_mul($factor).ok_or_else(|| S::Error::custom("Failed to serialize value as the value cannot be represented."))?;
282 $inner::<FORMAT, STRICTNESS>::serialize_as(&value, serializer)
283 }
284 }
285
286 impl<'de, FORMAT, STRICTNESS> DeserializeAs<'de, DurationSigned> for $outer<FORMAT, STRICTNESS>
287 where
288 FORMAT: Format,
289 STRICTNESS: Strictness,
290 $inner<FORMAT, STRICTNESS>: DeserializeAs<'de, DurationSigned>,
291 {
292 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
293 where
294 D: Deserializer<'de>,
295 {
296 let dur = $inner::<FORMAT, STRICTNESS>::deserialize_as(deserializer)?;
297 let dur = dur.checked_div($factor).ok_or_else(|| D::Error::custom("Failed to deserialize value as the value cannot be represented."))?;
298 Ok(dur)
299 }
300 }
301
302 )+)+ };
303}
304duration_impls!(
305 DurationSeconds {
306 1000u32 => DurationMilliSeconds,
307 1_000_000u32 => DurationMicroSeconds,
308 1_000_000_000u32 => DurationNanoSeconds,
309 }
310 DurationSecondsWithFrac {
311 1000u32 => DurationMilliSecondsWithFrac,
312 1_000_000u32 => DurationMicroSecondsWithFrac,
313 1_000_000_000u32 => DurationNanoSecondsWithFrac,
314 }
315);
316
317struct DurationVisitorFlexible;
318impl Visitor<'_> for DurationVisitorFlexible {
319 type Value = DurationSigned;
320
321 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
322 formatter.write_str("an integer, a float, or a string containing a number")
323 }
324
325 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
326 where
327 E: DeError,
328 {
329 let sign = if value >= 0 {
330 Sign::Positive
331 } else {
332 Sign::Negative
333 };
334 Ok(DurationSigned::new(sign, value.unsigned_abs(), 0))
335 }
336
337 fn visit_u64<E>(self, secs: u64) -> Result<Self::Value, E>
338 where
339 E: DeError,
340 {
341 Ok(DurationSigned::new(Sign::Positive, secs, 0))
342 }
343
344 fn visit_f64<E>(self, secs: f64) -> Result<Self::Value, E>
345 where
346 E: DeError,
347 {
348 utils::duration_signed_from_secs_f64(secs).map_err(DeError::custom)
349 }
350
351 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
352 where
353 E: DeError,
354 {
355 match parse_float_into_time_parts(value) {
356 Ok((sign, seconds, subseconds)) => Ok(DurationSigned::new(sign, seconds, subseconds)),
357 Err(ParseFloatError::InvalidValue) => {
358 Err(DeError::invalid_value(Unexpected::Str(value), &self))
359 }
360 Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
361 }
362 }
363}
364
365impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<u64, Strict> {
366 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
367 where
368 D: Deserializer<'de>,
369 {
370 u64::deserialize(deserializer).map(|secs: u64| DurationSigned::new(Sign::Positive, secs, 0))
371 }
372}
373
374impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<i64, Strict> {
375 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
376 where
377 D: Deserializer<'de>,
378 {
379 i64::deserialize(deserializer).map(|secs: i64| {
380 let sign = match secs.is_negative() {
381 true => Sign::Negative,
382 false => Sign::Positive,
383 };
384 DurationSigned::new(sign, secs.abs_diff(0), 0)
385 })
386 }
387}
388
389#[cfg(feature = "std")]
391impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<f64, Strict> {
392 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
393 where
394 D: Deserializer<'de>,
395 {
396 let val = f64::deserialize(deserializer)?.round();
397 utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
398 }
399}
400
401#[cfg(feature = "alloc")]
402impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds<String, Strict> {
403 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
404 where
405 D: Deserializer<'de>,
406 {
407 struct DurationDeserializationVisitor;
408
409 impl Visitor<'_> for DurationDeserializationVisitor {
410 type Value = DurationSigned;
411
412 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
413 formatter.write_str("a string containing a number")
414 }
415
416 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
417 where
418 E: DeError,
419 {
420 let secs: i64 = value.parse().map_err(DeError::custom)?;
421 let sign = match secs.is_negative() {
422 true => Sign::Negative,
423 false => Sign::Positive,
424 };
425 Ok(DurationSigned::new(sign, secs.abs_diff(0), 0))
426 }
427 }
428
429 deserializer.deserialize_str(DurationDeserializationVisitor)
430 }
431}
432
433impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSeconds<FORMAT, Flexible>
434where
435 FORMAT: Format,
436{
437 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
438 where
439 D: Deserializer<'de>,
440 {
441 deserializer.deserialize_any(DurationVisitorFlexible)
442 }
443}
444
445impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<f64, Strict> {
446 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
447 where
448 D: Deserializer<'de>,
449 {
450 let val = f64::deserialize(deserializer)?;
451 utils::duration_signed_from_secs_f64(val).map_err(DeError::custom)
452 }
453}
454
455#[cfg(feature = "alloc")]
456impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<String, Strict> {
457 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
458 where
459 D: Deserializer<'de>,
460 {
461 let value = String::deserialize(deserializer)?;
462 match parse_float_into_time_parts(&value) {
463 Ok((sign, seconds, subseconds)) => Ok(DurationSigned {
464 sign,
465 duration: Duration::new(seconds, subseconds),
466 }),
467 Err(ParseFloatError::InvalidValue) => Err(DeError::invalid_value(
468 Unexpected::Str(&value),
469 &"a string containing an integer or float",
470 )),
471 Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)),
472 }
473 }
474}
475
476impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac<FORMAT, Flexible>
477where
478 FORMAT: Format,
479{
480 fn deserialize_as<D>(deserializer: D) -> Result<DurationSigned, D::Error>
481 where
482 D: Deserializer<'de>,
483 {
484 deserializer.deserialize_any(DurationVisitorFlexible)
485 }
486}
487
488#[cfg_attr(test, derive(Debug, PartialEq))]
489pub(crate) enum ParseFloatError {
490 InvalidValue,
491 #[cfg(not(feature = "alloc"))]
492 Custom(&'static str),
493 #[cfg(feature = "alloc")]
494 Custom(String),
495}
496
497fn parse_float_into_time_parts(mut value: &str) -> Result<(Sign, u64, u32), ParseFloatError> {
498 let sign = match value.chars().next() {
499 Some('+') => {
501 value = &value[1..];
502 Sign::Positive
503 }
504 Some('-') => {
505 value = &value[1..];
506 Sign::Negative
507 }
508 _ => Sign::Positive,
509 };
510
511 let partslen = value.split('.').count();
512 let mut parts = value.split('.');
513 match partslen {
514 1 => {
515 let seconds = parts.next().expect("Float contains exactly one part");
516 if let Ok(seconds) = seconds.parse() {
517 Ok((sign, seconds, 0))
518 } else {
519 Err(ParseFloatError::InvalidValue)
520 }
521 }
522 2 => {
523 let seconds = parts.next().expect("Float contains exactly one part");
524 if let Ok(seconds) = seconds.parse() {
525 let subseconds = parts.next().expect("Float contains exactly one part");
526 let subseclen = u32::try_from(subseconds.chars().count()).map_err(|_| {
527 #[cfg(feature = "alloc")]
528 return ParseFloatError::Custom(alloc::format!(
529 "Duration and Timestamps with no more than 9 digits precision, but '{value}' has more"
530 ));
531 #[cfg(not(feature = "alloc"))]
532 return ParseFloatError::Custom(
533 "Duration and Timestamps with no more than 9 digits precision",
534 );
535 })?;
536 if subseclen > 9 {
537 #[cfg(feature = "alloc")]
538 return Err(ParseFloatError::Custom(alloc::format!(
539 "Duration and Timestamps with no more than 9 digits precision, but '{value}' has more"
540 )));
541 #[cfg(not(feature = "alloc"))]
542 return Err(ParseFloatError::Custom(
543 "Duration and Timestamps with no more than 9 digits precision",
544 ));
545 }
546
547 if let Ok(mut subseconds) = subseconds.parse() {
548 subseconds *= 10u32.pow(9 - subseclen);
550 Ok((sign, seconds, subseconds))
551 } else {
552 Err(ParseFloatError::InvalidValue)
553 }
554 } else {
555 Err(ParseFloatError::InvalidValue)
556 }
557 }
558
559 _ => Err(ParseFloatError::InvalidValue),
560 }
561}
562
563#[test]
564fn test_parse_float_into_time_parts() {
565 assert_eq!(
567 Ok((Sign::Positive, 123, 456_000_000)),
568 parse_float_into_time_parts("+123.456")
569 );
570 assert_eq!(
571 Ok((Sign::Negative, 123, 987_000)),
572 parse_float_into_time_parts("-123.000987")
573 );
574 assert_eq!(
575 Ok((Sign::Positive, 18446744073709551615, 123_456_789)),
576 parse_float_into_time_parts("18446744073709551615.123456789")
577 );
578
579 assert_eq!(
581 Ok((Sign::Positive, 0, 456_000_000)),
582 parse_float_into_time_parts("+0.456")
583 );
584 assert_eq!(
585 Ok((Sign::Negative, 0, 987_000)),
586 parse_float_into_time_parts("-0.000987")
587 );
588 assert_eq!(
589 Ok((Sign::Positive, 0, 123_456_789)),
590 parse_float_into_time_parts("0.123456789")
591 );
592}