time/format_description/well_known/
iso8601.rs

1//! The format described in ISO 8601.
2
3mod adt_hack;
4
5use core::num::NonZero;
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(#[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() }>;
33
34impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
35    #[inline]
36    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37        f.debug_struct("Iso8601")
38            .field("config", &Config::decode(CONFIG))
39            .finish()
40    }
41}
42
43/// Define associated constants for `Iso8601`.
44macro_rules! define_assoc_consts {
45    ($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
46        const $const_name: EncodedConfig = $format.encode();
47        impl Iso8601<$const_name> {
48            $(#[$doc])*
49            $vis const $const_name: Self = Self;
50        }
51    )*};
52}
53
54const 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.
69    pub const DEFAULT = Config::DEFAULT;
70    /// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
71    /// unspecified behavior.
72    pub const PARSING = Config::PARSING;
73    /// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
74    pub 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`].
76    pub 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`].
79    pub 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`].
82    pub 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`].
85    pub 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`].
88    pub const TIME_OFFSET = Config::DEFAULT
89        .set_formatted_components(FormattedComponents::TimeOffset);
90}
91
92/// 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.
97    None,
98    /// Format only the date.
99    Date,
100    /// Format only the time.
101    Time,
102    /// Format only the UTC offset.
103    Offset,
104    /// Format the date and time.
105    DateTime,
106    /// Format the date, time, and UTC offset.
107    DateTimeOffset,
108    /// Format the time and UTC offset.
109    TimeOffset,
110}
111
112/// 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.
116    Calendar,
117    /// Use the year-week-weekday format.
118    Week,
119    /// Use the week-ordinal format.
120    Ordinal,
121}
122
123/// 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.
128    Hour {
129        #[expect(missing_docs)]
130        decimal_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.
134    Minute {
135        #[expect(missing_docs)]
136        decimal_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.
140    Second {
141        #[expect(missing_docs)]
142        decimal_digits: Option<NonZero<u8>>,
143    },
144}
145
146/// 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.
150    Hour,
151    /// Format both the offset hour and minute.
152    Minute,
153}
154
155/// 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.
163    pub(crate) formatted_components: FormattedComponents,
164    /// Whether the format contains separators (such as `-` or `:`).
165    pub(crate) use_separators: bool,
166    /// Whether the year is six digits.
167    pub(crate) year_is_six_digits: bool,
168    /// The format used for the date.
169    pub(crate) date_kind: DateKind,
170    /// The precision and number of decimal digits present for the time.
171    pub(crate) time_precision: TimePrecision,
172    /// The precision for the UTC offset.
173    pub(crate) offset_precision: OffsetPrecision,
174}
175
176impl 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.
190    pub 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    };
200
201    /// A configuration that can only be used for parsing. Using this to format a value is
202    /// unspecified behavior.
203    const 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    };
213
214    /// Set whether the format the date, time, and/or UTC offset.
215    #[inline]
216    pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
217        Self {
218            formatted_components,
219            ..self
220        }
221    }
222
223    /// Set whether the format contains separators (such as `-` or `:`).
224    #[inline]
225    pub const fn set_use_separators(self, use_separators: bool) -> Self {
226        Self {
227            use_separators,
228            ..self
229        }
230    }
231
232    /// Set whether the year is six digits.
233    #[inline]
234    pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self {
235        Self {
236            year_is_six_digits,
237            ..self
238        }
239    }
240
241    /// Set the format used for the date.
242    #[inline]
243    pub const fn set_date_kind(self, date_kind: DateKind) -> Self {
244        Self { date_kind, ..self }
245    }
246
247    /// Set the precision and number of decimal digits present for the time.
248    #[inline]
249    pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
250        Self {
251            time_precision,
252            ..self
253        }
254    }
255
256    /// Set the precision for the UTC offset.
257    #[inline]
258    pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
259        Self {
260            offset_precision,
261            ..self
262        }
263    }
264}