1//! The format described in ISO 8601.
23mod adt_hack;
45use core::num::NonZero;
67#[doc(hidden, no_inline)]
8pub use self::adt_hack::DoNotRelyOnWhatThisIs;
9pub use self::adt_hack::EncodedConfig;
1011/// 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(#[automatically_derived]
impl<const CONFIG : EncodedConfig> ::core::clone::Clone for Iso8601<CONFIG> {
#[inline]
fn clone(&self) -> Iso8601<CONFIG> { *self }
}Clone, #[automatically_derived]
impl<const CONFIG : EncodedConfig> ::core::marker::Copy for Iso8601<CONFIG> {
}Copy, #[automatically_derived]
impl<const CONFIG : EncodedConfig> ::core::cmp::PartialEq for Iso8601<CONFIG>
{
#[inline]
fn eq(&self, other: &Iso8601<CONFIG>) -> bool { true }
}PartialEq, #[automatically_derived]
impl<const CONFIG : EncodedConfig> ::core::cmp::Eq for Iso8601<CONFIG> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq)]
32pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>;
3334impl<const CONFIG: EncodedConfig> core::fmt::Debugfor Iso8601<CONFIG> {
35#[inline]
36fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37f.debug_struct("Iso8601")
38 .field("config", &Config::decode(CONFIG))
39 .finish()
40 }
41}
4243/// Define associated constants for `Iso8601`.
44macro_rules! define_assoc_consts {
45 ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
46const $const_name: EncodedConfig = $format.encode();
47impl Iso8601<$const_name> {
48 $(#[$doc])*
49$vis const $const_name: Self = Self;
50 }
51 )*};
52}
5354const TIME_OFFSET: EncodedConfig =
Config::DEFAULT.set_formatted_components(FormattedComponents::TimeOffset).encode();
impl Iso8601<TIME_OFFSET> {
#[doc =
r" An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as"]
#[doc = r" [`Config::DEFAULT`]."]
pub const TIME_OFFSET: Self = Self;
}define_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.
69pub const DEFAULT = Config::DEFAULT;
70/// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
71 /// unspecified behavior.
72pub const PARSING = Config::PARSING;
73/// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
74pub 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`].
76pub 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`].
79pub 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`].
82pub 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`].
85pub 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`].
88pub const TIME_OFFSET = Config::DEFAULT89 .set_formatted_components(FormattedComponents::TimeOffset);
90}9192/// Which components to format.
93#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FormattedComponents {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
FormattedComponents::None => "None",
FormattedComponents::Date => "Date",
FormattedComponents::Time => "Time",
FormattedComponents::Offset => "Offset",
FormattedComponents::DateTime => "DateTime",
FormattedComponents::DateTimeOffset => "DateTimeOffset",
FormattedComponents::TimeOffset => "TimeOffset",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for FormattedComponents {
#[inline]
fn clone(&self) -> FormattedComponents { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for FormattedComponents { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for FormattedComponents {
#[inline]
fn eq(&self, other: &FormattedComponents) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for FormattedComponents {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq)]
94pub enum FormattedComponents {
95/// The configuration can only be used for parsing. Using this to format a value is
96 /// unspecified behavior.
97None,
98/// Format only the date.
99Date,
100/// Format only the time.
101Time,
102/// Format only the UTC offset.
103Offset,
104/// Format the date and time.
105DateTime,
106/// Format the date, time, and UTC offset.
107DateTimeOffset,
108/// Format the time and UTC offset.
109TimeOffset,
110}
111112/// Which format to use for the date.
113#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DateKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
DateKind::Calendar => "Calendar",
DateKind::Week => "Week",
DateKind::Ordinal => "Ordinal",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for DateKind {
#[inline]
fn clone(&self) -> DateKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DateKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for DateKind {
#[inline]
fn eq(&self, other: &DateKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DateKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq)]
114pub enum DateKind {
115/// Use the year-month-day format.
116Calendar,
117/// Use the year-week-weekday format.
118Week,
119/// Use the week-ordinal format.
120Ordinal,
121}
122123/// The precision and number of decimal digits present for the time.
124#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TimePrecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
TimePrecision::Hour { decimal_digits: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f, "Hour",
"decimal_digits", &__self_0),
TimePrecision::Minute { decimal_digits: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Minute", "decimal_digits", &__self_0),
TimePrecision::Second { decimal_digits: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Second", "decimal_digits", &__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for TimePrecision {
#[inline]
fn clone(&self) -> TimePrecision {
let _: ::core::clone::AssertParamIsClone<Option<NonZero<u8>>>;
let _: ::core::clone::AssertParamIsClone<Option<NonZero<u8>>>;
let _: ::core::clone::AssertParamIsClone<Option<NonZero<u8>>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TimePrecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for TimePrecision {
#[inline]
fn eq(&self, other: &TimePrecision) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(TimePrecision::Hour { decimal_digits: __self_0 },
TimePrecision::Hour { decimal_digits: __arg1_0 }) =>
__self_0 == __arg1_0,
(TimePrecision::Minute { decimal_digits: __self_0 },
TimePrecision::Minute { decimal_digits: __arg1_0 }) =>
__self_0 == __arg1_0,
(TimePrecision::Second { decimal_digits: __self_0 },
TimePrecision::Second { decimal_digits: __arg1_0 }) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TimePrecision {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<Option<NonZero<u8>>>;
let _: ::core::cmp::AssertParamIsEq<Option<NonZero<u8>>>;
let _: ::core::cmp::AssertParamIsEq<Option<NonZero<u8>>>;
}
}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.
128Hour {
129#[expect(missing_docs)]
130decimal_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.
134Minute {
135#[expect(missing_docs)]
136decimal_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.
140Second {
141#[expect(missing_docs)]
142decimal_digits: Option<NonZero<u8>>,
143 },
144}
145146/// The precision for the UTC offset.
147#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OffsetPrecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
OffsetPrecision::Hour => "Hour",
OffsetPrecision::Minute => "Minute",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OffsetPrecision {
#[inline]
fn clone(&self) -> OffsetPrecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OffsetPrecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for OffsetPrecision {
#[inline]
fn eq(&self, other: &OffsetPrecision) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for OffsetPrecision {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq)]
148pub enum OffsetPrecision {
149/// Format only the offset hour. Requires the offset minute to be zero.
150Hour,
151/// Format both the offset hour and minute.
152Minute,
153}
154155/// 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(#[automatically_derived]
impl ::core::fmt::Debug for Config {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["formatted_components", "use_separators", "year_is_six_digits",
"date_kind", "time_precision", "offset_precision"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.formatted_components, &self.use_separators,
&self.year_is_six_digits, &self.date_kind,
&self.time_precision, &&self.offset_precision];
::core::fmt::Formatter::debug_struct_fields_finish(f, "Config", names,
values)
}
}Debug)]
161pub struct Config {
162/// Which components, if any, will be formatted.
163pub(crate) formatted_components: FormattedComponents,
164/// Whether the format contains separators (such as `-` or `:`).
165pub(crate) use_separators: bool,
166/// Whether the year is six digits.
167pub(crate) year_is_six_digits: bool,
168/// The format used for the date.
169pub(crate) date_kind: DateKind,
170/// The precision and number of decimal digits present for the time.
171pub(crate) time_precision: TimePrecision,
172/// The precision for the UTC offset.
173pub(crate) offset_precision: OffsetPrecision,
174}
175176impl 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.
190pub 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 };
200201/// A configuration that can only be used for parsing. Using this to format a value is
202 /// unspecified behavior.
203const 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 };
213214/// Set whether the format the date, time, and/or UTC offset.
215#[inline]
216pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
217Self {
218formatted_components,
219 ..self220 }
221 }
222223/// Set whether the format contains separators (such as `-` or `:`).
224#[inline]
225pub const fn set_use_separators(self, use_separators: bool) -> Self {
226Self {
227use_separators,
228 ..self229 }
230 }
231232/// Set whether the year is six digits.
233#[inline]
234pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self {
235Self {
236year_is_six_digits,
237 ..self238 }
239 }
240241/// Set the format used for the date.
242#[inline]
243pub const fn set_date_kind(self, date_kind: DateKind) -> Self {
244Self { date_kind, ..self }
245 }
246247/// Set the precision and number of decimal digits present for the time.
248#[inline]
249pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
250Self {
251time_precision,
252 ..self253 }
254 }
255256/// Set the precision for the UTC offset.
257#[inline]
258pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
259Self {
260offset_precision,
261 ..self262 }
263 }
264}