time/format_description/well_known/
iso8601.rs

1//! The format described in ISO 8601.
2
3mod adt_hack;
4
5use core::num::NonZero;
6
7#[doc(hidden, no_inline)]
8pub use self::adt_hack::DoNotRelyOnWhatThisIs;
9pub use self::adt_hack::EncodedConfig;
10
11/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html).
12///
13/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions.
14///
15/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`].
16/// Passing any other value is **unspecified behavior**.
17///
18/// Example: 1997-11-21T09:55:06.000000000-06:00
19///
20/// # Examples
21#[cfg_attr(feature = "formatting", doc = "```rust")]
22#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
23/// # use time::format_description::well_known::Iso8601;
24/// # use time_macros::datetime;
25/// assert_eq!(
26///     datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?,
27///     "1997-11-12T09:55:06.000000000-06:00"
28/// );
29/// # Ok::<_, time::Error>(())
30/// ```
31#[derive(Clone, Copy, PartialEq, Eq)]
32pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>;
33
34impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
35    #[inline]
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        f.debug_struct("Iso8601")
38            .field("config", &Config::decode(CONFIG))
39            .finish()
40    }
41}
42
43/// Define associated constants for `Iso8601`.
44macro_rules! define_assoc_consts {
45    ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
46        const $const_name: EncodedConfig = $format.encode();
47        impl Iso8601<$const_name> {
48            $(#[$doc])*
49            $vis const $const_name: Self = Self;
50        }
51    )*};
52}
53
54define_assoc_consts! {
55    /// An [`Iso8601`] with the default configuration.
56    ///
57    /// The following is the default behavior:
58    ///
59    /// - The configuration can be used for both formatting and parsing.
60    /// - The date, time, and UTC offset are all formatted.
61    /// - Separators (such as `-` and `:`) are included.
62    /// - The year contains four digits, such that the year must be between 0 and 9999.
63    /// - The date uses the calendar format.
64    /// - The time has precision to the second and nine decimal digits.
65    /// - The UTC offset has precision to the minute.
66    ///
67    /// If you need different behavior, use another associated constant. For full customization, use
68    /// [`Config::DEFAULT`] and [`Config`]'s methods to create a custom configuration.
69    pub const DEFAULT = Config::DEFAULT;
70    /// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
71    /// unspecified behavior.
72    pub const PARSING = Config::PARSING;
73    /// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
74    pub const DATE = Config::DEFAULT.set_formatted_components(FormattedComponents::Date);
75    /// An [`Iso8601`] that handles only the time, but is otherwise the same as [`Config::DEFAULT`].
76    pub const TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::Time);
77    /// An [`Iso8601`] that handles only the UTC offset, but is otherwise the same as
78    /// [`Config::DEFAULT`].
79    pub const OFFSET = Config::DEFAULT.set_formatted_components(FormattedComponents::Offset);
80    /// An [`Iso8601`] that handles the date and time, but is otherwise the same as
81    /// [`Config::DEFAULT`].
82    pub const DATE_TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::DateTime);
83    /// An [`Iso8601`] that handles the date, time, and UTC offset. This is the same as
84    /// [`Config::DEFAULT`].
85    pub const DATE_TIME_OFFSET = Config::DEFAULT;
86    /// An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as
87    /// [`Config::DEFAULT`].
88    pub const TIME_OFFSET = Config::DEFAULT
89        .set_formatted_components(FormattedComponents::TimeOffset);
90}
91
92/// Which components to format.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum FormattedComponents {
95    /// The configuration can only be used for parsing. Using this to format a value is
96    /// unspecified behavior.
97    None,
98    /// Format only the date.
99    Date,
100    /// Format only the time.
101    Time,
102    /// Format only the UTC offset.
103    Offset,
104    /// Format the date and time.
105    DateTime,
106    /// Format the date, time, and UTC offset.
107    DateTimeOffset,
108    /// Format the time and UTC offset.
109    TimeOffset,
110}
111
112/// Which format to use for the date.
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum DateKind {
115    /// Use the year-month-day format.
116    Calendar,
117    /// Use the year-week-weekday format.
118    Week,
119    /// Use the week-ordinal format.
120    Ordinal,
121}
122
123/// The precision and number of decimal digits present for the time.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum TimePrecision {
126    /// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the
127    /// specified number of decimal digits, if any.
128    Hour {
129        #[expect(missing_docs)]
130        decimal_digits: Option<NonZero<u8>>,
131    },
132    /// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
133    /// number of decimal digits, if any.
134    Minute {
135        #[expect(missing_docs)]
136        decimal_digits: Option<NonZero<u8>>,
137    },
138    /// Format the hour, minute, and second. Nanoseconds will be represented with the specified
139    /// number of decimal digits, if any.
140    Second {
141        #[expect(missing_docs)]
142        decimal_digits: Option<NonZero<u8>>,
143    },
144}
145
146/// The precision for the UTC offset.
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum OffsetPrecision {
149    /// Format only the offset hour. Requires the offset minute to be zero.
150    Hour,
151    /// Format both the offset hour and minute.
152    Minute,
153}
154
155/// Configuration for [`Iso8601`].
156// This is only used as a const generic, so there's no need to have a number of implementations on
157// it.
158#[expect(missing_copy_implementations, reason = "forwards compatibility")]
159#[doc(alias = "EncodedConfig")] // People will likely search for `EncodedConfig`, so show them this.
160#[derive(Debug)]
161pub struct Config {
162    /// Which components, if any, will be formatted.
163    pub(crate) formatted_components: FormattedComponents,
164    /// Whether the format contains separators (such as `-` or `:`).
165    pub(crate) use_separators: bool,
166    /// Whether the year is six digits.
167    pub(crate) year_is_six_digits: bool,
168    /// The format used for the date.
169    pub(crate) date_kind: DateKind,
170    /// The precision and number of decimal digits present for the time.
171    pub(crate) time_precision: TimePrecision,
172    /// The precision for the UTC offset.
173    pub(crate) offset_precision: OffsetPrecision,
174}
175
176impl Config {
177    /// A configuration for the [`Iso8601`] format.
178    ///
179    /// The following is the default behavior:
180    ///
181    /// - The configuration can be used for both formatting and parsing.
182    /// - The date, time, and UTC offset are all formatted.
183    /// - Separators (such as `-` and `:`) are included.
184    /// - The year contains four digits, such that the year must be between 0 and 9999.
185    /// - The date uses the calendar format.
186    /// - The time has precision to the second and nine decimal digits.
187    /// - The UTC offset has precision to the minute.
188    ///
189    /// If you need different behavior, use the setter methods on this struct.
190    pub const DEFAULT: Self = Self {
191        formatted_components: FormattedComponents::DateTimeOffset,
192        use_separators: true,
193        year_is_six_digits: false,
194        date_kind: DateKind::Calendar,
195        time_precision: TimePrecision::Second {
196            decimal_digits: NonZero::new(9),
197        },
198        offset_precision: OffsetPrecision::Minute,
199    };
200
201    /// A configuration that can only be used for parsing. Using this to format a value is
202    /// unspecified behavior.
203    const PARSING: Self = Self {
204        formatted_components: FormattedComponents::None,
205        use_separators: false,
206        year_is_six_digits: false,
207        date_kind: DateKind::Calendar,
208        time_precision: TimePrecision::Hour {
209            decimal_digits: None,
210        },
211        offset_precision: OffsetPrecision::Hour,
212    };
213
214    /// Set whether the format the date, time, and/or UTC offset.
215    #[inline]
216    pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
217        Self {
218            formatted_components,
219            ..self
220        }
221    }
222
223    /// Set whether the format contains separators (such as `-` or `:`).
224    #[inline]
225    pub const fn set_use_separators(self, use_separators: bool) -> Self {
226        Self {
227            use_separators,
228            ..self
229        }
230    }
231
232    /// Set whether the year is six digits.
233    #[inline]
234    pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self {
235        Self {
236            year_is_six_digits,
237            ..self
238        }
239    }
240
241    /// Set the format used for the date.
242    #[inline]
243    pub const fn set_date_kind(self, date_kind: DateKind) -> Self {
244        Self { date_kind, ..self }
245    }
246
247    /// Set the precision and number of decimal digits present for the time.
248    #[inline]
249    pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
250        Self {
251            time_precision,
252            ..self
253        }
254    }
255
256    /// Set the precision for the UTC offset.
257    #[inline]
258    pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
259        Self {
260            offset_precision,
261            ..self
262        }
263    }
264}