1
/* This file is part of the KDE project
2
Copyright (C) 2006-2016 JarosÅaw Staniek <staniek@kde.org>
4
This program 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.
9
This program 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.
14
You should have received a copy of the GNU Library General Public License
15
along with this program; see the file COPYING. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
20
#include "kexidatetimeformatter.h"
22
#include <KLocalizedString>
29
const QString INPUT_MASK_BLANKS_FORMAT(QLatin1String(";_"));
31
//! Like replace(const QString &before, QLatin1String after) but also returns
32
//! @c true if replacement has been made.
33
bool tryReplace(QString *str, const char *from, const char *to) {
35
if (str->contains(QLatin1String(from))) {
36
str->replace(QLatin1String(from), QLatin1String(to));
43
class KexiDateFormatter::Private
47
// use "short date" format system settings
48
//! @todo allow to override the format using column property and/or global app settings
49
: inputFormat(QLocale().dateFormat(QLocale::ShortFormat))
51
outputFormat = inputFormat;
52
emptyFormat = inputFormat;
53
inputMask = inputFormat;
54
computeDaysFormatAndMask();
55
computeMonthsFormatAndMask();
56
computeYearsFormatAndMask();
57
inputMask += INPUT_MASK_BLANKS_FORMAT;
60
//! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
63
//! Date format used by fromString() and stringToVariant()
66
//! Date format used by toString()
69
//! Date format used by isEmpty()
73
void computeDaysFormatAndMask() {
74
// day (note, order or lookup is important):
75
// - dddd - named days not supported, fall back to "d":
76
if (tryReplace(&inputMask, "dddd", "90")) {
77
// also replace the input format
78
inputFormat.replace(QLatin1String("dddd"), QLatin1String("d"));
79
emptyFormat.remove(QLatin1String("dddd"));
82
// - ddd - named days not supported, fall back to "d":
83
if (tryReplace(&inputMask, "ddd", "90")) {
84
// also replace the input format
85
inputFormat.replace(QLatin1String("ddd"), QLatin1String("d"));
86
emptyFormat.remove(QLatin1String("ddd"));
89
// - dd - The day as a number with a leading zero (01 to 31)
90
// second character is optional, e.g. 1_ is OK
91
if (tryReplace(&inputMask, "dd", "90")) {
92
// also replace the input format
93
inputFormat.replace(QLatin1String("dd"), QLatin1String("d"));
94
emptyFormat.remove(QLatin1String("dd"));
97
// - d - The day as a number without a leading zero (1 to 31);
98
// second character is optional, e.g. 1_ is OK
99
if (tryReplace(&inputMask, "d", "90")) {
100
emptyFormat.remove(QLatin1String("d"));
103
qWarning() << "Not found 'days' part in format" << inputFormat;
105
void computeMonthsFormatAndMask() {
106
// month (note, order or lookup is important):
107
// - MMMM - named months not supported, fall back to "M"
108
if (tryReplace(&inputMask, "MMMM", "90")) {
109
// also replace the input format
110
inputFormat.replace(QLatin1String("MMMM"), QLatin1String("M"));
111
emptyFormat.remove(QLatin1String("MMMM"));
114
// - MMM - named months not supported, fall back to "M"
115
if (tryReplace(&inputMask, "MMM", "90")) {
116
// also replace the input format
117
inputFormat.replace(QLatin1String("MMM"), QLatin1String("M"));
118
emptyFormat.remove(QLatin1String("MMM"));
121
// - MM - The month as a number with a leading zero (01 to 12)
122
// second character is optional, e.g. 1_ is OK
123
if (tryReplace(&inputMask, "MM", "90")) {
124
// also replace the input format
125
inputFormat.replace(QLatin1String("MM"), QLatin1String("M"));
126
emptyFormat.remove(QLatin1String("MM"));
129
// - M - The month as a number without a leading zero (1 to 12);
130
// second character is optional, e.g. 1_ is OK
131
if (tryReplace(&inputMask, "M", "90")) {
132
emptyFormat.remove(QLatin1String("M"));
135
qWarning() << "Not found 'months' part in format" << inputFormat;
137
void computeYearsFormatAndMask() {
138
// - yyyy - The year as four digit number.
139
if (tryReplace(&inputMask, "yyyy", "9999")) {
140
emptyFormat.remove(QLatin1String("yyyy"));
143
// - yy - The year as four digit number.
144
if (tryReplace(&inputMask, "yy", "99")) {
145
emptyFormat.remove(QLatin1String("yy"));
148
qWarning() << "Not found 'years' part in format" << inputFormat;
152
class KexiTimeFormatter::Private
156
// use "short date" format system settings
157
//! @todo allow to override the format using column property and/or global app settings
158
: inputFormat(QLocale().timeFormat(QLocale::ShortFormat))
160
outputFormat = inputFormat;
161
emptyFormat = inputFormat;
162
inputMask = inputFormat;
163
computeHoursFormatAndMask();
164
computeMinutesFormatAndMask();
165
computeSecondsFormatAndMask();
166
computeMillisecondsFormatAndMask();
167
computeAmPmFormatAndMask();
168
inputMask += INPUT_MASK_BLANKS_FORMAT;
174
//! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
177
//! Time format used by fromString() and stringToVariant()
180
//! Time format used by toString()
181
QString outputFormat;
183
//! Date format used by isEmpty()
187
void computeHoursFormatAndMask() {
188
// - hh - the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display).
189
// second character is optional, e.g. 1_ is OK
190
if (tryReplace(&inputMask, "hh", "90")) {
191
// also replace the input format
192
inputFormat.replace(QLatin1String("hh"), QLatin1String("h"));
193
emptyFormat.remove(QLatin1String("hh"));
197
if (tryReplace(&inputMask, "HH", "90")) {
198
// also replace the input format
199
inputFormat.replace(QLatin1String("HH"), QLatin1String("h"));
200
emptyFormat.remove(QLatin1String("HH"));
203
// - h - the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display).
204
// second character is optional, e.g. 1_ is OK
205
if (tryReplace(&inputMask, "h", "90")) {
206
emptyFormat.remove(QLatin1String("h"));
210
if (tryReplace(&inputMask, "H", "90")) {
211
emptyFormat.remove(QLatin1String("H"));
214
qWarning() << "Not found 'hours' part in format" << inputFormat;
216
void computeMinutesFormatAndMask() {
217
// - mm - the minute with a leading zero (00 to 59).
218
if (tryReplace(&inputMask, "mm", "90")) {
219
// also replace the input format
220
inputFormat.replace(QLatin1String("mm"), QLatin1String("m"));
221
emptyFormat.remove(QLatin1String("mm"));
224
// - m - the minute without a leading zero (0 to 59).
225
// second character is optional, e.g. 1_ is OK
226
if (tryReplace(&inputMask, "m", "90")) {
227
emptyFormat.remove(QLatin1String("m"));
230
qWarning() << "Not found 'minutes' part in format" << inputFormat;
232
void computeSecondsFormatAndMask() {
233
// - ss - the second with a leading zero (00 to 59).
234
// second character is optional, e.g. 1_ is OK
235
if (tryReplace(&inputMask, "ss", "90")) {
236
// also replace the input format
237
inputFormat.replace(QLatin1String("ss"), QLatin1String("s"));
238
emptyFormat.remove(QLatin1String("ss"));
241
// - s - the second without a leading zero (0 to 59).
242
// second character is optional, e.g. 1_ is OK
243
if (tryReplace(&inputMask, "s", "90")) {
244
emptyFormat.remove(QLatin1String("s"));
247
//qDebug() << "Not found 'seconds' part in format" << inputFormat;
249
void computeMillisecondsFormatAndMask() {
250
// - zzz - the milliseconds with leading zeroes (000 to 999).
251
// last two characters are optional, e.g. 1_ is OK
252
if (tryReplace(&inputMask, "zzz", "900")) {
253
// also replace the input format
254
inputFormat.replace(QLatin1String("zzz"), QLatin1String("z"));
255
emptyFormat.remove(QLatin1String("zzz"));
258
// - m - the milliseconds without leading zeroes (0 to 999).
259
// last two characters are optional, e.g. 1_ is OK
260
if (tryReplace(&inputMask, "z", "900")) {
261
emptyFormat.remove(QLatin1String("z"));
264
//qDebug() << "Not found 'milliseconds' part in format" << inputFormat;
266
void computeAmPmFormatAndMask() {
267
// - AP - interpret as an AM/PM time. AP must be either "AM" or "PM".
268
//! @note not a 100% accurate approach, we're assuming that "AP" substring is only
269
//! used to indicate AM/PM
270
if (tryReplace(&inputMask, "AP", ">AA!")) { // we're also converting to upper case
271
emptyFormat.remove(QLatin1String("AP"));
274
// - ap - interpret as an AM/PM time. ap must be either "am" or "pm".
276
if (tryReplace(&inputMask, "ap", "<AA!")) { // we're also converting to upper case
277
emptyFormat.remove(QLatin1String("ap"));
280
//qDebug() << "Not found 'AM/PM' part in format" << inputFormat;
284
KexiDateFormatter::KexiDateFormatter()
289
KexiDateFormatter::~KexiDateFormatter()
294
QDate KexiDateFormatter::fromString(const QString& str) const
296
return QDate::fromString(str, d->inputFormat);
299
QVariant KexiDateFormatter::stringToVariant(const QString& str) const
301
const QDate date(fromString(str));
302
return date.isValid() ? date : QVariant();
305
bool KexiDateFormatter::isEmpty(const QString& str) const
307
const QString t(str.trimmed());
308
return t.isEmpty() || t == d->emptyFormat;
311
QString KexiDateFormatter::inputMask() const
316
QString KexiDateFormatter::toString(const QDate& date) const
318
return date.toString(d->outputFormat);
321
//------------------------------------------------
323
KexiTimeFormatter::KexiTimeFormatter()
328
KexiTimeFormatter::~KexiTimeFormatter()
333
QTime KexiTimeFormatter::fromString(const QString& str) const
335
return QTime::fromString(str, d->inputFormat);
338
QVariant KexiTimeFormatter::stringToVariant(const QString& str)
340
const QTime result(fromString(str));
341
return result.isValid() ? result : QVariant();
344
bool KexiTimeFormatter::isEmpty(const QString& str) const
346
const QString t(str.trimmed());
347
return t.isEmpty() || t == d->emptyFormat;
350
QString KexiTimeFormatter::toString(const QTime& time) const
352
return time.toString(d->outputFormat);
355
QString KexiTimeFormatter::inputMask() const
360
//------------------------------------------------
362
QString KexiDateTimeFormatter::inputMask(const KexiDateFormatter& dateFormatter,
363
const KexiTimeFormatter& timeFormatter)
365
QString mask(dateFormatter.inputMask());
366
mask.chop(INPUT_MASK_BLANKS_FORMAT.length());
367
return mask + " " + timeFormatter.inputMask();
370
QDateTime KexiDateTimeFormatter::fromString(
371
const KexiDateFormatter& dateFormatter,
372
const KexiTimeFormatter& timeFormatter, const QString& str)
374
QString s(str.trimmed());
375
const int timepos = s.indexOf(' ');
376
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(s.mid(timepos + 1));
379
if (timepos > 0 && !emptyTime) {
381
dateFormatter.fromString(s.left(timepos)),
382
timeFormatter.fromString(s.mid(timepos + 1))
386
dateFormatter.fromString(s),
392
QString KexiDateTimeFormatter::toString(const KexiDateFormatter &dateFormatter,
393
const KexiTimeFormatter &timeFormatter,
394
const QDateTime &value)
397
return dateFormatter.toString(value.date()) + ' '
398
+ timeFormatter.toString(value.time());
402
bool KexiDateTimeFormatter::isEmpty(const KexiDateFormatter& dateFormatter,
403
const KexiTimeFormatter& timeFormatter,
406
int timepos = str.indexOf(' ');
407
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos + 1));
408
return timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) && emptyTime;
411
bool KexiDateTimeFormatter::isValid(const KexiDateFormatter& dateFormatter,
412
const KexiTimeFormatter& timeFormatter, const QString& str)
414
int timepos = str.indexOf(' ');
415
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos + 1));
416
if (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos))
419
//empty date/time is valid
422
return timepos >= 0 && dateFormatter.fromString(str.left(timepos)).isValid()
423
&& (emptyTime /*date without time is also valid*/ || timeFormatter.fromString(str.mid(timepos + 1)).isValid());