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