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}