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