Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qttemporalpattern.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include "private/qttemporalpattern_p.h"
4
5#include <QtCore/qbitarray.h>
6#include "private/qlocale_p.h"
7#include "private/qtparseqttemporalformat_p.h"
8#if QT_CONFIG(datetimeparser)
9# include "private/qtparsetemporal_p.h"
10#endif
11
12QT_BEGIN_NAMESPACE
13
15/*!
16 \internal
17 \since 6.12
18 \namespace QtTemporalPattern
19 \brief Supporting types and functions for temporal patterns.
20
21 Temporal patterns describe how a date, time or datetime may be serialized as
22 text or parsed from text. This namespace provides the common tools used by
23 the classes describing such patterns: \l QDateTimePattern, \l QTimePattern
24 and \l QDatePattern.
25*/
26
27/* Note (QTBUG-70516): For now (Qt 6.12) it suffices to cover everything that
28 the existing Qt datetime format strings are capable of. However, the intent
29 is to eventually expand this to fully support CLDR's datetime formats so that
30 we can switch from converting those to Qt formats when we scan CLDR to
31 actually storing the CLDR format in the qlocale_data_p.h tables and
32 constructing our Q(Date|Time)+Pattern objects from those formats. See comment
33 in header for a link to the relevant parts of LDML.
34*/
35
36/*!
37 \enum QtTemporalPattern::TemporalFieldCategory
38
39 This enumeration characterizes the information supplied by a field.
40
41 For the details of how that information is conveyed, see \l
42 {QtTemporalPattern::}{TemporalFieldFlag}. For the classification of fields
43 by whether they specify date, time or zone, see \l
44 {QtTemporalPattern::}{DateTimePart}.
45
46 \value Literal Describes a text fragment that frames the data, such as
47 separators and delimiters.
48 \value TimeZone Identifies the timezone or offset from UTC of a datetime.
49 \value SecondFraction The fraction of a second (an optional time field).
50 \value Second The second within the minute (an optional time field).
51 \value Minute The minute within the hour (a time field).
52 \value PeriodInDay A subdivision of the day, such as before noon vs after
53 noon (a time field). May serve to disambiguate the hour
54 when specified only modulo 12. (Currently only the am/pm
55 distinction is supported.)
56 \value HourMod12 The hour in the range 1 through 12 (a time field). May be
57 ambiguous on its own.
58 \value Hour The hour in the range from 0 through 23 (a time field).
59
60 \value DayOfWeek The day within the week (a date field). Usually specified
61 by name. Some locales may use numbers for their Narrow
62 Verbal and/or Standalone forms. No form is supported for
63 Numeric | DayOfWeek.
64 \value DayOfMonth The day number within the month (a date field). Runs from
65 1 for the first day of the month up to the length of the
66 month. Always numeric, regardless of field flags.
67
68 \value Month The month within the year (a date field). When specified
69 numerically, the first month of the year is numbered 1; there
70 is no month 0. Some locales may use numbers for their Narrow
71 Verbal form.
72
73 \value YearWithinCentury The two-digit year number (a date field). Widely
74 used in date formats, despite its potential for
75 ambiguity. May be disambiguated by other fields or
76 by specifying base year for the hundred years
77 presumed when only two digits are given and other
78 fields don't disambiguate.
79 \value Year The full year number (a date field).
80
81 Fields identified as numbers, along with hour, minute, second and fraction
82 of second, are always given in numeric form (regardless of Numeric, Verbal
83 or Standalone flags).
84
85 The SecondFraction field describes the digits immediately following the
86 fractional-part separator. Leading zeros are significant and not considered
87 to be padding: trailing zeros are understood as padding. QTime and QDateTime
88 only handle times to millisecond precision, so if more than three digits are
89 found when parsing a SecondFraction field, the excess are used only to round
90 to the nearest millisecond. (If fewer than three digits are parsed, the
91 value is implicitly extended on the right with zeros to obtain milliseconds
92 precision. Whether such a field is accepted will depend on the setting of
93 the \c width of the field and the absence of \c ZeroPad from its
94 \c{options}, in the usual ways.) If more than three digits are specified
95 when serializing, the digits after the first three shall all be zeros (and
96 are omitted if the \c ZeroPad option is not specified).
97
98 \note For negative years, the YearWithinCentury will be understood as the
99 number of completed years since the start of the most recent year that is a
100 multiple of 100. Thus year -1 (which Qt understands, when using the
101 Gregorian calendar, to mean 1 BCE) has a YearWithinCentury value of 99,
102 since the last year that's a multiple of 100 is -100 (for Gregorian, 100
103 BCE).
104
105 \sa {QtTemporalPattern::}{TemporalFieldFlag}, {QtTemporalPattern::}{DateTimePart}
106*/
107
108/* For numbers as narrow days of the week, at least three different numberings
109 are used, all of which are used in locales whose "first day of the week" is
110 Monday. (Furthermore, some locales with digits as narrow names only do that
111 for some days, with letters for others.) So there is no way to determine how
112 days of the week are numbered from one locale to the next, except in so far
113 as their narrow Verbal or Standalone forms use digits. A Numeric version of
114 the day of the week field would thus either not know what to do for most
115 locales, be inconsistent between locales or be inconsistent, for some
116 locales, between Narrow Verbal / Standalone formats and Numeric format.
117 Hence the lack of a meaning of Numeric for DayOfWeek.
118
119 Some locales use month numbers for the narrow format of months; but none are
120 known to do so inconsistently with the usual month numbering (although Pashto
121 does this only partially, using non-numeric narrow standalone forms for the
122 first two months). This does still conflict with the numeric form of months,
123 at least in some cases, that use ASCII digits for narrow forms of month
124 numbers but locale-appropriate digits for their numeric forms. However, the
125 conflict is only in how the numbers are represented, not in the numeric
126 values used for months.
127*/
128
129/*!
130 \internal
131 \namespace QtTemporalPattern::FieldGroup
132 \brief Masks identifying mutually-exclusive families of options.
133
134 Each constant in this namespace is of type \l {QtTemporalPattern::}
135 {TemporalFieldFlags} and combines a mutually-exclusive family of \l
136 {QtTemporalPattern::} {TemporalFieldFlag} values into a mask by which to
137 identify that family. Within each such family, if none of the flags is
138 specified, any of them may be applied but if any of them are specified then
139 only one of those specified may be applied.
140
141 \value FormMask The three general forms of a field: numeric, verbal and
142 standalone.
143 \value WidthMask The four field widths: wide, short, abbreviated, and
144 narrow.
145 \value UtcPrefixMask The two options for prefixes on offset forms of
146 timezones, with and without a UTC prefix.
147 \value PaddingMask The two padding options for when fields don't fill the
148 width allotted to them: spaces and zeros.
149 \value SeasonMask The three perspectives from which to describe a timezone:
150 the generic, regardless of the time of year, the standard,
151 and how it's described when exercising daylight-saving
152 time (if it does at all).
153*/
154/* Namespace QtParseCommon has helper functions, defined in qtparsetimezone_p.h,
155 to apply the above rule related to these masks.
156*/
157
158/*!
159 \enum QtTemporalPattern::TemporalFieldFlag
160
161 This enumeration describes how datetime fields are expressed in text.
162
163 A combination of these flags is used to qualify a \l {QtTemporalPattern::}
164 {TemporalFieldCategory}, indicating whether it is conveyed by words or
165 numbers, how tersely or in which terms, depending on the field
166 category. Interpretation of a flag in this enumeration varies with the field
167 category it is qualifying and with other flags combined with it.
168
169 Some flags, such as the four width options, are mutually exclusive. For each
170 group of mutually-exclusive flags, a mask constant is defined in namespace
171 QtTemporalPattern::FieldGroup, of type \l {QtTemporalPattern::}
172 {TemporalFieldFlags}, that combines the members of that group. If no member
173 of such a group is set in a flags variable, it is treated as if all members
174 of that group were set. When serializing, the behavior when two or more
175 conflicting flags, in a group relevant to the field, are combined is
176 unspecified. It may depend on other fields or variables passed to the
177 serialization function. When parsing, such conflicting flags allow the
178 parser to match any of the conflicting options specified. If this leads to
179 ambiguity, parsing prefers the option that consumes more of the text to
180 parse.
181
182 Unless otherwise indicated, fields are localized. For example, ZeroPad pads
183 with, and Numeric uses, locale-appropriate digits when localized, and the
184 names of months, days of the week and timezones are translated into the
185 appropriate language (if possible).
186
187 The primary distinction in how fields are expressed is the form of the
188 field. These first three options are mutually incompatible and are grouped
189 together as the \l {QtTemporalPattern::FieldGroup::} {FormMask} constant:
190
191 \value Numeric Express the field numerically as a series of digits.
192 \value Verbal Name the field value in its usual in-format grammatical form.
193 \value Standalone Name the field value in its stand-alone grammatical form.
194
195 Next comes the width of the field. The meanings of these fields depends on
196 other flags and the field category, where relevant. Some field categories
197 may ignore these entirely, others may draw fewer distinctions and use the
198 same meaning for some widths. These four options are mutually exclusive and
199 grouped together as the \l {QtTemporalPattern::FieldGroup::} {WidthMask}
200 constant:
201
202 \value Narrow Use a the narrowest supported form for the field. In some
203 locales, the Narrow Verbal forms of some fields may use
204 numbers, potentially conflicting with one of its Numeric
205 forms.
206 \value Abbreviated Use an abbreviated form of the field.
207 \value Short Use a short form of the field.
208 \value Wide Use a wide form of the field.
209
210 Where a \c width is specified for a field but the field's value is naturally
211 shorter, it is necessary to indicate how to pad it to the desired width.
212 These two options are mutually exclusive and grouped together as the \l
213 {QtTemporalPattern::FieldGroup::} {PaddingMask} constant:
214
215 \value ZeroPad Only for numeric fields: pad to \c width with zeros. When
216 parsing, accept zeros that don't affect value, reject fields
217 that are narrower than their specified \c width.
218 \value SpacePad For a field with a positive \c width, pad to that \c width
219 with spaces. When parsing, allow leading and trailing
220 spacing characters.
221
222 If neither form of padding is indicated, the natural representation of a
223 value is used even if it fails to reach the \c width specified and parsing
224 will accept a narrower field (although it will prefer a match with full
225 width). In a \l {QtTemporalPattern::TemporalFieldCategory} {TimeZone} field
226 (see below) using an offset form, SpacePad is ignored and ZeroPad or its
227 absence only has its usual meaning for hour fields, while controlling the
228 presence of zero minute and second offsets following the hour field.
229
230 Where a text to be matched (for example, a literal or the name of a month or
231 day of the week) contains spaces, by default the spaces must match exactly.
232 Since users commonly treat anything that looks like a space the same, it is
233 usually desirable to match spaces flexibly. Where the text to be parsed is
234 taken from a larger text, it's also possible that this larger text has been
235 flowed, as a paragraph, which may have turned some spaces into line breaks,
236 possibly with added indentation. Coping with such cases is supported by the
237 option
238
239 \value FlexSpace Where a field to be matched contains spacing characters, or
240 a run of them, any spacing character or run of them will be
241 accepted as matching. A character is deemed to be a spacing
242 character if \l QChar::isSpace() is true for it.
243
244 The following options are only relevant to Verbal and Standalone
245 fields. They are not treated as a group or descrbed by a mask, as the locale
246 provides relevant fields with the appropriate (possibly mixed) case for the
247 lcoale. By default, the locale's form is used when serializing and matched
248 (case-sensitively) when parsing. The first two of these are mutually
249 exclusive. They change that default, forcing the case when serializing and
250 requiring the specified case (or one of the specified cases) when
251 parsing. The third has no effect when serializing and overrides the other
252 two, if either is present, when parsing.
253
254 \value LowerCase When serializing, force lower-case.
255 \value UpperCase When serializing, force upper-case.
256 \value IgnoreCase When parsing, match the field case-insensitively.
257
258 Modern revisions of ISO 8601 permit years outside the range from 0 through
259 9999 but require that they have a sign. By default a + sign on a positive
260 year is allowed and silently ignored (without counting towards its \c
261 width), but the YearSignIso8601 applies a modified version of the ISO rule:
262 the year field will only match a text with more digits than its specified \c
263 width if that text starts with a sign. When the field's \c width is 4, this
264 implements the ISO rule, but it can be applied to other widths, if needed.
265
266 In contrast to ISO's use of year 0000 to indicate 1 BCE, with negative year
267 values representing successively earlier years, where the calendar in use
268 has no year zero, Qt describes the year before year 1 as year -1, skipping
269 over 0 and treating 0 as an invalid value for the year. In particular, this
270 applies to the Gregorian calendar, which is used by default: 1 CE is
271 represented as year 1, with 1 BC as year -1. If the calendar in use reports
272 \c true from \l {QCalendar::} {hasYearZero()}, Qt duly accepts a year 0
273 between years -1 and +1. ISO also specifies that years before 1583 (the
274 first full year after the Gregorian calendar came into play) or after 9999
275 should not be used except by prior agreement between the producer and
276 consumer of the serialized date or datetime. Qt leaves such agreement as a
277 matter between the user and those they communicate with, simply accepting
278 any year number within the range of QDate or QDateTime, as appropriate.
279
280 \section2 Timezone representation
281
282 The following options are only relevant to timezones. If used with other
283 categories of field, they are ignored. By default, when the other fields of
284 a timestamp determine a datetime, the zone's localized name in effect at
285 that datetime is used when serializing and matched when parsing.
286
287 There are both localized and international standard formats available. The
288 following pair of options select which of those to use. They are grouped
289 together as the \l {QtTemporalPattern::FieldGroup::} {LocalizationMask}
290 constant:
291
292 \value LocalizedZone For a TimeZone, use localized forms.
293 \value Iso8601 For a TimeZone, use an ISO 8601 offset format.
294
295 When both are specified, or neither is, localized forms are preferred. See
296 \l {Locale-independent offset forms}, below, for details of the ISO
297 8601-based formats. See \c{Standalone | Short} below for the IANA DB and
298 \l{Local time} for a system-dependent form, both of which ignore the choice
299 between LocalizedZone and Iso8601.
300
301 The following three options, selecting seasonal variations, are mutually
302 exclusive and only relevant to Verbal or Standalone forms. They are
303 independent of the choice between LocalizedZone and Iso8601. They are
304 grouped together as the \l {QtTemporalPattern::FieldGroup::} {SeasonMask}
305 constant:
306
307 \value GenericTime For a non-Numeric TimeZone, use its generic name.
308 \value StandardTime For a non-Numeric TimeZone, use its standard-time name.
309 \value DaylightSavingTime For a non-Numeric TimeZone, use its
310 daylight-saving name.
311
312 For localized timezones, Numeric selects a basic offset format or an offset
313 format with a base prefix (typically a localized form of UTC or GMT), Verbal
314 selects a form of the zone's name, and Standalone selects the IANA ID or a
315 localized name based on a city that serves as exemplar for the zone (for the
316 given locale). The effects of the width options above then depend on which
317 of these is used. When LocalizedZone is set, the following forms are
318 available:
319
320 \list
321 \li For Numeric, the timezone offset is used:
322 \list
323 \li Wide uses the hour, minute and second, as for Short, but when
324 parsing it will accept a fractional part of the seconds (following
325 the locale-appropriate separator, after the whole number part of
326 the seconds), if present. As Qt only supports whole numbers of
327 seconds as offsets, this fractional part's only effect is to round
328 up the second part if it is at least a half second. (See note on
329 rounding in the next section.)
330 \li Short uses the hour, minute and second, rounding to the nearest
331 second if the offset is not a whole number of seconds.
332 \li Abbreviated uses the hour and minute, rounding to the nearest
333 minute if the offset is not a whole number of minutes.
334 \li Narrow uses the hour, rounding to nearest if the actual offset is
335 not a whole number of hours.
336 \endlist
337 \li For Verbal, the zone name is used:
338 \list
339 \li Wide uses the full name of the zone, e.g. "Pacific Time", "Pacific
340 Standard Time" or "Pacific Daylight Time", depending on season.
341 \li Short is treated as Wide.
342 \li Abbreviated uses the zone abbreviation, e.g. "PT", "PST" or "PDT".
343 Note that these cannot be reliably parsed.
344 \li Narrow is treated as Abbreviated.
345 \endlist
346 \li For Standalone, the city name:
347 \list
348 \li Wide uses the locale's generic location format, such as "Los
349 Angeles Time".
350 \li Short uses the full IANA ID, such as America/Los_Angeles.
351 This is not localized (the LocalizedZone flag is ignored).
352 \li Abbreviated just gives the localized exemplar city, e.g. "Los
353 Angeles".
354 \li Narrow is treated as Abbreviated.
355 \endlist
356 \endlist
357
358 Where a timezone is specified simply in terms of an offset from UTC, it does
359 not necessarily have an associated city or a name other than its offset
360 representations. For such zones, when serializing, Verbal and Standalone are
361 treated as Numeric, with the exception of \c{Staldalone | Short}, as the
362 IANA ID is not localized and this form, like an IANA ID, is accepted by the
363 QTimeZone(QByteArray) constructor. (Note that these do not include ZeroPad:
364 see the next section for its effect on offset forms.) When parsing,
365 therefore, these offset formats are accepted for the Verbal and Standalone
366 forms that would map to them for offset zones. This applies to \l
367 {QTimeZone::fromDurationAheadOfUtc()} lightweight time representations (for
368 which \l {QTimeZone::}{isUtcOrFixedOffset()} is true) as well as to those,
369 with a UTC-offset backend, constructed by QTimeZone(int) or, with an ID of
370 form \c{"UTC"} or \c{"UTC±HH:mm"}, by QTimeZone(QByteArray),
371
372 \section3 Offset modifiers
373
374 In the various timezone offset formats (both above and below), there are
375 potentially hour, minute and second parts of the offset, depending on the
376 width option selected. The minute and second parts, when present, always use
377 two digits, rendering the usual meaning of ZeroPad redundant. Instead, for
378 these parts, ZeroPad controls whether trailing zero parts are included.
379 When ZeroPad is set they are included when serializing and required to be
380 present when parsing. When ZeroPad is not set, they are omitted when
381 serializing and, if trailng parts are missing when parsing, they are taken
382 to have value zero. Zero parts are always accepted when parsing, even when
383 ZeroPad is not set.
384
385 The hour part of an offset is never omitted when serializing and is always
386 required when parsing. When ZeroPad is not set, when parsing offset formats
387 with separators between parts, a single-digit hour is accepted. (For the
388 localized offset formats above, whether there are separators between parts
389 depends on the locale.) When ZeroPad is not set, serialization only produces
390 a single-digit hour if there are no later parts (typically because they were
391 all zero, hence omitted). Otherwise, the hour part is always serialized with
392 two digits and parsing requires it to have two digits.
393
394 As ever, if more than one width is specified, any given is allowed. On
395 serializing, narrower formats are preferred unless they lose precision in
396 the offset that an allowed longer format would include. Thus \c{Narrow |
397 Short} would include only the hours for a whole-hour offset, but would
398 include hours, minutes and seconds for an hour-and-a-half offset (albeit
399 omitting the zero seconds unless ZeroPad is set). Where the width thus
400 selected requires rounding due to omitting a part of the offset that isn't
401 zero, exact halves round away from zero. On parsing, the longest allowed
402 form for which a match is found will be used.
403
404 Offset formats may also include a prefix (localized in the forms above) that
405 identifies the offset as being relative to UTC or GMT. The following pair of
406 mutually exclusive options control whether that is omitted or included.
407 They are grouped together as the \l {QtTemporalPattern::FieldGroup::}
408 {UtcPrefixMask} constant:
409
410 \value AcceptUtcPrefix Include a UTC prefix on offsets when serializing and
411 require it when parsing.
412 \value NeedNoUtcPrefix Leave off the UTC prefix when serializing and reject
413 it when parsing.
414
415 If neither or both of these are specified, serializing omits the prefix and
416 parsing permits it but does not require it.
417
418 \section3 Locale-independent offset forms
419
420 In addition to these localized forms, a timestamp may also represent its
421 zone in a locale-independent offset form. In effect, this use the C locale,
422 with ASCII digits 0 through 9, the ASCII + and - as signs, and the ASCII
423 colon as separator (where relevant). These are selected by the \c Iso8601
424 flag.
425
426 \value AllowZSuffix Modifies Iso8601 to allow use of a \c{Z} suffix to
427 denote a zero UTC offset. See RFC 9557 note below.
428
429 If AllowZSuffix is set and ZeroPad is not, serialization will use \c{Z} to
430 represent a zero offset. When ZeroPad is set, serialization ignores
431 AllowZSuffix. If AllowZSufix is set, regardless of ZeroPad, parsing will
432 accept a \c{Z} suffix as meaning zero offset.
433
434 The Iso8601 format is modified by other flags as follows (when not using
435 \c{Z} to represent a zero offset):
436 \list
437 \li With Numeric it uses a basic format (with no separators), with Verbal
438 or Standalone it uses separators between hours, minutes and seconds,
439 in so far as these appear.
440 \li The width options Wide, Short, Abbreviated and Narrow have the same
441 meanings as for Numeric localized offset formats, modified by the
442 ZeroPad option, as described above.
443 \endlist
444
445 \note In some contexts, notably where \l
446 {https://www.rfc-editor.org/rfc/rfc9557.html} {RFC 9557} is used, a zone
447 indicator on a timestamp may be understood as giving context to the
448 information in which it appears, as opposed to only indicating the zone with
449 respect to which the timestamp itself is expressed. In such contexts, a
450 \c{Z} suffix, applied by \c AllowZSuffix, only indicates that the timestamp
451 itself is given with respect to UTC and does not give any context to the
452 information in which it appears. For contrast, in such contexts, a +00:00
453 suffix does indicate not only that the timestamp is given in UTC but also
454 that UTC is the relevant zone for the context of the information. Prior to
455 RFC 9557, some RFCs (incompatibly with ISO 8601) proposed using -00:00 to
456 indicate what RFC 9557 has specified as the meaning for a \c{Z}
457 suffix. Nothing in Qt attends to this distinction.
458
459 \section3 Local time
460
461 Where Qt can determine how the local system describes its local time, Qt has
462 no control over the form in which the system supplies it, nor does Qt know
463 whether, or how, it is localized, with the result that this representation
464 is system-dependent. As another system may be using a different local time,
465 or representing it differently, there is no guarantee that what one system
466 supplies for local time can be successfully understood on another system. To
467 opt in to using this system-dependent representation of local time, supply
468 the following option. Even then, when serializing, if the options given
469 permit any other available representation of the timezone, that is preferred
470 over this.
471
472 \value LocalTimeName Allow the system-supplied local time name to describe
473 the system's local time.
474
475 The system local time may come in separate forms for standard time and
476 daylight-saving time. When it does, the options above for selecting between
477 these affect LocalTimeName as usual, with GenericTime ignored.
478
479 \sa {QtTemporalPattern::}{TemporalFieldCategory}, {QtTemporalPattern::}{TemporalFieldFlags}
480*/
481// RFC 9557: see QTBUG-114172
482
483// TemporalFieldFlags and DateTimeParts: docs taken care of automagically by
484// QDoc, recognizing QFLAG() usage.
485
486/*!
487 \fn constexpr bool QtTemporalPattern::matchesFlagWithin(QtTemporalPattern::TemporalFieldFlags flags, QtTemporalPattern::TemporalFieldFlag sought, QtTemporalPattern::TemporalFieldFlags group)
488
489 Tests \a flags for a \a sought flag within a \a group.
490
491 Implements the per-flag check for a single TemporalFieldFlag with respect to
492 its FieldGroup entry. If no flag in a group is given in \a flags, then all
493 flags in the group are allowed; otherwise, only the given flags in the group
494 match.
495*/
496
497/*!
498 \fn constexpr bool QtTemporalPattern::matchesFlagsWithin(QtTemporalPattern::TemporalFieldFlags flags, QtTemporalPattern::TemporalFieldFlags sought, QtTemporalPattern::TemporalFieldFlags group)
499
500 Similar to \l matchesFlagWithin(), but if any of those \a sought is found in
501 \a flags, or if none of group are, it's counted as a match.
502
503 In some contexts, some flags are treated as equivalent, so to check for what
504 they represent pass the equivalent flags |-joined as \a sought.
505*/
506
507
508/*!
509 \enum QtTemporalPattern::DateTimePart
510 \brief This enumeraction classifies the various field categories.
511
512 The classification addresses which parts of a datetime a field contributes
513 data to.
514
515 \value None A literal field contributes no data.
516 \value Date Various fields contribute to the date.
517 \value Time Various fields contribute to the time.
518 \value Zone Only the \l {QtTemporalPattern::TemporalFieldFlag::}{TimeZone}
519 field contributes information about the timezone.
520
521 These may be combined in a \l {QtTemporalPattern::}{DateTimeParts} to
522 express a set of parts that a set of fields might suffice to describe,
523 whether fully or only in part.
524
525 \sa {QtTemporalPattern::}{TemporalFieldCategory}, {QtTemporalPattern::}{supports()}
526*/
527
528/*!
529 \fn QtTemporalPattern::classify(QtTemporalPattern::TemporalFieldCategory category) noexcept
530 \brief Classify a field category according to the part of a datetime it contributes to.
531
532 This maps a \l {QtTemporalPattern::}{TemporalFieldCategory} to the \l
533 {QtTemporalPattern::}{DateTimePart} for which it supplies data.
534*/
535
536/*!
537 \class QtTemporalPattern::TemporalField
538 \brief Describes one field in a temporal pattern.
539
540 A single field is characterized by its \l
541 {QtTemporalPattern::TemporalFieldCategory}{category}, some \l
542 {QtTemporalPattern::TemporalFieldFlags}{options} identifying how the field
543 is to be expressed and, where relevant, a datum. For a Literal field, the
544 datum is the string to be matched.
545
546 For numeric fields, a width may optionally be specified, indicating
547 the expected number of digits in the field. If options specifies
548 zero-padding, this is a minimum number of digits; otherwise, shorter fields
549 are accepted. In any case, longer values are accepted, where the resulting
550 numeric value is valid for the field. A zero width does not mean an empty
551 field will be accepted: it is effectively equivalent to a width of 1.
552
553 \note in some locales, the digits may require surrogate pairs to encode, so
554 the UTF-16 length of the field may exceed the number of digits. In a year
555 field with a sign, the sign does not count towards the width of the
556 field. The width only counts digits. The width is, in any case, a lower
557 bound: more digits may be read, if present and not consumed by some later
558 field. The \c endIndex of any parse result is the only reliable source of
559 truth on the UTF-16 end of the text parsed.
560*/
561// TODO: should we support digit grouping in year numbers, at least with > 4 digits ?
562
563/*!
564 \fn QtTemporalPattern::hasFieldsFor(QSpan<const QtTemporalPattern::TemporalField> range)
565 \brief Identify the parts to which the given fields contribute data.
566
567 Takes a \a range of TemporalField instances and returns a \l
568 {QtTemporalPattern::}{DateTimeParts} indicating which parts \l classify()
569 says any of the fields contribute to. Note that this only tessts for
570 contribution to a part, not for full coverage of the part. See \l supports()
571 for that.
572
573 \sa classify(), supports()
574*/
575
576/*!
577 \enum QtTemporalPattern::SupportType
578 \brief This enumeration identifies how well some fields describe requested datetime parts.
579
580 The fields of a pattern may contain partial or complete data on each of the
581 \l {QtTemporalPattern::TemporalFieldPart}{parts of} a datetime. When parsing
582 or serializing only a date or only a time, fields for the other or for
583 timezone are extraneous, making the pattern unable to serialize just the
584 intended type, as it lacks the data for those fields. It would also, when
585 parsing, be obliged discard some of the data it parses, as the type it
586 returns cannot express it.
587
588 When serializing a datetime, if the fields present do not suffice to fully
589 encode the date or time, it will not be possible for a reader of the
590 resulting text to unambiguously determine the datetime. If timezone is not
591 specified, it is possible to convert the datetime to some specific choice of
592 zone: provided both ends of the communication use the same zone, it is then
593 possible to recover the exact point in time, albeit without knowing the
594 timezone originally used to encode it.
595
596 Where data is partially supplied, it is possible that the partial data
597 suffices to meet the readers needs, although this typically involves the
598 reader in making some default assumptions about the missing fields. For
599 example, a two-digit year may need some assumptions about the century
600 (possibly aided by information about the date and day of the week) to
601 determine what year to presume the sender intended.
602
603 \value None No fields were found.
604 \value HasStrays Some field not relevant to the requested parts was present.
605 \value Partial Either some requested part is present and some other is
606 missing or some fields for a requested part are present but
607 not enough to fully describe that part.
608 \value Clear Enough fields of the requested parts were found to fully
609 determine them and no fields of unwanted parts were present.
610
611 Where partial fields for the requested parts were found along with fields
612 for unwanted parts, HasStrays is used in preference to Partial, as it is
613 considered a more significant defect.
614
615 \sa {QtTemporalPattern::}{TemporalFieldPart}, {QtTemporalPattern::}{supports()}
616*/
617
618/*!
619 \fn QtTemporalPattern::supports(QtTemporalPattern::DateTimeParts wanted, QSpan<QtTemporalPattern::TemporalField> range, bool hasBaseYear) noexcept
620 \brief Assess how well the given fields support the \a wanted parts.
621
622 If any field in \a range belongs to a part not included in \a wanted,
623 returns \l {SupportType::}{HasStrays}. Otherwise,
624
625 \list
626 \li If no fields are present, aside from \l
627 {TemporalFieldCategory::}{Literal} ones, returns \l
628 {SupportType::}{None}.
629 \li If the fields present completely specify all parts in \a wanted,
630 returns \l {SupportType::}{Clear}.
631 \li If some fields are specified but some \a wanted part is inadequately
632 specified, or entirely unspecified, returns \l
633 {SupportType::}{Partial}.
634 \endlist
635
636 For these purposes,
637
638 \list
639 \li The \l {DateTimePart::}{Zone} part is specified by the \l
640 {TemporalFieldCategory::}{TimeZone}.
641 \li The \l {DateTimePart::}{Time} part is specified by any combination of
642 fields that identify the hour and minute. If the \l
643 {TemporalFieldCategory::}{SecondFractions} field is present, the part is
644 considered incompletely specified unless the \l
645 {TemporalFieldCategory::}{Seconds} field is also present.
646 \li The \l {DateTimePart::}{Date} part is specified by any combination of
647 fields that identify a date. This usually means \l
648 {TemporalFieldCategory::}{Year}, \l {TemporalFieldCategory::}{Month}
649 and \l {TemporalFieldCategory::}{DayOfMonth} although Year may be
650 indirectly specified by \l
651 {TemporalFieldCategory::}{YearWithinCentury} if the presence of \l
652 {TemporalFieldCategory::}{DayOfWeek} enables disambiguation among
653 centuries close to the present, given the other date fields.
654 \endlist
655*/
656
657SupportType supports(DateTimeParts wanted, QSpan<const TemporalField> range,
658 bool hasBaseYear) noexcept
659{
660 // TODO: may need to take into account calendar (and perhaps locale).
661 SupportType support = SupportType::HasStrays;
662 SupportType zone = SupportType::None;
663 QBitArray date(40);
664 QBitArray time(24);
665 for (const TemporalField &field : range) {
666 const DateTimePart part = field.part();
667 switch (part) {
668 case DateTimePart::None: // Literal contributes no data.
669 continue;
670 case DateTimePart::Date:
671 date.setBit(quint8(field.category) - 64);
672 break;
673 case DateTimePart::Time:
674 time.setBit(quint8(field.category) - 16);
675 break;
676 case DateTimePart::Zone:
677 if (field.options.testAnyFlags(TemporalFieldFlag::Wide | TemporalFieldFlag::Short)
678 || field.options.testAnyFlags(TemporalFieldFlag::Numeric
679 | TemporalFieldFlag::Standalone
680 | TemporalFieldFlag::Iso8601)) {
681 zone = SupportType::Clear;
682 } else if (zone == SupportType::None) {
683 // Zone name abbreviation: does not uniquely identify zone.
684 zone = SupportType::Partial;
685 }
686 break;
687 }
688 if (!wanted.testFlag(part))
689 return support;
690 }
691
692 bool partsSeen = wanted.testFlag(DateTimePart::Zone);
693 support = partsSeen ? zone : SupportType::None;
694
695 constexpr auto join = [](SupportType lhs, SupportType rhs) -> SupportType {
696 Q_PRE(lhs != SupportType::HasStrays);
697 Q_PRE(rhs != SupportType::HasStrays);
698 // For combining SupportTypes from different Parts to determine support
699 // for their composite. Simplest case is when they're the same:
700 if (lhs == rhs)
701 return lhs;
702 // Every combination of distinct values among the other three is partial:
703 return SupportType::Partial;
704 };
705
706 if (wanted.testFlag(DateTimePart::Date)) {
707 const SupportType hasDate = [=]() {
708 constexpr auto bitFor = [](TemporalFieldCategory cat) {
709 return quint8(cat) - 64;
710 };
711#define CHECK(field) date.testBit(bitFor(TemporalFieldCategory::field))
712 // if (CHECK(JulianDay)) return SupportType::Clear;
713 int fields = 0;
714 bool partial = false;
715 if (CHECK(Year)) {
716 ++fields;
717 } else if (CHECK(YearWithinCentury)) {
718 if (hasBaseYear) {
719 ++fields;
720 // } else if (CHECK(Century)) { ++fields;
721 } else if (CHECK(DayOfWeek) && CHECK(DayOfMonth) && CHECK(Month)) {
722 // We can infer century from those three by assuming it's
723 // close to the present.
724 ++fields;
725 } else {
726 partial = true;
727 }
728 // } else if (CHECK(Century)) { partial = true;
729 }
730 if (CHECK(Month))
731 ++fields;
732 if (CHECK(DayOfMonth))
733 ++fields;
734 else if (CHECK(DayOfWeek))
735 partial = true;
736#undef CHECK
737 if (fields == 3 && !partial)
738 return SupportType::Clear;
739 if (fields || partial)
740 return SupportType::Partial;
741 return SupportType::None;
742 }();
743 support = partsSeen ? join(support, hasDate) : hasDate;
744 partsSeen = true;
745 }
746
747 if (wanted.testFlag(DateTimePart::Time)) {
748 const SupportType hasTime = [=]() {
749 constexpr auto bitFor = [](TemporalFieldCategory cat) {
750 return quint8(cat) - 16;
751 };
752#define CHECK(field) time.testBit(bitFor(TemporalFieldCategory::field))
753 // if (CHECK(MillisecondInDay)) return SupportType::Clear;
754 int fields = 0;
755 bool partial = false;
756 if (CHECK(Hour)) {
757 ++fields;
758 } else if (CHECK(HourMod12)) {
759 if (CHECK(PeriodInDay))
760 ++fields;
761 else
762 partial = true;
763 } else if (CHECK(PeriodInDay)) {
764 partial = true;
765 }
766 if (CHECK(Minute))
767 ++fields;
768 // Hour and minute are required, but seconds and later are optional;
769 // however, having a finer field without an coarser one leaves a gap => Partial.
770 if (CHECK(Second))
771 partial = fields < 2;
772 else if (CHECK(SecondFraction))
773 partial = true;
774#undef CHECK
775 if (fields == 2 && !partial)
776 return SupportType::Clear;
777 if (fields || partial)
778 return SupportType::Partial;
779 return SupportType::None;
780 }();
781 support = partsSeen ? join(support, hasTime) : hasTime;
782 partsSeen = true;
783 }
784 // Shall still be None if we've seen no fields:
785 Q_ASSERT(partsSeen || support == SupportType::None);
786 return support;
787}
788
789} // namespace QtTemporalPattern
790
791/*!
792 \internal
793 \since 6.12
794 \class QDateTimePattern
795 \brief A description of a serialization format for a datetime
796*/
797
798/*!
799 \fn QDateTimePattern::forLocale(const QLocale &locale, QLocale::FormatType format)
800 Construct a QDateTimePattern appropriate to the given \a locale.
801
802 The \a format can be used to select how compact or expansive the pattern is.
803
804 \sa fromQtFormat
805*/
806
807/*
808//! [is-valid]
809 Returns \c true if this pattern can be used to reliably transmit {\1}s.
810
811 Returns \c false if there is unresolved ambiguity in the texts it will
812 produce when serializing or the texts that will match it when parsing.
813 Some apparent ambiguities may be resolved by interactions between other
814 fields or the \c{defaults} parameter to \l{parse()}.
815//! [is-valid]
816*/
817
818// TODO: currently (see supports(), above) doesn't take locale or calendar into account.
819/*!
820 \fn QDateTimePattern::isValid() const noexcept
821
822 \include qttemporalpattern.cpp {is-valid} {datetime}
823 \include qttemporalpattern.cpp base-year-disambiguates
824
825 Timezone abbreviations are ambiguous.
826*/
827
828/*!
829 \fn QDateTimePattern::setLocale(const QLocale &loc) noexcept
830
831//! [set-locale]
832 Sets the locale, for use for fields whose representation depends on locale,
833 to \a loc. By default the application's current default locale is used.
834
835 \sa QLocale::setDefault()
836//! [set-locale]
837*/
838
839/*!
840 \fn QDateTimePattern::locale() const noexcep
841
842 Returns the current locale in use by this pattern.
843
844 \sa setLocale()
845*/
846
847/*!
848 \fn QDateTimePattern::setCalendar(QCalendar calendar) noexcept
849
850 \include qttemporalpattern.cpp set-calendar
851*/
852
853/*!
854 \fn QDateTimePattern::calendar(QCalendar calendar) const noexcept
855
856 Returns the current calendar in use by this pattern.
857
858 \sa setCalendar()
859*/
860
861/*!
862 \fn QDateTimePattern::setBaseYear(int centuryStart) noexcept
863
864 \include qttemporalpattern.cpp set-base-year
865*/
866
867/*!
868 \fn QDateTimePattern::clearBaseYear() noexcept
869
870 \include qttemporalpattern.cpp clear-base-year
871*/
872
873/*!
874 \fn QDateTimePattern::baseYear() const noexcept
875
876 \include qttemporalpattern.cpp get-base-year
877*/
878
879/*!
880 Parse and return the datetime represented by \a text.
881
882//! [parser-defaults]
883 If \a defaults is provided and valid, any fields the format described by
884 this pattern does not provide will be copied from \a defaults to construct
885 the returned value.
886//! [parser-defaults]
887*/
888
889QtTemporalPattern::ParseResult<QDateTime>
890QDateTimePattern::parse(QStringView text, const QDateTime &defaults) const
891{
892#if QT_CONFIG(datetimeparser)
893 const auto seen = QtParseTemporal::prefix(text, m_fields, m_locale, m_calendar, m_baseYear);
894 if (!m_fields.isEmpty() && !seen) // Failed to parse.
895 return {};
896
897 const QDate date = seen.date(m_calendar, defaults.date());
898 const QTime time = seen.time(defaults.time());
899
900 const QDateTime::TransitionResolution res = [type = seen.timeType]() {
901 using Res = QDateTime::TransitionResolution;
902 switch (type) {
903 case QTimeZone::StandardTime: return Res::PreferStandard;
904 case QTimeZone::DaylightTime: return Res::PreferDaylightSaving;
905 case QTimeZone::GenericTime: return Res::LegacyBehavior;
906 }
907 Q_UNREACHABLE_RETURN(Res::LegacyBehavior);
908 }();
909
910 if (QDateTime result(date, time, seen.zone, res); result.isValid())
911 return {result, seen.size()};
912 // Fall back to default transition resolution:
913 return {QDateTime(date, time, seen.zone), seen.size()};
914#else
915 Q_UNUSED(text);
916 Q_UNUSED(defaults);
917 return {};
918#endif // datetimeparser
919}
920
921/*!
922 Serialize the given \a datetime to a string representation.
923*/
924
925QString QDateTimePattern::serialize(const QDateTime &datetime) const
926{
927 Q_UNUSED(datetime);
928 return {};
929}
930
931/*!
932 Construct a QDateTimePattern described by the given \a format string.
933
934 \sa forLocale()
935*/
936
937QDateTimePattern QDateTimePattern::fromQtFormat(QStringView format)
938{
939 using namespace QtTemporalPattern;
940 constexpr DateTimeParts form = DateTimePart::Date | DateTimePart::Time | DateTimePart::Zone;
941 const auto qt = QtParseQtTemporalFormat::prefix(format, form);
942 if (qt.size() == format.size())
943 return QDateTimePattern(qt.fields);
944 return QDateTimePattern({});
945}
946
947/*!
948 \internal
949 \since 6.12
950 \class QTimePattern
951 \brief A description of a serialization format for a time
952*/
953
954/*!
955 \fn QTimePattern::forLocale(const QLocale &locale, QLocale::FormatType format)
956 Construct a QTimePattern appropriate to the given \a locale.
957
958 The \a format can be used to select how compact or expansive the pattern is.
959
960 \sa fromQtFormat
961*/
962
963/*!
964 \fn QTimePattern::isValid() const noexcept
965
966 \include qttemporalpattern.cpp {is-valid} {time}
967*/
968
969/*!
970 \fn QTimePattern::setLocale(const QLocale &loc) noexcept
971
972 \include qttemporalpattern.cpp set-locale
973*/
974
975/*!
976 \fn QTimePattern::locale() const noexcep
977
978 Returns the current locale in use by this pattern.
979
980 \sa setLocale()
981*/
982
983/*!
984 Parse and return the time represented by \a text.
985
986 \include qttemporalpattern.cpp parser-defaults
987*/
988
989QtTemporalPattern::ParseResult<QTime>
990QTimePattern::parse(QStringView text, QTime defaults) const
991{
992#if QT_CONFIG(datetimeparser)
993 const auto seen = QtParseTemporal::prefix(text, m_fields, m_locale, QCalendar());
994 if (!m_fields.isEmpty() && !seen) // Failed to parse.
995 return {};
996 return {seen.time(defaults), seen.size()};
997#else
998 Q_UNUSED(text);
999 Q_UNUSED(defaults);
1000 return {};
1001#endif // datetimeparser
1002}
1003
1004/*!
1005 Serialize the given \a time to a string representation.
1006*/
1007
1008QString QTimePattern::serialize(const QTime &time) const
1009{
1010 Q_UNUSED(time);
1011 return {};
1012}
1013
1014/*!
1015 Construct a QTimePattern described by the given \a format string.
1016
1017 \sa forLocale()
1018*/
1019
1020QTimePattern QTimePattern::fromQtFormat(QStringView format)
1021{
1022 using namespace QtTemporalPattern;
1023 constexpr DateTimeParts form{DateTimePart::Time};
1024 const auto qt = QtParseQtTemporalFormat::prefix(format, form);
1025 if (qt.size() == format.size())
1026 return QTimePattern(qt.fields);
1027 return QTimePattern({});
1028}
1029
1030/*!
1031 \internal
1032 \since 6.12
1033 \class QDatePattern
1034 \brief A description of a serialization format for a date
1035*/
1036
1037/*!
1038 \fn QDatePattern::forLocale(const QLocale &locale, QLocale::FormatType format)
1039 Construct a QDatePattern appropriate to the given \a locale.
1040
1041 The \a format can be used to select how compact or expansive the pattern is.
1042
1043 \sa fromQtFormat
1044*/
1045
1046/*!
1047 \fn QDatePattern::isValid() const noexcept
1048
1049 \include qttemporalpattern.cpp {is-valid} {date}
1050
1051//! [base-year-disambiguates]
1052 The ambiguity of two-digit years may be resolved by configuring the hundred
1053 years among which to select a matching years.
1054//! [base-year-disambiguates]
1055
1056 \sa setBaseYear()
1057*/
1058
1059/*!
1060 \fn QDatePattern::setLocale(const QLocale &loc) noexcept
1061
1062 \include qttemporalpattern.cpp set-locale
1063*/
1064
1065/*!
1066 \fn QDatePattern::locale() const noexcep
1067
1068 Returns the current locale in use by this pattern.
1069
1070 \sa setLocale()
1071*/
1072
1073/*!
1074 \fn QDatePattern::setCalendar(QCalendar calendar) noexcept
1075
1076//! [set-calendar]
1077 Sets the calendar used by this pattern. This influences the names and
1078 lengths of months and how these vary from year to year. In some cases it may
1079 also influence the number of months in the year or even the pattern and
1080 names of days of the week.
1081
1082 By default the Gregorian calendar is used.
1083
1084 \sa getCalendar(), QCalendar
1085//! [set-calendar]
1086*/
1087
1088/*!
1089 \fn QDatePattern::calendar(QCalendar calendar) const noexcept
1090
1091 Returns the current calendar in use by this pattern.
1092
1093 \sa setCalendar()
1094*/
1095
1096/*!
1097 \fn QDatePattern::setBaseYear(int centuryStart) noexcept
1098
1099//! [set-base-year]
1100 Configures the handling of two-digit years, if any are present in the pattern.
1101
1102 If a two-digit year is present, the year ending in those two digits in the
1103 range from \a centuryStart to \c {centuryStart + 99} shall be its default
1104 interpretation. This may be amended if the month, day of the month and day
1105 of the week indicate some other nearby century.
1106
1107 \note the years here, including \a centuryStart, are expressed with respect
1108 to \c{calendar()}, whose year numbering need not match that of the Gregorian
1109 calendar.
1110
1111 Calling this method makes no difference unless the pattern does in fact use
1112 a two-digit year, nor does it affect serialization.
1113
1114 \sa clearBaseYear()
1115//! [set-base-year]
1116*/
1117
1118// TODO: should the default state be clear or some specific year ?
1119// That year could depend on the present, e.g. present minus 49 years.
1120
1121/*!
1122 \fn QDatePattern::clearBaseYear() noexcept
1123
1124//! [clear-base-year]
1125 Leaves the handling of two-digit years to other fields to disambiguate.
1126
1127 This is the default state, so has no effect unless \l setBaseYear() has
1128 previously been called.
1129
1130 If a two-digit year is present, this leaves unspecified which hundred years
1131 to presume it lies within. If a two-digit year is present and other fields
1132 of the pattern do not suffice to make clear which hundred years to select
1133 based on the given last two digits, the text produced when serializing with
1134 this format shall generally be ambiguous. While readers of the text may be
1135 able to disambiguate the year anyway, there is scope for misunderstanding if
1136 their heuristics for doing so do not match your expectation. On parsing, the
1137 pattern may deliver a result that is off by some whole number of centuries
1138 from what the parsed text's author intended.
1139
1140 \sa setBaseYear(), isValid()
1141//! [clear-base-year]
1142*/
1143
1144/*!
1145 \fn QDatePattern::baseYear() const noexcept
1146
1147//! [get-base-year]
1148
1149 Returns the current base year, with respect to \c{calendar()}, used by this
1150 pattern if it needs to resolve a two-digit year. The result is \c
1151 {std::nullopt} when no base year is set, which is the default state.
1152
1153 \sa setBaseYear()
1154//! [get-base-year]
1155*/
1156
1157/*!
1158 Parse and return the date represented by \a text.
1159
1160 \include qttemporalpattern.cpp parser-defaults
1161*/
1162
1163QtTemporalPattern::ParseResult<QDate>
1164QDatePattern::parse(QStringView text, QDate defaults) const
1165{
1166#if QT_CONFIG(datetimeparser)
1167 const auto seen = QtParseTemporal::prefix(text, m_fields, m_locale, m_calendar, m_baseYear);
1168 if (!m_fields.isEmpty() && !seen) // Failed to parse.
1169 return {};
1170 return {seen.date(m_calendar, defaults), seen.size()};
1171#else
1172 Q_UNUSED(text);
1173 Q_UNUSED(defaults);
1174 return {};
1175#endif // datetimeparser
1176}
1177
1178/*!
1179 Serialize the given \a date to a string representation.
1180
1181 \sa parse()
1182*/
1183
1184QString QDatePattern::serialize(const QDate &date) const
1185{
1186 Q_UNUSED(date);
1187 return {};
1188}
1189
1190/*!
1191 Construct a QDatePattern described by the given \a format string.
1192
1193 \sa forLocale()
1194*/
1195
1196QDatePattern QDatePattern::fromQtFormat(QStringView format)
1197{
1198 using namespace QtTemporalPattern;
1199 constexpr DateTimeParts form{DateTimePart::Date};
1200 const auto qt = QtParseQtTemporalFormat::prefix(format, form);
1201 if (qt.size() == format.size())
1202 return QDatePattern(qt.fields);
1203 return QDatePattern({});
1204}
1205
1206QT_END_NAMESPACE
Supporting types and functions for temporal patterns.
SupportType supports(DateTimeParts wanted, QSpan< const TemporalField > range, bool hasBaseYear) noexcept
#define CHECK(cvref)