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