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
43# Example: timestamps as an integer
44
45This example shows how to deserialize an integer number of seconds since the
46Unix epoch into a [`Timestamp`](crate::Timestamp). And the reverse operation
47for serialization:
48
49```
50use jiff::Timestamp;
51
52#[derive(Debug, serde::Deserialize, serde::Serialize)]
53struct Record {
54 #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
55 timestamp: Timestamp,
56}
57
58let json = r#"{"timestamp":1517644800}"#;
59let got: Record = serde_json::from_str(&json)?;
60assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
61assert_eq!(serde_json::to_string(&got)?, json);
62
63# Ok::<(), Box<dyn std::error::Error>>(())
64```
65
66# Example: optional timestamp support
67
68And this example shows how to use an `Option<Timestamp>` instead of a
69`Timestamp`. Note that in this case, we show how to roundtrip the number of
70**milliseconds** since the Unix epoch:
71
72```
73use jiff::Timestamp;
74
75#[derive(Debug, serde::Deserialize, serde::Serialize)]
76struct Record {
77 #[serde(with = "jiff::fmt::serde::timestamp::millisecond::optional")]
78 timestamp: Option<Timestamp>,
79}
80
81let json = r#"{"timestamp":1517644800123}"#;
82let got: Record = serde_json::from_str(&json)?;
83assert_eq!(got.timestamp, Some(Timestamp::from_millisecond(1517644800_123)?));
84assert_eq!(serde_json::to_string(&got)?, json);
85
86# Ok::<(), Box<dyn std::error::Error>>(())
87```
88
89# Example: the "friendly" duration format
90
91The [`Span`](crate::Span) and [`SignedDuration`](crate::SignedDuration) types
92in this crate both implement Serde's `Serialize` and `Deserialize` traits. For
93`Serialize`, they both use the [ISO 8601 Temporal duration format], but for
94`Deserialize`, they both support the ISO 8601 Temporal duration format and
95the ["friendly" duration format] simultaneously. In order to serialize either
96type in the "friendly" format, you can either define your own serialization
97functions or use one of the convenience routines provided by this module. For
98example:
99
100```
101use jiff::{ToSpan, Span};
102
103#[derive(Debug, serde::Deserialize, serde::Serialize)]
104struct Record {
105 #[serde(
106 serialize_with = "jiff::fmt::serde::span::friendly::compact::required"
107 )]
108 span: Span,
109}
110
111let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
112let got: Record = serde_json::from_str(&json)?;
113assert_eq!(
114 got.span,
115 1.year().months(2).hours(36).milliseconds(1100).fieldwise(),
116);
117
118let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
119assert_eq!(serde_json::to_string(&got).unwrap(), expected);
120
121# Ok::<(), Box<dyn std::error::Error>>(())
122```
123
124[Serde]: https://serde.rs/
125[`with` attribute]: https://serde.rs/field-attrs.html#with
126[`serialize_with` attribute]: https://serde.rs/field-attrs.html#serialize_with
127[ISO 8601 Temporal duration format]: crate::fmt::temporal
128["friendly" duration format]: crate::fmt::friendly
129*/
130
131/// Convenience routines for serializing [`SignedDuration`](crate::SignedDuration)
132/// values.
133///
134/// These convenience routines exist because the `Serialize` implementation for
135/// `SignedDuration` always uses the ISO 8601 duration format. These routines
136/// provide a way to use the "[friendly](crate::fmt::friendly)" format.
137///
138/// Only serialization routines are provided because a `SignedDuration`'s
139/// `Deserialize` implementation automatically handles both the ISO 8601
140/// duration format and the "friendly" format.
141///
142/// # Advice
143///
144/// The `Serialize` implementation uses ISO 8601 because it is a widely
145/// accepted interchange format for communicating durations. If you need to
146/// inter-operate with other systems, it is almost certainly the correct
147/// choice.
148///
149/// The "friendly" format does not adhere to any universal specified format.
150/// However, it is perhaps easier to read. Beyond that, its utility for
151/// `SignedDuration` is somewhat less compared to [`Span`](crate::Span), since
152/// for `Span`, the friendly format preserves all components of the `Span`
153/// faithfully. But a `SignedDuration` is just a 96-bit integer of nanoseconds,
154/// so there are no individual components to preserve. Still, even with a
155/// `SignedDuration`, you might prefer the friendly format.
156///
157/// # Available routines
158///
159/// A [`SpanPrinter`](crate::fmt::friendly::SpanPrinter) has a lot of different
160/// configuration options. The convenience routines provided by this module
161/// only cover a small space of those options since it isn't feasible to
162/// provide a convenience routine for every possible set of configuration
163/// options.
164///
165/// While more convenience routines could be added (please file an issue), only
166/// the most common or popular such routines can be feasibly added. So in the
167/// case where a convenience routine isn't available for the configuration you
168/// want, you can very easily define your own `serialize_with` routine.
169///
170/// The recommended approach is to define a function and a type that
171/// implements the `std::fmt::Display` trait. This way, if a serializer can
172/// efficiently support `Display` implementations, then an allocation can be
173/// avoided.
174///
175/// ```
176/// use jiff::SignedDuration;
177///
178/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
179/// struct Data {
180/// #[serde(serialize_with = "custom_friendly")]
181/// duration: SignedDuration,
182/// }
183///
184/// let json = r#"{"duration": "36 hours 1100ms"}"#;
185/// let got: Data = serde_json::from_str(&json).unwrap();
186/// assert_eq!(got.duration, SignedDuration::new(36 * 60 * 60 + 1, 100_000_000));
187///
188/// let expected = r#"{"duration":"36:00:01.100"}"#;
189/// assert_eq!(serde_json::to_string(&got).unwrap(), expected);
190///
191/// fn custom_friendly<S: serde::Serializer>(
192/// duration: &SignedDuration,
193/// se: S,
194/// ) -> Result<S::Ok, S::Error> {
195/// struct Custom<'a>(&'a SignedDuration);
196///
197/// impl<'a> std::fmt::Display for Custom<'a> {
198/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
199/// use jiff::fmt::{friendly::SpanPrinter, StdFmtWrite};
200///
201/// static PRINTER: SpanPrinter = SpanPrinter::new()
202/// .hours_minutes_seconds(true)
203/// .precision(Some(3));
204///
205/// PRINTER
206/// .print_duration(self.0, StdFmtWrite(f))
207/// .map_err(|_| core::fmt::Error)
208/// }
209/// }
210///
211/// se.collect_str(&Custom(duration))
212/// }
213/// ```
214///
215/// Recall from above that you only need a custom serialization routine
216/// for this. Namely, deserialization automatically supports parsing all
217/// configuration options for serialization unconditionally.
218pub mod duration {
219 /// Serialize a `Span` in the [`friendly`](crate::fmt::friendly) duration
220 /// format.
221 pub mod friendly {
222 /// Serialize a `SignedDuration` in the
223 /// [`friendly`](crate::fmt::friendly) duration format using compact
224 /// designators.
225 pub mod compact {
226 use crate::fmt::{friendly, StdFmtWrite};
227
228 struct CompactDuration<'a>(&'a crate::SignedDuration);
229
230 impl<'a> core::fmt::Display for CompactDuration<'a> {
231 fn fmt(
232 &self,
233 f: &mut core::fmt::Formatter,
234 ) -> core::fmt::Result {
235 static PRINTER: friendly::SpanPrinter =
236 friendly::SpanPrinter::new()
237 .designator(friendly::Designator::Compact);
238 PRINTER
239 .print_duration(self.0, StdFmtWrite(f))
240 .map_err(|_| core::fmt::Error)
241 }
242 }
243
244 /// Serialize a required `SignedDuration` in the [`friendly`]
245 /// duration format using compact designators.
246 #[inline]
247 pub fn required<S: serde::Serializer>(
248 duration: &crate::SignedDuration,
249 se: S,
250 ) -> Result<S::Ok, S::Error> {
251 se.collect_str(&CompactDuration(duration))
252 }
253
254 /// Serialize an optional `SignedDuration` in the [`friendly`]
255 /// duration format using compact designators.
256 #[inline]
257 pub fn optional<S: serde::Serializer>(
258 duration: &Option<crate::SignedDuration>,
259 se: S,
260 ) -> Result<S::Ok, S::Error> {
261 match *duration {
262 None => se.serialize_none(),
263 Some(ref duration) => required(duration, se),
264 }
265 }
266 }
267 }
268}
269
270/// Convenience routines for serializing [`Span`](crate::Span) values.
271///
272/// These convenience routines exist because the `Serialize` implementation for
273/// `Span` always uses the ISO 8601 duration format. These routines provide a
274/// way to use the "[friendly](crate::fmt::friendly)" format.
275///
276/// Only serialization routines are provided because a `Span`'s `Deserialize`
277/// implementation automatically handles both the ISO 8601 duration format and
278/// the "friendly" format.
279///
280/// # Advice
281///
282/// The `Serialize` implementation uses ISO 8601 because it is a widely
283/// accepted interchange format for communicating durations. If you need to
284/// inter-operate with other systems, it is almost certainly the correct choice.
285///
286/// The "friendly" format does not adhere to any universal specified format.
287/// However, it is perhaps easier to read, and crucially, unambiguously
288/// represents all components of a `Span` faithfully. (In contrast, the ISO
289/// 8601 format always normalizes sub-second durations into fractional seconds,
290/// which means durations like `1100ms` and `1s100ms` are alwasys considered
291/// equivalent.)
292///
293/// # Available routines
294///
295/// A [`SpanPrinter`](crate::fmt::friendly::SpanPrinter) has a lot of different
296/// configuration options. The convenience routines provided by this module
297/// only cover a small space of those options since it isn't feasible to
298/// provide a convenience routine for every possible set of configuration
299/// options.
300///
301/// While more convenience routines could be added (please file an issue), only
302/// the most common or popular such routines can be feasibly added. So in the
303/// case where a convenience routine isn't available for the configuration you
304/// want, you can very easily define your own `serialize_with` routine.
305///
306/// The recommended approach is to define a function and a type that
307/// implements the `std::fmt::Display` trait. This way, if a serializer can
308/// efficiently support `Display` implementations, then an allocation can be
309/// avoided.
310///
311/// ```
312/// use jiff::{Span, ToSpan};
313///
314/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
315/// struct Data {
316/// #[serde(serialize_with = "custom_friendly")]
317/// duration: Span,
318/// }
319///
320/// let json = r#"{"duration": "1 year 2 months 36 hours 1100ms"}"#;
321/// let got: Data = serde_json::from_str(&json).unwrap();
322/// assert_eq!(
323/// got.duration,
324/// 1.year().months(2).hours(36).milliseconds(1100).fieldwise(),
325/// );
326///
327/// let expected = r#"{"duration":"1 year, 2 months, 36:00:01.100"}"#;
328/// assert_eq!(serde_json::to_string(&got).unwrap(), expected);
329///
330/// fn custom_friendly<S: serde::Serializer>(
331/// span: &Span,
332/// se: S,
333/// ) -> Result<S::Ok, S::Error> {
334/// struct Custom<'a>(&'a Span);
335///
336/// impl<'a> std::fmt::Display for Custom<'a> {
337/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
338/// use jiff::fmt::{
339/// friendly::{Designator, Spacing, SpanPrinter},
340/// StdFmtWrite,
341/// };
342///
343/// static PRINTER: SpanPrinter = SpanPrinter::new()
344/// .designator(Designator::Verbose)
345/// .comma_after_designator(true)
346/// .spacing(Spacing::BetweenUnitsAndDesignators)
347/// .hours_minutes_seconds(true)
348/// .precision(Some(3));
349///
350/// PRINTER
351/// .print_span(self.0, StdFmtWrite(f))
352/// .map_err(|_| core::fmt::Error)
353/// }
354/// }
355///
356/// se.collect_str(&Custom(span))
357/// }
358/// ```
359///
360/// Recall from above that you only need a custom serialization routine
361/// for this. Namely, deserialization automatically supports parsing all
362/// configuration options for serialization unconditionally.
363pub mod span {
364 /// Serialize a `Span` in the [`friendly`](crate::fmt::friendly) duration
365 /// format.
366 pub mod friendly {
367 /// Serialize a `Span` in the [`friendly`](crate::fmt::friendly)
368 /// duration format using compact designators.
369 pub mod compact {
370 use crate::fmt::{friendly, StdFmtWrite};
371
372 struct CompactSpan<'a>(&'a crate::Span);
373
374 impl<'a> core::fmt::Display for CompactSpan<'a> {
375 fn fmt(
376 &self,
377 f: &mut core::fmt::Formatter,
378 ) -> core::fmt::Result {
379 static PRINTER: friendly::SpanPrinter =
380 friendly::SpanPrinter::new()
381 .designator(friendly::Designator::Compact);
382 PRINTER
383 .print_span(self.0, StdFmtWrite(f))
384 .map_err(|_| core::fmt::Error)
385 }
386 }
387
388 /// Serialize a required `Span` in the [`friendly`] duration format
389 /// using compact designators.
390 #[inline]
391 pub fn required<S: serde::Serializer>(
392 span: &crate::Span,
393 se: S,
394 ) -> Result<S::Ok, S::Error> {
395 se.collect_str(&CompactSpan(span))
396 }
397
398 /// Serialize an optional `Span` in the [`friendly`] duration
399 /// format using compact designators.
400 #[inline]
401 pub fn optional<S: serde::Serializer>(
402 span: &Option<crate::Span>,
403 se: S,
404 ) -> Result<S::Ok, S::Error> {
405 match *span {
406 None => se.serialize_none(),
407 Some(ref span) => required(span, se),
408 }
409 }
410 }
411 }
412}
413
414/// Convenience routines for (de)serializing [`Timestamp`](crate::Timestamp) as
415/// raw integer values.
416///
417/// At present, the helpers are limited to serializing and deserializing
418/// [`Timestamp`](crate::Timestamp) values as an integer number of seconds,
419/// milliseconds, microseconds or nanoseconds.
420///
421/// # Advice
422///
423/// In general, these helpers should only be used to interface with "legacy"
424/// APIs that transmit times as integer number of seconds (or milliseconds or
425/// whatever). If you're designing a new API and need to transmit instants in
426/// time that don't care about time zones, then you should use `Timestamp`
427/// directly. It will automatically use RFC 3339. (And if you do want to
428/// include the time zone, then using [`Zoned`](crate::Zoned) directly will
429/// work as well by utilizing the RFC 9557 extension to RFC 3339.)
430pub mod timestamp {
431 use serde::de;
432
433 /// A generic visitor for `Option<Timestamp>`.
434 struct OptionalVisitor<V>(V);
435
436 impl<'de, V: de::Visitor<'de, Value = crate::Timestamp>> de::Visitor<'de>
437 for OptionalVisitor<V>
438 {
439 type Value = Option<crate::Timestamp>;
440
441 fn expecting(
442 &self,
443 f: &mut core::fmt::Formatter,
444 ) -> core::fmt::Result {
445 f.write_str(
446 "an integer number of seconds from the Unix epoch or `None`",
447 )
448 }
449
450 #[inline]
451 fn visit_some<D: de::Deserializer<'de>>(
452 self,
453 de: D,
454 ) -> Result<Option<crate::Timestamp>, D::Error> {
455 de.deserialize_i64(self.0).map(Some)
456 }
457
458 #[inline]
459 fn visit_none<E: de::Error>(
460 self,
461 ) -> Result<Option<crate::Timestamp>, E> {
462 Ok(None)
463 }
464 }
465
466 /// (De)serialize an integer number of seconds from the Unix epoch.
467 pub mod second {
468 use serde::de;
469
470 struct Visitor;
471
472 impl<'de> de::Visitor<'de> for Visitor {
473 type Value = crate::Timestamp;
474
475 fn expecting(
476 &self,
477 f: &mut core::fmt::Formatter,
478 ) -> core::fmt::Result {
479 f.write_str("an integer number of seconds from the Unix epoch")
480 }
481
482 #[inline]
483 fn visit_i8<E: de::Error>(
484 self,
485 v: i8,
486 ) -> Result<crate::Timestamp, E> {
487 self.visit_i64(i64::from(v))
488 }
489
490 #[inline]
491 fn visit_u8<E: de::Error>(
492 self,
493 v: u8,
494 ) -> Result<crate::Timestamp, E> {
495 self.visit_i64(i64::from(v))
496 }
497
498 #[inline]
499 fn visit_i16<E: de::Error>(
500 self,
501 v: i16,
502 ) -> Result<crate::Timestamp, E> {
503 self.visit_i64(i64::from(v))
504 }
505
506 #[inline]
507 fn visit_u16<E: de::Error>(
508 self,
509 v: u16,
510 ) -> Result<crate::Timestamp, E> {
511 self.visit_i64(i64::from(v))
512 }
513
514 #[inline]
515 fn visit_i32<E: de::Error>(
516 self,
517 v: i32,
518 ) -> Result<crate::Timestamp, E> {
519 self.visit_i64(i64::from(v))
520 }
521
522 #[inline]
523 fn visit_u32<E: de::Error>(
524 self,
525 v: u32,
526 ) -> Result<crate::Timestamp, E> {
527 self.visit_i64(i64::from(v))
528 }
529
530 #[inline]
531 fn visit_i64<E: de::Error>(
532 self,
533 v: i64,
534 ) -> Result<crate::Timestamp, E> {
535 crate::Timestamp::from_second(v).map_err(de::Error::custom)
536 }
537
538 #[inline]
539 fn visit_u64<E: de::Error>(
540 self,
541 v: u64,
542 ) -> Result<crate::Timestamp, E> {
543 let v = i64::try_from(v).map_err(|_| {
544 de::Error::custom(format_args!(
545 "got unsigned integer {v} seconds, \
546 which is too big to fit in a Jiff `Timestamp`",
547 ))
548 })?;
549 self.visit_i64(v)
550 }
551
552 #[inline]
553 fn visit_i128<E: de::Error>(
554 self,
555 v: i128,
556 ) -> Result<crate::Timestamp, E> {
557 let v = i64::try_from(v).map_err(|_| {
558 de::Error::custom(format_args!(
559 "got signed integer {v} seconds, \
560 which is too big to fit in a Jiff `Timestamp`",
561 ))
562 })?;
563 self.visit_i64(v)
564 }
565
566 #[inline]
567 fn visit_u128<E: de::Error>(
568 self,
569 v: u128,
570 ) -> Result<crate::Timestamp, E> {
571 let v = i64::try_from(v).map_err(|_| {
572 de::Error::custom(format_args!(
573 "got unsigned integer {v} seconds, \
574 which is too big to fit in a Jiff `Timestamp`",
575 ))
576 })?;
577 self.visit_i64(v)
578 }
579 }
580
581 /// (De)serialize a required integer number of seconds from the Unix
582 /// epoch.
583 pub mod required {
584 /// Serialize a required integer number of seconds since the Unix
585 /// epoch.
586 #[inline]
587 pub fn serialize<S: serde::Serializer>(
588 timestamp: &crate::Timestamp,
589 se: S,
590 ) -> Result<S::Ok, S::Error> {
591 se.serialize_i64(timestamp.as_second())
592 }
593
594 /// Deserialize a required integer number of seconds since the
595 /// Unix epoch.
596 #[inline]
597 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
598 de: D,
599 ) -> Result<crate::Timestamp, D::Error> {
600 de.deserialize_i64(super::Visitor)
601 }
602 }
603
604 /// (De)serialize an optional integer number of seconds from the Unix
605 /// epoch.
606 pub mod optional {
607 /// Serialize an optional integer number of seconds since the Unix
608 /// epoch.
609 #[inline]
610 pub fn serialize<S: serde::Serializer>(
611 timestamp: &Option<crate::Timestamp>,
612 se: S,
613 ) -> Result<S::Ok, S::Error> {
614 match *timestamp {
615 None => se.serialize_none(),
616 Some(ts) => se.serialize_i64(ts.as_second()),
617 }
618 }
619
620 /// Deserialize an optional integer number of seconds since the
621 /// Unix epoch.
622 #[inline]
623 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
624 de: D,
625 ) -> Result<Option<crate::Timestamp>, D::Error> {
626 de.deserialize_option(super::super::OptionalVisitor(
627 super::Visitor,
628 ))
629 }
630 }
631 }
632
633 /// (De)serialize an integer number of milliseconds from the Unix epoch.
634 pub mod millisecond {
635 use serde::de;
636
637 struct Visitor;
638
639 impl<'de> de::Visitor<'de> for Visitor {
640 type Value = crate::Timestamp;
641
642 fn expecting(
643 &self,
644 f: &mut core::fmt::Formatter,
645 ) -> core::fmt::Result {
646 f.write_str(
647 "an integer number of milliseconds from the Unix epoch",
648 )
649 }
650
651 #[inline]
652 fn visit_i8<E: de::Error>(
653 self,
654 v: i8,
655 ) -> Result<crate::Timestamp, E> {
656 self.visit_i64(i64::from(v))
657 }
658
659 #[inline]
660 fn visit_u8<E: de::Error>(
661 self,
662 v: u8,
663 ) -> Result<crate::Timestamp, E> {
664 self.visit_i64(i64::from(v))
665 }
666
667 #[inline]
668 fn visit_i16<E: de::Error>(
669 self,
670 v: i16,
671 ) -> Result<crate::Timestamp, E> {
672 self.visit_i64(i64::from(v))
673 }
674
675 #[inline]
676 fn visit_u16<E: de::Error>(
677 self,
678 v: u16,
679 ) -> Result<crate::Timestamp, E> {
680 self.visit_i64(i64::from(v))
681 }
682
683 #[inline]
684 fn visit_i32<E: de::Error>(
685 self,
686 v: i32,
687 ) -> Result<crate::Timestamp, E> {
688 self.visit_i64(i64::from(v))
689 }
690
691 #[inline]
692 fn visit_u32<E: de::Error>(
693 self,
694 v: u32,
695 ) -> Result<crate::Timestamp, E> {
696 self.visit_i64(i64::from(v))
697 }
698
699 #[inline]
700 fn visit_i64<E: de::Error>(
701 self,
702 v: i64,
703 ) -> Result<crate::Timestamp, E> {
704 crate::Timestamp::from_millisecond(v)
705 .map_err(de::Error::custom)
706 }
707
708 #[inline]
709 fn visit_u64<E: de::Error>(
710 self,
711 v: u64,
712 ) -> Result<crate::Timestamp, E> {
713 let v = i64::try_from(v).map_err(|_| {
714 de::Error::custom(format_args!(
715 "got unsigned integer {v} milliseconds, \
716 which is too big to fit in a Jiff `Timestamp`",
717 ))
718 })?;
719 self.visit_i64(v)
720 }
721
722 #[inline]
723 fn visit_i128<E: de::Error>(
724 self,
725 v: i128,
726 ) -> Result<crate::Timestamp, E> {
727 let v = i64::try_from(v).map_err(|_| {
728 de::Error::custom(format_args!(
729 "got signed integer {v} milliseconds, \
730 which is too big to fit in a Jiff `Timestamp`",
731 ))
732 })?;
733 self.visit_i64(v)
734 }
735
736 #[inline]
737 fn visit_u128<E: de::Error>(
738 self,
739 v: u128,
740 ) -> Result<crate::Timestamp, E> {
741 let v = i64::try_from(v).map_err(|_| {
742 de::Error::custom(format_args!(
743 "got unsigned integer {v} milliseconds, \
744 which is too big to fit in a Jiff `Timestamp`",
745 ))
746 })?;
747 self.visit_i64(v)
748 }
749 }
750
751 /// (De)serialize a required integer number of milliseconds from the
752 /// Unix epoch.
753 pub mod required {
754 /// Serialize a required integer number of milliseconds since the
755 /// Unix epoch.
756 #[inline]
757 pub fn serialize<S: serde::Serializer>(
758 timestamp: &crate::Timestamp,
759 se: S,
760 ) -> Result<S::Ok, S::Error> {
761 se.serialize_i64(timestamp.as_millisecond())
762 }
763
764 /// Deserialize a required integer number of milliseconds since the
765 /// Unix epoch.
766 #[inline]
767 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
768 de: D,
769 ) -> Result<crate::Timestamp, D::Error> {
770 de.deserialize_i64(super::Visitor)
771 }
772 }
773
774 /// (De)serialize an optional integer number of milliseconds from the
775 /// Unix epoch.
776 pub mod optional {
777 /// Serialize an optional integer number of milliseconds since the
778 /// Unix epoch.
779 #[inline]
780 pub fn serialize<S: serde::Serializer>(
781 timestamp: &Option<crate::Timestamp>,
782 se: S,
783 ) -> Result<S::Ok, S::Error> {
784 match *timestamp {
785 None => se.serialize_none(),
786 Some(ts) => se.serialize_i64(ts.as_millisecond()),
787 }
788 }
789
790 /// Deserialize an optional integer number of milliseconds since
791 /// the Unix epoch.
792 #[inline]
793 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
794 de: D,
795 ) -> Result<Option<crate::Timestamp>, D::Error> {
796 de.deserialize_option(super::super::OptionalVisitor(
797 super::Visitor,
798 ))
799 }
800 }
801 }
802
803 /// (De)serialize an integer number of microseconds from the Unix epoch.
804 pub mod microsecond {
805 use serde::de;
806
807 struct Visitor;
808
809 impl<'de> de::Visitor<'de> for Visitor {
810 type Value = crate::Timestamp;
811
812 fn expecting(
813 &self,
814 f: &mut core::fmt::Formatter,
815 ) -> core::fmt::Result {
816 f.write_str(
817 "an integer number of microseconds from the Unix epoch",
818 )
819 }
820
821 #[inline]
822 fn visit_i8<E: de::Error>(
823 self,
824 v: i8,
825 ) -> Result<crate::Timestamp, E> {
826 self.visit_i64(i64::from(v))
827 }
828
829 #[inline]
830 fn visit_u8<E: de::Error>(
831 self,
832 v: u8,
833 ) -> Result<crate::Timestamp, E> {
834 self.visit_i64(i64::from(v))
835 }
836
837 #[inline]
838 fn visit_i16<E: de::Error>(
839 self,
840 v: i16,
841 ) -> Result<crate::Timestamp, E> {
842 self.visit_i64(i64::from(v))
843 }
844
845 #[inline]
846 fn visit_u16<E: de::Error>(
847 self,
848 v: u16,
849 ) -> Result<crate::Timestamp, E> {
850 self.visit_i64(i64::from(v))
851 }
852
853 #[inline]
854 fn visit_i32<E: de::Error>(
855 self,
856 v: i32,
857 ) -> Result<crate::Timestamp, E> {
858 self.visit_i64(i64::from(v))
859 }
860
861 #[inline]
862 fn visit_u32<E: de::Error>(
863 self,
864 v: u32,
865 ) -> Result<crate::Timestamp, E> {
866 self.visit_i64(i64::from(v))
867 }
868
869 #[inline]
870 fn visit_i64<E: de::Error>(
871 self,
872 v: i64,
873 ) -> Result<crate::Timestamp, E> {
874 crate::Timestamp::from_microsecond(v)
875 .map_err(de::Error::custom)
876 }
877
878 #[inline]
879 fn visit_u64<E: de::Error>(
880 self,
881 v: u64,
882 ) -> Result<crate::Timestamp, E> {
883 let v = i64::try_from(v).map_err(|_| {
884 de::Error::custom(format_args!(
885 "got unsigned integer {v} microseconds, \
886 which is too big to fit in a Jiff `Timestamp`",
887 ))
888 })?;
889 self.visit_i64(v)
890 }
891
892 #[inline]
893 fn visit_i128<E: de::Error>(
894 self,
895 v: i128,
896 ) -> Result<crate::Timestamp, E> {
897 let v = i64::try_from(v).map_err(|_| {
898 de::Error::custom(format_args!(
899 "got signed integer {v} microseconds, \
900 which is too big to fit in a Jiff `Timestamp`",
901 ))
902 })?;
903 self.visit_i64(v)
904 }
905
906 #[inline]
907 fn visit_u128<E: de::Error>(
908 self,
909 v: u128,
910 ) -> Result<crate::Timestamp, E> {
911 let v = i64::try_from(v).map_err(|_| {
912 de::Error::custom(format_args!(
913 "got unsigned integer {v} microseconds, \
914 which is too big to fit in a Jiff `Timestamp`",
915 ))
916 })?;
917 self.visit_i64(v)
918 }
919 }
920
921 /// (De)serialize a required integer number of microseconds from the
922 /// Unix epoch.
923 pub mod required {
924 /// Serialize a required integer number of microseconds since the
925 /// Unix epoch.
926 #[inline]
927 pub fn serialize<S: serde::Serializer>(
928 timestamp: &crate::Timestamp,
929 se: S,
930 ) -> Result<S::Ok, S::Error> {
931 se.serialize_i64(timestamp.as_microsecond())
932 }
933
934 /// Deserialize a required integer number of microseconds since the
935 /// Unix epoch.
936 #[inline]
937 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
938 de: D,
939 ) -> Result<crate::Timestamp, D::Error> {
940 de.deserialize_i64(super::Visitor)
941 }
942 }
943
944 /// (De)serialize an optional integer number of microseconds from the
945 /// Unix epoch.
946 pub mod optional {
947 /// Serialize an optional integer number of microseconds since the
948 /// Unix epoch.
949 #[inline]
950 pub fn serialize<S: serde::Serializer>(
951 timestamp: &Option<crate::Timestamp>,
952 se: S,
953 ) -> Result<S::Ok, S::Error> {
954 match *timestamp {
955 None => se.serialize_none(),
956 Some(ts) => se.serialize_i64(ts.as_microsecond()),
957 }
958 }
959
960 /// Deserialize an optional integer number of microseconds since
961 /// the Unix epoch.
962 #[inline]
963 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
964 de: D,
965 ) -> Result<Option<crate::Timestamp>, D::Error> {
966 de.deserialize_option(super::super::OptionalVisitor(
967 super::Visitor,
968 ))
969 }
970 }
971 }
972
973 /// (De)serialize an integer number of nanoseconds from the Unix epoch.
974 pub mod nanosecond {
975 use serde::de;
976
977 struct Visitor;
978
979 impl<'de> de::Visitor<'de> for Visitor {
980 type Value = crate::Timestamp;
981
982 fn expecting(
983 &self,
984 f: &mut core::fmt::Formatter,
985 ) -> core::fmt::Result {
986 f.write_str(
987 "an integer number of nanoseconds from the Unix epoch",
988 )
989 }
990
991 #[inline]
992 fn visit_i64<E: de::Error>(
993 self,
994 v: i64,
995 ) -> Result<crate::Timestamp, E> {
996 self.visit_i128(i128::from(v))
997 }
998
999 #[inline]
1000 fn visit_u64<E: de::Error>(
1001 self,
1002 v: u64,
1003 ) -> Result<crate::Timestamp, E> {
1004 self.visit_u128(u128::from(v))
1005 }
1006
1007 #[inline]
1008 fn visit_i128<E: de::Error>(
1009 self,
1010 v: i128,
1011 ) -> Result<crate::Timestamp, E> {
1012 crate::Timestamp::from_nanosecond(v).map_err(de::Error::custom)
1013 }
1014
1015 #[inline]
1016 fn visit_u128<E: de::Error>(
1017 self,
1018 v: u128,
1019 ) -> Result<crate::Timestamp, E> {
1020 let v = i128::try_from(v).map_err(|_| {
1021 de::Error::custom(format_args!(
1022 "got unsigned integer {v} nanoseconds, \
1023 which is too big to fit in a Jiff `Timestamp`",
1024 ))
1025 })?;
1026 self.visit_i128(v)
1027 }
1028 }
1029
1030 /// (De)serialize a required integer number of nanoseconds from the
1031 /// Unix epoch.
1032 pub mod required {
1033 /// Serialize a required integer number of nanoseconds since the
1034 /// Unix epoch.
1035 #[inline]
1036 pub fn serialize<S: serde::Serializer>(
1037 timestamp: &crate::Timestamp,
1038 se: S,
1039 ) -> Result<S::Ok, S::Error> {
1040 se.serialize_i128(timestamp.as_nanosecond())
1041 }
1042
1043 /// Deserialize a required integer number of nanoseconds since the
1044 /// Unix epoch.
1045 #[inline]
1046 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
1047 de: D,
1048 ) -> Result<crate::Timestamp, D::Error> {
1049 de.deserialize_i128(super::Visitor)
1050 }
1051 }
1052
1053 /// (De)serialize an optional integer number of nanoseconds from the
1054 /// Unix epoch.
1055 pub mod optional {
1056 /// Serialize an optional integer number of nanoseconds since the
1057 /// Unix epoch.
1058 #[inline]
1059 pub fn serialize<S: serde::Serializer>(
1060 timestamp: &Option<crate::Timestamp>,
1061 se: S,
1062 ) -> Result<S::Ok, S::Error> {
1063 match *timestamp {
1064 None => se.serialize_none(),
1065 Some(ts) => se.serialize_i128(ts.as_nanosecond()),
1066 }
1067 }
1068
1069 /// Deserialize an optional integer number of nanoseconds since the
1070 /// Unix epoch.
1071 #[inline]
1072 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
1073 de: D,
1074 ) -> Result<Option<crate::Timestamp>, D::Error> {
1075 de.deserialize_option(super::super::OptionalVisitor(
1076 super::Visitor,
1077 ))
1078 }
1079 }
1080 }
1081}
1082
1083/// Convenience routines for (de)serializing [`TimeZone`](crate::tz::TimeZone)
1084/// values.
1085///
1086/// The `required` and `optional` sub-modules each provide serialization and
1087/// deserialization routines. They are meant to be used with Serde's
1088/// [`with` attribute].
1089///
1090/// # Advice
1091///
1092/// Serializing time zones is useful when you want to accept user configuration
1093/// selecting a time zone to use. This might be beneficial when one cannot rely
1094/// on a system's time zone.
1095///
1096/// Note that when deserializing time zones that are IANA time zone
1097/// identifiers, Jiff will automatically use the implicit global database to
1098/// resolve the identifier to an actual time zone. If you do not want to use
1099/// Jiff's global time zone database for this, you'll need to write your own
1100/// Serde integration.
1101///
1102/// [`with` attribute]: https://serde.rs/field-attrs.html#with
1103///
1104/// # Example
1105///
1106/// ```
1107/// use jiff::tz::TimeZone;
1108///
1109/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1110/// struct Record {
1111/// #[serde(with = "jiff::fmt::serde::tz::required")]
1112/// tz: TimeZone,
1113/// }
1114///
1115/// let json = r#"{"tz":"America/Nuuk"}"#;
1116/// let got: Record = serde_json::from_str(&json)?;
1117/// assert_eq!(got.tz, TimeZone::get("America/Nuuk")?);
1118/// assert_eq!(serde_json::to_string(&got)?, json);
1119///
1120/// # Ok::<(), Box<dyn std::error::Error>>(())
1121/// ```
1122///
1123/// # Example: serializing an unknown `TimeZone` works
1124///
1125/// For example, when a time zone was created from
1126/// [`TimeZone::system`](crate::tz::TimeZone::system) and a system configured
1127/// time zone could not be found. One can artifically create this situation
1128/// with [`TimeZone::unknown`](crate::tz::TimeZone::unknown):
1129///
1130/// ```
1131/// use jiff::tz::TimeZone;
1132///
1133/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1134/// struct Record {
1135/// #[serde(with = "jiff::fmt::serde::tz::required")]
1136/// tz: TimeZone,
1137/// }
1138///
1139/// let record = Record { tz: TimeZone::unknown() };
1140/// assert_eq!(
1141/// serde_json::to_string(&record)?,
1142/// r#"{"tz":"Etc/Unknown"}"#,
1143/// );
1144///
1145/// # Ok::<(), Box<dyn std::error::Error>>(())
1146/// ```
1147///
1148/// And it deserializes as well:
1149///
1150/// ```
1151/// use jiff::tz::TimeZone;
1152///
1153/// #[derive(Debug, serde::Deserialize, serde::Serialize)]
1154/// struct Record {
1155/// #[serde(with = "jiff::fmt::serde::tz::required")]
1156/// tz: TimeZone,
1157/// }
1158///
1159/// let json = r#"{"tz":"Etc/Unknown"}"#;
1160/// let got: Record = serde_json::from_str(&json)?;
1161/// assert!(got.tz.is_unknown());
1162///
1163/// # Ok::<(), Box<dyn std::error::Error>>(())
1164/// ```
1165///
1166/// An unknown time zone is "allowed" to percolate through Jiff because it's
1167/// usually not desirable to return an error and completely fail if a system
1168/// time zone could not be detected. On the other hand, by using a special
1169/// `Etc/Unknown` identifier for this case, it still surfaces the fact that
1170/// something has gone wrong.
1171pub mod tz {
1172 use serde::de;
1173
1174 use crate::fmt::{temporal, StdFmtWrite};
1175
1176 struct TemporalTimeZone<'a>(&'a crate::tz::TimeZone);
1177
1178 impl<'a> core::fmt::Display for TemporalTimeZone<'a> {
1179 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1180 static PRINTER: temporal::DateTimePrinter =
1181 temporal::DateTimePrinter::new();
1182 PRINTER
1183 .print_time_zone(self.0, StdFmtWrite(f))
1184 .map_err(|_| core::fmt::Error)
1185 }
1186 }
1187
1188 /// A required visitor for `TimeZone`.
1189 struct Visitor;
1190
1191 impl<'de> de::Visitor<'de> for Visitor {
1192 type Value = crate::tz::TimeZone;
1193
1194 fn expecting(
1195 &self,
1196 f: &mut core::fmt::Formatter,
1197 ) -> core::fmt::Result {
1198 f.write_str(
1199 "a string representing a time zone via an \
1200 IANA time zone identifier, fixed offset from UTC \
1201 or a POSIX time zone string",
1202 )
1203 }
1204
1205 #[inline]
1206 fn visit_bytes<E: de::Error>(
1207 self,
1208 value: &[u8],
1209 ) -> Result<crate::tz::TimeZone, E> {
1210 static PARSER: temporal::DateTimeParser =
1211 temporal::DateTimeParser::new();
1212 PARSER.parse_time_zone(value).map_err(de::Error::custom)
1213 }
1214
1215 #[inline]
1216 fn visit_str<E: de::Error>(
1217 self,
1218 value: &str,
1219 ) -> Result<crate::tz::TimeZone, E> {
1220 self.visit_bytes(value.as_bytes())
1221 }
1222 }
1223
1224 /// A generic optional visitor for `TimeZone`.
1225 struct OptionalVisitor<V>(V);
1226
1227 impl<'de, V: de::Visitor<'de, Value = crate::tz::TimeZone>>
1228 de::Visitor<'de> for OptionalVisitor<V>
1229 {
1230 type Value = Option<crate::tz::TimeZone>;
1231
1232 fn expecting(
1233 &self,
1234 f: &mut core::fmt::Formatter,
1235 ) -> core::fmt::Result {
1236 f.write_str(
1237 "a string representing a time zone via an \
1238 IANA time zone identifier, fixed offset from UTC \
1239 or a POSIX time zone string",
1240 )
1241 }
1242
1243 #[inline]
1244 fn visit_some<D: de::Deserializer<'de>>(
1245 self,
1246 de: D,
1247 ) -> Result<Option<crate::tz::TimeZone>, D::Error> {
1248 de.deserialize_str(self.0).map(Some)
1249 }
1250
1251 #[inline]
1252 fn visit_none<E: de::Error>(
1253 self,
1254 ) -> Result<Option<crate::tz::TimeZone>, E> {
1255 Ok(None)
1256 }
1257 }
1258
1259 /// (De)serialize a required [`TimeZone`](crate::tz::TimeZone).
1260 pub mod required {
1261 /// Serialize a required [`TimeZone`](crate::tz::TimeZone).
1262 ///
1263 /// This will result in an IANA time zone identifier, fixed offset or a
1264 /// POSIX time zone string.
1265 ///
1266 /// This can return an error in some cases when the `TimeZone` has no
1267 /// succinct string representation. For example, when the `TimeZone` is
1268 /// derived from a system `/etc/localtime` for which no IANA time zone
1269 /// identifier could be found.
1270 #[inline]
1271 pub fn serialize<S: serde::Serializer>(
1272 tz: &crate::tz::TimeZone,
1273 se: S,
1274 ) -> Result<S::Ok, S::Error> {
1275 if !tz.has_succinct_serialization() {
1276 return Err(<S::Error as serde::ser::Error>::custom(
1277 "time zones without IANA identifiers that aren't either \
1278 fixed offsets or a POSIX time zone can't be serialized \
1279 (this typically occurs when this is a system time zone \
1280 derived from `/etc/localtime` on Unix systems that \
1281 isn't symlinked to an entry in `/usr/share/zoneinfo)",
1282 ));
1283 }
1284 se.collect_str(&super::TemporalTimeZone(tz))
1285 }
1286
1287 /// Deserialize a required [`TimeZone`](crate::tz::TimeZone).
1288 ///
1289 /// This will attempt to parse an IANA time zone identifier, a fixed
1290 /// offset or a POSIX time zone string.
1291 #[inline]
1292 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
1293 de: D,
1294 ) -> Result<crate::tz::TimeZone, D::Error> {
1295 de.deserialize_str(super::Visitor)
1296 }
1297 }
1298
1299 /// (De)serialize an optional [`TimeZone`](crate::tz::TimeZone).
1300 pub mod optional {
1301 /// Serialize an optional [`TimeZone`](crate::tz::TimeZone).
1302 ///
1303 /// This will result in an IANA time zone identifier, fixed offset or a
1304 /// POSIX time zone string.
1305 ///
1306 /// This can return an error in some cases when the `TimeZone` has no
1307 /// succinct string representation. For example, when the `TimeZone` is
1308 /// derived from a system `/etc/localtime` for which no IANA time zone
1309 /// identifier could be found.
1310 #[inline]
1311 pub fn serialize<S: serde::Serializer>(
1312 tz: &Option<crate::tz::TimeZone>,
1313 se: S,
1314 ) -> Result<S::Ok, S::Error> {
1315 match *tz {
1316 None => se.serialize_none(),
1317 Some(ref tz) => super::required::serialize(tz, se),
1318 }
1319 }
1320
1321 /// Deserialize an optional [`TimeZone`](crate::tz::TimeZone).
1322 ///
1323 /// This will attempt to parse an IANA time zone identifier, a fixed
1324 /// offset or a POSIX time zone string.
1325 #[inline]
1326 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
1327 de: D,
1328 ) -> Result<Option<crate::tz::TimeZone>, D::Error> {
1329 de.deserialize_option(super::OptionalVisitor(super::Visitor))
1330 }
1331 }
1332}
1333
1334#[cfg(test)]
1335mod tests {
1336 use crate::{
1337 span::span_eq, SignedDuration, Span, SpanFieldwise, Timestamp, ToSpan,
1338 };
1339
1340 #[test]
1341 fn duration_friendly_compact_required() {
1342 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1343 struct Data {
1344 #[serde(
1345 serialize_with = "crate::fmt::serde::duration::friendly::compact::required"
1346 )]
1347 duration: SignedDuration,
1348 }
1349
1350 let json = r#"{"duration":"36 hours 1100ms"}"#;
1351 let got: Data = serde_json::from_str(&json).unwrap();
1352 assert_eq!(
1353 got.duration,
1354 SignedDuration::new(36 * 60 * 60 + 1, 100_000_000)
1355 );
1356
1357 let expected = r#"{"duration":"36h 1s 100ms"}"#;
1358 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1359 }
1360
1361 #[test]
1362 fn duration_friendly_compact_optional() {
1363 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1364 struct Data {
1365 #[serde(
1366 serialize_with = "crate::fmt::serde::duration::friendly::compact::optional"
1367 )]
1368 duration: Option<SignedDuration>,
1369 }
1370
1371 let json = r#"{"duration":"36 hours 1100ms"}"#;
1372 let got: Data = serde_json::from_str(&json).unwrap();
1373 assert_eq!(
1374 got.duration,
1375 Some(SignedDuration::new(36 * 60 * 60 + 1, 100_000_000))
1376 );
1377
1378 let expected = r#"{"duration":"36h 1s 100ms"}"#;
1379 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1380 }
1381
1382 #[test]
1383 fn span_friendly_compact_required() {
1384 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1385 struct Data {
1386 #[serde(
1387 serialize_with = "crate::fmt::serde::span::friendly::compact::required"
1388 )]
1389 span: Span,
1390 }
1391
1392 let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
1393 let got: Data = serde_json::from_str(&json).unwrap();
1394 span_eq!(got.span, 1.year().months(2).hours(36).milliseconds(1100));
1395
1396 let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
1397 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1398 }
1399
1400 #[test]
1401 fn span_friendly_compact_optional() {
1402 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1403 struct Data {
1404 #[serde(
1405 serialize_with = "crate::fmt::serde::span::friendly::compact::optional"
1406 )]
1407 span: Option<Span>,
1408 }
1409
1410 let json = r#"{"span":"1 year 2 months 36 hours 1100ms"}"#;
1411 let got: Data = serde_json::from_str(&json).unwrap();
1412 assert_eq!(
1413 got.span.map(SpanFieldwise),
1414 Some(1.year().months(2).hours(36).milliseconds(1100).fieldwise())
1415 );
1416
1417 let expected = r#"{"span":"1y 2mo 36h 1100ms"}"#;
1418 assert_eq!(serde_json::to_string(&got).unwrap(), expected);
1419 }
1420
1421 #[test]
1422 fn timestamp_second_required() {
1423 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1424 struct Data {
1425 #[serde(with = "crate::fmt::serde::timestamp::second::required")]
1426 ts: Timestamp,
1427 }
1428
1429 let json = r#"{"ts":1517644800}"#;
1430 let got: Data = serde_json::from_str(&json).unwrap();
1431 assert_eq!(got.ts, Timestamp::from_second(1517644800).unwrap());
1432 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1433 }
1434
1435 #[test]
1436 fn timestamp_second_optional() {
1437 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1438 struct Data {
1439 #[serde(with = "crate::fmt::serde::timestamp::second::optional")]
1440 ts: Option<Timestamp>,
1441 }
1442
1443 let json = r#"{"ts":1517644800}"#;
1444 let got: Data = serde_json::from_str(&json).unwrap();
1445 assert_eq!(got.ts, Some(Timestamp::from_second(1517644800).unwrap()));
1446 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1447 }
1448
1449 #[test]
1450 fn timestamp_millisecond_required() {
1451 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1452 struct Data {
1453 #[serde(
1454 with = "crate::fmt::serde::timestamp::millisecond::required"
1455 )]
1456 ts: Timestamp,
1457 }
1458
1459 let json = r#"{"ts":1517644800000}"#;
1460 let got: Data = serde_json::from_str(&json).unwrap();
1461 assert_eq!(
1462 got.ts,
1463 Timestamp::from_millisecond(1517644800_000).unwrap()
1464 );
1465 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1466
1467 let json = r#"{"ts":1517644800123}"#;
1468 let got: Data = serde_json::from_str(&json).unwrap();
1469 assert_eq!(
1470 got.ts,
1471 Timestamp::from_millisecond(1517644800_123).unwrap()
1472 );
1473 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1474 }
1475
1476 #[test]
1477 fn timestamp_millisecond_optional() {
1478 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1479 struct Data {
1480 #[serde(
1481 with = "crate::fmt::serde::timestamp::millisecond::optional"
1482 )]
1483 ts: Option<Timestamp>,
1484 }
1485
1486 let json = r#"{"ts":1517644800000}"#;
1487 let got: Data = serde_json::from_str(&json).unwrap();
1488 assert_eq!(
1489 got.ts,
1490 Some(Timestamp::from_millisecond(1517644800_000).unwrap())
1491 );
1492 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1493
1494 let json = r#"{"ts":1517644800123}"#;
1495 let got: Data = serde_json::from_str(&json).unwrap();
1496 assert_eq!(
1497 got.ts,
1498 Some(Timestamp::from_millisecond(1517644800_123).unwrap())
1499 );
1500 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1501 }
1502
1503 #[test]
1504 fn timestamp_microsecond_required() {
1505 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1506 struct Data {
1507 #[serde(
1508 with = "crate::fmt::serde::timestamp::microsecond::required"
1509 )]
1510 ts: Timestamp,
1511 }
1512
1513 let json = r#"{"ts":1517644800000000}"#;
1514 let got: Data = serde_json::from_str(&json).unwrap();
1515 assert_eq!(
1516 got.ts,
1517 Timestamp::from_microsecond(1517644800_000000).unwrap()
1518 );
1519 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1520
1521 let json = r#"{"ts":1517644800123456}"#;
1522 let got: Data = serde_json::from_str(&json).unwrap();
1523 assert_eq!(
1524 got.ts,
1525 Timestamp::from_microsecond(1517644800_123456).unwrap()
1526 );
1527 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1528 }
1529
1530 #[test]
1531 fn timestamp_microsecond_optional() {
1532 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1533 struct Data {
1534 #[serde(
1535 with = "crate::fmt::serde::timestamp::microsecond::optional"
1536 )]
1537 ts: Option<Timestamp>,
1538 }
1539
1540 let json = r#"{"ts":1517644800000000}"#;
1541 let got: Data = serde_json::from_str(&json).unwrap();
1542 assert_eq!(
1543 got.ts,
1544 Some(Timestamp::from_microsecond(1517644800_000000).unwrap())
1545 );
1546 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1547
1548 let json = r#"{"ts":1517644800123456}"#;
1549 let got: Data = serde_json::from_str(&json).unwrap();
1550 assert_eq!(
1551 got.ts,
1552 Some(Timestamp::from_microsecond(1517644800_123456).unwrap())
1553 );
1554 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1555 }
1556
1557 #[test]
1558 fn timestamp_nanosecond_required() {
1559 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1560 struct Data {
1561 #[serde(
1562 with = "crate::fmt::serde::timestamp::nanosecond::required"
1563 )]
1564 ts: Timestamp,
1565 }
1566
1567 let json = r#"{"ts":1517644800000000000}"#;
1568 let got: Data = serde_json::from_str(&json).unwrap();
1569 assert_eq!(
1570 got.ts,
1571 Timestamp::from_nanosecond(1517644800_000000000).unwrap()
1572 );
1573 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1574
1575 let json = r#"{"ts":1517644800123456789}"#;
1576 let got: Data = serde_json::from_str(&json).unwrap();
1577 assert_eq!(
1578 got.ts,
1579 Timestamp::from_nanosecond(1517644800_123456789).unwrap()
1580 );
1581 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1582 }
1583
1584 #[test]
1585 fn timestamp_nanosecond_optional() {
1586 #[derive(Debug, serde::Deserialize, serde::Serialize)]
1587 struct Data {
1588 #[serde(
1589 with = "crate::fmt::serde::timestamp::nanosecond::optional"
1590 )]
1591 ts: Option<Timestamp>,
1592 }
1593
1594 let json = r#"{"ts":1517644800000000000}"#;
1595 let got: Data = serde_json::from_str(&json).unwrap();
1596 assert_eq!(
1597 got.ts,
1598 Some(Timestamp::from_nanosecond(1517644800_000000000).unwrap())
1599 );
1600 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1601
1602 let json = r#"{"ts":1517644800123456789}"#;
1603 let got: Data = serde_json::from_str(&json).unwrap();
1604 assert_eq!(
1605 got.ts,
1606 Some(Timestamp::from_nanosecond(1517644800_123456789).unwrap())
1607 );
1608 assert_eq!(serde_json::to_string(&got).unwrap(), json);
1609 }
1610}