time/format_description/well_known/
iso8601.rs

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