1 | //! The format described in ISO 8601. |
2 | |
3 | mod adt_hack; |
4 | |
5 | use core::num::NonZeroU8; |
6 | |
7 | pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig}; |
8 | |
9 | /// A configuration for [`Iso8601`] that only parses values. |
10 | const 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`]. |
23 | const 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)] |
46 | pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>; |
47 | |
48 | impl<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 | |
56 | impl 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 | |
74 | impl 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)] |
82 | pub 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)] |
102 | pub 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)] |
113 | pub 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)] |
136 | pub 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)] |
149 | pub 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 | |
164 | impl 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 | |