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