1// Copyright (C) 2021 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
4#ifndef QDATETIMEPARSER_P_H
5#define QDATETIMEPARSER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/private/qglobal_p.h>
19#include "qplatformdefs.h"
20#include "QtCore/qatomic.h"
21#include "QtCore/qcalendar.h"
22#include "QtCore/qcoreapplication.h"
23#include "QtCore/qdatetime.h"
24#include "QtCore/qlist.h"
25#include "QtCore/qlocale.h"
26#include "QtCore/qstringlist.h"
27#ifndef QT_BOOTSTRAPPED
28# include "QtCore/qvariant.h"
29#endif
30
31QT_REQUIRE_CONFIG(datetimeparser);
32
33#define QDATETIMEEDIT_TIME_MIN QTime(0, 0) // Prefer QDate::startOfDay()
34#define QDATETIMEEDIT_TIME_MAX QTime(23, 59, 59, 999) // Prefer QDate::endOfDay()
35#define QDATETIMEEDIT_DATE_MIN QDate(100, 1, 1)
36#define QDATETIMEEDIT_COMPAT_DATE_MIN QDate(1752, 9, 14)
37#define QDATETIMEEDIT_DATE_MAX QDate(9999, 12, 31)
38#define QDATETIMEEDIT_DATE_INITIAL QDate(2000, 1, 1)
39
40QT_BEGIN_NAMESPACE
41
42class Q_CORE_EXPORT QDateTimeParser
43{
44public:
45 enum Context {
46 FromString,
47 DateTimeEdit
48 };
49 QDateTimeParser(QMetaType::Type t, Context ctx, const QCalendar &cal = QCalendar())
50 : parserType(t), context(ctx), calendar(cal)
51 {
52 defaultLocale = QLocale::system();
53 first.type = FirstSection;
54 first.pos = -1;
55 first.count = -1;
56 first.zeroesAdded = 0;
57 last.type = LastSection;
58 last.pos = -1;
59 last.count = -1;
60 last.zeroesAdded = 0;
61 none.type = NoSection;
62 none.pos = -1;
63 none.count = -1;
64 none.zeroesAdded = 0;
65 }
66 virtual ~QDateTimeParser();
67
68 enum Section {
69 NoSection = 0x00000,
70 AmPmSection = 0x00001,
71 MSecSection = 0x00002,
72 SecondSection = 0x00004,
73 MinuteSection = 0x00008,
74 Hour12Section = 0x00010,
75 Hour24Section = 0x00020,
76 TimeZoneSection = 0x00040,
77 HourSectionMask = (Hour12Section | Hour24Section),
78 TimeSectionMask = (MSecSection | SecondSection | MinuteSection |
79 HourSectionMask | AmPmSection | TimeZoneSection),
80
81 DaySection = 0x00100,
82 MonthSection = 0x00200,
83 YearSection = 0x00400,
84 YearSection2Digits = 0x00800,
85 YearSectionMask = YearSection | YearSection2Digits,
86 DayOfWeekSectionShort = 0x01000,
87 DayOfWeekSectionLong = 0x02000,
88 DayOfWeekSectionMask = DayOfWeekSectionShort | DayOfWeekSectionLong,
89 DaySectionMask = DaySection | DayOfWeekSectionMask,
90 DateSectionMask = DaySectionMask | MonthSection | YearSectionMask,
91
92 Internal = 0x10000,
93 FirstSection = 0x20000 | Internal,
94 LastSection = 0x40000 | Internal,
95 CalendarPopupSection = 0x80000 | Internal,
96
97 NoSectionIndex = -1,
98 FirstSectionIndex = -2,
99 LastSectionIndex = -3,
100 CalendarPopupIndex = -4
101 }; // extending qdatetimeedit.h's equivalent
102 Q_DECLARE_FLAGS(Sections, Section)
103
104 struct Q_CORE_EXPORT SectionNode {
105 Section type;
106 mutable int pos;
107 int count; // (used as Case(count) indicator for AmPmSection)
108 int zeroesAdded;
109
110 static QString name(Section s);
111 QString name() const { return name(s: type); }
112 QString format() const;
113 int maxChange() const;
114 };
115
116 enum State { // duplicated from QValidator
117 Invalid,
118 Intermediate,
119 Acceptable
120 };
121
122 struct StateNode {
123 StateNode() : state(Invalid), padded(0), conflicts(false) {}
124 StateNode(const QDateTime &val, State ok=Acceptable, int pad=0, bool bad=false)
125 : value(val), state(ok), padded(pad), conflicts(bad) {}
126 QDateTime value;
127 State state;
128 int padded;
129 bool conflicts;
130 };
131
132 enum AmPm {
133 AmText,
134 PmText
135 };
136
137 StateNode parse(const QString &input, int position,
138 const QDateTime &defaultValue, bool fixup) const;
139 bool fromString(const QString &text, QDate *date, QTime *time) const;
140 bool fromString(const QString &text, QDateTime* datetime) const;
141 bool parseFormat(QStringView format);
142
143 enum FieldInfoFlag {
144 Numeric = 0x01,
145 FixedWidth = 0x02,
146 AllowPartial = 0x04,
147 Fraction = 0x08
148 };
149 Q_DECLARE_FLAGS(FieldInfo, FieldInfoFlag)
150
151 FieldInfo fieldInfo(int index) const;
152
153 void setDefaultLocale(const QLocale &loc) { defaultLocale = loc; }
154 virtual QString displayText() const { return m_text; }
155 void setCalendar(const QCalendar &calendar);
156
157private:
158 int sectionMaxSize(Section s, int count) const;
159 QString sectionText(const QString &text, int sectionIndex, int index) const;
160 StateNode scanString(const QDateTime &defaultValue, bool fixup) const;
161 struct ParsedSection {
162 int value;
163 int used;
164 int zeroes;
165 State state;
166 constexpr ParsedSection(State ok = Invalid,
167 int val = 0, int read = 0, int zs = 0)
168 : value(ok == Invalid ? -1 : val), used(read), zeroes(zs), state(ok)
169 {}
170 };
171 ParsedSection parseSection(const QDateTime &currentValue, int sectionIndex, int offset) const;
172 int findMonth(const QString &str1, int monthstart, int sectionIndex,
173 int year, QString *monthName = nullptr, int *used = nullptr) const;
174 int findDay(const QString &str1, int intDaystart, int sectionIndex,
175 QString *dayName = nullptr, int *used = nullptr) const;
176 ParsedSection findUtcOffset(QStringView str, int mode) const;
177 ParsedSection findTimeZoneName(QStringView str, const QDateTime &when) const;
178 ParsedSection findTimeZone(QStringView str, const QDateTime &when,
179 int maxVal, int minVal, int mode) const;
180
181 enum AmPmFinder {
182 Neither = -1,
183 AM = 0,
184 PM = 1,
185 PossibleAM = 2,
186 PossiblePM = 3,
187 PossibleBoth = 4
188 };
189 AmPmFinder findAmPm(QString &str, int index, int *used = nullptr) const;
190
191 bool potentialValue(QStringView str, int min, int max, int index,
192 const QDateTime &currentValue, int insert) const;
193 bool potentialValue(const QString &str, int min, int max, int index,
194 const QDateTime &currentValue, int insert) const
195 {
196 return potentialValue(str: QStringView(str), min, max, index, currentValue, insert);
197 }
198
199 enum Case {
200 NativeCase,
201 LowerCase,
202 UpperCase
203 };
204
205 QString getAmPmText(AmPm ap, Case cs) const;
206
207 friend class QDTPUnitTestParser;
208
209protected: // for the benefit of QDateTimeEditPrivate
210 int sectionSize(int index) const;
211 int sectionMaxSize(int index) const;
212 int sectionPos(int index) const;
213 int sectionPos(const SectionNode &sn) const;
214
215 const SectionNode &sectionNode(int index) const;
216 Section sectionType(int index) const;
217 QString sectionText(int sectionIndex) const;
218 int getDigit(const QDateTime &dt, int index) const;
219 bool setDigit(QDateTime &t, int index, int newval) const;
220
221 int absoluteMax(int index, const QDateTime &value = QDateTime()) const;
222 int absoluteMin(int index) const;
223
224 bool skipToNextSection(int section, const QDateTime &current, QStringView sectionText) const;
225 bool skipToNextSection(int section, const QDateTime &current, const QString &sectionText) const
226 {
227 return skipToNextSection(section, current, sectionText: QStringView(sectionText));
228 }
229 QString stateName(State s) const;
230
231 virtual QDateTime getMinimum() const;
232 virtual QDateTime getMaximum() const;
233 virtual int cursorPosition() const { return -1; }
234 virtual QLocale locale() const { return defaultLocale; }
235
236 mutable int currentSectionIndex = int(NoSectionIndex);
237 Sections display;
238 /*
239 This stores the most recently selected day.
240 It is useful when considering the following scenario:
241
242 1. Date is: 31/01/2000
243 2. User increments month: 29/02/2000
244 3. User increments month: 31/03/2000
245
246 At step 1, cachedDay stores 31. At step 2, the 31 is invalid for February, so the cachedDay is not updated.
247 At step 3, the month is changed to March, for which 31 is a valid day. Since 29 < 31, the day is set to cachedDay.
248 This is good for when users have selected their desired day and are scrolling up or down in the month or year section
249 and do not want smaller months (or non-leap years) to alter the day that they chose.
250 */
251 mutable int cachedDay = -1;
252 mutable QString m_text;
253 QList<SectionNode> sectionNodes;
254 SectionNode first, last, none, popup;
255 QStringList separators;
256 QString displayFormat;
257 QLocale defaultLocale;
258 QMetaType::Type parserType;
259 bool fixday = false;
260 Context context;
261 QCalendar calendar;
262};
263Q_DECLARE_TYPEINFO(QDateTimeParser::SectionNode, Q_PRIMITIVE_TYPE);
264
265Q_CORE_EXPORT bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2);
266
267Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::Sections)
268Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::FieldInfo)
269
270QT_END_NAMESPACE
271
272#endif // QDATETIME_P_H
273

source code of qtbase/src/corelib/time/qdatetimeparser_p.h