KDb

KDbDateTime.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2018 Jarosław Staniek <staniek@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 * Boston, MA 02110-1301, USA.
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 */
19
20#include "KDbDateTime.h"
21
22#include <QRegularExpression>
23
24const int UNCACHED_YEAR = -1;
25const int INVALID_YEAR = -2;
26
27namespace {
28template <typename T>
29std::function<QString(const T&)> byteArrayToString()
30{
31 return [](const T &v) { return QString::fromLatin1(v.toString()); };
32}
33
34struct KDbDateTimeMetatypeInitializer {
35 KDbDateTimeMetatypeInitializer()
36 {
37 using namespace std::placeholders;
38 QMetaType::registerConverter<KDbYear, QString>(byteArrayToString<KDbYear>());
40 QMetaType::registerConverter<KDbDate, QString>(byteArrayToString<KDbDate>());
42 QMetaType::registerConverter<KDbTime, QString>(byteArrayToString<KDbTime>());
44 QMetaType::registerConverter<KDbDateTime, QString>(byteArrayToString<KDbDateTime>());
46 QMetaType::registerComparators<KDbYear>();
47 QMetaType::registerComparators<KDbDate>();
48 QMetaType::registerComparators<KDbTime>();
49 QMetaType::registerComparators<KDbDateTime>();
50 }
51};
52
53KDbDateTimeMetatypeInitializer s_init;
54}
55
56bool KDbYear::operator==(const KDbYear &other) const
57{
58 return m_sign == other.sign() && m_string == other.yearString();
59}
60
61bool KDbYear::operator<(const KDbYear &other) const
62{
63 return toQDateValue() < other.toQDateValue();
64}
65
66bool KDbYear::isValid() const
67{
68 return std::get<1>(intValue());
69}
70
71bool KDbYear::isNull() const
72{
73 return m_sign == Sign::None && m_string.isEmpty();
74}
75
76QByteArray KDbYear::signString() const
77{
78 QByteArray result;
79 switch (m_sign) {
80 case Sign::Plus:
81 result = QByteArrayLiteral("+");
82 break;
83 case Sign::Minus:
84 result = QByteArrayLiteral("-");
85 break;
86 default:
87 break;
88 }
89 return result;
90}
91
92KDB_EXPORT QDebug operator<<(QDebug dbg, KDbYear::Sign sign)
93{
94 QDebugStateSaver saver(dbg);
95 switch (sign) {
96 case KDbYear::Sign::None:
97 break;
98 case KDbYear::Sign::Plus:
99 dbg.nospace() << '+';
100 break;
101 case KDbYear::Sign::Minus:
102 dbg.nospace() << '-';
103 break;
104 }
105 return dbg.maybeSpace();
106}
107
108KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbYear& year)
109{
110 QDebugStateSaver saver(dbg);
111 dbg.nospace().noquote() << "KDbYear(" << year.sign() << year.yearString();
112 if (!year.isValid()) {
113 dbg.nospace() << " INVALID";
114 }
115 dbg.nospace() << ")";
116 return dbg.maybeSpace();
117}
118
120{
121 QByteArray result;
122 if (isNull()) {
123 result = QByteArrayLiteral("<NULL_YEAR>");
124 } else { // can be invalid, that's OK
125 result = signString() + m_string;
126 }
127 return result;
128}
129
131{
132 return std::get<0>(intValue());
133}
134
136{
137 int result;
138 bool ok;
139 std::tie(result, ok) = intValue();
140 if (!ok) {
141 return 0;
142 }
143 if (result > 0) {
144 return result;
145 }
146 return result - 1;
147}
148
149namespace {
150int intValueInternal(KDbYear::Sign sign, const QByteArray &string)
151{
152 const int length = string.length();
153 if (length < 4) {
154 // TODO message: at least 4 digits required
155 return INVALID_YEAR;
156 } else if (length > 4) {
157 if (sign == KDbYear::Sign::None) {
158 // TODO message: more than 4 digits, sign required
159 return INVALID_YEAR;
160 }
161 }
162
163 static const QRegularExpression digitsRegExp(QStringLiteral("^\\d+$"));
164 if (!digitsRegExp.match(QString::fromLatin1(string)).hasMatch()) {
165 // TODO message: only digits are accepted for year
166 return INVALID_YEAR;
167 }
168
169 bool ok;
170 int result = string.toInt(&ok);
171 if (!ok || result < 0) {
172 // TODO message: failed to convert year to integer >= 0
173 return INVALID_YEAR;
174 }
175 int qDateYear;
176 if (result == 0) {
177 if (sign != KDbYear::Sign::Plus) {
178 // TODO message: + required for 0000
179 return INVALID_YEAR;
180 }
181 qDateYear = -1;
182 } else if (sign == KDbYear::Sign::Minus) {
183 qDateYear = - result - 1;
184 } else { // Plus or None
185 qDateYear = result;
186 }
187 // verify if this year is within the limits of QDate (see QDate::minJd(), QDate::maxJd())
188 if (!QDate(qDateYear, 1, 1).isValid()) {
189 // TODO message: year is not within limits
190 return INVALID_YEAR;
191 }
192 return result;
193}
194}
195
196std::tuple<int, bool> KDbYear::intValue() const
197{
198 if (m_isoValue == UNCACHED_YEAR) {
199 const_cast<int&>(m_isoValue) = intValueInternal(m_sign, m_string); // cache
200 }
201 if (m_isoValue == INVALID_YEAR) {
202 return std::make_tuple(0, false);
203 }
204 return std::make_tuple(m_sign == Sign::Minus ? -m_isoValue : m_isoValue, true);
205}
206
207bool KDbDate::operator==(const KDbDate &other) const
208{
209 return m_year == other.year() && m_monthString == other.monthString()
210 && m_dayString == other.dayString();
211}
212
213bool KDbDate::operator<(const KDbDate &other) const
214{
215 return toQDate() < other.toQDate();
216}
217
219{
220 return toQDate().isValid();
221}
222
223bool KDbDate::isNull() const
224{
225 return m_year.isNull() && m_monthString.isEmpty() && m_dayString.isEmpty();
226}
227
229{
230 return { m_year.toQDateValue(), month(), day() };
231}
232
233namespace {
234int toInt(const QByteArray &string, int min, int max, int minLength, int maxLength)
235{
236 if (string.length() < minLength || string.length() > maxLength) {
237 // TODO message: invalid length
238 return -1;
239 }
240 bool ok = true;
241 const int result = string.isEmpty() ? 0 : string.toInt(&ok);
242 if (!ok || result < min || result > max) {
243 // TODO message: could not convert string to integer
244 return -1;
245 }
246 return result;
247}
248}
249
250int KDbDate::month() const
251{
252 return toInt(m_monthString, 1, 12, 1, 2);
253}
254
255int KDbDate::day() const
256{
257 return toInt(m_dayString, 1, 31, 1, 2);
258}
259
261{
262 QByteArray result;
263 if (isNull()) {
264 result = QByteArrayLiteral("<NULL_DATE>");
265 } else { // can be invalid, that's OK
266 result = m_year.toString() + '-' + m_monthString + '-' + m_dayString;
267 }
268 return result;
269}
270
271KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbDate &date)
272{
273 QDebugStateSaver saver(dbg);
274 dbg.nospace().noquote() << "KDbDate(" << date.toString();
275 if (!date.isValid()) {
276 dbg.nospace() << " INVALID";
277 }
278 dbg.nospace() << ")";
279 return dbg.maybeSpace();
280}
281
282bool KDbTime::operator==(const KDbTime &other) const
283{
284 return m_hourString == other.hourString() && m_minuteString == other.minuteString()
285 && m_secondString == other.secondString() && m_msecString == other.msecString()
286 && m_period == other.period();
287}
288
289bool KDbTime::operator<(const KDbTime &other) const
290{
291 return toQTime() < other.toQTime();
292}
293
295{
296 // Rules for hours based on https://www.timeanddate.com/time/am-and-pm.html#converting
297 int h = hour();
298 if (h == -1) {
299 return {};
300 }
301 const int m = minute();
302 if (m == -1) {
303 return {};
304 }
305 const int s = second();
306 if (s == -1) {
307 return {};
308 }
309 const int ms = msec();
310 if (ms == -1) {
311 return {};
312 }
313 if (m_period == Period::None) {
314 return { h, m, s, ms };
315 }
316 return QTime::fromString(
317 QStringLiteral("%1:%2:%3.%4 %5")
318 .arg(h)
319 .arg(m)
320 .arg(s)
321 .arg(ms)
322 .arg(m_period == Period::Am ? QLatin1String("AM") : QLatin1String("PM")),
323 QStringLiteral("h:m:s.z AP"));
324}
325
326int KDbTime::hour() const
327{
328 switch (m_period) {
329 case Period::None:
330 return toInt(m_hourString, 0, 23, 1, 2);
331 case Period::Am:
332 case Period::Pm:
333 return toInt(m_hourString, 1, 12, 1, 2);
334 }
335 return -1;
336}
337
339{
340 return toInt(m_minuteString, 0, 59, 1, 2);
341}
342
344{
345 return toInt(m_secondString, 0, 59, 0, 2);
346}
347
348int KDbTime::msec() const
349{
350 return toInt(m_msecString, 0, 999, 0, 3);
351}
352
354{
355 return toQTime().isValid();
356}
357
358bool KDbTime::isNull() const
359{
360 return m_hourString.isEmpty() || m_minuteString.isEmpty();
361}
362
364{
365 QByteArray result;
366 if (isNull()) {
367 result = QByteArrayLiteral("<NULL_TIME>");
368 } else if (m_msecString.isEmpty()) { // can be invalid, that's OK
369 if (m_secondString.isEmpty()) {
370 result = m_hourString + ':' + m_minuteString;
371 } else {
372 result = m_hourString + ':' + m_minuteString + ':' + m_secondString;
373 }
374 } else { // can be invalid, that's OK
375 result = m_hourString + ':' + m_minuteString + ':' + m_secondString + '.' + m_msecString;
376 }
377 switch (m_period) {
379 result += " AM";
380 break;
382 result += " PM";
383 break;
384 default:
385 break;
386 }
387 return result;
388}
389
390KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbTime &time)
391{
392 QDebugStateSaver saver(dbg);
393 dbg.nospace().noquote() << "KDbTime(" << time.toString();
394 if (!time.isValid()) {
395 dbg.nospace() << " INVALID";
396 }
397 dbg.nospace() << ")";
398 return dbg.maybeSpace();
399}
400
401bool KDbDateTime::operator==(const KDbDateTime &other) const
402{
403 return date() == other.date() && time() == other.time();
404}
405
406bool KDbDateTime::operator<(const KDbDateTime &other) const
407{
408 return toQDateTime() < other.toQDateTime();
409}
410
412{
413 return m_date.isValid() && m_time.isValid();
414}
415
417{
418 return m_date.isNull() || m_time.isNull();
419}
420
422{
423 return { m_date.toQDate(), m_time.toQTime() };
424}
425
427{
428 QByteArray result;
429 if (isNull()) {
430 result = QByteArrayLiteral("<NULL_DATETIME>");
431 } else {
432 result = m_date.toString() + ' ' + m_time.toString(); // can be invalid, that's OK
433 }
434 return result;
435}
436
437KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbDateTime &dateTime)
438{
439 QDebugStateSaver saver(dbg);
440 dbg.nospace().noquote() << "KDbDateTime(" << dateTime.toString();
441 if (!dateTime.isValid()) {
442 dbg.nospace() << "INVALID";
443 }
444 dbg.nospace() << ")";
445 return dbg.maybeSpace();
446}
Generic date/time constant.
KDbDate date() const
Returns the date part of the date/time.
bool isNull() const
Returns true if the date/time is null.
QByteArray toString() const
Returns the date/time value converted to string even if it is invalid.
QDateTime toQDateTime() const
Returns the date/time converted to QDateTime value.
KDbTime time() const
Returns the time part of the date/time.
bool isValid() const
Returns true if the date/time is valid.
Generic date constant.
int month() const
Returns the month part of the date converted to integer.
KDbYear year() const
Returns the year part of the date.
bool isValid() const
Returns true if the date is valid.
int day() const
Returns the day part of the date converted to integer.
QByteArray dayString() const
Returns the day part of the date.
bool isNull() const
Returns true if the date is null.
QByteArray monthString() const
Returns the month part of the date.
QByteArray toString() const
Returns the date value converted to string even if it is invalid.
QDate toQDate() const
Returns the date converted to QDate value.
Generic time constant.
Period period() const
Specifies hour period.
QByteArray msecString() const
Returns the milliseconds part of the date.
QByteArray minuteString() const
Returns the minute part of the date.
QByteArray secondString() const
Returns the second part of the date.
int minute() const
Returns the minute part of the time converted to integer.
int hour() const
Returns the hour part of the time converted to integer.
@ Pm
PM, after noon, before midnight.
@ None
2-hour time
@ Am
AM, before noon.
QTime toQTime() const
Returns the time value converted to QTime type.
QByteArray toString() const
Returns the time value converted to string even if it is invalid.
int second() const
Returns the second part of the time converted to integer.
QByteArray hourString() const
Returns the hour part of the date.
bool isValid() const
Returns true if the time is valid.
bool isNull() const
Returns true if the time is null.
Generic year constant based on extended ISO 8601 specification.
Definition KDbDateTime.h:43
QByteArray toString() const
Returns entire year value (with sign) converted to string even if it is invalid.
Sign sign() const
Returns the sign which is used to annotate year.
Sign
Specifies sign which is used to annotate year.
Definition KDbDateTime.h:48
QByteArray yearString() const
Returns the string representation of year value even if it is invalid.
int toIsoValue() const
Returns the integer year value as defined by extended ISO 8601.
bool isValid() const
Returns true if the year is valid.
bool isNull() const
Returns true if the year is null.
int toQDateValue() const
Returns the integer year value as defined by the QDate API.
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
bool isValid(QStringView ifopt)
bool isEmpty() const const
int toInt(bool *ok, int base) const const
bool isValid(int year, int month, int day)
QDebug & maybeSpace()
QDebug & noquote()
QDebug & nospace()
bool registerConverter()
QString fromLatin1(QByteArrayView str)
QTime fromString(QStringView string, QStringView format)
bool isValid(int h, int m, int s, int ms)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:11:01 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.