20 using namespace QtTemporalPattern;
22 ParsedDateTimeFormat result;
23 constexpr char32_t Invalid = ~
char32_t(0);
24 static_assert(Invalid > QChar::LastValidCodePoint);
25 const bool includeDate = form.testFlag(DateTimePart::Date);
26 const bool includeTime = form.testFlag(DateTimePart::Time);
27 const bool includeZone = form.testFlag(DateTimePart::Zone);
29 QStringIterator iter(pattern);
31 const auto countRepeats = [&pending, &iter, &result](
char32_t first, qsizetype bound) {
33 Q_ASSERT(!QChar::requiresSurrogates(first));
34 Q_ASSERT(pending == 0);
36 result.endIndex = iter.index();
37 while (iter.hasNext() && count < bound) {
38 const auto read = iter.next(Invalid);
39 if (read > QChar::LastValidCodePoint) {
48 result.endIndex = iter.index();
53 constexpr char32_t SingleQuote = U'\'';
54 static constexpr auto matchTimeFormats = QtPrivate::makeCharacterSetMatch<timeFormats>();
55 static constexpr auto matchDateFormats = QtPrivate::makeCharacterSetMatch<dateFormats>();
56 const auto isFormatChar = [includeDate, includeTime, includeZone](
char32_t ch) {
60 if (matchTimeFormats.matches(uchar(ch)) || ch == U'A' || ch == U'a')
63 if (includeZone && ch == U't')
65 return includeDate && matchDateFormats.matches(uchar(ch));
68 constexpr auto formatCategory = [](uchar ch,
int count) {
69 using Cat = TemporalFieldCategory;
73 return Cat::PeriodInDay;
74 case 'd':
return count < 3 ? Cat::DayOfMonth : Cat::DayOfWeek;
75 case 'H':
return Cat::Hour;
76 case 'h':
return Cat::HourMod12;
77 case 'M':
return Cat::Month;
78 case 'm':
return Cat::Minute;
79 case 's':
return Cat::Second;
80 case 't':
return Cat::TimeZone;
81 case 'y':
return count < 3 ? Cat::YearWithinCentury : Cat::Year;
82 case 'z':
return Cat::SecondFraction;
85 Q_UNREACHABLE_RETURN(Cat::Literal);
87 constexpr auto formatFlags = [](uchar ch,
int count) -> TemporalFieldFlags {
88 using F = TemporalFieldFlag;
89 constexpr TemporalFieldFlags TextCommon = F::IgnoreCase | F::FlexSpace;
92 return count ? F::UpperCase | TextCommon : TextCommon;
94 return count ? F::LowerCase | TextCommon : TextCommon;
97 case 2:
return F::Numeric | F::ZeroPad;
98 case 3:
return F::Verbal | F::Abbreviated | TextCommon;
100 return count < 2 ? F::Numeric : F::Verbal | F::Wide | TextCommon;
104 case 'H':
case 'h':
case 'm':
case 's':
105 return count > 1 ? F::Numeric | F::ZeroPad : F::Numeric;
109 return F::AllowZSuffix | F::LocalTimeName;
115 return F::Iso8601 | F::Numeric | F::ZeroPad;
117 return F::Iso8601 | F::Verbal | F::ZeroPad;
119 return F::LocalizedZone | F::Verbal | F::Standalone | F::Wide | F::Short;
126 return F::Numeric | F::ZeroPad | F::YearSignIso8601;
127 return F::Numeric | F::ZeroPad;
130 return F::Numeric | F::ZeroPad;
134 Q_UNREACHABLE_RETURN({});
137 const auto store = [&result](QString &&literal, qsizetype count,
138 TemporalFieldFlags flags,
139 TemporalFieldCategory category) {
140 result.fields.append(TemporalField{std::move(literal), count, flags, category});
143 bool seenDayPeriod =
false, seenHourMod12 =
false;
144 while (pending <= QChar::LastValidCodePoint && (pending || iter.hasNext())) {
147 ch = std::exchange(pending, 0);
149 result.endIndex = iter.index();
150 ch = iter.next(Invalid);
152 if (ch > QChar::LastValidCodePoint)
155 if (ch < 0x80 && includeTime) {
156 if (matchTimeFormats.matches(uchar(ch))) {
157 qsizetype count = countRepeats(ch, ch == U'z' ? 3 : 2);
158 if (ch == U'z' && count == 2)
160 store(QString(), count, formatFlags(uchar(ch), count),
161 formatCategory(uchar(ch), count));
163 seenHourMod12 =
true;
166 if (ch == U'A' || ch == U'a') {
168 qsizetype count = ch == U'a' ? 1 : 2;
171 result.endIndex = iter.index();
172 const auto read = iter.hasNext() ? iter.next(Invalid) : Invalid;
173 if (read > QChar::LastValidCodePoint) {
175 }
else if (read == U'P') {
178 result.endIndex = iter.index();
179 }
else if (read == U'p') {
182 result.endIndex = iter.index();
186 store(QString(), count, formatFlags(uchar(ch), count),
187 formatCategory(uchar(ch), count));
188 seenDayPeriod =
true;
193 if (ch == U'y' && includeDate) {
203 QStringView tail = pattern.sliced(iter.index() - 1);
205 tail = tail.first(4);
206 while (count < tail.size() &&
char32_t(tail[count].unicode()) == ch)
211 Q_ASSERT(count == 2 || count == 4);
213 iter.setPosition(iter.position() - 1 + count);
214 store(QString(), count, formatFlags(uchar(ch), count),
215 formatCategory(uchar(ch), count));
216 result.endIndex = iter.index();
220 }
else if (ch < 0x80 && ((includeDate && matchDateFormats.matches(uchar(ch)))
221 || (includeZone && ch == U't'))) {
222 qsizetype count = countRepeats(ch, 4);
223 store(QString(), count, formatFlags(uchar(ch), count),
224 formatCategory(uchar(ch), count));
227 Q_ASSERT(pending == 0);
232 if (ch == SingleQuote) {
235 literal = QString(QStringView(QChar::fromUcs4(ch)));
236 result.endIndex = iter.index();
238 while (pending <= QChar::LastValidCodePoint && (pending || iter.hasNext())) {
240 ch = std::exchange(pending, 0);
243 result.endIndex = iter.index();
244 ch = iter.next(Invalid);
246 if (ch > QChar::LastValidCodePoint)
249 if (ch == SingleQuote) {
250 if (quote.isNull()) {
257 result.endIndex = iter.index();
260 ch = iter.hasNext() ? iter.next(Invalid) : Invalid;
261 if (ch == SingleQuote) {
263 if (quote.isNull()) {
270 Q_ASSERT(quote.isEmpty());
272 literal.append(u'\'');
273 result.endIndex = iter.index();
278 if (ch > QChar::LastValidCodePoint) {
284 Q_ASSERT(ch != SingleQuote);
285 if (quote.isNull() && isFormatChar(ch)) {
289 if (ch <= QChar::LastValidCodePoint) {
290 if (quote.isNull()) {
291 result.endIndex = iter.index();
292 literal.append(QStringView(QChar::fromUcs4(ch)));
294 quote.append(QStringView(QChar::fromUcs4(ch)));
300 if (!literal.isEmpty()) {
301 store(std::move(literal), 0,
302 TemporalFieldFlag::FlexSpace, TemporalFieldCategory::Literal);
305 Q_ASSERT(quote.isNull() || !pending || pending > QChar::LastValidCodePoint);
310 if (seenHourMod12 && !seenDayPeriod) {
311 for (TemporalField &field : result.fields) {
312 if (field.category == TemporalFieldCategory::HourMod12)
313 field.category = TemporalFieldCategory::Hour;