1#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
46#[non_exhaustive]
47pub struct Duration {
48 seconds: i64,
54
55 nanos: i32,
64}
65
66#[derive(thiserror::Error, Debug)]
84#[non_exhaustive]
85pub enum DurationError {
86 #[error("seconds and/or nanoseconds out of range")]
88 OutOfRange,
89
90 #[error("if seconds and nanoseconds are not zero, they must have the same sign")]
92 MismatchedSigns,
93
94 #[error("cannot deserialize the duration: {0}")]
96 Deserialize(#[source] BoxedError),
97}
98
99type BoxedError = Box<dyn std::error::Error + Send + Sync>;
100type Error = DurationError;
101
102impl Duration {
103 const NS: i32 = 1_000_000_000;
104
105 pub const MAX_SECONDS: i64 = 315_576_000_000;
107
108 pub const MIN_SECONDS: i64 = -Self::MAX_SECONDS;
110
111 pub const MAX_NANOS: i32 = Self::NS - 1;
113
114 pub const MIN_NANOS: i32 = -Self::MAX_NANOS;
116
117 pub fn new(seconds: i64, nanos: i32) -> Result<Self, Error> {
151 if !(Self::MIN_SECONDS..=Self::MAX_SECONDS).contains(&seconds) {
152 return Err(Error::OutOfRange);
153 }
154 if !(Self::MIN_NANOS..=Self::MAX_NANOS).contains(&nanos) {
155 return Err(Error::OutOfRange);
156 }
157 if (seconds != 0 && nanos != 0) && ((seconds < 0) != (nanos < 0)) {
158 return Err(Error::MismatchedSigns);
159 }
160 Ok(Self { seconds, nanos })
161 }
162
163 pub fn clamp(seconds: i64, nanos: i32) -> Self {
188 let mut seconds = seconds;
189 seconds = seconds.saturating_add((nanos / Self::NS) as i64);
190 let mut nanos = nanos % Self::NS;
191 if seconds > 0 && nanos < 0 {
192 seconds = seconds.saturating_sub(1);
193 nanos += Self::NS;
194 } else if seconds < 0 && nanos > 0 {
195 seconds = seconds.saturating_add(1);
196 nanos = -(Self::NS - nanos);
197 }
198 if seconds > Self::MAX_SECONDS {
199 return Self {
200 seconds: Self::MAX_SECONDS,
201 nanos: 0,
202 };
203 }
204 if seconds < Self::MIN_SECONDS {
205 return Self {
206 seconds: Self::MIN_SECONDS,
207 nanos: 0,
208 };
209 }
210 Self { seconds, nanos }
211 }
212
213 pub fn seconds(&self) -> i64 {
222 self.seconds
223 }
224
225 pub fn nanos(&self) -> i32 {
234 self.nanos
235 }
236}
237
238impl crate::message::Message for Duration {
239 fn typename() -> &'static str {
240 "type.googleapis.com/google.protobuf.Duration"
241 }
242
243 #[allow(private_interfaces)]
244 fn serializer() -> impl crate::message::MessageSerializer<Self> {
245 crate::message::ValueSerializer::<Self>::new()
246 }
247}
248
249impl From<Duration> for String {
258 fn from(duration: Duration) -> String {
259 let sign = if duration.seconds < 0 || duration.nanos < 0 {
260 "-"
261 } else {
262 ""
263 };
264 if duration.nanos == 0 {
265 return format!("{sign}{}s", duration.seconds.abs());
266 }
267 let ns = format!("{:09}", duration.nanos.abs());
268 format!(
269 "{sign}{}.{}s",
270 duration.seconds.abs(),
271 ns.trim_end_matches('0')
272 )
273 }
274}
275
276impl TryFrom<&str> for Duration {
287 type Error = DurationError;
288 fn try_from(value: &str) -> Result<Self, Self::Error> {
289 if !value.ends_with('s') {
290 return Err(DurationError::Deserialize("missing trailing 's'".into()));
291 }
292 let digits = &value[..(value.len() - 1)];
293 let (sign, digits) = if let Some(stripped) = digits.strip_prefix('-') {
294 (-1, stripped)
295 } else {
296 (1, &digits[0..])
297 };
298 let mut split = digits.splitn(2, '.');
299 let (seconds, nanos) = (split.next(), split.next());
300 let seconds = seconds
301 .map(str::parse::<i64>)
302 .transpose()
303 .map_err(|e| DurationError::Deserialize(e.into()))?
304 .unwrap_or(0);
305 let nanos = nanos
306 .map(|s| {
307 if s.is_empty() || !s.chars().all(|c| c.is_ascii_digit()) {
308 return Err(DurationError::Deserialize(
309 format!("nanos are not a number [{s}]").into(),
310 ));
311 }
312 let len = s.len();
313 let (digits, power) = if len > 9 { (&s[..9], 0) } else { (s, 9 - len) };
314 let mut val = digits
315 .parse::<i32>()
316 .map_err(|e| DurationError::Deserialize(e.into()))?;
317 if power > 0 {
318 val *= 10_i32.pow(power as u32)
319 }
320 Ok(val)
321 })
322 .transpose()?
323 .unwrap_or(0);
324
325 Duration::new(sign * seconds, sign as i32 * nanos)
326 }
327}
328
329impl TryFrom<&String> for Duration {
341 type Error = DurationError;
342 fn try_from(value: &String) -> Result<Self, Self::Error> {
343 Duration::try_from(value.as_str())
344 }
345}
346
347impl TryFrom<std::time::Duration> for Duration {
358 type Error = DurationError;
359
360 fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> {
361 if value.as_secs() > (i64::MAX as u64) {
362 return Err(Error::OutOfRange);
363 }
364 assert!(value.as_secs() <= (i64::MAX as u64));
365 assert!(value.subsec_nanos() <= (i32::MAX as u32));
366 Self::new(value.as_secs() as i64, value.subsec_nanos() as i32)
367 }
368}
369
370impl TryFrom<Duration> for std::time::Duration {
385 type Error = DurationError;
386
387 fn try_from(value: Duration) -> Result<Self, Self::Error> {
388 if value.seconds < 0 {
389 return Err(Error::OutOfRange);
390 }
391 if value.nanos < 0 {
392 return Err(Error::OutOfRange);
393 }
394 Ok(Self::new(value.seconds as u64, value.nanos as u32))
395 }
396}
397
398#[cfg(feature = "time")]
411#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
412impl TryFrom<time::Duration> for Duration {
413 type Error = DurationError;
414
415 fn try_from(value: time::Duration) -> Result<Self, Self::Error> {
416 Self::new(value.whole_seconds(), value.subsec_nanoseconds())
417 }
418}
419
420#[cfg(feature = "time")]
434#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
435impl From<Duration> for time::Duration {
436 fn from(value: Duration) -> Self {
437 Self::new(value.seconds(), value.nanos())
438 }
439}
440
441#[cfg(feature = "chrono")]
454#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
455impl TryFrom<chrono::Duration> for Duration {
456 type Error = DurationError;
457
458 fn try_from(value: chrono::Duration) -> Result<Self, Self::Error> {
459 Self::new(value.num_seconds(), value.subsec_nanos())
460 }
461}
462
463#[cfg(feature = "chrono")]
474#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
475impl From<Duration> for chrono::Duration {
476 fn from(value: Duration) -> Self {
477 Self::seconds(value.seconds) + Self::nanoseconds(value.nanos as i64)
478 }
479}
480
481#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
483impl serde::ser::Serialize for Duration {
484 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
485 where
486 S: serde::ser::Serializer,
487 {
488 let formatted = String::from(*self);
489 formatted.serialize(serializer)
490 }
491}
492
493struct DurationVisitor;
494
495impl serde::de::Visitor<'_> for DurationVisitor {
496 type Value = Duration;
497
498 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
499 formatter.write_str("a string with a duration in Google format ([sign]{seconds}.{nanos}s)")
500 }
501
502 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
503 where
504 E: serde::de::Error,
505 {
506 let d = Duration::try_from(value).map_err(E::custom)?;
507 Ok(d)
508 }
509}
510
511#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
513impl<'de> serde::de::Deserialize<'de> for Duration {
514 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
515 where
516 D: serde::Deserializer<'de>,
517 {
518 deserializer.deserialize_str(DurationVisitor)
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525 use serde_json::json;
526 use test_case::test_case;
527 type Result = std::result::Result<(), Box<dyn std::error::Error>>;
528
529 #[test]
531 fn zero() -> Result {
532 let proto = Duration {
533 seconds: 0,
534 nanos: 0,
535 };
536 let json = serde_json::to_value(proto)?;
537 let expected = json!(r#"0s"#);
538 assert_eq!(json, expected);
539 let roundtrip = serde_json::from_value::<Duration>(json)?;
540 assert_eq!(proto, roundtrip);
541 Ok(())
542 }
543
544 const SECONDS_IN_DAY: i64 = 24 * 60 * 60;
547 const SECONDS_IN_YEAR: i64 = 365 * SECONDS_IN_DAY + SECONDS_IN_DAY / 4;
550
551 #[test_case(10_000 * SECONDS_IN_YEAR , 0 ; "exactly 10,000 years")]
552 #[test_case(- 10_000 * SECONDS_IN_YEAR , 0 ; "exactly negative 10,000 years")]
553 #[test_case(10_000 * SECONDS_IN_YEAR , 999_999_999 ; "exactly 10,000 years and 999,999,999 nanos"
554 )]
555 #[test_case(- 10_000 * SECONDS_IN_YEAR , -999_999_999 ; "exactly negative 10,000 years and 999,999,999 nanos"
556 )]
557 #[test_case(0, 999_999_999 ; "exactly 999,999,999 nanos")]
558 #[test_case(0 , -999_999_999 ; "exactly negative 999,999,999 nanos")]
559 fn edge_of_range(seconds: i64, nanos: i32) -> Result {
560 let d = Duration::new(seconds, nanos)?;
561 assert_eq!(seconds, d.seconds());
562 assert_eq!(nanos, d.nanos());
563 Ok(())
564 }
565
566 #[test_case(10_000 * SECONDS_IN_YEAR + 1, 0 ; "more seconds than in 10,000 years")]
567 #[test_case(- 10_000 * SECONDS_IN_YEAR - 1, 0 ; "more negative seconds than in -10,000 years")]
568 #[test_case(0, 1_000_000_000 ; "too many positive nanoseconds")]
569 #[test_case(0, -1_000_000_000 ; "too many negative nanoseconds")]
570 fn out_of_range(seconds: i64, nanos: i32) -> Result {
571 let d = Duration::new(seconds, nanos);
572 assert!(matches!(d, Err(Error::OutOfRange)), "{d:?}");
573 Ok(())
574 }
575
576 #[test_case(1 , -1 ; "mismatched sign case 1")]
577 #[test_case(-1 , 1 ; "mismatched sign case 2")]
578 fn mismatched_sign(seconds: i64, nanos: i32) -> Result {
579 let d = Duration::new(seconds, nanos);
580 assert!(matches!(d, Err(Error::MismatchedSigns)), "{d:?}");
581 Ok(())
582 }
583
584 #[test_case(20_000 * SECONDS_IN_YEAR, 0, 10_000 * SECONDS_IN_YEAR, 0 ; "too many positive seconds"
585 )]
586 #[test_case(-20_000 * SECONDS_IN_YEAR, 0, -10_000 * SECONDS_IN_YEAR, 0 ; "too many negative seconds"
587 )]
588 #[test_case(10_000 * SECONDS_IN_YEAR - 1, 1_999_999_999, 10_000 * SECONDS_IN_YEAR, 999_999_999 ; "upper edge of range"
589 )]
590 #[test_case(-10_000 * SECONDS_IN_YEAR + 1, -1_999_999_999, -10_000 * SECONDS_IN_YEAR, -999_999_999 ; "lower edge of range"
591 )]
592 #[test_case(10_000 * SECONDS_IN_YEAR - 1 , 2 * 1_000_000_000_i32, 10_000 * SECONDS_IN_YEAR, 0 ; "nanos push over 10,000 years"
593 )]
594 #[test_case(-10_000 * SECONDS_IN_YEAR + 1, -2 * 1_000_000_000_i32, -10_000 * SECONDS_IN_YEAR, 0 ; "one push under -10,000 years"
595 )]
596 #[test_case(0, 0, 0, 0 ; "all inputs are zero")]
597 #[test_case(1, 0, 1, 0 ; "positive seconds and zero nanos")]
598 #[test_case(1, 200_000, 1, 200_000 ; "positive seconds and nanos")]
599 #[test_case(-1, 0, -1, 0; "negative seconds and zero nanos")]
600 #[test_case(-1, -500_000_000, -1, -500_000_000; "negative seconds and nanos")]
601 #[test_case(2, -400_000_000, 1, 600_000_000; "positive seconds and negative nanos")]
602 #[test_case(-2, 400_000_000, -1, -600_000_000; "negative seconds and positive nanos")]
603 fn clamp(seconds: i64, nanos: i32, want_seconds: i64, want_nanos: i32) -> Result {
604 let got = Duration::clamp(seconds, nanos);
605 let want = Duration {
606 seconds: want_seconds,
607 nanos: want_nanos,
608 };
609 assert_eq!(want, got);
610 Ok(())
611 }
612
613 #[test_case(0, 0, "0s" ; "zero")]
615 #[test_case(0, 2, "0.000000002s" ; "2ns")]
616 #[test_case(0, 200_000_000, "0.2s" ; "200ms")]
617 #[test_case(12, 0, "12s"; "round positive seconds")]
618 #[test_case(12, 123, "12.000000123s"; "positive seconds and nanos")]
619 #[test_case(12, 123_000, "12.000123s"; "positive seconds and micros")]
620 #[test_case(12, 123_000_000, "12.123s"; "positive seconds and millis")]
621 #[test_case(12, 123_456_789, "12.123456789s"; "positive seconds and full nanos")]
622 #[test_case(-12, -0, "-12s"; "round negative seconds")]
623 #[test_case(-12, -123, "-12.000000123s"; "negative seconds and nanos")]
624 #[test_case(-12, -123_000, "-12.000123s"; "negative seconds and micros")]
625 #[test_case(-12, -123_000_000, "-12.123s"; "negative seconds and millis")]
626 #[test_case(-12, -123_456_789, "-12.123456789s"; "negative seconds and full nanos")]
627 #[test_case(-10_000 * SECONDS_IN_YEAR, -999_999_999, "-315576000000.999999999s"; "range edge start"
628 )]
629 #[test_case(10_000 * SECONDS_IN_YEAR, 999_999_999, "315576000000.999999999s"; "range edge end")]
630 fn roundtrip(seconds: i64, nanos: i32, want: &str) -> Result {
631 let input = Duration::new(seconds, nanos)?;
632 let got = serde_json::to_value(input)?
633 .as_str()
634 .map(str::to_string)
635 .ok_or("cannot convert value to string")?;
636 assert_eq!(want, got);
637
638 let rt = serde_json::from_value::<Duration>(serde_json::Value::String(got))?;
639 assert_eq!(input, rt);
640 Ok(())
641 }
642
643 #[test_case("-315576000001s"; "range edge start")]
644 #[test_case("315576000001s"; "range edge end")]
645 fn deserialize_out_of_range(input: &str) -> Result {
646 let value = serde_json::to_value(input)?;
647 let got = serde_json::from_value::<Duration>(value);
648 assert!(got.is_err(), "{got:?}");
649 Ok(())
650 }
651
652 #[test_case(time::Duration::default(), Duration::default() ; "default")]
653 #[test_case(time::Duration::new(0, 0), Duration::new(0, 0).unwrap() ; "zero")]
654 #[test_case(time::Duration::new(10_000 * SECONDS_IN_YEAR , 0), Duration::new(10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly 10,000 years"
655 )]
656 #[test_case(time::Duration::new(-10_000 * SECONDS_IN_YEAR , 0), Duration::new(-10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly negative 10,000 years"
657 )]
658 fn from_time_in_range(value: time::Duration, want: Duration) -> Result {
659 let got = Duration::try_from(value)?;
660 assert_eq!(got, want);
661 Ok(())
662 }
663
664 #[test_case(time::Duration::new(10_001 * SECONDS_IN_YEAR, 0) ; "above the range")]
665 #[test_case(time::Duration::new(-10_001 * SECONDS_IN_YEAR, 0) ; "below the range")]
666 fn from_time_out_of_range(value: time::Duration) {
667 let got = Duration::try_from(value);
668 assert!(matches!(got, Err(DurationError::OutOfRange)), "{got:?}");
669 }
670
671 #[test_case(Duration::default(), time::Duration::default() ; "default")]
672 #[test_case(Duration::new(0, 0).unwrap(), time::Duration::new(0, 0) ; "zero")]
673 #[test_case(Duration::new(10_000 * SECONDS_IN_YEAR , 0).unwrap(), time::Duration::new(10_000 * SECONDS_IN_YEAR, 0) ; "exactly 10,000 years"
674 )]
675 #[test_case(Duration::new(-10_000 * SECONDS_IN_YEAR , 0).unwrap(), time::Duration::new(-10_000 * SECONDS_IN_YEAR, 0) ; "exactly negative 10,000 years"
676 )]
677 fn to_time_in_range(value: Duration, want: time::Duration) -> Result {
678 let got = time::Duration::from(value);
679 assert_eq!(got, want);
680 Ok(())
681 }
682
683 #[test_case("" ; "empty")]
684 #[test_case("1.0" ; "missing final s")]
685 #[test_case("1.2.3.4s" ; "too many periods")]
686 #[test_case("aaas" ; "not a number")]
687 #[test_case("aaaa.0s" ; "seconds are not a number [aaa]")]
688 #[test_case("1a.0s" ; "seconds are not a number [1a]")]
689 #[test_case("1.aaas" ; "nanos are not a number [aaa]")]
690 #[test_case("1.0as" ; "nanos are not a number [0a]")]
691 #[test_case("1.1234567890as" ; "nanos with trailing chars [1234567890a]")]
692 #[test_case("1.s" ; "empty nanos")]
693 fn parse_detect_bad_input(input: &str) -> Result {
694 let got = Duration::try_from(input);
695 assert!(got.is_err(), "{got:?}");
696 let err = got.err().unwrap();
697 assert!(
698 matches!(err, DurationError::Deserialize(_)),
699 "unexpected error {err:?}"
700 );
701 Ok(())
702 }
703
704 #[test]
705 fn fractional_seconds_exceed_9_digits() -> Result {
706 let d = Duration::try_from("1.1234567890s")?;
707 assert_eq!(d, Duration::new(1, 123_456_789)?);
708
709 let d = Duration::try_from("1.123456789012s")?;
710 assert_eq!(d, Duration::new(1, 123_456_789)?);
711
712 let d: Duration = serde_json::from_str(r#""1.1234567890s""#)?;
713 assert_eq!(d, Duration::new(1, 123_456_789)?);
714
715 let d: Duration = serde_json::from_str(r#""1.123456789012s""#)?;
716 assert_eq!(d, Duration::new(1, 123_456_789)?);
717
718 let d = Duration::try_from("-1.1234567890s")?;
719 assert_eq!(d, Duration::new(-1, -123_456_789)?);
720
721 let d: Duration = serde_json::from_str(r#""-1.123456789012s""#)?;
722 assert_eq!(d, Duration::new(-1, -123_456_789)?);
723 Ok(())
724 }
725
726 #[test]
727 fn deserialize_unexpected_input_type() -> Result {
728 let got = serde_json::from_value::<Duration>(serde_json::json!({}));
729 assert!(got.is_err(), "{got:?}");
730 let msg = format!("{got:?}");
731 assert!(msg.contains("duration in Google format"), "message={msg}");
732 Ok(())
733 }
734
735 #[test_case(std::time::Duration::new(0, 0), Duration::clamp(0, 0))]
736 #[test_case(
737 std::time::Duration::new(0, 400_000_000),
738 Duration::clamp(0, 400_000_000)
739 )]
740 #[test_case(
741 std::time::Duration::new(1, 400_000_000),
742 Duration::clamp(1, 400_000_000)
743 )]
744 #[test_case(std::time::Duration::new(10_000 * SECONDS_IN_YEAR as u64, 999_999_999), Duration::clamp(10_000 * SECONDS_IN_YEAR, 999_999_999))]
745 fn from_std_time_in_range(input: std::time::Duration, want: Duration) {
746 let got = Duration::try_from(input).unwrap();
747 assert_eq!(got, want);
748 }
749
750 #[test]
751 fn convert_from_string() -> Result {
752 let input = "12.750s".to_string();
753 let a = Duration::try_from(input.as_str())?;
754 let b = Duration::try_from(&input)?;
755 assert_eq!(a, b);
756 Ok(())
757 }
758
759 #[test_case(std::time::Duration::new(i64::MAX as u64, 0))]
760 #[test_case(std::time::Duration::new(i64::MAX as u64 + 10, 0))]
761 fn from_std_time_out_of_range(input: std::time::Duration) {
762 let got = Duration::try_from(input);
763 assert!(got.is_err(), "{got:?}");
764 }
765
766 #[test_case(chrono::Duration::default(), Duration::default() ; "default")]
767 #[test_case(chrono::Duration::new(0, 0).unwrap(), Duration::new(0, 0).unwrap() ; "zero")]
768 #[test_case(chrono::Duration::new(10_000 * SECONDS_IN_YEAR, 0).unwrap(), Duration::new(10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly 10,000 years"
769 )]
770 #[test_case(chrono::Duration::new(-10_000 * SECONDS_IN_YEAR, 0).unwrap(), Duration::new(-10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly negative 10,000 years"
771 )]
772 fn from_chrono_time_in_range(value: chrono::Duration, want: Duration) -> Result {
773 let got = Duration::try_from(value)?;
774 assert_eq!(got, want);
775 Ok(())
776 }
777
778 #[test_case(Duration::default(), chrono::Duration::default() ; "default")]
779 #[test_case(Duration::new(0, 0).unwrap(), chrono::Duration::new(0, 0).unwrap() ; "zero")]
780 #[test_case(Duration::new(0, 500_000).unwrap(), chrono::Duration::new(0, 500_000).unwrap() ; "500us")]
781 #[test_case(Duration::new(1, 400_000_000).unwrap(), chrono::Duration::new(1, 400_000_000).unwrap() ; "1.4s")]
782 #[test_case(Duration::new(0, -400_000_000).unwrap(), chrono::Duration::new(-1, 600_000_000).unwrap() ; "minus 0.4s")]
783 #[test_case(Duration::new(-1, -400_000_000).unwrap(), chrono::Duration::new(-2, 600_000_000).unwrap() ; "minus 1.4s")]
784 #[test_case(Duration::new(10_000 * SECONDS_IN_YEAR , 0).unwrap(), chrono::Duration::new(10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly 10,000 years"
785 )]
786 #[test_case(Duration::new(-10_000 * SECONDS_IN_YEAR , 0).unwrap(), chrono::Duration::new(-10_000 * SECONDS_IN_YEAR, 0).unwrap() ; "exactly negative 10,000 years"
787 )]
788 fn to_chrono_time_in_range(value: Duration, want: chrono::Duration) -> Result {
789 let got = chrono::Duration::from(value);
790 assert_eq!(got, want);
791 Ok(())
792 }
793
794 #[test_case(chrono::Duration::new(10_001 * SECONDS_IN_YEAR, 0).unwrap() ; "above the range")]
795 #[test_case(chrono::Duration::new(-10_001 * SECONDS_IN_YEAR, 0).unwrap() ; "below the range")]
796 fn from_chrono_time_out_of_range(value: chrono::Duration) {
797 let got = Duration::try_from(value);
798 assert!(matches!(got, Err(DurationError::OutOfRange)), "{got:?}");
799 }
800}