1 | //! The format described in ISO 8601. |
2 | |
3 | mod adt_hack; |
4 | |
5 | use core::num::NonZeroU8; |
6 | |
7 | #[doc (hidden, no_inline)] |
8 | pub use self::adt_hack::DoNotRelyOnWhatThisIs; |
9 | pub 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)] |
32 | pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>; |
33 | |
34 | impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> { |
35 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
36 | f&mut DebugStruct<'_, '_>.debug_struct("Iso8601" ) |
37 | .field(name:"config" , &Config::decode(CONFIG)) |
38 | .finish() |
39 | } |
40 | } |
41 | |
42 | /// Define associated constants for `Iso8601`. |
43 | macro_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 | |
53 | define_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)] |
93 | pub 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)] |
113 | pub 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)] |
124 | pub 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)] |
147 | pub 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)] |
160 | pub 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 | |
175 | impl 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 | } |
258 | |