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}