1//! Various modifiers for components.
2
3use core::num::NonZeroU16;
4
5// region: date modifiers
6/// Day of the month.
7#[non_exhaustive]
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Day {
10 /// The padding to obtain the minimum width.
11 pub padding: Padding,
12}
13
14/// The representation of a month.
15#[non_exhaustive]
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum MonthRepr {
18 /// The number of the month (January is 1, December is 12).
19 Numerical,
20 /// The long form of the month name (e.g. "January").
21 Long,
22 /// The short form of the month name (e.g. "Jan").
23 Short,
24}
25
26/// Month of the year.
27#[non_exhaustive]
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Month {
30 /// The padding to obtain the minimum width.
31 pub padding: Padding,
32 /// What form of representation should be used?
33 pub repr: MonthRepr,
34 /// Is the value case sensitive when parsing?
35 pub case_sensitive: bool,
36}
37
38/// Ordinal day of the year.
39#[non_exhaustive]
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct Ordinal {
42 /// The padding to obtain the minimum width.
43 pub padding: Padding,
44}
45
46/// The representation used for the day of the week.
47#[non_exhaustive]
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum WeekdayRepr {
50 /// The short form of the weekday (e.g. "Mon").
51 Short,
52 /// The long form of the weekday (e.g. "Monday").
53 Long,
54 /// A numerical representation using Sunday as the first day of the week.
55 ///
56 /// Sunday is either 0 or 1, depending on the other modifier's value.
57 Sunday,
58 /// A numerical representation using Monday as the first day of the week.
59 ///
60 /// Monday is either 0 or 1, depending on the other modifier's value.
61 Monday,
62}
63
64/// Day of the week.
65#[non_exhaustive]
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub struct Weekday {
68 /// What form of representation should be used?
69 pub repr: WeekdayRepr,
70 /// When using a numerical representation, should it be zero or one-indexed?
71 pub one_indexed: bool,
72 /// Is the value case sensitive when parsing?
73 pub case_sensitive: bool,
74}
75
76/// The representation used for the week number.
77#[non_exhaustive]
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum WeekNumberRepr {
80 /// Week 1 is the week that contains January 4.
81 Iso,
82 /// Week 1 begins on the first Sunday of the calendar year.
83 Sunday,
84 /// Week 1 begins on the first Monday of the calendar year.
85 Monday,
86}
87
88/// Week within the year.
89#[non_exhaustive]
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub struct WeekNumber {
92 /// The padding to obtain the minimum width.
93 pub padding: Padding,
94 /// What kind of representation should be used?
95 pub repr: WeekNumberRepr,
96}
97
98/// The representation used for a year value.
99#[non_exhaustive]
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum YearRepr {
102 /// The full value of the year.
103 Full,
104 /// Only the last two digits of the year.
105 LastTwo,
106}
107
108/// Year of the date.
109#[non_exhaustive]
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct Year {
112 /// The padding to obtain the minimum width.
113 pub padding: Padding,
114 /// What kind of representation should be used?
115 pub repr: YearRepr,
116 /// Whether the value is based on the ISO week number or the Gregorian calendar.
117 pub iso_week_based: bool,
118 /// Whether the `+` sign is present when a positive year contains fewer than five digits.
119 pub sign_is_mandatory: bool,
120}
121// endregion date modifiers
122
123// region: time modifiers
124/// Hour of the day.
125#[non_exhaustive]
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub struct Hour {
128 /// The padding to obtain the minimum width.
129 pub padding: Padding,
130 /// Is the hour displayed using a 12 or 24-hour clock?
131 pub is_12_hour_clock: bool,
132}
133
134/// Minute within the hour.
135#[non_exhaustive]
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub struct Minute {
138 /// The padding to obtain the minimum width.
139 pub padding: Padding,
140}
141
142/// AM/PM part of the time.
143#[non_exhaustive]
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub struct Period {
146 /// Is the period uppercase or lowercase?
147 pub is_uppercase: bool,
148 /// Is the value case sensitive when parsing?
149 ///
150 /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior.
151 pub case_sensitive: bool,
152}
153
154/// Second within the minute.
155#[non_exhaustive]
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub struct Second {
158 /// The padding to obtain the minimum width.
159 pub padding: Padding,
160}
161
162/// The number of digits present in a subsecond representation.
163#[non_exhaustive]
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum SubsecondDigits {
166 /// Exactly one digit.
167 One,
168 /// Exactly two digits.
169 Two,
170 /// Exactly three digits.
171 Three,
172 /// Exactly four digits.
173 Four,
174 /// Exactly five digits.
175 Five,
176 /// Exactly six digits.
177 Six,
178 /// Exactly seven digits.
179 Seven,
180 /// Exactly eight digits.
181 Eight,
182 /// Exactly nine digits.
183 Nine,
184 /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits
185 /// necessary will be used.
186 OneOrMore,
187}
188
189/// Subsecond within the second.
190#[non_exhaustive]
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub struct Subsecond {
193 /// How many digits are present in the component?
194 pub digits: SubsecondDigits,
195}
196// endregion time modifiers
197
198// region: offset modifiers
199/// Hour of the UTC offset.
200#[non_exhaustive]
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub struct OffsetHour {
203 /// Whether the `+` sign is present on positive values.
204 pub sign_is_mandatory: bool,
205 /// The padding to obtain the minimum width.
206 pub padding: Padding,
207}
208
209/// Minute within the hour of the UTC offset.
210#[non_exhaustive]
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct OffsetMinute {
213 /// The padding to obtain the minimum width.
214 pub padding: Padding,
215}
216
217/// Second within the minute of the UTC offset.
218#[non_exhaustive]
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub struct OffsetSecond {
221 /// The padding to obtain the minimum width.
222 pub padding: Padding,
223}
224// endregion offset modifiers
225
226/// Type of padding to ensure a minimum width.
227#[non_exhaustive]
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub enum Padding {
230 /// A space character (` `) should be used as padding.
231 Space,
232 /// A zero character (`0`) should be used as padding.
233 Zero,
234 /// There is no padding. This can result in a width below the otherwise minimum number of
235 /// characters.
236 None,
237}
238
239/// Ignore some number of bytes.
240///
241/// This has no effect when formatting.
242#[non_exhaustive]
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub struct Ignore {
245 /// The number of bytes to ignore.
246 pub count: NonZeroU16,
247}
248
249// Needed as `Default` is deliberately not implemented for `Ignore`. The number of bytes to ignore
250// must be explicitly provided.
251impl Ignore {
252 /// Create an instance of `Ignore` with the provided number of bytes to ignore.
253 pub const fn count(count: NonZeroU16) -> Self {
254 Self { count }
255 }
256}
257
258/// The precision of a Unix timestamp.
259#[non_exhaustive]
260#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub enum UnixTimestampPrecision {
262 /// Seconds since the Unix epoch.
263 Second,
264 /// Milliseconds since the Unix epoch.
265 Millisecond,
266 /// Microseconds since the Unix epoch.
267 Microsecond,
268 /// Nanoseconds since the Unix epoch.
269 Nanosecond,
270}
271
272/// A Unix timestamp.
273#[non_exhaustive]
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub struct UnixTimestamp {
276 /// The precision of the timestamp.
277 pub precision: UnixTimestampPrecision,
278 /// Whether the `+` sign must be present for a non-negative timestamp.
279 pub sign_is_mandatory: bool,
280}
281
282/// Generate the provided code if and only if `pub` is present.
283macro_rules! if_pub {
284 (pub $(#[$attr:meta])*; $($x:tt)*) => {
285 $(#[$attr])*
286 ///
287 /// This function exists since [`Default::default()`] cannot be used in a `const` context.
288 /// It may be removed once that becomes possible. As the [`Default`] trait is in the
289 /// prelude, removing this function in the future will not cause any resolution failures for
290 /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be
291 /// affected. As such it will not be considered a breaking change.
292 $($x)*
293 };
294 ($($_:tt)*) => {};
295}
296
297/// Implement `Default` for the given type. This also generates an inherent implementation of a
298/// `default` method that is `const fn`, permitting the default value to be used in const contexts.
299// Every modifier should use this macro rather than a derived `Default`.
300macro_rules! impl_const_default {
301 ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$(
302 impl $type {
303 if_pub! {
304 $($pub)?
305 $(#[$doc])*;
306 pub const fn default() -> Self {
307 $default
308 }
309 }
310 }
311
312 $(#[$doc])*
313 impl Default for $type {
314 fn default() -> Self {
315 $default
316 }
317 }
318 )*};
319}
320
321impl_const_default! {
322 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
323 @pub Day => Self { padding: Padding::Zero };
324 /// Creates a modifier that indicates the value uses the
325 /// [`Numerical`](Self::Numerical) representation.
326 MonthRepr => Self::Numerical;
327 /// Creates an instance of this type that indicates the value uses the
328 /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero),
329 /// and is case-sensitive when parsing.
330 @pub Month => Self {
331 padding: Padding::Zero,
332 repr: MonthRepr::Numerical,
333 case_sensitive: true,
334 };
335 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
336 @pub Ordinal => Self { padding: Padding::Zero };
337 /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation.
338 WeekdayRepr => Self::Long;
339 /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long)
340 /// representation and is case-sensitive when parsing. If the representation is changed to a
341 /// numerical one, the instance defaults to one-based indexing.
342 @pub Weekday => Self {
343 repr: WeekdayRepr::Long,
344 one_indexed: true,
345 case_sensitive: true,
346 };
347 /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation.
348 WeekNumberRepr => Self::Iso;
349 /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero)
350 /// and uses the [`Iso`](WeekNumberRepr::Iso) representation.
351 @pub WeekNumber => Self {
352 padding: Padding::Zero,
353 repr: WeekNumberRepr::Iso,
354 };
355 /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation.
356 YearRepr => Self::Full;
357 /// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full)
358 /// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its
359 /// base, and only includes the year's sign if necessary.
360 @pub Year => Self {
361 padding: Padding::Zero,
362 repr: YearRepr::Full,
363 iso_week_based: false,
364 sign_is_mandatory: false,
365 };
366 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and
367 /// has the 24-hour representation.
368 @pub Hour => Self {
369 padding: Padding::Zero,
370 is_12_hour_clock: false,
371 };
372 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
373 @pub Minute => Self { padding: Padding::Zero };
374 /// Creates a modifier that indicates the value uses the upper-case representation and is
375 /// case-sensitive when parsing.
376 @pub Period => Self {
377 is_uppercase: true,
378 case_sensitive: true,
379 };
380 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
381 @pub Second => Self { padding: Padding::Zero };
382 /// Creates a modifier that indicates the stringified value contains [one or more
383 /// digits](Self::OneOrMore).
384 SubsecondDigits => Self::OneOrMore;
385 /// Creates a modifier that indicates the stringified value contains [one or more
386 /// digits](SubsecondDigits::OneOrMore).
387 @pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
388 /// Creates a modifier that indicates the value uses the `+` sign for all positive values
389 /// and is [padded with zeroes](Padding::Zero).
390 @pub OffsetHour => Self {
391 sign_is_mandatory: true,
392 padding: Padding::Zero,
393 };
394 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
395 @pub OffsetMinute => Self { padding: Padding::Zero };
396 /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
397 @pub OffsetSecond => Self { padding: Padding::Zero };
398 /// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
399 Padding => Self::Zero;
400 /// Creates a modifier that indicates the value represents the [number of seconds](Self::Second)
401 /// since the Unix epoch.
402 UnixTimestampPrecision => Self::Second;
403 /// Creates a modifier that indicates the value represents the [number of
404 /// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory.
405 @pub UnixTimestamp => Self {
406 precision: UnixTimestampPrecision::Second,
407 sign_is_mandatory: false,
408 };
409}
410