jiff/fmt/serde.rs
1/*!
2This module provides helpers to use with [Serde].
3
4Some helpers, like those for `Timestamp`, are exposed as modules meant
5to be used with Serde's [`with` attribute]. Others, like for `Span` and
6`SignedDuration`, only provide serialization helpers to be used with Serde's
7[`serialize_with` attribute].
8
9# Module hierarchy
10
11The available helpers can be more quickly understood by looking at a fully
12rendered tree of this module's hierarchy. Only the leaves of the tree are
13usable with Serde's attributes. For each leaf, the full path is spelled out for
14easy copy & paste.
15
16* [`duration`]
17 * [`friendly`](self::duration::friendly)
18 * [`compact`](self::duration::friendly::compact)
19 * [`jiff::fmt::serde::duration::friendly::compact::required`](self::duration::friendly::compact::required)
20 * [`jiff::fmt::serde::duration::friendly::compact::optional`](self::duration::friendly::compact::optional)
21* [`span`]
22 * [`friendly`](self::span::friendly)
23 * [`compact`](self::span::friendly::compact)
24 * [`jiff::fmt::serde::span::friendly::compact::required`](self::span::friendly::compact::required)
25 * [`jiff::fmt::serde::span::friendly::compact::optional`](self::span::friendly::compact::optional)
26* [`timestamp`]
27 * [`second`](self::timestamp::second)
28 * [`jiff::fmt::serde::timestamp::second::required`](self::timestamp::second::required)
29 * [`jiff::fmt::serde::timestamp::second::optional`](self::timestamp::second::optional)
30 * [`millisecond`](self::timestamp::millisecond)
31 * [`jiff::fmt::serde::timestamp::millisecond::required`](self::timestamp::millisecond::required)
32 * [`jiff::fmt::serde::timestamp::millisecond::optional`](self::timestamp::millisecond::optional)
33 * [`microsecond`](self::timestamp::millisecond)
34 * [`jiff::fmt::serde::timestamp::microsecond::required`](self::timestamp::microsecond::required)
35 * [`jiff::fmt::serde::timestamp::microsecond::optional`](self::timestamp::microsecond::optional)
36 * [`nanosecond`](self::timestamp::millisecond)
37 * [`jiff::fmt::serde::timestamp::nanosecond::required`](self::timestamp::nanosecond::required)
38 * [`jiff::fmt::serde::timestamp::nanosecond::optional`](self::timestamp::nanosecond::optional)
39* [`tz`]
40 * [`jiff::fmt::serde::tz::required`](self::tz::required)
41 * [`jiff::fmt::serde::tz::optional`](self::tz::optional)
42* [`unsigned_duration`]
43 * [`friendly`](self::unsigned_duration::friendly)
44 * [`compact`](self::unsigned_duration::friendly::compact)
45 * [`jiff::fmt::serde::unsigned_duration::friendly::compact::required`](self::unsigned_duration::friendly::compact::required)
46 * [`jiff::fmt::serde::unsigned_duration::friendly::compact::optional`](self::unsigned_duration::friendly::compact::optional)
47 * [`required`](self::unsigned_duration::required)
48 * [`optional`](self::unsigned_duration::optional)
49
50# Example: timestamps as an integer
51
52This example shows how to deserialize an integer number of seconds since the
53Unix epoch into a [`Timestamp`](crate::Timestamp). And the reverse operation
54for serialization:
55
56```
57use jiff::Timestamp;
58
59#[derive(Debug, serde::Deserialize, serde::Serialize)]
60struct Record {
61 #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
62 timestamp: Timestamp,
63}
64
65let json = r#"{"timestamp":1517644800}"#;
66let got: Record = serde_json::from_str(&json)?;
67assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
68assert_eq!(serde_json::to_string(&got)?, json);
69
70# Ok::<(), Box<dyn std::error::Error>>(())
71```
72
73# Example: optional timestamp support
74
75And this example shows how to use an `Option<Timestamp>` instead of a
76`Timestamp`. Note that in this case, we show how to roundtrip the number of
77**milliseconds** since the Unix epoch:
78
79```
80use jiff::Timestamp;
81
82#[derive(Debug, serde::Deserialize, serde::Serialize)]
83struct Record {
84 #[serde(with = "jiff::fmt::serde::timestamp::millisecond::optional")]
85 timestamp: Option<Timestamp>,
86}
87
88let json = r#"{"timestamp":1517644800123}"#;
89let got: Record = serde_json::from_str(&json)?;
90assert_eq!(got.timestamp, Some(Timestamp::from_millisecond(1517644800_123)?));
91assert_eq!(serde_json::to_string(&got)?, json);
92
93# Ok::<(), Box<dyn std::error::Error>>(())
94```
95
96# Example: the "friendly" duration format
97
98The [`Span`](crate::Span) and [`SignedDuration`](crate::SignedDuration) types
99in this crate both implement Serde's `Serialize` and `Deserialize` traits. For
100`Serialize`, they both use the [ISO 8601 Temporal duration format], but for
101`Deserialize`, they both support the ISO 8601 Temporal duration format and
102the ["friendly" duration format] simultaneously. In order to serialize either
103type in the "friendly" format, you can either define your own serialization
104functions or use one of the convenience routines provided by this module. For
105example:
106
107```
108use jiff::{ToSpan, Span};
109
110#[derive(Debug, serde::Deserialize, serde::Serialize)]
111struct Record {
112 #[serde(
113 serialize_with = "jiff::fmt::serde::span::friendly::compact::required"
114 )]
115 span: Span,
116}
117
118let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
119let got: Record = serde_json::from_str(&json)?;
120assert_eq!(
121 got.span,
122 1.year().months(2).hours(36).milliseconds(1100).fieldwise(),
123);
124
125let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
126assert_eq!(serde_json::to_string(&got).unwrap(), expected);
127
128# Ok::<(), Box<dyn std::error::Error>>(())
129```
130
131[Serde]: https://serde.rs/
132[`with` attribute]: https://serde.rs/field-attrs.html#with
133[`serialize_with` attribute]: https://serde.rs/field-attrs.html#serialize_with
134[ISO 8601 Temporal duration format]: crate::fmt::temporal
135["friendly" duration format]: crate::fmt::friendly
136*/
137
138/// Convenience routines for serializing
139/// [`SignedDuration`](crate::SignedDuration) values.
140///
141/// These convenience routines exist because the `Serialize` implementation for
142/// `SignedDuration` always uses the ISO 8601 duration format. These routines
143/// provide a way to use the "[friendly](crate::fmt::friendly)" format.
144///
145/// Only serialization routines are provided because a `SignedDuration`'s
146/// `Deserialize` implementation automatically handles both the ISO 8601
147/// duration format and the "friendly" format.
148///
149/// # Advice
150///
151/// The `Serialize` implementation uses ISO 8601 because it is a widely
152/// accepted interchange format for communicating durations. If you need to
153/// inter-operate with other systems, it is almost certainly the correct
154/// choice.
155///
156/// The "friendly" format does not adhere to any universal specified format.
157/// However, it is perhaps easier to read. Beyond that, its utility for
158/// `SignedDuration` is somewhat less compared to [`Span`](crate::Span), since
159/// for `Span`, the friendly format preserves all components of the `Span`
160/// faithfully. But a `SignedDuration` is just a 96-bit integer of nanoseconds,
161/// so there are no individual components to preserve. Still, even with a
162/// `SignedDuration`, you might prefer the friendly format.
163///
164/// # Available routines
165///
166/// A [`SpanPrinter`](crate::fmt::friendly::SpanPrinter) has a lot of different
167/// configuration options. The convenience routines provided by this module
168/// only cover a small space of those options since it isn't feasible to
169/// provide a convenience routine for every possible set of configuration
170/// options.
171///
172/// While more convenience routines could be added (please file an issue), only
173/// the most common or popular such routines can be feasibly added. So in the
174/// case where a convenience routine isn't available for the configuration you
175/// want, you can very easily define your own `serialize_with` routine.
176///
177/// The recommended approach is to define a function and a type that
178/// implements the `std::fmt::Display` trait. This way, if a serializer can
179/// efficiently support `Display` implementations, then an allocation can be
180/// avoided.
181///
182/// ```
183/// use jiff::SignedDuration;
184///
185/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
186/// struct Data {
187/// #[serde(serialize_with = "custom_friendly")]
188/// duration: SignedDuration,
189/// }
190///
191/// let json = r#"{"duration": "36 hours 1100ms"}"#;
192/// let got: Data = serde_json::from_str(&json).unwrap();
193/// assert_eq!(got.duration, SignedDuration::new(36 * 60 * 60 + 1, 100_000_000));
194///
195/// let expected = r#"{"duration":"36:00:01.100"}"#;
196/// assert_eq!(serde_json::to_string(&got).unwrap(), expected);
197///
198/// fn custom_friendly<S: serde::Serializer>(
199/// duration: &SignedDuration,
200/// se: S,
201/// ) -> Result<S::Ok, S::Error> {
202/// struct Custom<'a>(&'a SignedDuration);
203///
204/// impl<'a> std::fmt::Display for Custom<'a> {
205/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
206/// use jiff::fmt::{friendly::SpanPrinter, StdFmtWrite};
207///
208/// static PRINTER: SpanPrinter = SpanPrinter::new()
209/// .hours_minutes_seconds(true)
210/// .precision(Some(3));
211///
212/// PRINTER
213/// .print_duration(self.0, StdFmtWrite(f))
214/// .map_err(|_| core::fmt::Error)
215/// }
216/// }
217///
218/// se.collect_str(&Custom(duration))
219/// }
220/// ```
221///
222/// Recall from above that you only need a custom serialization routine
223/// for this. Namely, deserialization automatically supports parsing all
224/// configuration options for serialization unconditionally.
225pub mod duration {
226 /// Serialize a `SignedDuration` in the [`friendly`](crate::fmt::friendly) duration
227 /// format.
228 pub mod friendly {
229 /// Serialize a `SignedDuration` in the
230 /// [`friendly`](crate::fmt::friendly) duration format using compact
231 /// designators.
232 pub mod compact {
233 use crate::fmt::{friendly, StdFmtWrite};
234
235 struct CompactDuration<'a>(&'a crate::SignedDuration);
236
237 impl<'a> core::fmt::Display for CompactDuration<'a> {
238 fn fmt(
239 &self,
240 f: &mut core::fmt::Formatter,
241 ) -> core::fmt::Result {
242 static PRINTER: friendly::SpanPrinter =
243 friendly::SpanPrinter::new()
244 .designator(friendly::Designator::Compact);
245 PRINTER
246 .print_duration(self.0, StdFmtWrite(f))
247 .map_err(|_| core::fmt::Error)
248 }
249 }
250
251 impl<'a> serde_core::Serialize for CompactDuration<'a> {
252 fn serialize<S: serde_core::Serializer>(
253 &self,
254 se: S,
255 ) -> Result<S::Ok, S::Error> {
256 se.collect_str(self)
257 }
258 }
259
260 /// Serialize a required `SignedDuration` in the [`friendly`]
261 /// duration format using compact designators.
262 #[inline]
263 pub fn required<S: serde_core::Serializer>(
264 duration: &crate::SignedDuration,
265 se: S,
266 ) -> Result<S::Ok, S::Error> {
267 se.collect_str(&CompactDuration(duration))
268 }
269
270 /// Serialize an optional `SignedDuration` in the [`friendly`]
271 /// duration format using compact designators.
272 #[inline]
273 pub fn optional<S: serde_core::Serializer>(
274 duration: &Option<crate::SignedDuration>,
275 se: S,
276 ) -> Result<S::Ok, S::Error> {
277 match *duration {
278 None => se.serialize_none(),
279 Some(ref duration) => {
280 se.serialize_some(&CompactDuration(duration))
281 }
282 }
283 }
284 }
285 }
286}
287
288/// Convenience routines for serializing [`Span`](crate::Span) values.
289///
290/// These convenience routines exist because the `Serialize` implementation for
291/// `Span` always uses the ISO 8601 duration format. These routines provide a
292/// way to use the "[friendly](crate::fmt::friendly)" format.
293///
294/// Only serialization routines are provided because a `Span`'s `Deserialize`
295/// implementation automatically handles both the ISO 8601 duration format and
296/// the "friendly" format.
297///
298/// # Advice
299///
300/// The `Serialize` implementation uses ISO 8601 because it is a widely
301/// accepted interchange format for communicating durations. If you need to
302/// inter-operate with other systems, it is almost certainly the correct choice.
303///
304/// The "friendly" format does not adhere to any universal specified format.
305/// However, it is perhaps easier to read, and crucially, unambiguously
306/// represents all components of a `Span` faithfully. (In contrast, the ISO
307/// 8601 format always normalizes sub-second durations into fractional seconds,
308/// which means durations like `1100ms` and `1s100ms` are always considered
309/// equivalent.)
310///
311/// # Available routines
312///
313/// A [`SpanPrinter`](crate::fmt::friendly::SpanPrinter) has a lot of different
314/// configuration options. The convenience routines provided by this module
315/// only cover a small space of those options since it isn't feasible to
316/// provide a convenience routine for every possible set of configuration
317/// options.
318///
319/// While more convenience routines could be added (please file an issue), only
320/// the most common or popular such routines can be feasibly added. So in the
321/// case where a convenience routine isn't available for the configuration you
322/// want, you can very easily define your own `serialize_with` routine.
323///
324/// The recommended approach is to define a function and a type that
325/// implements the `std::fmt::Display` trait. This way, if a serializer can
326/// efficiently support `Display` implementations, then an allocation can be
327/// avoided.
328///
329/// ```
330/// use jiff::{Span, ToSpan};
331///
332/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
333/// struct Data {
334/// #[serde(serialize_with = "custom_friendly")]
335/// duration: Span,
336/// }
337///
338/// let json = r#"{"duration": "1 year 2 months 36 hours 1100ms"}"#;
339/// let got: Data = serde_json::from_str(&json).unwrap();
340/// assert_eq!(
341/// got.duration,
342/// 1.year().months(2).hours(36).milliseconds(1100).fieldwise(),
343/// );
344///
345/// let expected = r#"{"duration":"1 year, 2 months, 36:00:01.100"}"#;
346/// assert_eq!(serde_json::to_string(&got).unwrap(), expected);
347///
348/// fn custom_friendly<S: serde::Serializer>(
349/// span: &Span,
350/// se: S,
351/// ) -> Result<S::Ok, S::Error> {
352/// struct Custom<'a>(&'a Span);
353///
354/// impl<'a> std::fmt::Display for Custom<'a> {
355/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
356/// use jiff::fmt::{
357/// friendly::{Designator, Spacing, SpanPrinter},
358/// StdFmtWrite,
359/// };
360///
361/// static PRINTER: SpanPrinter = SpanPrinter::new()
362/// .designator(Designator::Verbose)
363/// .comma_after_designator(true)
364/// .spacing(Spacing::BetweenUnitsAndDesignators)
365/// .hours_minutes_seconds(true)
366/// .precision(Some(3));
367///
368/// PRINTER
369/// .print_span(self.0, StdFmtWrite(f))
370/// .map_err(|_| core::fmt::Error)
371/// }
372/// }
373///
374/// se.collect_str(&Custom(span))
375/// }
376/// ```
377///
378/// Recall from above that you only need a custom serialization routine
379/// for this. Namely, deserialization automatically supports parsing all
380/// configuration options for serialization unconditionally.
381pub mod span {
382 /// Serialize a `Span` in the [`friendly`](crate::fmt::friendly) duration
383 /// format.
384 pub mod friendly {
385 /// Serialize a `Span` in the [`friendly`](crate::fmt::friendly)
386 /// duration format using compact designators.
387 pub mod compact {
388 use crate::fmt::{friendly, StdFmtWrite};
389
390 struct CompactSpan<'a>(&'a crate::Span);
391
392 impl<'a> core::fmt::Display for CompactSpan<'a> {
393 fn fmt(
394 &self,
395 f: &mut core::fmt::Formatter,
396 ) -> core::fmt::Result {
397 static PRINTER: friendly::SpanPrinter =
398 friendly::SpanPrinter::new()
399 .designator(friendly::Designator::Compact);
400 PRINTER
401 .print_span(self.0, StdFmtWrite(f))
402 .map_err(|_| core::fmt::Error)
403 }
404 }
405
406 impl<'a> serde_core::Serialize for CompactSpan<'a> {
407 fn serialize<S: serde_core::Serializer>(
408 &self,
409 se: S,
410 ) -> Result<S::Ok, S::Error> {
411 se.collect_str(self)
412 }
413 }
414
415 /// Serialize a required `Span` in the [`friendly`] duration format
416 /// using compact designators.
417 #[inline]
418 pub fn required<S: serde_core::Serializer>(
419 span: &crate::Span,
420 se: S,
421 ) -> Result<S::Ok, S::Error> {
422 se.collect_str(&CompactSpan(span))
423 }
424
425 /// Serialize an optional `Span` in the [`friendly`] duration
426 /// format using compact designators.
427 #[inline]
428 pub fn optional<S: serde_core::Serializer>(
429 span: &Option<crate::Span>,
430 se: S,
431 ) -> Result<S::Ok, S::Error> {
432 match *span {
433 None => se.serialize_none(),
434 Some(ref span) => se.serialize_some(&CompactSpan(span)),
435 }
436 }
437 }
438 }
439}
440
441/// Convenience routines for (de)serializing [`Timestamp`](crate::Timestamp) as
442/// raw integer values.
443///
444/// At present, the helpers are limited to serializing and deserializing
445/// [`Timestamp`](crate::Timestamp) values as an integer number of seconds,
446/// milliseconds, microseconds or nanoseconds.
447///
448/// # Advice
449///
450/// In general, these helpers should only be used to interface with "legacy"
451/// APIs that transmit times as integer number of seconds (or milliseconds or
452/// whatever). If you're designing a new API and need to transmit instants in
453/// time that don't care about time zones, then you should use `Timestamp`
454/// directly. It will automatically use RFC 3339. (And if you do want to
455/// include the time zone, then using [`Zoned`](crate::Zoned) directly will
456/// work as well by utilizing the RFC 9557 extension to RFC 3339.)
457pub mod timestamp {
458 use serde_core::de;
459
460 /// A generic visitor for `Option<Timestamp>`.
461 struct OptionalVisitor<V>(V);
462
463 impl<'de, V: de::Visitor<'de, Value = crate::Timestamp>> de::Visitor<'de>
464 for OptionalVisitor<V>
465 {
466 type Value = Option<crate::Timestamp>;
467
468 fn expecting(
469 &self,
470 f: &mut core::fmt::Formatter,
471 ) -> core::fmt::Result {
472 f.write_str(
473 "an integer number of seconds from the Unix epoch or `None`",
474 )
475 }
476
477 #[inline]
478 fn visit_some<D: de::Deserializer<'de>>(
479 self,
480 de: D,
481 ) -> Result<Option<crate::Timestamp>, D::Error> {
482 de.deserialize_i64(self.0).map(Some)
483 }
484
485 #[inline]
486 fn visit_none<E: de::Error>(
487 self,
488 ) -> Result<Option<crate::Timestamp>, E> {
489 Ok(None)
490 }
491 }
492
493 /// (De)serialize an integer number of seconds from the Unix epoch.
494 pub mod second {
495 use serde_core::de;
496
497 struct Visitor;
498
499 impl<'de> de::Visitor<'de> for Visitor {
500 type Value = crate::Timestamp;
501
502 fn expecting(
503 &self,
504 f: &mut core::fmt::Formatter,
505 ) -> core::fmt::Result {
506 f.write_str("an integer number of seconds from the Unix epoch")
507 }
508
509 #[inline]
510 fn visit_i8<E: de::Error>(
511 self,
512 v: i8,
513 ) -> Result<crate::Timestamp, E> {
514 self.visit_i64(i64::from(v))
515 }
516
517 #[inline]
518 fn visit_u8<E: de::Error>(
519 self,
520 v: u8,
521 ) -> Result<crate::Timestamp, E> {
522 self.visit_i64(i64::from(v))
523 }
524
525 #[inline]
526 fn visit_i16<E: de::Error>(
527 self,
528 v: i16,
529 ) -> Result<crate::Timestamp, E> {
530 self.visit_i64(i64::from(v))
531 }
532
533 #[inline]
534 fn visit_u16<E: de::Error>(
535 self,
536 v: u16,
537 ) -> Result<crate::Timestamp, E> {
538 self.visit_i64(i64::from(v))
539 }
540
541 #[inline]
542 fn visit_i32<E: de::Error>(
543 self,
544 v: i32,
545 ) -> Result<crate::Timestamp, E> {
546 self.visit_i64(i64::from(v))
547 }
548
549 #[inline]
550 fn visit_u32<E: de::Error>(
551 self,
552 v: u32,
553 ) -> Result<crate::Timestamp, E> {
554 self.visit_i64(i64::from(v))
555 }
556
557 #[inline]
558 fn visit_i64<E: de::Error>(
559 self,
560 v: i64,
561 ) -> Result<crate::Timestamp, E> {
562 crate::Timestamp::from_second(v).map_err(de::Error::custom)
563 }
564
565 #[inline]
566 fn visit_u64<E: de::Error>(
567 self,
568 v: u64,
569 ) -> Result<crate::Timestamp, E> {
570 let v = i64::try_from(v).map_err(|_| {
571 de::Error::custom(format_args!(
572 "got unsigned integer {v} seconds, \
573 which is too big to fit in a Jiff `Timestamp`",
574 ))
575 })?;
576 self.visit_i64(v)
577 }
578
579 #[inline]
580 fn visit_i128<E: de::Error>(
581 self,
582 v: i128,
583 ) -> Result<crate::Timestamp, E> {
584 let v = i64::try_from(v).map_err(|_| {
585 de::Error::custom(format_args!(
586 "got signed integer {v} seconds, \
587 which is too big to fit in a Jiff `Timestamp`",
588 ))
589 })?;
590 self.visit_i64(v)
591 }
592
593 #[inline]
594 fn visit_u128<E: de::Error>(
595 self,
596 v: u128,
597 ) -> Result<crate::Timestamp, E> {
598 let v = i64::try_from(v).map_err(|_| {
599 de::Error::custom(format_args!(
600 "got unsigned integer {v} seconds, \
601 which is too big to fit in a Jiff `Timestamp`",
602 ))
603 })?;
604 self.visit_i64(v)
605 }
606 }
607
608 /// (De)serialize a required integer number of seconds from the Unix
609 /// epoch.
610 pub mod required {
611 /// Serialize a required integer number of seconds since the Unix
612 /// epoch.
613 #[inline]
614 pub fn serialize<S: serde_core::Serializer>(
615 timestamp: &crate::Timestamp,
616 se: S,
617 ) -> Result<S::Ok, S::Error> {
618 se.serialize_i64(timestamp.as_second())
619 }
620
621 /// Deserialize a required integer number of seconds since the
622 /// Unix epoch.
623 #[inline]
624 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
625 de: D,
626 ) -> Result<crate::Timestamp, D::Error> {
627 de.deserialize_i64(super::Visitor)
628 }
629 }
630
631 /// (De)serialize an optional integer number of seconds from the Unix
632 /// epoch.
633 pub mod optional {
634 /// Serialize an optional integer number of seconds since the Unix
635 /// epoch.
636 #[inline]
637 pub fn serialize<S: serde_core::Serializer>(
638 timestamp: &Option<crate::Timestamp>,
639 se: S,
640 ) -> Result<S::Ok, S::Error> {
641 match *timestamp {
642 None => se.serialize_none(),
643 Some(ref ts) => se.serialize_some(&ts.as_second()),
644 }
645 }
646
647 /// Deserialize an optional integer number of seconds since the
648 /// Unix epoch.
649 #[inline]
650 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
651 de: D,
652 ) -> Result<Option<crate::Timestamp>, D::Error> {
653 de.deserialize_option(super::super::OptionalVisitor(
654 super::Visitor,
655 ))
656 }
657 }
658 }
659
660 /// (De)serialize an integer number of milliseconds from the Unix epoch.
661 pub mod millisecond {
662 use serde_core::de;
663
664 struct Visitor;
665
666 impl<'de> de::Visitor<'de> for Visitor {
667 type Value = crate::Timestamp;
668
669 fn expecting(
670 &self,
671 f: &mut core::fmt::Formatter,
672 ) -> core::fmt::Result {
673 f.write_str(
674 "an integer number of milliseconds from the Unix epoch",
675 )
676 }
677
678 #[inline]
679 fn visit_i8<E: de::Error>(
680 self,
681 v: i8,
682 ) -> Result<crate::Timestamp, E> {
683 self.visit_i64(i64::from(v))
684 }
685
686 #[inline]
687 fn visit_u8<E: de::Error>(
688 self,
689 v: u8,
690 ) -> Result<crate::Timestamp, E> {
691 self.visit_i64(i64::from(v))
692 }
693
694 #[inline]
695 fn visit_i16<E: de::Error>(
696 self,
697 v: i16,
698 ) -> Result<crate::Timestamp, E> {
699 self.visit_i64(i64::from(v))
700 }
701
702 #[inline]
703 fn visit_u16<E: de::Error>(
704 self,
705 v: u16,
706 ) -> Result<crate::Timestamp, E> {
707 self.visit_i64(i64::from(v))
708 }
709
710 #[inline]
711 fn visit_i32<E: de::Error>(
712 self,
713 v: i32,
714 ) -> Result<crate::Timestamp, E> {
715 self.visit_i64(i64::from(v))
716 }
717
718 #[inline]
719 fn visit_u32<E: de::Error>(
720 self,
721 v: u32,
722 ) -> Result<crate::Timestamp, E> {
723 self.visit_i64(i64::from(v))
724 }
725
726 #[inline]
727 fn visit_i64<E: de::Error>(
728 self,
729 v: i64,
730 ) -> Result<crate::Timestamp, E> {
731 crate::Timestamp::from_millisecond(v)
732 .map_err(de::Error::custom)
733 }
734
735 #[inline]
736 fn visit_u64<E: de::Error>(
737 self,
738 v: u64,
739 ) -> Result<crate::Timestamp, E> {
740 let v = i64::try_from(v).map_err(|_| {
741 de::Error::custom(format_args!(
742 "got unsigned integer {v} milliseconds, \
743 which is too big to fit in a Jiff `Timestamp`",
744 ))
745 })?;
746 self.visit_i64(v)
747 }
748
749 #[inline]
750 fn visit_i128<E: de::Error>(
751 self,
752 v: i128,
753 ) -> Result<crate::Timestamp, E> {
754 let v = i64::try_from(v).map_err(|_| {
755 de::Error::custom(format_args!(
756 "got signed integer {v} milliseconds, \
757 which is too big to fit in a Jiff `Timestamp`",
758 ))
759 })?;
760 self.visit_i64(v)
761 }
762
763 #[inline]
764 fn visit_u128<E: de::Error>(
765 self,
766 v: u128,
767 ) -> Result<crate::Timestamp, E> {
768 let v = i64::try_from(v).map_err(|_| {
769 de::Error::custom(format_args!(
770 "got unsigned integer {v} milliseconds, \
771 which is too big to fit in a Jiff `Timestamp`",
772 ))
773 })?;
774 self.visit_i64(v)
775 }
776 }
777
778 /// (De)serialize a required integer number of milliseconds from the
779 /// Unix epoch.
780 pub mod required {
781 /// Serialize a required integer number of milliseconds since the
782 /// Unix epoch.
783 #[inline]
784 pub fn serialize<S: serde_core::Serializer>(
785 timestamp: &crate::Timestamp,
786 se: S,
787 ) -> Result<S::Ok, S::Error> {
788 se.serialize_i64(timestamp.as_millisecond())
789 }
790
791 /// Deserialize a required integer number of milliseconds since the
792 /// Unix epoch.
793 #[inline]
794 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
795 de: D,
796 ) -> Result<crate::Timestamp, D::Error> {
797 de.deserialize_i64(super::Visitor)
798 }
799 }
800
801 /// (De)serialize an optional integer number of milliseconds from the
802 /// Unix epoch.
803 pub mod optional {
804 /// Serialize an optional integer number of milliseconds since the
805 /// Unix epoch.
806 #[inline]
807 pub fn serialize<S: serde_core::Serializer>(
808 timestamp: &Option<crate::Timestamp>,
809 se: S,
810 ) -> Result<S::Ok, S::Error> {
811 match *timestamp {
812 None => se.serialize_none(),
813 Some(ref ts) => se.serialize_some(&ts.as_millisecond()),
814 }
815 }
816
817 /// Deserialize an optional integer number of milliseconds since
818 /// the Unix epoch.
819 #[inline]
820 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
821 de: D,
822 ) -> Result<Option<crate::Timestamp>, D::Error> {
823 de.deserialize_option(super::super::OptionalVisitor(
824 super::Visitor,
825 ))
826 }
827 }
828 }
829
830 /// (De)serialize an integer number of microseconds from the Unix epoch.
831 pub mod microsecond {
832 use serde_core::de;
833
834 struct Visitor;
835
836 impl<'de> de::Visitor<'de> for Visitor {
837 type Value = crate::Timestamp;
838
839 fn expecting(
840 &self,
841 f: &mut core::fmt::Formatter,
842 ) -> core::fmt::Result {
843 f.write_str(
844 "an integer number of microseconds from the Unix epoch",
845 )
846 }
847
848 #[inline]
849 fn visit_i8<E: de::Error>(
850 self,
851 v: i8,
852 ) -> Result<crate::Timestamp, E> {
853 self.visit_i64(i64::from(v))
854 }
855
856 #[inline]
857 fn visit_u8<E: de::Error>(
858 self,
859 v: u8,
860 ) -> Result<crate::Timestamp, E> {
861 self.visit_i64(i64::from(v))
862 }
863
864 #[inline]
865 fn visit_i16<E: de::Error>(
866 self,
867 v: i16,
868 ) -> Result<crate::Timestamp, E> {
869 self.visit_i64(i64::from(v))
870 }
871
872 #[inline]
873 fn visit_u16<E: de::Error>(
874 self,
875 v: u16,
876 ) -> Result<crate::Timestamp, E> {
877 self.visit_i64(i64::from(v))
878 }
879
880 #[inline]
881 fn visit_i32<E: de::Error>(
882 self,
883 v: i32,
884 ) -> Result<crate::Timestamp, E> {
885 self.visit_i64(i64::from(v))
886 }
887
888 #[inline]
889 fn visit_u32<E: de::Error>(
890 self,
891 v: u32,
892 ) -> Result<crate::Timestamp, E> {
893 self.visit_i64(i64::from(v))
894 }
895
896 #[inline]
897 fn visit_i64<E: de::Error>(
898 self,
899 v: i64,
900 ) -> Result<crate::Timestamp, E> {
901 crate::Timestamp::from_microsecond(v)
902 .map_err(de::Error::custom)
903 }
904
905 #[inline]
906 fn visit_u64<E: de::Error>(
907 self,
908 v: u64,
909 ) -> Result<crate::Timestamp, E> {
910 let v = i64::try_from(v).map_err(|_| {
911 de::Error::custom(format_args!(
912 "got unsigned integer {v} microseconds, \
913 which is too big to fit in a Jiff `Timestamp`",
914 ))
915 })?;
916 self.visit_i64(v)
917 }
918
919 #[inline]
920 fn visit_i128<E: de::Error>(
921 self,
922 v: i128,
923 ) -> Result<crate::Timestamp, E> {
924 let v = i64::try_from(v).map_err(|_| {
925 de::Error::custom(format_args!(
926 "got signed integer {v} microseconds, \
927 which is too big to fit in a Jiff `Timestamp`",
928 ))
929 })?;
930 self.visit_i64(v)
931 }
932
933 #[inline]
934 fn visit_u128<E: de::Error>(
935 self,
936 v: u128,
937 ) -> Result<crate::Timestamp, E> {
938 let v = i64::try_from(v).map_err(|_| {
939 de::Error::custom(format_args!(
940 "got unsigned integer {v} microseconds, \
941 which is too big to fit in a Jiff `Timestamp`",
942 ))
943 })?;
944 self.visit_i64(v)
945 }
946 }
947
948 /// (De)serialize a required integer number of microseconds from the
949 /// Unix epoch.
950 pub mod required {
951 /// Serialize a required integer number of microseconds since the
952 /// Unix epoch.
953 #[inline]
954 pub fn serialize<S: serde_core::Serializer>(
955 timestamp: &crate::Timestamp,
956 se: S,
957 ) -> Result<S::Ok, S::Error> {
958 se.serialize_i64(timestamp.as_microsecond())
959 }
960
961 /// Deserialize a required integer number of microseconds since the
962 /// Unix epoch.
963 #[inline]
964 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
965 de: D,
966 ) -> Result<crate::Timestamp, D::Error> {
967 de.deserialize_i64(super::Visitor)
968 }
969 }
970
971 /// (De)serialize an optional integer number of microseconds from the
972 /// Unix epoch.
973 pub mod optional {
974 /// Serialize an optional integer number of microseconds since the
975 /// Unix epoch.
976 #[inline]
977 pub fn serialize<S: serde_core::Serializer>(
978 timestamp: &Option<crate::Timestamp>,
979 se: S,
980 ) -> Result<S::Ok, S::Error> {
981 match *timestamp {
982 None => se.serialize_none(),
983 Some(ref ts) => se.serialize_some(&&ts.as_microsecond()),
984 }
985 }
986
987 /// Deserialize an optional integer number of microseconds since
988 /// the Unix epoch.
989 #[inline]
990 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
991 de: D,
992 ) -> Result<Option<crate::Timestamp>, D::Error> {
993 de.deserialize_option(super::super::OptionalVisitor(
994 super::Visitor,
995 ))
996 }
997 }
998 }
999
1000 /// (De)serialize an integer number of nanoseconds from the Unix epoch.
1001 pub mod nanosecond {
1002 use serde_core::de;
1003
1004 struct Visitor;
1005
1006 impl<'de> de::Visitor<'de> for Visitor {
1007 type Value = crate::Timestamp;
1008
1009 fn expecting(
1010 &self,
1011 f: &mut core::fmt::Formatter,
1012 ) -> core::fmt::Result {
1013 f.write_str(
1014 "an integer number of nanoseconds from the Unix epoch",
1015 )
1016 }
1017
1018 #[inline]
1019 fn visit_i64<E: de::Error>(
1020 self,
1021 v: i64,
1022 ) -> Result<crate::Timestamp, E> {
1023 self.visit_i128(i128::from(v))
1024 }
1025
1026 #[inline]
1027 fn visit_u64<E: de::Error>(
1028 self,
1029 v: u64,
1030 ) -> Result<crate::Timestamp, E> {
1031 self.visit_u128(u128::from(v))
1032 }
1033
1034 #[inline]
1035 fn visit_i128<E: de::Error>(
1036 self,
1037 v: i128,
1038 ) -> Result<crate::Timestamp, E> {
1039 crate::Timestamp::from_nanosecond(v).map_err(de::Error::custom)
1040 }
1041
1042 #[inline]
1043 fn visit_u128<E: de::Error>(
1044 self,
1045 v: u128,
1046 ) -> Result<crate::Timestamp, E> {
1047 let v = i128::try_from(v).map_err(|_| {
1048 de::Error::custom(format_args!(
1049 "got unsigned integer {v} nanoseconds, \
1050 which is too big to fit in a Jiff `Timestamp`",
1051 ))
1052 })?;
1053 self.visit_i128(v)
1054 }
1055 }
1056
1057 /// (De)serialize a required integer number of nanoseconds from the
1058 /// Unix epoch.
1059 pub mod required {
1060 /// Serialize a required integer number of nanoseconds since the
1061 /// Unix epoch.
1062 #[inline]
1063 pub fn serialize<S: serde_core::Serializer>(
1064 timestamp: &crate::Timestamp,
1065 se: S,
1066 ) -> Result<S::Ok, S::Error> {
1067 se.serialize_i128(timestamp.as_nanosecond())
1068 }
1069
1070 /// Deserialize a required integer number of nanoseconds since the
1071 /// Unix epoch.
1072 #[inline]
1073 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1074 de: D,
1075 ) -> Result<crate::Timestamp, D::Error> {
1076 de.deserialize_i128(super::Visitor)
1077 }
1078 }
1079
1080 /// (De)serialize an optional integer number of nanoseconds from the
1081 /// Unix epoch.
1082 pub mod optional {
1083 /// Serialize an optional integer number of nanoseconds since the
1084 /// Unix epoch.
1085 #[inline]
1086 pub fn serialize<S: serde_core::Serializer>(
1087 timestamp: &Option<crate::Timestamp>,
1088 se: S,
1089 ) -> Result<S::Ok, S::Error> {
1090 match *timestamp {
1091 None => se.serialize_none(),
1092 Some(ref ts) => se.serialize_some(&ts.as_nanosecond()),
1093 }
1094 }
1095
1096 /// Deserialize an optional integer number of nanoseconds since the
1097 /// Unix epoch.
1098 #[inline]
1099 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1100 de: D,
1101 ) -> Result<Option<crate::Timestamp>, D::Error> {
1102 de.deserialize_option(super::super::OptionalVisitor(
1103 super::Visitor,
1104 ))
1105 }
1106 }
1107 }
1108}
1109
1110/// Convenience routines for (de)serializing [`TimeZone`](crate::tz::TimeZone)
1111/// values.
1112///
1113/// The `required` and `optional` sub-modules each provide serialization and
1114/// deserialization routines. They are meant to be used with Serde's
1115/// [`with` attribute].
1116///
1117/// # Advice
1118///
1119/// Serializing time zones is useful when you want to accept user configuration
1120/// selecting a time zone to use. This might be beneficial when one cannot rely
1121/// on a system's time zone.
1122///
1123/// Note that when deserializing time zones that are IANA time zone
1124/// identifiers, Jiff will automatically use the implicit global database to
1125/// resolve the identifier to an actual time zone. If you do not want to use
1126/// Jiff's global time zone database for this, you'll need to write your own
1127/// Serde integration.
1128///
1129/// [`with` attribute]: https://serde.rs/field-attrs.html#with
1130///
1131/// # Example
1132///
1133/// ```
1134/// use jiff::tz::TimeZone;
1135///
1136/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1137/// struct Record {
1138/// #[serde(with = "jiff::fmt::serde::tz::required")]
1139/// tz: TimeZone,
1140/// }
1141///
1142/// let json = r#"{"tz":"America/Nuuk"}"#;
1143/// let got: Record = serde_json::from_str(&json)?;
1144/// assert_eq!(got.tz, TimeZone::get("America/Nuuk")?);
1145/// assert_eq!(serde_json::to_string(&got)?, json);
1146///
1147/// # Ok::<(), Box<dyn std::error::Error>>(())
1148/// ```
1149///
1150/// # Example: serializing an unknown `TimeZone` works
1151///
1152/// For example, when a time zone was created from
1153/// [`TimeZone::system`](crate::tz::TimeZone::system) and a system configured
1154/// time zone could not be found. One can artificially create this situation
1155/// with [`TimeZone::unknown`](crate::tz::TimeZone::unknown):
1156///
1157/// ```
1158/// use jiff::tz::TimeZone;
1159///
1160/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1161/// struct Record {
1162/// #[serde(with = "jiff::fmt::serde::tz::required")]
1163/// tz: TimeZone,
1164/// }
1165///
1166/// let record = Record { tz: TimeZone::unknown() };
1167/// assert_eq!(
1168/// serde_json::to_string(&record)?,
1169/// r#"{"tz":"Etc/Unknown"}"#,
1170/// );
1171///
1172/// # Ok::<(), Box<dyn std::error::Error>>(())
1173/// ```
1174///
1175/// And it deserializes as well:
1176///
1177/// ```
1178/// use jiff::tz::TimeZone;
1179///
1180/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1181/// struct Record {
1182/// #[serde(with = "jiff::fmt::serde::tz::required")]
1183/// tz: TimeZone,
1184/// }
1185///
1186/// let json = r#"{"tz":"Etc/Unknown"}"#;
1187/// let got: Record = serde_json::from_str(&json)?;
1188/// assert!(got.tz.is_unknown());
1189///
1190/// # Ok::<(), Box<dyn std::error::Error>>(())
1191/// ```
1192///
1193/// An unknown time zone is "allowed" to percolate through Jiff because it's
1194/// usually not desirable to return an error and completely fail if a system
1195/// time zone could not be detected. On the other hand, by using a special
1196/// `Etc/Unknown` identifier for this case, it still surfaces the fact that
1197/// something has gone wrong.
1198pub mod tz {
1199 use serde_core::de;
1200
1201 use crate::fmt::{temporal, StdFmtWrite};
1202
1203 struct TemporalTimeZone<'a>(&'a crate::tz::TimeZone);
1204
1205 impl<'a> core::fmt::Display for TemporalTimeZone<'a> {
1206 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1207 static PRINTER: temporal::DateTimePrinter =
1208 temporal::DateTimePrinter::new();
1209 PRINTER
1210 .print_time_zone(self.0, StdFmtWrite(f))
1211 .map_err(|_| core::fmt::Error)
1212 }
1213 }
1214
1215 impl<'a> serde_core::Serialize for TemporalTimeZone<'a> {
1216 fn serialize<S: serde_core::Serializer>(
1217 &self,
1218 se: S,
1219 ) -> Result<S::Ok, S::Error> {
1220 se.collect_str(self)
1221 }
1222 }
1223
1224 fn check_succinct_serialization<S: serde_core::Serializer>(
1225 tz: &crate::tz::TimeZone,
1226 ) -> Result<&crate::tz::TimeZone, S::Error> {
1227 if tz.has_succinct_serialization() {
1228 Ok(tz)
1229 } else {
1230 Err(<S::Error as serde_core::ser::Error>::custom(
1231 "time zones without IANA identifiers that aren't either \
1232 fixed offsets or a POSIX time zone can't be serialized \
1233 (this typically occurs when this is a system time zone \
1234 derived from `/etc/localtime` on Unix systems that \
1235 isn't symlinked to an entry in `/usr/share/zoneinfo)",
1236 ))
1237 }
1238 }
1239
1240 /// A required visitor for `TimeZone`.
1241 struct Visitor;
1242
1243 impl<'de> de::Visitor<'de> for Visitor {
1244 type Value = crate::tz::TimeZone;
1245
1246 fn expecting(
1247 &self,
1248 f: &mut core::fmt::Formatter,
1249 ) -> core::fmt::Result {
1250 f.write_str(
1251 "a string representing a time zone via an \
1252 IANA time zone identifier, fixed offset from UTC \
1253 or a POSIX time zone string",
1254 )
1255 }
1256
1257 #[inline]
1258 fn visit_bytes<E: de::Error>(
1259 self,
1260 value: &[u8],
1261 ) -> Result<crate::tz::TimeZone, E> {
1262 static PARSER: temporal::DateTimeParser =
1263 temporal::DateTimeParser::new();
1264 PARSER.parse_time_zone(value).map_err(de::Error::custom)
1265 }
1266
1267 #[inline]
1268 fn visit_str<E: de::Error>(
1269 self,
1270 value: &str,
1271 ) -> Result<crate::tz::TimeZone, E> {
1272 self.visit_bytes(value.as_bytes())
1273 }
1274 }
1275
1276 /// A generic optional visitor for `TimeZone`.
1277 struct OptionalVisitor<V>(V);
1278
1279 impl<'de, V: de::Visitor<'de, Value = crate::tz::TimeZone>>
1280 de::Visitor<'de> for OptionalVisitor<V>
1281 {
1282 type Value = Option<crate::tz::TimeZone>;
1283
1284 fn expecting(
1285 &self,
1286 f: &mut core::fmt::Formatter,
1287 ) -> core::fmt::Result {
1288 f.write_str(
1289 "a string representing a time zone via an \
1290 IANA time zone identifier, fixed offset from UTC \
1291 or a POSIX time zone string",
1292 )
1293 }
1294
1295 #[inline]
1296 fn visit_some<D: de::Deserializer<'de>>(
1297 self,
1298 de: D,
1299 ) -> Result<Option<crate::tz::TimeZone>, D::Error> {
1300 de.deserialize_str(self.0).map(Some)
1301 }
1302
1303 #[inline]
1304 fn visit_none<E: de::Error>(
1305 self,
1306 ) -> Result<Option<crate::tz::TimeZone>, E> {
1307 Ok(None)
1308 }
1309 }
1310
1311 /// (De)serialize a required [`TimeZone`](crate::tz::TimeZone).
1312 pub mod required {
1313 /// Serialize a required [`TimeZone`](crate::tz::TimeZone).
1314 ///
1315 /// This will result in an IANA time zone identifier, fixed offset or a
1316 /// POSIX time zone string.
1317 ///
1318 /// This can return an error in some cases when the `TimeZone` has no
1319 /// succinct string representation. For example, when the `TimeZone` is
1320 /// derived from a system `/etc/localtime` for which no IANA time zone
1321 /// identifier could be found.
1322 #[inline]
1323 pub fn serialize<S: serde_core::Serializer>(
1324 tz: &crate::tz::TimeZone,
1325 se: S,
1326 ) -> Result<S::Ok, S::Error> {
1327 let tz = super::check_succinct_serialization::<S>(tz)?;
1328 se.collect_str(&super::TemporalTimeZone(tz))
1329 }
1330
1331 /// Deserialize a required [`TimeZone`](crate::tz::TimeZone).
1332 ///
1333 /// This will attempt to parse an IANA time zone identifier, a fixed
1334 /// offset or a POSIX time zone string.
1335 #[inline]
1336 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1337 de: D,
1338 ) -> Result<crate::tz::TimeZone, D::Error> {
1339 de.deserialize_str(super::Visitor)
1340 }
1341 }
1342
1343 /// (De)serialize an optional [`TimeZone`](crate::tz::TimeZone).
1344 pub mod optional {
1345 /// Serialize an optional [`TimeZone`](crate::tz::TimeZone).
1346 ///
1347 /// This will result in an IANA time zone identifier, fixed offset or a
1348 /// POSIX time zone string.
1349 ///
1350 /// This can return an error in some cases when the `TimeZone` has no
1351 /// succinct string representation. For example, when the `TimeZone` is
1352 /// derived from a system `/etc/localtime` for which no IANA time zone
1353 /// identifier could be found.
1354 #[inline]
1355 pub fn serialize<S: serde_core::Serializer>(
1356 tz: &Option<crate::tz::TimeZone>,
1357 se: S,
1358 ) -> Result<S::Ok, S::Error> {
1359 match *tz {
1360 None => se.serialize_none(),
1361 Some(ref tz) => {
1362 let tz = super::check_succinct_serialization::<S>(tz)?;
1363 se.serialize_some(&super::TemporalTimeZone(tz))
1364 }
1365 }
1366 }
1367
1368 /// Deserialize an optional [`TimeZone`](crate::tz::TimeZone).
1369 ///
1370 /// This will attempt to parse an IANA time zone identifier, a fixed
1371 /// offset or a POSIX time zone string.
1372 #[inline]
1373 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1374 de: D,
1375 ) -> Result<Option<crate::tz::TimeZone>, D::Error> {
1376 de.deserialize_option(super::OptionalVisitor(super::Visitor))
1377 }
1378 }
1379}
1380
1381/// Convenience routines for serializing [`std::time::Duration`] values.
1382///
1383/// The principal helpers in this module are the
1384/// [`required`](crate::fmt::serde::unsigned_duration::required)
1385/// and
1386/// [`optional`](crate::fmt::serde::unsigned_duration::optional) sub-modules.
1387/// Either may be used with Serde's `with` attribute. Each sub-module
1388/// provides both a serialization and a deserialization routine for
1389/// [`std::time::Duration`]. Deserialization supports either ISO 8601 or the
1390/// "[friendly](crate::fmt::friendly)" format. Serialization always uses ISO
1391/// 8601 for reasons of increased interoperability. These helpers are meant to
1392/// approximate the `Deserialize` and `Serialize` trait implementations for
1393/// Jiff's own [`SignedDuration`](crate::SignedDuration).
1394///
1395/// If you want to serialize a `std::time::Duration` using the
1396/// [friendly](crate::fmt::friendly), then you can make use of the
1397/// helpers in
1398/// [`friendly::compact`](crate::fmt::serde::unsigned_duration::friendly::compact),
1399/// also via Serde's `with` attribute. These helpers change their serialization
1400/// to the "friendly" format using compact unit designators. Their deserialization
1401/// remains the same as the top-level helpers (that is, both ISO 8601 and
1402/// friendly formatted duration strings are parsed).
1403///
1404/// Unlike Jiff's own [`SignedDuration`](crate::SignedDuration), deserializing
1405/// a `std::time::Duration` does not support negative durations. If a negative
1406/// duration is found, then deserialization will fail. Moreover, as an unsigned
1407/// type, a `std::time::Duration` can represent larger durations than a
1408/// `SignedDuration`. This means that a `SignedDuration` cannot deserialize
1409/// all valid values of a `std::time::Duration`. In other words, be careful not
1410/// to mix them.
1411///
1412/// # Example: maximally interoperable serialization
1413///
1414/// This example shows how to achieve Serde integration for `std::time::Duration`
1415/// in a way that mirrors [`SignedDuration`](crate::SignedDuration). In
1416/// particular, this supports deserializing ISO 8601 or "friendly" format
1417/// duration strings. In order to be maximally interoperable, this serializes
1418/// only in the ISO 8601 format.
1419///
1420/// ```
1421/// use std::time::Duration;
1422///
1423/// use serde::{Deserialize, Serialize};
1424///
1425/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
1426/// struct Task {
1427/// name: String,
1428/// #[serde(with = "jiff::fmt::serde::unsigned_duration::required")]
1429/// timeout: Duration,
1430/// #[serde(with = "jiff::fmt::serde::unsigned_duration::optional")]
1431/// retry_delay: Option<Duration>,
1432/// }
1433///
1434/// let task = Task {
1435/// name: "Task 1".to_string(),
1436/// // 1 hour 30 minutes
1437/// timeout: Duration::from_secs(60 * 60 + 30 * 60),
1438/// // 2 seconds 500 milliseconds
1439/// retry_delay: Some(Duration::from_millis(2500)),
1440/// };
1441///
1442/// let expected_json = r#"{"name":"Task 1","timeout":"PT1H30M","retry_delay":"PT2.5S"}"#;
1443/// let actual_json = serde_json::to_string(&task)?;
1444/// assert_eq!(actual_json, expected_json);
1445///
1446/// let deserialized_task: Task = serde_json::from_str(&actual_json)?;
1447/// assert_eq!(deserialized_task, task);
1448///
1449/// // Example with None for optional field
1450/// let task_no_retry = Task {
1451/// name: "Task 2".to_string(),
1452/// timeout: Duration::from_secs(5),
1453/// retry_delay: None,
1454/// };
1455/// let expected_json_no_retry = r#"{"name":"Task 2","timeout":"PT5S","retry_delay":null}"#;
1456/// let actual_json_no_retry = serde_json::to_string(&task_no_retry)?;
1457/// assert_eq!(actual_json_no_retry, expected_json_no_retry);
1458///
1459/// let deserialized_task_no_retry: Task = serde_json::from_str(&actual_json_no_retry)?;
1460/// assert_eq!(deserialized_task_no_retry, task_no_retry);
1461///
1462/// # Ok::<(), Box<dyn std::error::Error>>(())
1463/// ```
1464///
1465/// # Example: Round-tripping `std::time::Duration`
1466///
1467/// This example demonstrates how to serialize and deserialize a
1468/// `std::time::Duration` field using the helpers from this module. In
1469/// particular, this serializes durations in the more human readable
1470/// "friendly" format, but can still deserialize ISO 8601 duration strings.
1471///
1472/// ```
1473/// use std::time::Duration;
1474///
1475/// use serde::{Deserialize, Serialize};
1476///
1477/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
1478/// struct Task {
1479/// name: String,
1480/// #[serde(with = "jiff::fmt::serde::unsigned_duration::friendly::compact::required")]
1481/// timeout: Duration,
1482/// #[serde(with = "jiff::fmt::serde::unsigned_duration::friendly::compact::optional")]
1483/// retry_delay: Option<Duration>,
1484/// }
1485///
1486/// let task = Task {
1487/// name: "Task 1".to_string(),
1488/// // 1 hour 30 minutes
1489/// timeout: Duration::from_secs(60 * 60 + 30 * 60),
1490/// // 2 seconds 500 milliseconds
1491/// retry_delay: Some(Duration::from_millis(2500)),
1492/// };
1493///
1494/// let expected_json = r#"{"name":"Task 1","timeout":"1h 30m","retry_delay":"2s 500ms"}"#;
1495/// let actual_json = serde_json::to_string(&task)?;
1496/// assert_eq!(actual_json, expected_json);
1497///
1498/// let deserialized_task: Task = serde_json::from_str(&actual_json)?;
1499/// assert_eq!(deserialized_task, task);
1500///
1501/// // Example with None for optional field
1502/// let task_no_retry = Task {
1503/// name: "Task 2".to_string(),
1504/// timeout: Duration::from_secs(5),
1505/// retry_delay: None,
1506/// };
1507/// let expected_json_no_retry = r#"{"name":"Task 2","timeout":"5s","retry_delay":null}"#;
1508/// let actual_json_no_retry = serde_json::to_string(&task_no_retry)?;
1509/// assert_eq!(actual_json_no_retry, expected_json_no_retry);
1510///
1511/// let deserialized_task_no_retry: Task = serde_json::from_str(&actual_json_no_retry)?;
1512/// assert_eq!(deserialized_task_no_retry, task_no_retry);
1513///
1514/// # Ok::<(), Box<dyn std::error::Error>>(())
1515/// ```
1516///
1517/// # Example: custom "friendly" format options
1518///
1519/// When using
1520/// [`friendly::compact`](crate::fmt::serde::unsigned_duration::friendly::compact),
1521/// the serialization implementation uses a fixed friendly format
1522/// configuration. To use your own configuration, you'll need to write your own
1523/// serialization function:
1524///
1525/// ```
1526/// use std::time::Duration;
1527///
1528/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1529/// struct Data {
1530/// #[serde(serialize_with = "custom_friendly")]
1531/// // We can reuse an existing deserialization helper so that you
1532/// // don't have to write your own.
1533/// #[serde(deserialize_with = "jiff::fmt::serde::unsigned_duration::required::deserialize")]
1534/// duration: Duration,
1535/// }
1536///
1537/// let json = r#"{"duration": "36 hours 1100ms"}"#;
1538/// let got: Data = serde_json::from_str(&json).unwrap();
1539/// assert_eq!(got.duration, Duration::new(36 * 60 * 60 + 1, 100_000_000));
1540///
1541/// let expected = r#"{"duration":"36:00:01.100"}"#;
1542/// assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1543///
1544/// fn custom_friendly<S: serde::Serializer>(
1545/// duration: &Duration,
1546/// se: S,
1547/// ) -> Result<S::Ok, S::Error> {
1548/// struct Custom<'a>(&'a Duration);
1549///
1550/// impl<'a> std::fmt::Display for Custom<'a> {
1551/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1552/// use jiff::fmt::{friendly::SpanPrinter, StdFmtWrite};
1553///
1554/// static PRINTER: SpanPrinter = SpanPrinter::new()
1555/// .hours_minutes_seconds(true)
1556/// .precision(Some(3));
1557///
1558/// PRINTER
1559/// .print_unsigned_duration(self.0, StdFmtWrite(f))
1560/// .map_err(|_| core::fmt::Error)
1561/// }
1562/// }
1563///
1564/// se.collect_str(&Custom(duration))
1565/// }
1566/// ```
1567pub mod unsigned_duration {
1568 /// (De)serialize a `std::time::Duration`
1569 /// in the [`friendly`](crate::fmt::friendly) duration format.
1570 ///
1571 /// Note that these will still deserialize ISO 8601 duration strings.
1572 /// The main feature of this module is that serialization will use the
1573 /// friendly format instead of the ISO 8601 format.
1574 pub mod friendly {
1575 /// (De)serialize a `std::time::Duration`
1576 /// in the [`friendly`](crate::fmt::friendly) duration format using
1577 /// compact designators.
1578 ///
1579 /// Note that these will still deserialize ISO 8601 duration strings.
1580 /// The main feature of this module is that serialization will use the
1581 /// friendly format instead of the ISO 8601 format.
1582 pub mod compact {
1583 /// (De)serialize a required `std::time::Duration`
1584 /// in the [`friendly`](crate::fmt::friendly) duration format using
1585 /// compact designators.
1586 ///
1587 /// Note that this will still deserialize ISO 8601 duration
1588 /// strings. The main feature of this module is that serialization
1589 /// will use the friendly format instead of the ISO 8601 format.
1590 ///
1591 /// This is meant to be used with Serde's `with` attribute.
1592 pub mod required {
1593 /// Serialize a required "friendly" duration from a
1594 /// [`std::time::Duration`].
1595 #[inline]
1596 pub fn serialize<S: serde_core::Serializer>(
1597 duration: &core::time::Duration,
1598 se: S,
1599 ) -> Result<S::Ok, S::Error> {
1600 se.collect_str(&super::DisplayFriendlyCompact(duration))
1601 }
1602
1603 /// Deserialize a required ISO 8601 or friendly duration from a
1604 /// [`std::time::Duration`].
1605 #[inline]
1606 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1607 de: D,
1608 ) -> Result<core::time::Duration, D::Error> {
1609 super::super::super::required::deserialize(de)
1610 }
1611 }
1612
1613 /// (De)serialize an optional `std::time::Duration`
1614 /// in the [`friendly`](crate::fmt::friendly) duration format using
1615 /// compact designators.
1616 ///
1617 /// Note that this will still deserialize ISO 8601 duration
1618 /// strings. The main feature of this module is that serialization
1619 /// will use the friendly format instead of the ISO 8601 format.
1620 ///
1621 /// This is meant to be used with Serde's `with` attribute.
1622 pub mod optional {
1623 /// Serialize an optional "friendly" duration from a
1624 /// [`std::time::Duration`].
1625 #[inline]
1626 pub fn serialize<S: serde_core::Serializer>(
1627 duration: &Option<core::time::Duration>,
1628 se: S,
1629 ) -> Result<S::Ok, S::Error> {
1630 match *duration {
1631 None => se.serialize_none(),
1632 Some(ref duration) => se.serialize_some(
1633 &super::DisplayFriendlyCompact(duration),
1634 ),
1635 }
1636 }
1637
1638 /// Deserialize a required ISO 8601 or friendly duration from a
1639 /// [`std::time::Duration`].
1640 #[inline]
1641 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1642 de: D,
1643 ) -> Result<Option<core::time::Duration>, D::Error>
1644 {
1645 super::super::super::optional::deserialize(de)
1646 }
1647 }
1648
1649 /// A helper for printing a `std::time::Duration` in the friendly
1650 /// format using compact unit designators.
1651 struct DisplayFriendlyCompact<'a>(&'a core::time::Duration);
1652
1653 impl<'a> serde_core::Serialize for DisplayFriendlyCompact<'a> {
1654 fn serialize<S: serde_core::Serializer>(
1655 &self,
1656 se: S,
1657 ) -> Result<S::Ok, S::Error> {
1658 se.collect_str(self)
1659 }
1660 }
1661
1662 impl<'a> core::fmt::Display for DisplayFriendlyCompact<'a> {
1663 fn fmt(
1664 &self,
1665 f: &mut core::fmt::Formatter,
1666 ) -> core::fmt::Result {
1667 use crate::fmt::{
1668 friendly::{Designator, SpanPrinter},
1669 StdFmtWrite,
1670 };
1671
1672 static PRINTER: SpanPrinter =
1673 SpanPrinter::new().designator(Designator::Compact);
1674 PRINTER
1675 .print_unsigned_duration(self.0, StdFmtWrite(f))
1676 .map_err(|_| core::fmt::Error)
1677 }
1678 }
1679 }
1680 }
1681
1682 /// (De)serialize a required ISO 8601 or friendly duration from a
1683 /// [`std::time::Duration`].
1684 ///
1685 /// This is meant to be used with Serde's `with` attribute.
1686 pub mod required {
1687 pub(super) struct Visitor;
1688
1689 impl<'de> serde_core::de::Visitor<'de> for Visitor {
1690 type Value = core::time::Duration;
1691
1692 fn expecting(
1693 &self,
1694 f: &mut core::fmt::Formatter,
1695 ) -> core::fmt::Result {
1696 f.write_str("an unsigned duration string")
1697 }
1698
1699 #[inline]
1700 fn visit_bytes<E: serde_core::de::Error>(
1701 self,
1702 value: &[u8],
1703 ) -> Result<core::time::Duration, E> {
1704 super::parse_iso_or_friendly(value)
1705 .map_err(serde_core::de::Error::custom)
1706 }
1707
1708 #[inline]
1709 fn visit_str<E: serde_core::de::Error>(
1710 self,
1711 value: &str,
1712 ) -> Result<core::time::Duration, E> {
1713 self.visit_bytes(value.as_bytes())
1714 }
1715 }
1716
1717 /// Serialize a required ISO 8601 duration from a
1718 /// [`std::time::Duration`].
1719 #[inline]
1720 pub fn serialize<S: serde_core::Serializer>(
1721 duration: &core::time::Duration,
1722 se: S,
1723 ) -> Result<S::Ok, S::Error> {
1724 se.collect_str(&super::DisplayISO8601(duration))
1725 }
1726
1727 /// Deserialize a required ISO 8601 or friendly duration from a
1728 /// [`std::time::Duration`].
1729 #[inline]
1730 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1731 de: D,
1732 ) -> Result<core::time::Duration, D::Error> {
1733 de.deserialize_str(Visitor)
1734 }
1735 }
1736
1737 /// (De)serialize an optional ISO 8601 or friendly duration from a
1738 /// [`std::time::Duration`].
1739 ///
1740 /// This is meant to be used with Serde's `with` attribute.
1741 pub mod optional {
1742 struct Visitor<V>(V);
1743
1744 impl<
1745 'de,
1746 V: serde_core::de::Visitor<'de, Value = core::time::Duration>,
1747 > serde_core::de::Visitor<'de> for Visitor<V>
1748 {
1749 type Value = Option<core::time::Duration>;
1750
1751 fn expecting(
1752 &self,
1753 f: &mut core::fmt::Formatter,
1754 ) -> core::fmt::Result {
1755 f.write_str("an unsigned duration string")
1756 }
1757
1758 #[inline]
1759 fn visit_some<D: serde_core::de::Deserializer<'de>>(
1760 self,
1761 de: D,
1762 ) -> Result<Option<core::time::Duration>, D::Error> {
1763 de.deserialize_str(self.0).map(Some)
1764 }
1765
1766 #[inline]
1767 fn visit_none<E: serde_core::de::Error>(
1768 self,
1769 ) -> Result<Option<core::time::Duration>, E> {
1770 Ok(None)
1771 }
1772 }
1773
1774 /// Serialize an optional ISO 8601 duration from a
1775 /// [`std::time::Duration`].
1776 #[inline]
1777 pub fn serialize<S: serde_core::Serializer>(
1778 duration: &Option<core::time::Duration>,
1779 se: S,
1780 ) -> Result<S::Ok, S::Error> {
1781 match *duration {
1782 None => se.serialize_none(),
1783 Some(ref duration) => {
1784 se.serialize_some(&super::DisplayISO8601(duration))
1785 }
1786 }
1787 }
1788
1789 /// Deserialize an optional ISO 8601 or friendly duration from a
1790 /// [`std::time::Duration`].
1791 #[inline]
1792 pub fn deserialize<'de, D: serde_core::Deserializer<'de>>(
1793 de: D,
1794 ) -> Result<Option<core::time::Duration>, D::Error> {
1795 de.deserialize_option(Visitor(super::required::Visitor))
1796 }
1797 }
1798
1799 /// A helper for printing a `std::time::Duration` in ISO 8601 format.
1800 struct DisplayISO8601<'a>(&'a core::time::Duration);
1801
1802 impl<'a> serde_core::Serialize for DisplayISO8601<'a> {
1803 fn serialize<S: serde_core::Serializer>(
1804 &self,
1805 se: S,
1806 ) -> Result<S::Ok, S::Error> {
1807 se.collect_str(self)
1808 }
1809 }
1810
1811 impl<'a> core::fmt::Display for DisplayISO8601<'a> {
1812 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1813 use crate::fmt::temporal::SpanPrinter;
1814
1815 static PRINTER: SpanPrinter = SpanPrinter::new();
1816 PRINTER
1817 .print_unsigned_duration(self.0, crate::fmt::StdFmtWrite(f))
1818 .map_err(|_| core::fmt::Error)
1819 }
1820 }
1821
1822 /// A common parsing function that works in bytes.
1823 ///
1824 /// Specifically, this parses either an ISO 8601 duration into
1825 /// a `std::time::Duration` or a "friendly" duration into a
1826 /// `std::time::Duration`. It also tries to give decent error messages.
1827 ///
1828 /// This works because the friendly and ISO 8601 formats have
1829 /// non-overlapping prefixes. Both can start with a `+` or `-`, but aside
1830 /// from that, an ISO 8601 duration _always_ has to start with a `P` or
1831 /// `p`. We can utilize this property to very quickly determine how to
1832 /// parse the input. We just need to handle the possibly ambiguous case
1833 /// with a leading sign a little carefully in order to ensure good error
1834 /// messages.
1835 ///
1836 /// (We do the same thing for `Span` and `SignedDuration`.)
1837 #[cfg_attr(feature = "perf-inline", inline(always))]
1838 fn parse_iso_or_friendly(
1839 bytes: &[u8],
1840 ) -> Result<core::time::Duration, crate::Error> {
1841 let Some((&byte, tail)) = bytes.split_first() else {
1842 return Err(crate::Error::from(
1843 crate::error::fmt::Error::HybridDurationEmpty,
1844 ));
1845 };
1846 let mut first = byte;
1847 // N.B. Unsigned durations don't support negative durations (of
1848 // course), but we still check for it here so that we can defer to
1849 // the dedicated parsers. They will provide their own error messages.
1850 if first == b'+' || first == b'-' {
1851 let Some(&byte) = tail.first() else {
1852 return Err(crate::Error::from(
1853 crate::error::fmt::Error::HybridDurationPrefix {
1854 sign: first,
1855 },
1856 ));
1857 };
1858 first = byte;
1859 }
1860 let dur = if first == b'P' || first == b'p' {
1861 crate::fmt::temporal::DEFAULT_SPAN_PARSER
1862 .parse_unsigned_duration(bytes)
1863 } else {
1864 crate::fmt::friendly::DEFAULT_SPAN_PARSER
1865 .parse_unsigned_duration(bytes)
1866 }?;
1867 Ok(dur)
1868 }
1869}
1870
1871#[cfg(test)]
1872mod tests {
1873 use crate::{
1874 span::span_eq, tz::TimeZone, SignedDuration, Span, SpanFieldwise,
1875 Timestamp, ToSpan,
1876 };
1877 use core::time::Duration as UnsignedDuration;
1878
1879 #[test]
1880 fn duration_friendly_compact_required() {
1881 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1882 struct Data {
1883 #[serde(
1884 serialize_with = "crate::fmt::serde::duration::friendly::compact::required"
1885 )]
1886 duration: SignedDuration,
1887 }
1888
1889 let json = r#"{"duration":"36 hours 1100ms"}"#;
1890 let got: Data = serde_json::from_str(&json).unwrap();
1891 assert_eq!(
1892 got.duration,
1893 SignedDuration::new(36 * 60 * 60 + 1, 100_000_000)
1894 );
1895
1896 let expected = r#"{"duration":"36h 1s 100ms"}"#;
1897 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1898 }
1899
1900 #[test]
1901 fn duration_friendly_compact_optional() {
1902 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1903 struct Data {
1904 #[serde(
1905 serialize_with = "crate::fmt::serde::duration::friendly::compact::optional"
1906 )]
1907 duration: Option<SignedDuration>,
1908 }
1909
1910 let json = r#"{"duration":"36 hours 1100ms"}"#;
1911 let got: Data = serde_json::from_str(&json).unwrap();
1912 assert_eq!(
1913 got.duration,
1914 Some(SignedDuration::new(36 * 60 * 60 + 1, 100_000_000))
1915 );
1916
1917 let expected = r#"{"duration":"36h 1s 100ms"}"#;
1918 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1919 }
1920
1921 #[test]
1922 fn duration_friendly_compact_optional_postcard() {
1923 #[derive(
1924 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
1925 )]
1926 struct Data {
1927 #[serde(
1928 serialize_with = "crate::fmt::serde::duration::friendly::compact::optional"
1929 )]
1930 ts: Option<SignedDuration>,
1931 }
1932
1933 let expected = Data {
1934 ts: Some(SignedDuration::new(36 * 60 * 60 + 1, 100_000_000)),
1935 };
1936
1937 let serialized = postcard::to_allocvec(&expected).unwrap();
1938 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
1939
1940 assert_eq!(expected, deserialized);
1941 }
1942
1943 #[test]
1944 fn unsigned_duration_required() {
1945 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1946 struct Data {
1947 #[serde(with = "crate::fmt::serde::unsigned_duration::required")]
1948 duration: UnsignedDuration,
1949 }
1950
1951 let json = r#"{"duration":"PT36H1.1S"}"#;
1952 let got: Data = serde_json::from_str(&json).unwrap();
1953 assert_eq!(
1954 got.duration,
1955 UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000)
1956 );
1957 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1958
1959 // Check that we can parse a number of seconds that exceeds
1960 // `i64::MAX`. In this case, precisely `u64::MAX`.
1961 let json = r#"{"duration":"PT18446744073709551615S"}"#;
1962 let got: Data = serde_json::from_str(&json).unwrap();
1963 assert_eq!(
1964 got.duration,
1965 UnsignedDuration::new(18446744073709551615, 0)
1966 );
1967 // Printing ISO 8601 durations balances up to hours, so
1968 // it won't match the one we parsed. But the actual duration
1969 // value is equivalent.
1970 let expected = r#"{"duration":"PT5124095576030431H15S"}"#;
1971 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1972 }
1973
1974 #[test]
1975 fn unsigned_duration_optional() {
1976 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1977 struct Data {
1978 #[serde(with = "crate::fmt::serde::unsigned_duration::optional")]
1979 duration: Option<UnsignedDuration>,
1980 }
1981
1982 let json = r#"{"duration":"PT36H1.1S"}"#;
1983 let got: Data = serde_json::from_str(&json).unwrap();
1984 assert_eq!(
1985 got.duration,
1986 Some(UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000))
1987 );
1988 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1989
1990 let json = r#"{"duration":null}"#;
1991 let got: Data = serde_json::from_str(&json).unwrap();
1992 assert_eq!(got.duration, None,);
1993 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1994 }
1995
1996 #[test]
1997 fn unsigned_duration_optional_postcard() {
1998 #[derive(
1999 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2000 )]
2001 struct Data {
2002 #[serde(with = "crate::fmt::serde::unsigned_duration::optional")]
2003 ts: Option<UnsignedDuration>,
2004 }
2005
2006 let expected = Data {
2007 ts: Some(UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000)),
2008 };
2009
2010 let serialized = postcard::to_allocvec(&expected).unwrap();
2011 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2012
2013 assert_eq!(expected, deserialized);
2014 }
2015
2016 #[test]
2017 fn unsigned_duration_compact_required() {
2018 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2019 struct Data {
2020 #[serde(
2021 with = "crate::fmt::serde::unsigned_duration::friendly::compact::required"
2022 )]
2023 duration: UnsignedDuration,
2024 }
2025
2026 let json = r#"{"duration":"36h 1s 100ms"}"#;
2027 let got: Data = serde_json::from_str(&json).unwrap();
2028 assert_eq!(
2029 got.duration,
2030 UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000)
2031 );
2032 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2033 }
2034
2035 #[test]
2036 fn unsigned_duration_compact_optional() {
2037 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2038 struct Data {
2039 #[serde(
2040 with = "crate::fmt::serde::unsigned_duration::friendly::compact::optional"
2041 )]
2042 duration: Option<UnsignedDuration>,
2043 }
2044
2045 let json = r#"{"duration":"36h 1s 100ms"}"#;
2046 let got: Data = serde_json::from_str(&json).unwrap();
2047 assert_eq!(
2048 got.duration,
2049 Some(UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000))
2050 );
2051 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2052 }
2053
2054 #[test]
2055 fn unsigned_duration_compact_optional_postcard() {
2056 #[derive(
2057 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2058 )]
2059 struct Data {
2060 #[serde(
2061 with = "crate::fmt::serde::unsigned_duration::friendly::compact::optional"
2062 )]
2063 ts: Option<UnsignedDuration>,
2064 }
2065
2066 let expected = Data {
2067 ts: Some(UnsignedDuration::new(36 * 60 * 60 + 1, 100_000_000)),
2068 };
2069
2070 let serialized = postcard::to_allocvec(&expected).unwrap();
2071 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2072
2073 assert_eq!(expected, deserialized);
2074 }
2075
2076 #[test]
2077 fn span_friendly_compact_required() {
2078 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2079 struct Data {
2080 #[serde(
2081 serialize_with = "crate::fmt::serde::span::friendly::compact::required"
2082 )]
2083 span: Span,
2084 }
2085
2086 let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
2087 let got: Data = serde_json::from_str(&json).unwrap();
2088 span_eq!(got.span, 1.year().months(2).hours(36).milliseconds(1100));
2089
2090 let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
2091 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
2092 }
2093
2094 #[test]
2095 fn span_friendly_compact_optional() {
2096 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2097 struct Data {
2098 #[serde(
2099 serialize_with = "crate::fmt::serde::span::friendly::compact::optional"
2100 )]
2101 span: Option<Span>,
2102 }
2103
2104 let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
2105 let got: Data = serde_json::from_str(&json).unwrap();
2106 assert_eq!(
2107 got.span.map(SpanFieldwise),
2108 Some(1.year().months(2).hours(36).milliseconds(1100).fieldwise())
2109 );
2110
2111 let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
2112 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
2113 }
2114
2115 #[test]
2116 fn span_friendly_compact_optional_postcard() {
2117 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2118 struct Data {
2119 #[serde(
2120 serialize_with = "crate::fmt::serde::span::friendly::compact::optional"
2121 )]
2122 ts: Option<Span>,
2123 }
2124
2125 let expected = Data {
2126 ts: Some(
2127 Span::new().years(1).months(2).hours(36).milliseconds(1100),
2128 ),
2129 };
2130
2131 let serialized = postcard::to_allocvec(&expected).unwrap();
2132 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2133
2134 assert_eq!(
2135 expected.ts.map(|span| span.fieldwise()),
2136 deserialized.ts.map(|span| span.fieldwise())
2137 );
2138 }
2139
2140 #[test]
2141 fn timestamp_second_required() {
2142 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2143 struct Data {
2144 #[serde(with = "crate::fmt::serde::timestamp::second::required")]
2145 ts: Timestamp,
2146 }
2147
2148 let json = r#"{"ts":1517644800}"#;
2149 let got: Data = serde_json::from_str(&json).unwrap();
2150 assert_eq!(got.ts, Timestamp::from_second(1517644800).unwrap());
2151 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2152 }
2153
2154 #[test]
2155 fn timestamp_second_optional() {
2156 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2157 struct Data {
2158 #[serde(with = "crate::fmt::serde::timestamp::second::optional")]
2159 ts: Option<Timestamp>,
2160 }
2161
2162 let json = r#"{"ts":1517644800}"#;
2163 let got: Data = serde_json::from_str(&json).unwrap();
2164 assert_eq!(got.ts, Some(Timestamp::from_second(1517644800).unwrap()));
2165 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2166 }
2167
2168 #[test]
2169 fn timestamp_second_optional_postcard() {
2170 #[derive(
2171 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2172 )]
2173 struct Data {
2174 #[serde(with = "crate::fmt::serde::timestamp::second::optional")]
2175 ts: Option<Timestamp>,
2176 }
2177
2178 let expected = Data { ts: Some(Timestamp::constant(123_456_789, 0)) };
2179
2180 let serialized = postcard::to_allocvec(&expected).unwrap();
2181 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2182
2183 assert_eq!(expected, deserialized);
2184 }
2185
2186 #[test]
2187 fn timestamp_millisecond_required() {
2188 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2189 struct Data {
2190 #[serde(
2191 with = "crate::fmt::serde::timestamp::millisecond::required"
2192 )]
2193 ts: Timestamp,
2194 }
2195
2196 let json = r#"{"ts":1517644800000}"#;
2197 let got: Data = serde_json::from_str(&json).unwrap();
2198 assert_eq!(
2199 got.ts,
2200 Timestamp::from_millisecond(1517644800_000).unwrap()
2201 );
2202 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2203
2204 let json = r#"{"ts":1517644800123}"#;
2205 let got: Data = serde_json::from_str(&json).unwrap();
2206 assert_eq!(
2207 got.ts,
2208 Timestamp::from_millisecond(1517644800_123).unwrap()
2209 );
2210 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2211 }
2212
2213 #[test]
2214 fn timestamp_millisecond_optional() {
2215 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2216 struct Data {
2217 #[serde(
2218 with = "crate::fmt::serde::timestamp::millisecond::optional"
2219 )]
2220 ts: Option<Timestamp>,
2221 }
2222
2223 let json = r#"{"ts":1517644800000}"#;
2224 let got: Data = serde_json::from_str(&json).unwrap();
2225 assert_eq!(
2226 got.ts,
2227 Some(Timestamp::from_millisecond(1517644800_000).unwrap())
2228 );
2229 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2230
2231 let json = r#"{"ts":1517644800123}"#;
2232 let got: Data = serde_json::from_str(&json).unwrap();
2233 assert_eq!(
2234 got.ts,
2235 Some(Timestamp::from_millisecond(1517644800_123).unwrap())
2236 );
2237 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2238 }
2239
2240 #[test]
2241 fn timestamp_millisecond_optional_postcard() {
2242 #[derive(
2243 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2244 )]
2245 struct Data {
2246 #[serde(
2247 with = "crate::fmt::serde::timestamp::millisecond::optional"
2248 )]
2249 ts: Option<Timestamp>,
2250 }
2251
2252 let expected = Data { ts: Some(Timestamp::constant(123_456_789, 0)) };
2253
2254 let serialized = postcard::to_allocvec(&expected).unwrap();
2255 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2256
2257 assert_eq!(expected, deserialized);
2258 }
2259
2260 #[test]
2261 fn timestamp_microsecond_required() {
2262 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2263 struct Data {
2264 #[serde(
2265 with = "crate::fmt::serde::timestamp::microsecond::required"
2266 )]
2267 ts: Timestamp,
2268 }
2269
2270 let json = r#"{"ts":1517644800000000}"#;
2271 let got: Data = serde_json::from_str(&json).unwrap();
2272 assert_eq!(
2273 got.ts,
2274 Timestamp::from_microsecond(1517644800_000000).unwrap()
2275 );
2276 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2277
2278 let json = r#"{"ts":1517644800123456}"#;
2279 let got: Data = serde_json::from_str(&json).unwrap();
2280 assert_eq!(
2281 got.ts,
2282 Timestamp::from_microsecond(1517644800_123456).unwrap()
2283 );
2284 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2285 }
2286
2287 #[test]
2288 fn timestamp_microsecond_optional() {
2289 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2290 struct Data {
2291 #[serde(
2292 with = "crate::fmt::serde::timestamp::microsecond::optional"
2293 )]
2294 ts: Option<Timestamp>,
2295 }
2296
2297 let json = r#"{"ts":1517644800000000}"#;
2298 let got: Data = serde_json::from_str(&json).unwrap();
2299 assert_eq!(
2300 got.ts,
2301 Some(Timestamp::from_microsecond(1517644800_000000).unwrap())
2302 );
2303 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2304
2305 let json = r#"{"ts":1517644800123456}"#;
2306 let got: Data = serde_json::from_str(&json).unwrap();
2307 assert_eq!(
2308 got.ts,
2309 Some(Timestamp::from_microsecond(1517644800_123456).unwrap())
2310 );
2311 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2312 }
2313
2314 #[test]
2315 fn timestamp_microsecond_optional_postcard() {
2316 #[derive(
2317 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2318 )]
2319 struct Data {
2320 #[serde(
2321 with = "crate::fmt::serde::timestamp::microsecond::optional"
2322 )]
2323 ts: Option<Timestamp>,
2324 }
2325
2326 let expected = Data { ts: Some(Timestamp::constant(123_456_789, 0)) };
2327
2328 let serialized = postcard::to_allocvec(&expected).unwrap();
2329 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2330
2331 assert_eq!(expected, deserialized);
2332 }
2333
2334 #[test]
2335 fn timestamp_nanosecond_required() {
2336 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2337 struct Data {
2338 #[serde(
2339 with = "crate::fmt::serde::timestamp::nanosecond::required"
2340 )]
2341 ts: Timestamp,
2342 }
2343
2344 let json = r#"{"ts":1517644800000000000}"#;
2345 let got: Data = serde_json::from_str(&json).unwrap();
2346 assert_eq!(
2347 got.ts,
2348 Timestamp::from_nanosecond(1517644800_000000000).unwrap()
2349 );
2350 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2351
2352 let json = r#"{"ts":1517644800123456789}"#;
2353 let got: Data = serde_json::from_str(&json).unwrap();
2354 assert_eq!(
2355 got.ts,
2356 Timestamp::from_nanosecond(1517644800_123456789).unwrap()
2357 );
2358 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2359 }
2360
2361 #[test]
2362 fn timestamp_nanosecond_optional() {
2363 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2364 struct Data {
2365 #[serde(
2366 with = "crate::fmt::serde::timestamp::nanosecond::optional"
2367 )]
2368 ts: Option<Timestamp>,
2369 }
2370
2371 let json = r#"{"ts":1517644800000000000}"#;
2372 let got: Data = serde_json::from_str(&json).unwrap();
2373 assert_eq!(
2374 got.ts,
2375 Some(Timestamp::from_nanosecond(1517644800_000000000).unwrap())
2376 );
2377 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2378
2379 let json = r#"{"ts":1517644800123456789}"#;
2380 let got: Data = serde_json::from_str(&json).unwrap();
2381 assert_eq!(
2382 got.ts,
2383 Some(Timestamp::from_nanosecond(1517644800_123456789).unwrap())
2384 );
2385 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2386 }
2387
2388 #[test]
2389 fn timestamp_nanosecond_optional_postcard() {
2390 #[derive(
2391 Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize,
2392 )]
2393 struct Data {
2394 #[serde(
2395 with = "crate::fmt::serde::timestamp::nanosecond::optional"
2396 )]
2397 ts: Option<Timestamp>,
2398 }
2399
2400 let expected = Data { ts: Some(Timestamp::constant(123_456_789, 0)) };
2401
2402 let serialized = postcard::to_allocvec(&expected).unwrap();
2403 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2404
2405 assert_eq!(expected, deserialized);
2406 }
2407
2408 #[test]
2409 fn timezone_required() {
2410 if crate::tz::db().is_definitively_empty() {
2411 return;
2412 }
2413
2414 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2415 struct Record {
2416 #[serde(with = "crate::fmt::serde::tz::required")]
2417 tz: TimeZone,
2418 }
2419
2420 let json = r#"{"tz":"America/Nuuk"}"#;
2421 let got: Record = serde_json::from_str(&json).unwrap();
2422 assert_eq!(got.tz, TimeZone::get("America/Nuuk").unwrap());
2423 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2424 }
2425
2426 #[test]
2427 fn timezone_optional() {
2428 if crate::tz::db().is_definitively_empty() {
2429 return;
2430 }
2431
2432 #[derive(Debug, serde::Deserialize, serde::Serialize)]
2433 struct Record {
2434 #[serde(with = "crate::fmt::serde::tz::optional")]
2435 tz: Option<TimeZone>,
2436 }
2437
2438 let json = r#"{"tz":"America/Nuuk"}"#;
2439 let got: Record = serde_json::from_str(&json).unwrap();
2440 assert_eq!(got.tz, Some(TimeZone::get("America/Nuuk").unwrap()));
2441 assert_eq!(serde_json::to_string(&got).unwrap(), json);
2442 }
2443
2444 #[test]
2445 fn timezone_optional_postcard() {
2446 if crate::tz::db().is_definitively_empty() {
2447 return;
2448 }
2449
2450 #[derive(
2451 Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize,
2452 )]
2453 struct Data {
2454 #[serde(with = "crate::fmt::serde::tz::optional")]
2455 tz: Option<TimeZone>,
2456 }
2457
2458 let expected =
2459 Data { tz: Some(TimeZone::get("America/Nuuk").unwrap()) };
2460 let serialized = postcard::to_allocvec(&expected).unwrap();
2461 let deserialized: Data = postcard::from_bytes(&serialized).unwrap();
2462 assert_eq!(expected, deserialized);
2463 }
2464}