KDb

KDbDateTime.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2018 JarosÅ‚aw Staniek <[email protected]>
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 
24 const int UNCACHED_YEAR = -1;
25 const int INVALID_YEAR = -2;
26 
27 namespace {
28 template <typename T>
29 std::function<QString(const T&)> byteArrayToString()
30 {
31  return [](const T &v) { return QString::fromLatin1(v.toString()); };
32 }
33 
34 struct KDbDateTimeMetatypeInitializer {
35  KDbDateTimeMetatypeInitializer()
36  {
37  using namespace std::placeholders;
38  QMetaType::registerConverter<KDbYear, QString>(byteArrayToString<KDbYear>());
39  QMetaType::registerConverter<KDbYear, int>(std::bind(&KDbYear::toIsoValue, _1));
40  QMetaType::registerConverter<KDbDate, QString>(byteArrayToString<KDbDate>());
41  QMetaType::registerConverter<KDbDate, QDate>(std::bind(&KDbDate::toQDate, _1));
42  QMetaType::registerConverter<KDbTime, QString>(byteArrayToString<KDbTime>());
43  QMetaType::registerConverter<KDbTime, QTime>(std::bind(&KDbTime::toQTime, _1));
44  QMetaType::registerConverter<KDbDateTime, QString>(byteArrayToString<KDbDateTime>());
45  QMetaType::registerConverter<KDbDateTime, QDateTime>(std::bind(&KDbDateTime::toQDateTime, _1));
46  QMetaType::registerComparators<KDbYear>();
47  QMetaType::registerComparators<KDbDate>();
48  QMetaType::registerComparators<KDbTime>();
49  QMetaType::registerComparators<KDbDateTime>();
50  }
51 };
52 
53 KDbDateTimeMetatypeInitializer s_init;
54 }
55 
56 bool KDbYear::operator==(const KDbYear &other) const
57 {
58  return m_sign == other.sign() && m_string == other.yearString();
59 }
60 
61 bool KDbYear::operator<(const KDbYear &other) const
62 {
63  return toQDateValue() < other.toQDateValue();
64 }
65 
66 bool KDbYear::isValid() const
67 {
68  return std::get<1>(intValue());
69 }
70 
71 bool KDbYear::isNull() const
72 {
73  return m_sign == Sign::None && m_string.isEmpty();
74 }
75 
76 QByteArray 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 
92 KDB_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 
108 KDB_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 
149 namespace {
150 int 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 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 
196 std::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 
207 bool KDbDate::operator==(const KDbDate &other) const
208 {
209  return m_year == other.year() && m_monthString == other.monthString()
210  && m_dayString == other.dayString();
211 }
212 
213 bool KDbDate::operator<(const KDbDate &other) const
214 {
215  return toQDate() < other.toQDate();
216 }
217 
218 bool KDbDate::isValid() const
219 {
220  return toQDate().isValid();
221 }
222 
223 bool 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 
233 namespace {
234 int 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 
250 int KDbDate::month() const
251 {
252  return toInt(m_monthString, 1, 12, 1, 2);
253 }
254 
255 int 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 
271 KDB_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 
282 bool 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 
289 bool 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 
326 int 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 
338 int KDbTime::minute() const
339 {
340  return toInt(m_minuteString, 0, 59, 1, 2);
341 }
342 
343 int KDbTime::second() const
344 {
345  return toInt(m_secondString, 0, 59, 0, 2);
346 }
347 
348 int KDbTime::msec() const
349 {
350  return toInt(m_msecString, 0, 999, 0, 3);
351 }
352 
353 bool KDbTime::isValid() const
354 {
355  return toQTime().isValid();
356 }
357 
358 bool 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) {
378  case KDbTime::Period::Am:
379  result += " AM";
380  break;
381  case KDbTime::Period::Pm:
382  result += " PM";
383  break;
384  default:
385  break;
386  }
387  return result;
388 }
389 
390 KDB_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 
401 bool KDbDateTime::operator==(const KDbDateTime &other) const
402 {
403  return date() == other.date() && time() == other.time();
404 }
405 
406 bool 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 
437 KDB_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 }
QByteArray monthString() const
Returns the month part of the date.
Definition: KDbDateTime.h:227
bool isNull() const
Returns true if the time is null.
bool isValid() const
Returns true if the year is valid.
Definition: KDbDateTime.cpp:66
QByteArray minuteString() const
Returns the minute part of the date.
Definition: KDbDateTime.h:355
Sign
Specifies sign which is used to annotate year.
Definition: KDbDateTime.h:48
KDbDate date() const
Returns the date part of the date/time.
Definition: KDbDateTime.h:460
QTime fromString(const QString &string, Qt::DateFormat format)
QDebug & nospace()
QByteArray yearString() const
Returns the string representation of year value even if it is invalid.
Definition: KDbDateTime.h:115
QDate toQDate() const
Returns the date converted to QDate value.
QByteArray msecString() const
Returns the milliseconds part of the date.
Definition: KDbDateTime.h:378
Period period() const
Specifies hour period.
Definition: KDbDateTime.h:383
QDataStream & operator<<(QDataStream &out, const KDateTime &dateTime)
int day() const
Returns the day part of the date converted to integer.
KDbTime time() const
Returns the time part of the date/time.
Definition: KDbDateTime.h:465
int second() const
Returns the second part of the time converted to integer.
bool isValid() const
Returns true if the time is valid.
QDebug & maybeSpace()
int minute() const
Returns the minute part of the time converted to integer.
QByteArray toString() const
Returns the date value converted to string even if it is invalid.
bool isNull() const
Returns true if the date is null.
QDebug & noquote()
bool isNull() const
Returns true if the date/time is null.
@ Am
AM, before noon.
bool isValid() const
Returns true if the date is valid.
QByteArray dayString() const
Returns the day part of the date.
Definition: KDbDateTime.h:242
int month() const
Returns the month part of the date converted to integer.
Sign sign() const
Returns the sign which is used to annotate year.
Definition: KDbDateTime.h:108
Generic time constant.
Definition: KDbDateTime.h:260
QDateTime toQDateTime() const
Returns the date/time converted to QDateTime value.
QByteArray toString() const
Returns entire year value (with sign) converted to string even if it is invalid.
QTime toQTime() const
Returns the time value converted to QTime type.
Generic date/time constant.
Definition: KDbDateTime.h:403
Generic year constant based on extended ISO 8601 specification.
Definition: KDbDateTime.h:42
int toInt(bool *ok, int base) const const
bool isNull() const
Returns true if the year is null.
Definition: KDbDateTime.cpp:71
QString fromLatin1(const char *str, int size)
bool isValid(QStringView ifopt)
@ Pm
PM, after noon, before midnight.
QByteArray secondString() const
Returns the second part of the date.
Definition: KDbDateTime.h:369
int toIsoValue() const
Returns the integer year value as defined by extended ISO 8601.
Generic date constant.
Definition: KDbDateTime.h:157
QByteArray toString() const
Returns the time value converted to string even if it is invalid.
KDbYear year() const
Returns the year part of the date.
Definition: KDbDateTime.h:213
bool isValid() const
Returns true if the date/time is valid.
int hour() const
Returns the hour part of the time converted to integer.
QByteArray toString() const
Returns the date/time value converted to string even if it is invalid.
QByteArray hourString() const
Returns the hour part of the date.
Definition: KDbDateTime.h:341
int toQDateValue() const
Returns the integer year value as defined by the QDate API.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.