~ubuntu-branches/debian/sid/kexi/sid

« back to all changes in this revision

Viewing changes to src/widget/utils/kexidatetimeformatter.cpp

  • Committer: Package Import Robot
  • Author(s): Pino Toscano
  • Date: 2017-06-24 20:10:10 UTC
  • Revision ID: package-import@ubuntu.com-20170624201010-5lrzd5r2vwthwifp
Tags: upstream-3.0.1.1
ImportĀ upstreamĀ versionĀ 3.0.1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE project
 
2
   Copyright (C) 2006-2016 Jarosław Staniek <staniek@kde.org>
 
3
 
 
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.
 
8
 
 
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.
 
13
 
 
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.
 
18
*/
 
19
 
 
20
#include "kexidatetimeformatter.h"
 
21
 
 
22
#include <KLocalizedString>
 
23
 
 
24
#include <QDebug>
 
25
#include <QLineEdit>
 
26
#include <QLocale>
 
27
 
 
28
namespace {
 
29
    const QString INPUT_MASK_BLANKS_FORMAT(QLatin1String(";_"));
 
30
 
 
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) {
 
34
        Q_ASSERT(str);
 
35
        if (str->contains(QLatin1String(from))) {
 
36
            str->replace(QLatin1String(from), QLatin1String(to));
 
37
            return true;
 
38
        }
 
39
        return false;
 
40
    }
 
41
}
 
42
 
 
43
class KexiDateFormatter::Private
 
44
{
 
45
public:
 
46
    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))
 
50
    {
 
51
        outputFormat = inputFormat;
 
52
        emptyFormat = inputFormat;
 
53
        inputMask = inputFormat;
 
54
        computeDaysFormatAndMask();
 
55
        computeMonthsFormatAndMask();
 
56
        computeYearsFormatAndMask();
 
57
        inputMask += INPUT_MASK_BLANKS_FORMAT;
 
58
    }
 
59
 
 
60
    //! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
 
61
    QString inputMask;
 
62
 
 
63
    //! Date format used by fromString() and stringToVariant()
 
64
    QString inputFormat;
 
65
 
 
66
    //! Date format used by toString()
 
67
    QString outputFormat;
 
68
 
 
69
    //! Date format used by isEmpty()
 
70
    QString emptyFormat;
 
71
 
 
72
private:
 
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"));
 
80
            return;
 
81
        }
 
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"));
 
87
            return;
 
88
        }
 
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"));
 
95
            return;
 
96
        }
 
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"));
 
101
            return;
 
102
        }
 
103
        qWarning() << "Not found 'days' part in format" << inputFormat;
 
104
    }
 
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"));
 
112
            return;
 
113
        }
 
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"));
 
119
            return;
 
120
        }
 
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"));
 
127
            return;
 
128
        }
 
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"));
 
133
            return;
 
134
        }
 
135
        qWarning() << "Not found 'months' part in format" << inputFormat;
 
136
    }
 
137
    void computeYearsFormatAndMask() {
 
138
        // - yyyy - The year as four digit number.
 
139
        if (tryReplace(&inputMask, "yyyy", "9999")) {
 
140
            emptyFormat.remove(QLatin1String("yyyy"));
 
141
            return;
 
142
        }
 
143
        // - yy - The year as four digit number.
 
144
        if (tryReplace(&inputMask, "yy", "99")) {
 
145
            emptyFormat.remove(QLatin1String("yy"));
 
146
            return;
 
147
        }
 
148
        qWarning() << "Not found 'years' part in format" << inputFormat;
 
149
    }
 
150
};
 
151
 
 
152
class KexiTimeFormatter::Private
 
153
{
 
154
public:
 
155
    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))
 
159
    {
 
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;
 
169
    }
 
170
 
 
171
    ~Private() {
 
172
    }
 
173
 
 
174
    //! Input mask generated using the formatter settings. Can be used in QLineEdit::setInputMask().
 
175
    QString inputMask;
 
176
 
 
177
    //! Time format used by fromString() and stringToVariant()
 
178
    QString inputFormat;
 
179
 
 
180
    //! Time format used by toString()
 
181
    QString outputFormat;
 
182
 
 
183
    //! Date format used by isEmpty()
 
184
    QString emptyFormat;
 
185
 
 
186
private:
 
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"));
 
194
            return;
 
195
        }
 
196
        // the same for 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"));
 
201
            return;
 
202
        }
 
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"));
 
207
            return;
 
208
        }
 
209
        // the same for H
 
210
        if (tryReplace(&inputMask, "H", "90")) {
 
211
            emptyFormat.remove(QLatin1String("H"));
 
212
            return;
 
213
        }
 
214
        qWarning() << "Not found 'hours' part in format" << inputFormat;
 
215
    }
 
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"));
 
222
            return;
 
223
        }
 
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"));
 
228
            return;
 
229
        }
 
230
        qWarning() << "Not found 'minutes' part in format" << inputFormat;
 
231
    }
 
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"));
 
239
            return;
 
240
        }
 
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"));
 
245
            return;
 
246
        }
 
247
        //qDebug() << "Not found 'seconds' part in format" << inputFormat;
 
248
    }
 
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"));
 
256
            return;
 
257
        }
 
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"));
 
262
            return;
 
263
        }
 
264
        //qDebug() << "Not found 'milliseconds' part in format" << inputFormat;
 
265
    }
 
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"));
 
272
            return;
 
273
        }
 
274
        // - ap - interpret as an AM/PM time. ap must be either "am" or "pm".
 
275
        //! @note see above
 
276
        if (tryReplace(&inputMask, "ap", "<AA!")) { // we're also converting to upper case
 
277
            emptyFormat.remove(QLatin1String("ap"));
 
278
            return;
 
279
        }
 
280
        //qDebug() << "Not found 'AM/PM' part in format" << inputFormat;
 
281
    }
 
282
};
 
283
 
 
284
KexiDateFormatter::KexiDateFormatter()
 
285
  : d(new Private)
 
286
{
 
287
}
 
288
 
 
289
KexiDateFormatter::~KexiDateFormatter()
 
290
{
 
291
    delete d;
 
292
}
 
293
 
 
294
QDate KexiDateFormatter::fromString(const QString& str) const
 
295
{
 
296
    return QDate::fromString(str, d->inputFormat);
 
297
}
 
298
 
 
299
QVariant KexiDateFormatter::stringToVariant(const QString& str) const
 
300
{
 
301
    const QDate date(fromString(str));
 
302
    return date.isValid() ? date : QVariant();
 
303
}
 
304
 
 
305
bool KexiDateFormatter::isEmpty(const QString& str) const
 
306
{
 
307
    const QString t(str.trimmed());
 
308
    return t.isEmpty() || t == d->emptyFormat;
 
309
}
 
310
 
 
311
QString KexiDateFormatter::inputMask() const
 
312
{
 
313
    return d->inputMask;
 
314
}
 
315
 
 
316
QString KexiDateFormatter::toString(const QDate& date) const
 
317
{
 
318
    return date.toString(d->outputFormat);
 
319
}
 
320
 
 
321
//------------------------------------------------
 
322
 
 
323
KexiTimeFormatter::KexiTimeFormatter()
 
324
        : d(new Private)
 
325
{
 
326
}
 
327
 
 
328
KexiTimeFormatter::~KexiTimeFormatter()
 
329
{
 
330
    delete d;
 
331
}
 
332
 
 
333
QTime KexiTimeFormatter::fromString(const QString& str) const
 
334
{
 
335
    return QTime::fromString(str, d->inputFormat);
 
336
}
 
337
 
 
338
QVariant KexiTimeFormatter::stringToVariant(const QString& str)
 
339
{
 
340
    const QTime result(fromString(str));
 
341
    return result.isValid() ? result : QVariant();
 
342
}
 
343
 
 
344
bool KexiTimeFormatter::isEmpty(const QString& str) const
 
345
{
 
346
    const QString t(str.trimmed());
 
347
    return t.isEmpty() || t == d->emptyFormat;
 
348
}
 
349
 
 
350
QString KexiTimeFormatter::toString(const QTime& time) const
 
351
{
 
352
    return time.toString(d->outputFormat);
 
353
}
 
354
 
 
355
QString KexiTimeFormatter::inputMask() const
 
356
{
 
357
    return d->inputMask;
 
358
}
 
359
 
 
360
//------------------------------------------------
 
361
 
 
362
QString KexiDateTimeFormatter::inputMask(const KexiDateFormatter& dateFormatter,
 
363
                                       const KexiTimeFormatter& timeFormatter)
 
364
{
 
365
    QString mask(dateFormatter.inputMask());
 
366
    mask.chop(INPUT_MASK_BLANKS_FORMAT.length());
 
367
    return mask + " " + timeFormatter.inputMask();
 
368
}
 
369
 
 
370
QDateTime KexiDateTimeFormatter::fromString(
 
371
    const KexiDateFormatter& dateFormatter,
 
372
    const KexiTimeFormatter& timeFormatter, const QString& str)
 
373
{
 
374
    QString s(str.trimmed());
 
375
    const int timepos = s.indexOf(' ');
 
376
    const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(s.mid(timepos + 1));
 
377
    if (emptyTime)
 
378
        s = s.left(timepos);
 
379
    if (timepos > 0 && !emptyTime) {
 
380
        return QDateTime(
 
381
                   dateFormatter.fromString(s.left(timepos)),
 
382
                   timeFormatter.fromString(s.mid(timepos + 1))
 
383
               );
 
384
    } else {
 
385
        return QDateTime(
 
386
                   dateFormatter.fromString(s),
 
387
                   QTime(0, 0, 0)
 
388
               );
 
389
    }
 
390
}
 
391
 
 
392
QString KexiDateTimeFormatter::toString(const KexiDateFormatter &dateFormatter,
 
393
                                        const KexiTimeFormatter &timeFormatter,
 
394
                                        const QDateTime &value)
 
395
{
 
396
    if (value.isValid())
 
397
        return dateFormatter.toString(value.date()) + ' '
 
398
               + timeFormatter.toString(value.time());
 
399
    return QString();
 
400
}
 
401
 
 
402
bool KexiDateTimeFormatter::isEmpty(const KexiDateFormatter& dateFormatter,
 
403
                                    const KexiTimeFormatter& timeFormatter,
 
404
                                    const QString& str)
 
405
{
 
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;
 
409
}
 
410
 
 
411
bool KexiDateTimeFormatter::isValid(const KexiDateFormatter& dateFormatter,
 
412
                                    const KexiTimeFormatter& timeFormatter, const QString& str)
 
413
{
 
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))
 
417
            && emptyTime)
 
418
    {
 
419
        //empty date/time is valid
 
420
        return true;
 
421
    }
 
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());
 
424
}