| 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 | |