@@ -54,6 +54,88 @@ use crate::{expect, try_opt};
5454/// // Encoding 1½ year as a duration in seconds:
5555/// let _duration = CalendarDuration::new().with_seconds(548 * 24 * 60 * 60).unwrap();
5656/// ```
57+ ///
58+ /// # Formatting and parsing
59+ ///
60+ /// The [`Display`](fmt::Display) implementation will format a `CalendarDuration` to the ISO 8601
61+ /// duration format with designators.
62+ ///
63+ /// The [`FromStr`](str::FromStr) implementation currently only supports the same duration with
64+ /// designators format, and not yet the ISO 8601 alternate format that is similar to how dates and
65+ /// times are encoded.
66+ ///
67+ /// The designator format always starts with `P` (period), followed by number-designator pairs.
68+ /// First are the pairs for the "nominal" components, then a `T` (time), and then the pairs for the
69+ /// "accurate" components. Any number-designator pair may be missing when zero, as long as there is
70+ /// at least one pair. The last pair may contain a decimal fraction instead of an integer.
71+ ///
72+ /// Supported formats:
73+ /// - `Pnn̲Ynn̲Mnn̲DTnn̲Hnn̲Mnn̲S`. Components: `Y` for years, `M` for months, `D` for days, `H` for
74+ /// hours, `M` for minutes, and `S` for seconds.
75+ /// - `Pnn̲W`. The only allowed component is `W` for weeks.
76+ ///
77+ /// ```
78+ /// # #[cfg(feature = "alloc")] {
79+ /// use std::str::FromStr;
80+ /// use chrono::CalendarDuration;
81+ ///
82+ /// let duration = CalendarDuration::new().with_months(105).with_days(5).with_hms(6, 5, 4).unwrap();
83+ ///
84+ /// // Formatting and parsing
85+ /// let formatted = duration.to_string();
86+ /// assert_eq!(formatted, "P8Y9M5DT6H5M4S"); // months and minutes are decomposed
87+ /// assert_eq!(CalendarDuration::from_str(&formatted)?, duration);
88+ /// assert_eq!(formatted.parse(), Ok(duration));
89+ ///
90+ /// // Seconds are not decomposed
91+ /// assert_eq!(CalendarDuration::new().with_seconds(123_456).unwrap().to_string(), "PT123456S");
92+ ///
93+ /// // Multiple ISO 8601 duration strings can parse to the same `CalendarDate` because we consider
94+ /// // years a shorthand for 12 months, and consider hours a shorthand for 60 minutes.
95+ /// assert_eq!("P105M5DT6H5M4S".parse(), Ok(duration));
96+ /// assert_eq!("P8Y9M5DT6H5M4S".parse(), Ok(duration));
97+ /// assert_eq!("P8Y9M5DT365M4S".parse(), Ok(duration));
98+ ///
99+ /// // The weeks format can be parsed, but we consistently format a duration with days.
100+ /// assert_eq!("P5W".parse(), Ok(CalendarDuration::new().with_weeks_and_days(5, 0).unwrap()));
101+ /// assert_eq!("P5W".parse::<CalendarDuration>()?.to_string(), "P35D");
102+ ///
103+ /// // Any number-designator pair can be used to encode a duration of zero. We format it as "P0D".
104+ /// assert_eq!(CalendarDuration::new().to_string(), "P0D");
105+ /// assert_eq!("PT0S".parse(), Ok(CalendarDuration::new())); // 0 seconds equals 0 days
106+ ///
107+ /// # }
108+ /// # Ok::<(), chrono::ParseError>(())
109+ /// ```
110+ ///
111+ /// ## Fractional values
112+ ///
113+ /// ISO 8601 optionally allows fractional values, even in places where they are ill-defined. Chrono
114+ /// only supports parsing a fractional value for components that can be encoded in the same base
115+ /// component:
116+ /// - Fractional years will be expressed in months.
117+ /// - Fractional weeks will be expressed in days.
118+ /// - Fractional hours, minutes or seconds will be expressed in minutes, seconds and nanoseconds.
119+ ///
120+ /// The ISO 8601 format has no designator for subsecond components but expects them to be specified
121+ /// as a fraction. We format nanoseconds as a fraction of a second without trailing zero's.
122+ ///
123+ /// Both `,` and `.` are supported as decimal separators when parsing. Although the comma is
124+ /// preferred by ISO 8601 we use `.` when formatting.
125+ ///
126+ /// ```
127+ /// # #[cfg(feature = "alloc")] {
128+ /// # use chrono::CalendarDuration;
129+ /// let duration = CalendarDuration::new().with_hms(0, 3, 5).unwrap().with_millis(330).unwrap();
130+ /// assert_eq!(duration.to_string(), "PT3M5.33S");
131+ /// assert_eq!("PT3M5,33S".parse(), Ok(duration));
132+ ///
133+ /// assert_eq!("P12,5Y".parse(), Ok(CalendarDuration::new().with_years_and_months(12, 6).unwrap()));
134+ /// assert_eq!("P1.43W".parse(), Ok(CalendarDuration::new().with_days(10))); // horrible :-)
135+ /// assert_eq!("PT6,25H".parse(), Ok(CalendarDuration::new().with_hms(6, 15, 0).unwrap()));
136+ /// # }
137+ /// # Ok::<(), chrono::ParseError>(())
138+ /// ```
57139#[ derive( Clone , Copy , PartialEq , Eq , Hash ) ]
58140pub struct CalendarDuration {
59141 // Components with a nominal duration
0 commit comments