1use core::{fmt, marker::PhantomData};
66
67use serde::{
68 de::{self, Deserializer, MapAccess, SeqAccess, Visitor},
69 ser::{SerializeStruct, SerializeTuple, Serializer},
70 Deserialize, Serialize,
71};
72
73use crate::{Duration, DurationParts, GnssTimeError, Time, TimeScale};
74
75const TIME_FIELDS: &[&str] = &["scale", "nanos"];
76const DURATION_PARTS_FIELDS: &[&str] = &["seconds", "nanos"];
77
78enum TimeField {
79 Scale,
80 Nanos,
81}
82
83enum DurationField {
84 Nanos,
85}
86
87enum DurationPartsField {
88 Seconds,
89 Nanos,
90}
91
92struct TimeVisitor<S: TimeScale>(PhantomData<S>);
93
94struct ScaleMismatch<'a> {
96 expected: &'a str,
97 got: &'a str,
98}
99
100struct DurationVisitor;
101
102struct DurationPartsMapVisitor;
103
104struct DurationPartsTupleVisitor;
105
106impl<S: TimeScale> Serialize for Time<S> {
107 fn serialize<Ser: Serializer>(
108 &self,
109 serializer: Ser,
110 ) -> Result<Ser::Ok, Ser::Error> {
111 if serializer.is_human_readable() {
112 let mut s = serializer.serialize_struct("Time", 2)?;
114 s.serialize_field("scale", S::NAME)?;
115 s.serialize_field("nanos", &self.as_nanos())?;
116 s.end()
117 } else {
118 serializer.serialize_u64(self.as_nanos())
120 }
121 }
122}
123
124impl<'de, S: TimeScale> Deserialize<'de> for Time<S> {
125 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
126 if deserializer.is_human_readable() {
127 deserializer.deserialize_struct("Time", TIME_FIELDS, TimeVisitor::<S>(PhantomData))
128 } else {
129 let nanos = u64::deserialize(deserializer)?;
130 Ok(Time::from_nanos(nanos))
131 }
132 }
133}
134
135impl<'de, S: TimeScale> Visitor<'de> for TimeVisitor<S> {
136 type Value = Time<S>;
137
138 fn expecting(
139 &self,
140 f: &mut fmt::Formatter<'_>,
141 ) -> fmt::Result {
142 write!(f, r#"a map {{ "scale": "{}", "nanos": u64 }}"#, S::NAME)
143 }
144
145 fn visit_map<A: MapAccess<'de>>(
146 self,
147 mut map: A,
148 ) -> Result<Self::Value, A::Error> {
149 let mut nanos: Option<u64> = None;
150
151 while let Some(key) = map.next_key::<TimeField>()? {
152 match key {
153 TimeField::Scale => {
154 let value: &str = map.next_value()?;
155 if value != S::NAME {
156 return Err(de::Error::custom(ScaleMismatch {
157 expected: S::NAME,
158 got: value,
159 }));
160 }
161 }
162 TimeField::Nanos => {
163 nanos = Some(map.next_value()?);
164 }
165 }
166 }
167
168 let nanos = nanos.ok_or_else(|| de::Error::missing_field("nanos"))?;
169 Ok(Time::from_nanos(nanos))
170 }
171}
172
173impl<'de> Deserialize<'de> for TimeField {
174 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
175 struct TimeFieldVisitor;
176
177 impl Visitor<'_> for TimeFieldVisitor {
178 type Value = TimeField;
179
180 fn expecting(
181 &self,
182 f: &mut fmt::Formatter<'_>,
183 ) -> fmt::Result {
184 f.write_str("`scale` or `nanos`")
185 }
186
187 fn visit_str<E: de::Error>(
188 self,
189 v: &str,
190 ) -> Result<TimeField, E> {
191 match v {
192 "scale" => Ok(TimeField::Scale),
193 "nanos" => Ok(TimeField::Nanos),
194 other => Err(de::Error::unknown_field(other, TIME_FIELDS)),
195 }
196 }
197 }
198
199 deserializer.deserialize_identifier(TimeFieldVisitor)
200 }
201}
202
203impl Serialize for Duration {
204 fn serialize<Ser: Serializer>(
205 &self,
206 serializer: Ser,
207 ) -> Result<Ser::Ok, Ser::Error> {
208 if serializer.is_human_readable() {
209 let mut s = serializer.serialize_struct("Duration", 1)?;
211
212 s.serialize_field("nanos", &self.as_nanos())?;
213 s.end()
214 } else {
215 serializer.serialize_i64(self.as_nanos())
216 }
217 }
218}
219
220impl<'de> Deserialize<'de> for Duration {
221 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
222 if deserializer.is_human_readable() {
223 deserializer.deserialize_struct("Duration", &["nanos"], DurationVisitor)
224 } else {
225 let nanos = i64::deserialize(deserializer)?;
226
227 Ok(Duration::from_nanos(nanos))
228 }
229 }
230}
231
232impl<'de> Visitor<'de> for DurationVisitor {
233 type Value = Duration;
234
235 fn expecting(
236 &self,
237 f: &mut fmt::Formatter<'_>,
238 ) -> fmt::Result {
239 f.write_str(r#"a map { "nanos": i64 }"#)
240 }
241
242 fn visit_map<A: MapAccess<'de>>(
243 self,
244 mut map: A,
245 ) -> Result<Duration, A::Error> {
246 let mut nanos: Option<i64> = None;
247
248 while let Some(key) = map.next_key::<DurationField>()? {
249 match key {
250 DurationField::Nanos => {
251 nanos = Some(map.next_value()?);
252 }
253 }
254 }
255
256 let nanos = nanos.ok_or_else(|| de::Error::missing_field("nanos"))?;
257
258 Ok(Duration::from_nanos(nanos))
259 }
260}
261
262impl<'de> Deserialize<'de> for DurationField {
263 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
264 struct DurationFieldVisitor;
265
266 impl Visitor<'_> for DurationFieldVisitor {
267 type Value = DurationField;
268
269 fn expecting(
270 &self,
271 f: &mut fmt::Formatter<'_>,
272 ) -> fmt::Result {
273 f.write_str("`nanos`")
274 }
275
276 fn visit_str<E: de::Error>(
277 self,
278 v: &str,
279 ) -> Result<DurationField, E> {
280 match v {
281 "nanos" => Ok(DurationField::Nanos),
282 other => Err(de::Error::unknown_field(other, &["nanos"])),
283 }
284 }
285 }
286
287 deserializer.deserialize_identifier(DurationFieldVisitor)
288 }
289}
290
291impl Serialize for DurationParts {
292 fn serialize<Ser: Serializer>(
293 &self,
294 serializer: Ser,
295 ) -> Result<Ser::Ok, Ser::Error> {
296 if serializer.is_human_readable() {
297 let mut s = serializer.serialize_struct("DurationParts", 2)?;
299
300 s.serialize_field("seconds", &self.seconds)?;
301 s.serialize_field("nanos", &self.nanos)?;
302 s.end()
303 } else {
304 let mut t = serializer.serialize_tuple(2)?;
306
307 t.serialize_element(&self.seconds)?;
308 t.serialize_element(&self.nanos)?;
309 t.end()
310 }
311 }
312}
313
314impl<'de> Deserialize<'de> for DurationParts {
315 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
316 if deserializer.is_human_readable() {
317 deserializer.deserialize_struct(
318 "DurationParts",
319 DURATION_PARTS_FIELDS,
320 DurationPartsMapVisitor,
321 )
322 } else {
323 deserializer.deserialize_tuple(2, DurationPartsTupleVisitor)
324 }
325 }
326}
327
328impl<'de> Visitor<'de> for DurationPartsMapVisitor {
329 type Value = DurationParts;
330
331 fn expecting(
332 &self,
333 f: &mut fmt::Formatter<'_>,
334 ) -> fmt::Result {
335 f.write_str(r#"a map { "seconds": u64, "nanos": u32 }"#)
336 }
337
338 fn visit_map<A: MapAccess<'de>>(
339 self,
340 mut map: A,
341 ) -> Result<DurationParts, A::Error> {
342 let mut seconds: Option<u64> = None;
343 let mut nanos: Option<u32> = None;
344
345 while let Some(key) = map.next_key::<DurationPartsField>()? {
346 match key {
347 DurationPartsField::Seconds => {
348 seconds = Some(map.next_value()?);
349 }
350 DurationPartsField::Nanos => {
351 nanos = Some(map.next_value()?);
352 }
353 }
354 }
355
356 let seconds = seconds.ok_or_else(|| de::Error::missing_field("seconds"))?;
357 let nanos = nanos.ok_or_else(|| de::Error::missing_field("nanos"))?;
358
359 DurationParts::new(seconds, nanos).map_err(|e| match e {
360 GnssTimeError::InvalidInput(msg) => de::Error::custom(msg),
361 _ => de::Error::custom("invalid DurationParts"),
362 })
363 }
364}
365
366impl<'de> Deserialize<'de> for DurationPartsField {
367 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
368 struct DurationPartsFieldVisitor;
369
370 impl Visitor<'_> for DurationPartsFieldVisitor {
371 type Value = DurationPartsField;
372
373 fn expecting(
374 &self,
375 f: &mut fmt::Formatter<'_>,
376 ) -> fmt::Result {
377 f.write_str("`seconds` or `nanos`")
378 }
379
380 fn visit_str<E: de::Error>(
381 self,
382 v: &str,
383 ) -> Result<DurationPartsField, E> {
384 match v {
385 "seconds" => Ok(DurationPartsField::Seconds),
386 "nanos" => Ok(DurationPartsField::Nanos),
387 other => Err(de::Error::unknown_field(other, DURATION_PARTS_FIELDS)),
388 }
389 }
390 }
391
392 deserializer.deserialize_identifier(DurationPartsFieldVisitor)
393 }
394}
395
396impl<'de> Visitor<'de> for DurationPartsTupleVisitor {
397 type Value = DurationParts;
398
399 fn expecting(
400 &self,
401 f: &mut fmt::Formatter<'_>,
402 ) -> fmt::Result {
403 f.write_str("a 2-element tuple [u64, u32]")
404 }
405
406 fn visit_seq<A: SeqAccess<'de>>(
407 self,
408 mut seq: A,
409 ) -> Result<DurationParts, A::Error> {
410 let seconds: u64 = seq
411 .next_element()?
412 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
413 let nanos: u32 = seq
414 .next_element()?
415 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
416
417 DurationParts::new(seconds, nanos).map_err(|e| match e {
418 GnssTimeError::InvalidInput(msg) => de::Error::custom(msg),
419 _ => de::Error::custom("invalid DurationParts"),
420 })
421 }
422}
423
424impl fmt::Display for ScaleMismatch<'_> {
425 fn fmt(
426 &self,
427 f: &mut fmt::Formatter<'_>,
428 ) -> fmt::Result {
429 write!(
430 f,
431 "scale mismatch: expected \"{}\", got \"{}\"",
432 self.expected, self.got,
433 )
434 }
435}
436
437#[cfg(test)]
442mod tests {
443 use super::*;
444 use crate::{std::string::ToString, Beidou, Galileo, Glonass, Gps, Tai, Utc};
445
446 #[test]
447 fn test_gps_serialize_json_exact_format() {
448 let t = Time::<Gps>::from_seconds(1_356_566_418);
449 let json = serde_json::to_string(&t).unwrap();
450
451 assert_eq!(json, r#"{"scale":"GPS","nanos":1356566418000000000}"#);
452 }
453
454 #[test]
455 fn test_gps_deserialize_json() {
456 let json = r#"{"scale":"GPS","nanos":1356566418000000000}"#;
457 let t: Time<Gps> = serde_json::from_str(json).unwrap();
458
459 assert_eq!(t, Time::<Gps>::from_seconds(1_356_566_418));
460 }
461
462 #[test]
463 fn test_gps_json_roundtrip_with_sub_second() {
464 let original = Time::<Gps>::from_nanos(1_356_566_418_123_456_789);
465 let json = serde_json::to_string(&original).unwrap();
466 let back: Time<Gps> = serde_json::from_str(&json).unwrap();
467
468 assert_eq!(original, back);
469 }
470
471 #[test]
472 fn test_gps_epoch_json_roundtrip() {
473 let t = Time::<Gps>::EPOCH;
474 let json = serde_json::to_string(&t).unwrap();
475
476 assert_eq!(json, r#"{"scale":"GPS","nanos":0}"#);
477
478 let back: Time<Gps> = serde_json::from_str(&json).unwrap();
479
480 assert_eq!(t, back);
481 }
482
483 #[test]
484 fn test_gps_max_json_roundtrip() {
485 let t = Time::<Gps>::MAX;
486 let json = serde_json::to_string(&t).unwrap();
487 let back: Time<Gps> = serde_json::from_str(&json).unwrap();
488
489 assert_eq!(t, back);
490 }
491
492 #[test]
493 fn test_utc_json_roundtrip() {
494 let t = Time::<Utc>::from_nanos(1_514_764_800_000_000_000);
495 let json = serde_json::to_string(&t).unwrap();
496 let back: Time<Utc> = serde_json::from_str(&json).unwrap();
497
498 assert_eq!(t, back);
499 }
500
501 #[test]
502 fn test_tai_json_roundtrip() {
503 let t = Time::<Tai>::from_seconds(100_000_000);
504 let json = serde_json::to_string(&t).unwrap();
505 let back: Time<Tai> = serde_json::from_str(&json).unwrap();
506
507 assert_eq!(t, back);
508 }
509
510 #[test]
511 fn test_galileo_json_roundtrip() {
512 let t = Time::<Galileo>::from_nanos(999_999_999_999);
513 let json = serde_json::to_string(&t).unwrap();
514 let back: Time<Galileo> = serde_json::from_str(&json).unwrap();
515
516 assert_eq!(t, back);
517 }
518
519 #[test]
520 fn test_beidou_json_roundtrip() {
521 let t = Time::<Beidou>::EPOCH;
522 let json = serde_json::to_string(&t).unwrap();
523 let back: Time<Beidou> = serde_json::from_str(&json).unwrap();
524
525 assert_eq!(t, back);
526 }
527
528 #[test]
529 fn test_glonass_json_roundtrip() {
530 let t = Time::<Glonass>::from_nanos(42_000_000_000);
531 let json = serde_json::to_string(&t).unwrap();
532 let back: Time<Glonass> = serde_json::from_str(&json).unwrap();
533
534 assert_eq!(t, back);
535 }
536
537 #[test]
538 fn test_all_scales_json_scale_field() {
539 let gps_v: serde_json::Value = serde_json::to_value(Time::<Gps>::EPOCH).unwrap();
540 let utc_v: serde_json::Value = serde_json::to_value(Time::<Utc>::EPOCH).unwrap();
541 let tai_v: serde_json::Value = serde_json::to_value(Time::<Tai>::EPOCH).unwrap();
542 let gal_v: serde_json::Value = serde_json::to_value(Time::<Galileo>::EPOCH).unwrap();
543 let bdt_v: serde_json::Value = serde_json::to_value(Time::<Beidou>::EPOCH).unwrap();
544 let glo_v: serde_json::Value = serde_json::to_value(Time::<Glonass>::EPOCH).unwrap();
545
546 assert_eq!(gps_v["scale"], "GPS");
547 assert_eq!(utc_v["scale"], "UTC");
548 assert_eq!(tai_v["scale"], "TAI");
549 assert_eq!(gal_v["scale"], "GAL");
550 assert_eq!(bdt_v["scale"], "BDT");
551 assert_eq!(glo_v["scale"], "GLO");
552 }
553
554 #[test]
555 fn test_scale_mismatch_gps_into_utc_fails() {
556 let json = r#"{"scale":"GPS","nanos":0}"#;
557 let result: Result<Time<Utc>, _> = serde_json::from_str(json);
558
559 assert!(result.is_err(), "scale mismatch must fail");
560
561 let msg = result.unwrap_err().to_string();
562
563 assert!(
564 msg.contains("GPS") || msg.contains("UTC") || msg.contains("scale"),
565 "error should mention scale: {msg}"
566 );
567 }
568
569 #[test]
570 fn test_scale_mismatch_tai_into_gps_fails() {
571 let json = r#"{"scale":"TAI","nanos":0}"#;
572 let result: Result<Time<Gps>, _> = serde_json::from_str(json);
573
574 assert!(result.is_err());
575 }
576
577 #[test]
578 fn test_deserialize_without_scale_field_uses_nanos() {
579 let json = r#"{"nanos":12345678}"#;
581 let t: Time<Gps> = serde_json::from_str(json).unwrap();
582
583 assert_eq!(t.as_nanos(), 12_345_678);
584 }
585
586 #[test]
587 fn test_deserialize_missing_nanos_field_fails() {
588 let json = r#"{"scale":"GPS"}"#;
589 let result: Result<Time<Gps>, _> = serde_json::from_str(json);
590
591 assert!(result.is_err());
592 }
593
594 #[test]
595 fn test_gps_postcard_roundtrip() {
596 let original = Time::<Gps>::from_nanos(1_356_566_418_123_456_789);
597 let bytes = postcard::to_allocvec(&original).unwrap();
598 let back: Time<Gps> = postcard::from_bytes(&bytes).unwrap();
599
600 assert_eq!(original, back);
601 }
602
603 #[test]
604 fn test_utc_postcard_roundtrip() {
605 let t = Time::<Utc>::from_nanos(1_514_764_800_000_000_000);
606 let bytes = postcard::to_allocvec(&t).unwrap();
607 let back: Time<Utc> = postcard::from_bytes(&bytes).unwrap();
608
609 assert_eq!(t, back);
610 }
611
612 #[test]
613 fn test_gps_epoch_postcard_roundtrip() {
614 let t = Time::<Gps>::EPOCH;
615 let bytes = postcard::to_allocvec(&t).unwrap();
616 let back: Time<Gps> = postcard::from_bytes(&bytes).unwrap();
617
618 assert_eq!(t, back);
619 }
620
621 #[test]
622 fn test_gps_max_postcard_roundtrip() {
623 let t = Time::<Gps>::MAX;
624 let bytes = postcard::to_allocvec(&t).unwrap();
625 let back: Time<Gps> = postcard::from_bytes(&bytes).unwrap();
626
627 assert_eq!(t, back);
628 }
629
630 #[test]
631 fn test_postcard_is_smaller_than_json() {
632 let t = Time::<Gps>::from_seconds(1_000_000);
633 let json_len = serde_json::to_vec(&t).unwrap().len();
634 let postcard_len = postcard::to_allocvec(&t).unwrap().len();
635
636 assert!(
637 postcard_len < json_len,
638 "postcard ({postcard_len}B) should be smaller than JSON ({json_len}B)"
639 );
640 }
641
642 #[test]
643 fn test_duration_serialize_json_exact() {
644 let d = Duration::from_seconds(-7);
645 let json = serde_json::to_string(&d).unwrap();
646
647 assert_eq!(json, r#"{"nanos":-7000000000}"#);
648 }
649
650 #[test]
651 fn test_duration_zero_json() {
652 let d = Duration::ZERO;
653 let json = serde_json::to_string(&d).unwrap();
654
655 assert_eq!(json, r#"{"nanos":0}"#);
656
657 let back: Duration = serde_json::from_str(&json).unwrap();
658
659 assert_eq!(d, back);
660 }
661
662 #[test]
663 fn test_duration_json_roundtrip_cases() {
664 let cases = [
665 Duration::ZERO,
666 Duration::from_seconds(42),
667 Duration::from_seconds(-100),
668 Duration::from_nanos(1),
669 Duration::from_nanos(-1),
670 Duration::from_millis(500),
671 Duration::MAX,
672 Duration::MIN,
673 ];
674 for d in cases {
675 let json = serde_json::to_string(&d).unwrap();
676 let back: Duration = serde_json::from_str(&json).unwrap();
677
678 assert_eq!(d, back, "round-trip failed for {d:?}");
679 }
680 }
681
682 #[test]
683 fn test_duration_missing_nanos_field_fails() {
684 let result: Result<Duration, _> = serde_json::from_str(r"{}");
685
686 assert!(result.is_err());
687 }
688
689 #[test]
690 fn test_duration_postcard_roundtrip() {
691 let cases = [
692 Duration::ZERO,
693 Duration::from_seconds(1),
694 Duration::from_seconds(-1),
695 Duration::from_nanos(123_456_789),
696 Duration::MAX,
697 Duration::MIN,
698 ];
699 for d in cases {
700 let bytes = postcard::to_allocvec(&d).unwrap();
701 let back: Duration = postcard::from_bytes(&bytes).unwrap();
702
703 assert_eq!(d, back);
704 }
705 }
706
707 #[test]
708 fn test_duration_parts_serialize_json_exact() {
709 let p = DurationParts {
710 seconds: 5,
711 nanos: 500_000_000,
712 };
713 let json = serde_json::to_string(&p).unwrap();
714
715 assert_eq!(json, r#"{"seconds":5,"nanos":500000000}"#);
716 }
717
718 #[test]
719 fn test_duration_parts_zero_json() {
720 let p = DurationParts {
721 seconds: 0,
722 nanos: 0,
723 };
724 let json = serde_json::to_string(&p).unwrap();
725
726 assert_eq!(json, r#"{"seconds":0,"nanos":0}"#);
727
728 let back: DurationParts = serde_json::from_str(&json).unwrap();
729
730 assert_eq!(p, back);
731 }
732
733 #[test]
734 fn test_duration_parts_json_roundtrip() {
735 let cases = [
736 DurationParts {
737 seconds: 0,
738 nanos: 0,
739 },
740 DurationParts {
741 seconds: 1,
742 nanos: 0,
743 },
744 DurationParts {
745 seconds: 86_400,
746 nanos: 123_456_789,
747 },
748 DurationParts {
749 seconds: 604_799,
750 nanos: 999_999_999,
751 },
752 ];
753 for p in cases {
754 let json = serde_json::to_string(&p).unwrap();
755 let back: DurationParts = serde_json::from_str(&json).unwrap();
756
757 assert_eq!(p, back);
758 }
759 }
760
761 #[test]
762 fn test_duration_parts_invalid_nanos_rejected() {
763 let json = r#"{"seconds":0,"nanos":1000000000}"#;
765 let result: Result<DurationParts, _> = serde_json::from_str(json);
766
767 assert!(result.is_err(), "nanos >= 1_000_000_000 must fail");
768 }
769
770 #[test]
771 fn test_duration_parts_missing_seconds_fails() {
772 let result: Result<DurationParts, _> = serde_json::from_str(r#"{"nanos":0}"#);
773
774 assert!(result.is_err());
775 }
776
777 #[test]
778 fn test_duration_parts_missing_nanos_fails() {
779 let result: Result<DurationParts, _> = serde_json::from_str(r#"{"seconds":0}"#);
780
781 assert!(result.is_err());
782 }
783
784 #[test]
785 fn test_duration_parts_postcard_roundtrip() {
786 let cases = [
787 DurationParts {
788 seconds: 0,
789 nanos: 0,
790 },
791 DurationParts {
792 seconds: 86_400,
793 nanos: 123_456_789,
794 },
795 DurationParts {
796 seconds: u64::MAX / 1_000_000_000,
797 nanos: 999_999_999,
798 },
799 ];
800
801 for p in cases {
802 let bytes = postcard::to_allocvec(&p).unwrap();
803 let back: DurationParts = postcard::from_bytes(&bytes).unwrap();
804
805 assert_eq!(p, back);
806 }
807 }
808
809 #[test]
810 fn test_from_week_tow_json_roundtrip() {
811 let tow = DurationParts {
812 seconds: 432_000,
813 nanos: 0,
814 };
815 let gps = Time::<Gps>::from_week_tow(2345, tow).unwrap();
816
817 let gps_json = serde_json::to_string(&gps).unwrap();
819 let tow_json = serde_json::to_string(&tow).unwrap();
820
821 let gps_back: Time<Gps> = serde_json::from_str(&gps_json).unwrap();
822 let tow_back: DurationParts = serde_json::from_str(&tow_json).unwrap();
823
824 assert_eq!(gps, gps_back);
825 assert_eq!(tow, tow_back);
826
827 let gps_from_tow = Time::<Gps>::from_week_tow(2345, tow_back).unwrap();
829
830 assert_eq!(gps, gps_from_tow);
831 }
832
833 #[test]
834 fn test_time_duration_combined_json() {
835 let t = Time::<Gps>::from_seconds(1_000_000);
837 let d = Duration::from_seconds(3600);
838
839 let t_json = serde_json::to_string(&t).unwrap();
840 let d_json = serde_json::to_string(&d).unwrap();
841
842 let t_back: Time<Gps> = serde_json::from_str(&t_json).unwrap();
843 let d_back: Duration = serde_json::from_str(&d_json).unwrap();
844
845 assert_eq!(t + d, t_back + d_back);
846 }
847}