QDateTime - Clean up Qt::DateFormat formatting and parsing
Clean-up the implementation of toString() and fromString() methods in QDate, QTime and QDateTime code to be more consistent in ISODate and TextDate behavior, especially when handling TimeSpec. Reformat some code so all methods are consistent in appearance and function to make maintenance easier. This changes some corner-case behavior in TextDate and ISODate, but this either fixes bugs or makes the behavior match the documentation. Change-Id: I457aa1d7cd4f448cd9f8a2e80ec635f3cb98e58c Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Mitch Curtis <mitch.curtis@digia.com>
This commit is contained in:
parent
8b02a62685
commit
61b56a89a1
@ -872,45 +872,42 @@ QString QDate::longDayName(int weekday, MonthNameType type)
|
||||
|
||||
\sa shortDayName(), shortMonthName()
|
||||
*/
|
||||
QString QDate::toString(Qt::DateFormat f) const
|
||||
QString QDate::toString(Qt::DateFormat format) const
|
||||
{
|
||||
if (!isValid())
|
||||
return QString();
|
||||
|
||||
int y, m, d;
|
||||
getDateFromJulianDay(jd, &y, &m, &d);
|
||||
switch (f) {
|
||||
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return QLocale::system().toString(*this, f == Qt::SystemLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat);
|
||||
return QLocale::system().toString(*this, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return QLocale().toString(*this, f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat);
|
||||
return QLocale().toString(*this, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date:
|
||||
return toString(QStringLiteral("dd MMM yyyy"));
|
||||
default:
|
||||
#ifndef QT_NO_TEXTDATE
|
||||
case Qt::TextDate:
|
||||
{
|
||||
return QString::fromLatin1("%0 %1 %2 %3")
|
||||
.arg(shortDayName(dayOfWeek()))
|
||||
.arg(shortMonthName(m))
|
||||
.arg(d)
|
||||
.arg(y);
|
||||
}
|
||||
getDateFromJulianDay(jd, &y, &m, &d);
|
||||
return QString::fromUtf8("%1 %2 %3 %4").arg(shortDayName(dayOfWeek()))
|
||||
.arg(shortMonthName(m))
|
||||
.arg(d)
|
||||
.arg(y);
|
||||
#endif
|
||||
case Qt::RFC2822Date:
|
||||
return toString(QStringLiteral("dd MMM yyyy"));
|
||||
case Qt::ISODate:
|
||||
{
|
||||
if (year() < 0 || year() > 9999)
|
||||
return QString();
|
||||
QString year(QString::number(y).rightJustified(4, QLatin1Char('0')));
|
||||
QString month(QString::number(m).rightJustified(2, QLatin1Char('0')));
|
||||
QString day(QString::number(d).rightJustified(2, QLatin1Char('0')));
|
||||
return year + QLatin1Char('-') + month + QLatin1Char('-') + day;
|
||||
}
|
||||
getDateFromJulianDay(jd, &y, &m, &d);
|
||||
if (y < 0 || y > 9999)
|
||||
return QString();
|
||||
return QString::fromUtf8("%1-%2-%3").arg(y, 4, 10, QLatin1Char('0'))
|
||||
.arg(m, 2, 10, QLatin1Char('0'))
|
||||
.arg(d, 2, 10, QLatin1Char('0'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1224,44 +1221,34 @@ qint64 QDate::daysTo(const QDate &d) const
|
||||
English short month names (e.g. "Jan"). Although localized month
|
||||
names can also be used, they depend on the user's locale settings.
|
||||
*/
|
||||
QDate QDate::fromString(const QString& s, Qt::DateFormat f)
|
||||
QDate QDate::fromString(const QString& string, Qt::DateFormat format)
|
||||
{
|
||||
if (s.isEmpty())
|
||||
if (string.isEmpty())
|
||||
return QDate();
|
||||
|
||||
switch (f) {
|
||||
case Qt::ISODate:
|
||||
{
|
||||
int year(s.mid(0, 4).toInt());
|
||||
int month(s.mid(5, 2).toInt());
|
||||
int day(s.mid(8, 2).toInt());
|
||||
if (year && month && day)
|
||||
return QDate(year, month, day);
|
||||
}
|
||||
break;
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toDate(string, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return fromString(s, QLocale::system().dateFormat(f == Qt::SystemLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat));
|
||||
return QLocale::system().toDate(string, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toDate(string, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return fromString(s, QLocale().dateFormat(f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat));
|
||||
return QLocale().toDate(string, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date: {
|
||||
QDate date;
|
||||
rfcDateImpl(s, &date);
|
||||
rfcDateImpl(string, &date);
|
||||
return date;
|
||||
}
|
||||
default:
|
||||
#ifndef QT_NO_TEXTDATE
|
||||
case Qt::TextDate: {
|
||||
QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
QStringList parts = string.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
|
||||
if (parts.count() != 4) {
|
||||
if (parts.count() != 4)
|
||||
return QDate();
|
||||
}
|
||||
|
||||
QString monthName = parts.at(1);
|
||||
int month = -1;
|
||||
@ -1280,28 +1267,25 @@ QDate QDate::fromString(const QString& s, Qt::DateFormat f)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (month == -1) {
|
||||
if (month == -1)
|
||||
// Month name matches neither English nor other localised name.
|
||||
return QDate();
|
||||
}
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int day = parts.at(2).toInt(&ok);
|
||||
if (!ok) {
|
||||
return QDate();
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
int year = parts.at(3).toInt(&ok);
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
return QDate();
|
||||
}
|
||||
|
||||
return QDate(year, month, day);
|
||||
}
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
return QDate(year, month, parts.at(2).toInt());
|
||||
}
|
||||
#endif // QT_NO_TEXTDATE
|
||||
case Qt::ISODate: {
|
||||
const int year = string.mid(0, 4).toInt();
|
||||
if (year <= 0 || year > 9999)
|
||||
return QDate();
|
||||
return QDate(year, string.mid(5, 2).toInt(), string.mid(8, 2).toInt());
|
||||
}
|
||||
}
|
||||
return QDate();
|
||||
}
|
||||
@ -1633,6 +1617,8 @@ int QTime::msec() const
|
||||
"23:59:20".
|
||||
|
||||
If the time is invalid, an empty string will be returned.
|
||||
|
||||
\sa QDate::toString(), QDateTime::toString()
|
||||
*/
|
||||
|
||||
QString QTime::toString(Qt::DateFormat format) const
|
||||
@ -1643,28 +1629,25 @@ QString QTime::toString(Qt::DateFormat format) const
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return QLocale::system().toString(*this, format == Qt::SystemLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat);
|
||||
return QLocale::system().toString(*this, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return QLocale().toString(*this, format == Qt::DefaultLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat);
|
||||
|
||||
return QLocale().toString(*this, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date:
|
||||
return QString::fromLatin1("%1:%2:%3")
|
||||
.arg(hour(), 2, 10, QLatin1Char('0'))
|
||||
.arg(minute(), 2, 10, QLatin1Char('0'))
|
||||
.arg(second(), 2, 10, QLatin1Char('0'));
|
||||
default:
|
||||
return QString::fromLatin1("%1:%2:%3").arg(hour(), 2, 10, QLatin1Char('0'))
|
||||
.arg(minute(), 2, 10, QLatin1Char('0'))
|
||||
.arg(second(), 2, 10, QLatin1Char('0'));
|
||||
case Qt::ISODate:
|
||||
case Qt::TextDate:
|
||||
return QString::fromLatin1("%1:%2:%3.%4")
|
||||
.arg(hour(), 2, 10, QLatin1Char('0'))
|
||||
.arg(minute(), 2, 10, QLatin1Char('0'))
|
||||
.arg(second(), 2, 10, QLatin1Char('0'))
|
||||
.arg(msec(), 3, 10, QLatin1Char('0'));
|
||||
default:
|
||||
return QString::fromUtf8("%1:%2:%3.%4").arg(hour(), 2, 10, QLatin1Char('0'))
|
||||
.arg(minute(), 2, 10, QLatin1Char('0'))
|
||||
.arg(second(), 2, 10, QLatin1Char('0'))
|
||||
.arg(msec(), 3, 10, QLatin1Char('0'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1899,104 +1882,68 @@ int QTime::msecsTo(const QTime &t) const
|
||||
|
||||
#ifndef QT_NO_DATESTRING
|
||||
|
||||
// These anonymous functions tidy up QDateTime::fromString()
|
||||
// and avoid confusion of responsibility between it and QTime::fromString().
|
||||
namespace {
|
||||
inline bool isMidnight(int hour, int minute, int second, int msec)
|
||||
static QTime fromIsoTimeString(const QString &string, Qt::DateFormat format, bool *isMidnight24)
|
||||
{
|
||||
return hour == 24 && minute == 0 && second == 0 && msec == 0;
|
||||
}
|
||||
if (isMidnight24)
|
||||
*isMidnight24 = false;
|
||||
|
||||
QTime fromStringImpl(const QString &s, Qt::DateFormat f, bool &isMidnight24)
|
||||
{
|
||||
if (s.isEmpty()) {
|
||||
// Return a null time.
|
||||
const int size = string.size();
|
||||
if (size < 5)
|
||||
return QTime();
|
||||
}
|
||||
|
||||
switch (f) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
case Qt::SystemLocaleLongDate:
|
||||
{
|
||||
QLocale::FormatType formatType(Qt::SystemLocaleLongDate ? QLocale::LongFormat : QLocale::ShortFormat);
|
||||
return QTime::fromString(s, QLocale::system().timeFormat(formatType));
|
||||
}
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
{
|
||||
QLocale::FormatType formatType(f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat : QLocale::ShortFormat);
|
||||
return QTime::fromString(s, QLocale().timeFormat(formatType));
|
||||
}
|
||||
case Qt::RFC2822Date: {
|
||||
QTime time;
|
||||
rfcDateImpl(s, 0, &time);
|
||||
return time;
|
||||
}
|
||||
case Qt::TextDate:
|
||||
case Qt::ISODate:
|
||||
{
|
||||
bool ok = true;
|
||||
const int hour(s.mid(0, 2).toInt(&ok));
|
||||
bool ok = false;
|
||||
int hour = string.mid(0, 2).toInt(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
const int minute = string.mid(3, 2).toInt(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
int second = 0;
|
||||
int msec = 0;
|
||||
|
||||
if (size == 5) {
|
||||
// HH:MM format
|
||||
second = 0;
|
||||
msec = 0;
|
||||
} else if (string.at(5) == QLatin1Char(',') || string.at(5) == QLatin1Char('.')) {
|
||||
if (format == Qt::TextDate)
|
||||
return QTime();
|
||||
// ISODate HH:MM.SSSSSS format
|
||||
// We only want 5 digits worth of fraction of minute. This follows the existing
|
||||
// behavior that determines how milliseconds are read; 4 millisecond digits are
|
||||
// read and then rounded to 3. If we read at most 5 digits for fraction of minute,
|
||||
// the maximum amount of millisecond digits it will expand to once converted to
|
||||
// seconds is 4. E.g. 12:34,99999 will expand to 12:34:59.9994. The milliseconds
|
||||
// will then be rounded up AND clamped to 999.
|
||||
const float minuteFraction = QString::fromUtf8("0.%1").arg(string.mid(6, 5)).toFloat(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
const int minute(s.mid(3, 2).toInt(&ok));
|
||||
const float secondWithMs = minuteFraction * 60;
|
||||
const float secondNoMs = std::floor(secondWithMs);
|
||||
const float secondFraction = secondWithMs - secondNoMs;
|
||||
second = secondNoMs;
|
||||
msec = qMin(qRound(secondFraction * 1000.0), 999);
|
||||
} else {
|
||||
// HH:MM:SS or HH:MM:SS.sssss
|
||||
second = string.mid(6, 2).toInt(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
if (f == Qt::ISODate) {
|
||||
if (s.size() == 5) {
|
||||
// Do not need to specify seconds if using ISO format.
|
||||
return QTime(hour, minute, 0, 0);
|
||||
} else if ((s.size() > 6) && (s[5] == QLatin1Char(',') || s[5] == QLatin1Char('.'))) {
|
||||
// Possibly specifying fraction of a minute.
|
||||
|
||||
// We only want 5 digits worth of fraction of minute. This follows the existing
|
||||
// behaviour that determines how milliseconds are read; 4 millisecond digits are
|
||||
// read and then rounded to 3. If we read at most 5 digits for fraction of minute,
|
||||
// the maximum amount of millisecond digits it will expand to once converted to
|
||||
// seconds is 4. E.g. 12:34,99999 will expand to 12:34:59.9994. The milliseconds
|
||||
// will then be rounded up AND clamped to 999.
|
||||
const QString minuteFractionStr(QLatin1String("0.") + s.mid(6, 5));
|
||||
const float minuteFraction = minuteFractionStr.toFloat(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
const float secondWithMs = minuteFraction * 60;
|
||||
const float second = std::floor(secondWithMs);
|
||||
const float millisecond = 1000 * (secondWithMs - second);
|
||||
const int millisecondRounded = qMin(qRound(millisecond), 999);
|
||||
|
||||
if (isMidnight(hour, minute, second, millisecondRounded)) {
|
||||
isMidnight24 = true;
|
||||
return QTime(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return QTime(hour, minute, second, millisecondRounded);
|
||||
}
|
||||
if (size > 8 && (string.at(8) == QLatin1Char(',') || string.at(8) == QLatin1Char('.'))) {
|
||||
const double secondFraction = QString::fromUtf8("0.%1").arg(string.mid(9, 4)).toDouble(&ok);
|
||||
if (!ok)
|
||||
return QTime();
|
||||
msec = qMin(qRound(secondFraction * 1000.0), 999);
|
||||
}
|
||||
|
||||
const int second(s.mid(6, 2).toInt(&ok));
|
||||
if (!ok)
|
||||
return QTime();
|
||||
const QString msec_s(QLatin1String("0.") + s.mid(9, 4));
|
||||
const double msec(msec_s.toDouble(&ok));
|
||||
if (!ok)
|
||||
return QTime(hour, minute, second, 0);
|
||||
|
||||
if (f == Qt::ISODate) {
|
||||
if (isMidnight(hour, minute, second, msec)) {
|
||||
isMidnight24 = true;
|
||||
return QTime(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
return QTime(hour, minute, second, qMin(qRound(msec * 1000.0), 999));
|
||||
}
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
return QTime();
|
||||
}
|
||||
}
|
||||
|
||||
if (format == Qt::ISODate && hour == 24 && minute == 0 && second == 0 && msec == 0) {
|
||||
if (isMidnight24)
|
||||
*isMidnight24 = true;
|
||||
hour = 0;
|
||||
}
|
||||
|
||||
return QTime(hour, minute, second, msec);
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QTime QTime::fromString(const QString &string, Qt::DateFormat format)
|
||||
@ -2010,10 +1957,32 @@ QTime fromStringImpl(const QString &s, Qt::DateFormat f, bool &isMidnight24)
|
||||
fails for the default locale). This should be considered an
|
||||
implementation detail.
|
||||
*/
|
||||
QTime QTime::fromString(const QString& s, Qt::DateFormat f)
|
||||
QTime QTime::fromString(const QString& string, Qt::DateFormat format)
|
||||
{
|
||||
bool unused;
|
||||
return fromStringImpl(s, f, unused);
|
||||
if (string.isEmpty())
|
||||
return QTime();
|
||||
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toTime(string, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return QLocale::system().toTime(string, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toTime(string, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return QLocale().toTime(string, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date: {
|
||||
QTime time;
|
||||
rfcDateImpl(string, 0, &time);
|
||||
return time;
|
||||
}
|
||||
case Qt::ISODate:
|
||||
case Qt::TextDate:
|
||||
default:
|
||||
return fromIsoTimeString(string, format, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -2814,30 +2783,24 @@ void QDateTime::setTime_t(uint secsSince1Jan1970UTC)
|
||||
\sa QDate::toString(), QTime::toString(), Qt::DateFormat
|
||||
*/
|
||||
|
||||
QString QDateTime::toString(Qt::DateFormat f) const
|
||||
QString QDateTime::toString(Qt::DateFormat format) const
|
||||
{
|
||||
QString buf;
|
||||
if (!isValid())
|
||||
return buf;
|
||||
|
||||
if (f == Qt::ISODate) {
|
||||
buf = d->date.toString(Qt::ISODate);
|
||||
if (buf.isEmpty())
|
||||
return QString(); // failed to convert
|
||||
buf += QLatin1Char('T');
|
||||
buf += d->time.toString(Qt::ISODate);
|
||||
switch (d->spec) {
|
||||
case QDateTimePrivate::UTC:
|
||||
buf += QLatin1Char('Z');
|
||||
break;
|
||||
case QDateTimePrivate::OffsetFromUTC: {
|
||||
buf += toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (f == Qt::RFC2822Date) {
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return QLocale::system().toString(*this, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toString(*this, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return QLocale().toString(*this, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date: {
|
||||
buf = toString(QStringLiteral("dd MMM yyyy hh:mm:ss "));
|
||||
|
||||
int utcOffset = d->m_offsetFromUtc;
|
||||
@ -2859,50 +2822,42 @@ QString QDateTime::toString(Qt::DateFormat f) const
|
||||
if (min < 10)
|
||||
buf += QLatin1Char('0');
|
||||
buf += QString::number(min);
|
||||
return buf;
|
||||
}
|
||||
default:
|
||||
#ifndef QT_NO_TEXTDATE
|
||||
else if (f == Qt::TextDate) {
|
||||
#ifndef Q_OS_WIN
|
||||
buf = d->date.shortDayName(d->date.dayOfWeek());
|
||||
buf += QLatin1Char(' ');
|
||||
buf += d->date.shortMonthName(d->date.month());
|
||||
buf += QLatin1Char(' ');
|
||||
buf += QString::number(d->date.day());
|
||||
#else
|
||||
wchar_t out[255];
|
||||
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILDATE, out, 255);
|
||||
QString winstr = QString::fromWCharArray(out);
|
||||
switch (winstr.toInt()) {
|
||||
case 1:
|
||||
buf = d->date.shortDayName(d->date.dayOfWeek());
|
||||
buf += QLatin1Char(' ');
|
||||
buf += QString::number(d->date.day());
|
||||
buf += QLatin1String(". ");
|
||||
buf += d->date.shortMonthName(d->date.month());
|
||||
break;
|
||||
default:
|
||||
buf = d->date.shortDayName(d->date.dayOfWeek());
|
||||
buf += QLatin1Char(' ');
|
||||
buf += d->date.shortMonthName(d->date.month());
|
||||
buf += QLatin1Char(' ');
|
||||
buf += QString::number(d->date.day());
|
||||
case Qt::TextDate:
|
||||
//We cant use date.toString(Qt::TextDate) as we need to insert the time before the year
|
||||
buf = QString::fromUtf8("%1 %2 %3 %4 %5").arg(d->date.shortDayName(d->date.dayOfWeek()))
|
||||
.arg(d->date.shortMonthName(d->date.month()))
|
||||
.arg(d->date.day())
|
||||
.arg(d->time.toString(Qt::TextDate))
|
||||
.arg(d->date.year());
|
||||
if (timeSpec() != Qt::LocalTime) {
|
||||
buf += QStringLiteral(" GMT");
|
||||
if (d->spec == QDateTimePrivate::OffsetFromUTC)
|
||||
buf += toOffsetString(Qt::TextDate, d->m_offsetFromUtc);
|
||||
}
|
||||
return buf;
|
||||
#endif
|
||||
buf += QLatin1Char(' ');
|
||||
buf += d->time.toString();
|
||||
buf += QLatin1Char(' ');
|
||||
buf += QString::number(d->date.year());
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
buf = d->date.toString(f);
|
||||
case Qt::ISODate:
|
||||
buf = d->date.toString(Qt::ISODate);
|
||||
if (buf.isEmpty())
|
||||
return QString(); // failed to convert
|
||||
buf += QLatin1Char(' ');
|
||||
buf += d->time.toString(f);
|
||||
buf += QLatin1Char('T');
|
||||
buf += d->time.toString(Qt::ISODate);
|
||||
switch (d->spec) {
|
||||
case QDateTimePrivate::UTC:
|
||||
buf += QLatin1Char('Z');
|
||||
break;
|
||||
case QDateTimePrivate::OffsetFromUTC:
|
||||
buf += toOffsetString(Qt::ISODate, d->m_offsetFromUtc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -3675,55 +3630,27 @@ static int fromShortMonthName(const QString &monthName)
|
||||
English short month names (e.g. "Jan"). Although localized month
|
||||
names can also be used, they depend on the user's locale settings.
|
||||
*/
|
||||
QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f)
|
||||
QDateTime QDateTime::fromString(const QString& string, Qt::DateFormat format)
|
||||
{
|
||||
if (s.isEmpty()) {
|
||||
if (string.isEmpty())
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
switch (f) {
|
||||
case Qt::ISODate: {
|
||||
QString tmp = s;
|
||||
Qt::TimeSpec ts = Qt::LocalTime;
|
||||
QDate date = QDate::fromString(tmp.left(10), Qt::ISODate);
|
||||
if (tmp.size() == 10)
|
||||
return QDateTime(date);
|
||||
|
||||
tmp = tmp.mid(11);
|
||||
|
||||
// Recognize UTC specifications
|
||||
if (tmp.endsWith(QLatin1Char('Z'))) {
|
||||
ts = Qt::UTC;
|
||||
tmp.chop(1);
|
||||
}
|
||||
|
||||
// Recognize timezone specifications
|
||||
int offset = 0;
|
||||
const int signIndex = tmp.indexOf(QRegExp(QStringLiteral("[+-]")));
|
||||
if (signIndex >= 0) {
|
||||
bool ok;
|
||||
offset = fromOffsetString(tmp.mid(signIndex), &ok);
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
tmp = tmp.left(signIndex);
|
||||
ts = Qt::OffsetFromUTC;
|
||||
}
|
||||
|
||||
bool isMidnight24 = false;
|
||||
// Might be end of day (24:00, including variants), which QTime considers invalid.
|
||||
QTime time(fromStringImpl(tmp, Qt::ISODate, isMidnight24));
|
||||
if (isMidnight24) {
|
||||
// ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day.
|
||||
date = date.addDays(1);
|
||||
}
|
||||
|
||||
return QDateTime(date, time, ts, offset);
|
||||
}
|
||||
switch (format) {
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
return QLocale::system().toDateTime(string, QLocale::ShortFormat);
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return QLocale::system().toDateTime(string, QLocale::LongFormat);
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
return QLocale().toDateTime(string, QLocale::ShortFormat);
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return QLocale().toDateTime(string, QLocale::LongFormat);
|
||||
case Qt::RFC2822Date: {
|
||||
QDate date;
|
||||
QTime time;
|
||||
int utcOffset = 0;
|
||||
rfcDateImpl(s, &date, &time, &utcOffset);
|
||||
rfcDateImpl(string, &date, &time, &utcOffset);
|
||||
|
||||
if (!date.isValid() || !time.isValid())
|
||||
return QDateTime();
|
||||
@ -3732,81 +3659,115 @@ QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f)
|
||||
dateTime.setOffsetFromUtc(utcOffset);
|
||||
return dateTime;
|
||||
}
|
||||
case Qt::SystemLocaleDate:
|
||||
case Qt::SystemLocaleShortDate:
|
||||
case Qt::SystemLocaleLongDate:
|
||||
return fromString(s, QLocale::system().dateTimeFormat(f == Qt::SystemLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat));
|
||||
case Qt::LocaleDate:
|
||||
case Qt::DefaultLocaleShortDate:
|
||||
case Qt::DefaultLocaleLongDate:
|
||||
return fromString(s, QLocale().dateTimeFormat(f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat
|
||||
: QLocale::ShortFormat));
|
||||
case Qt::ISODate: {
|
||||
const int size = string.size();
|
||||
if (size < 10)
|
||||
return QDateTime();
|
||||
|
||||
QString isoString = string;
|
||||
Qt::TimeSpec spec = Qt::LocalTime;
|
||||
|
||||
QDate date = QDate::fromString(isoString.left(10), Qt::ISODate);
|
||||
if (!date.isValid())
|
||||
return QDateTime();
|
||||
if (size == 10)
|
||||
return QDateTime(date);
|
||||
|
||||
isoString.remove(0, 11);
|
||||
int offset = 0;
|
||||
// Check end of string for Time Zone definition, either Z for UTC or [+-]HH:MM for Offset
|
||||
if (isoString.endsWith(QLatin1Char('Z'))) {
|
||||
spec = Qt::UTC;
|
||||
isoString.chop(1);
|
||||
} else {
|
||||
const int signIndex = isoString.indexOf(QRegExp(QStringLiteral("[+-]")));
|
||||
if (signIndex >= 0) {
|
||||
bool ok;
|
||||
offset = fromOffsetString(isoString.mid(signIndex), &ok);
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
isoString = isoString.left(signIndex);
|
||||
spec = Qt::OffsetFromUTC;
|
||||
}
|
||||
}
|
||||
|
||||
// Might be end of day (24:00, including variants), which QTime considers invalid.
|
||||
// ISO 8601 (section 4.2.3) says that 24:00 is equivalent to 00:00 the next day.
|
||||
bool isMidnight24 = false;
|
||||
QTime time = fromIsoTimeString(isoString, Qt::ISODate, &isMidnight24);
|
||||
if (!time.isValid())
|
||||
return QDateTime();
|
||||
if (isMidnight24)
|
||||
date = date.addDays(1);
|
||||
return QDateTime(date, time, spec, offset);
|
||||
}
|
||||
#if !defined(QT_NO_TEXTDATE)
|
||||
case Qt::TextDate: {
|
||||
QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
QStringList parts = string.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
|
||||
if ((parts.count() < 5) || (parts.count() > 6)) {
|
||||
if ((parts.count() < 5) || (parts.count() > 6))
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
// Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974"
|
||||
int month = -1, day = -1;
|
||||
bool ok;
|
||||
int month = 0;
|
||||
int day = 0;
|
||||
bool ok = false;
|
||||
|
||||
// First try month then day
|
||||
month = fromShortMonthName(parts.at(1));
|
||||
if (month != -1) {
|
||||
day = parts.at(2).toInt(&ok);
|
||||
if (!ok)
|
||||
day = -1;
|
||||
}
|
||||
if (month)
|
||||
day = parts.at(2).toInt();
|
||||
|
||||
if (month == -1 || day == -1) {
|
||||
// first variant failed, lets try the other
|
||||
// If failed try day then month
|
||||
if (!month || !day) {
|
||||
month = fromShortMonthName(parts.at(2));
|
||||
if (month != -1) {
|
||||
if (month) {
|
||||
QString dayStr = parts.at(1);
|
||||
if (dayStr.endsWith(QLatin1Char('.'))) {
|
||||
dayStr.chop(1);
|
||||
day = dayStr.toInt(&ok);
|
||||
if (!ok)
|
||||
day = -1;
|
||||
} else {
|
||||
day = -1;
|
||||
day = dayStr.toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (month == -1 || day == -1) {
|
||||
// both variants failed, give up
|
||||
// If both failed, give up
|
||||
if (!month || !day)
|
||||
return QDateTime();
|
||||
|
||||
// Year can be before or after time, "Sun Dec 1 1974 13:02:00" or "Sun Dec 1 13:02:00 1974"
|
||||
// Guess which by looking for ':' in the time
|
||||
int year = 0;
|
||||
int yearPart = 0;
|
||||
int timePart = 0;
|
||||
if (parts.at(3).contains(QLatin1Char(':'))) {
|
||||
yearPart = 4;
|
||||
timePart = 3;
|
||||
} else if (parts.at(4).contains(QLatin1Char(':'))) {
|
||||
yearPart = 3;
|
||||
timePart = 4;
|
||||
} else {
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
int year;
|
||||
QStringList timeParts = parts.at(3).split(QLatin1Char(':'));
|
||||
if ((timeParts.count() == 3) || (timeParts.count() == 2)) {
|
||||
// Year is after time, e.g. "Sun Dec 1 13:02:00 1974"
|
||||
year = parts.at(4).toInt(&ok);
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
} else { // Year is before time, e.g. "Sun Dec 1 1974 13:02:00"
|
||||
timeParts = parts.at(4).split(QLatin1Char(':'));
|
||||
if ((timeParts.count() != 3) && (timeParts.count() != 2))
|
||||
return QDateTime();
|
||||
year = parts.at(3).toInt(&ok);
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
}
|
||||
year = parts.at(yearPart).toInt(&ok);
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
|
||||
QDate date(year, month, day);
|
||||
if (!date.isValid())
|
||||
return QDateTime();
|
||||
|
||||
QStringList timeParts = parts.at(timePart).split(QLatin1Char(':'));
|
||||
if (timeParts.count() < 2 || timeParts.count() > 3)
|
||||
return QDateTime();
|
||||
|
||||
int hour = timeParts.at(0).toInt(&ok);
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
int minute = timeParts.at(1).toInt(&ok);
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
int second = 0;
|
||||
int millisecond = 0;
|
||||
@ -3829,8 +3790,9 @@ QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f)
|
||||
}
|
||||
}
|
||||
|
||||
QDate date(year, month, day);
|
||||
QTime time(hour, minute, second, millisecond);
|
||||
if (!time.isValid())
|
||||
return QDateTime();
|
||||
|
||||
if (parts.count() == 5)
|
||||
return QDateTime(date, time, Qt::LocalTime);
|
||||
|
@ -85,6 +85,8 @@ private slots:
|
||||
void fromMSecsSinceEpoch();
|
||||
void toString_isoDate_data();
|
||||
void toString_isoDate();
|
||||
void toString_textDate_data();
|
||||
void toString_textDate();
|
||||
void toString_rfcDate_data();
|
||||
void toString_rfcDate();
|
||||
void toString_enumformat();
|
||||
@ -622,8 +624,8 @@ void tst_QDateTime::fromMSecsSinceEpoch()
|
||||
|
||||
void tst_QDateTime::toString_isoDate_data()
|
||||
{
|
||||
QTest::addColumn<QDateTime>("dt");
|
||||
QTest::addColumn<QString>("formatted");
|
||||
QTest::addColumn<QDateTime>("datetime");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("localtime")
|
||||
<< QDateTime(QDate(1978, 11, 9), QTime(13, 28, 34))
|
||||
@ -650,10 +652,68 @@ void tst_QDateTime::toString_isoDate_data()
|
||||
|
||||
void tst_QDateTime::toString_isoDate()
|
||||
{
|
||||
QFETCH(QDateTime, dt);
|
||||
QFETCH(QString, formatted);
|
||||
QFETCH(QDateTime, datetime);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QCOMPARE(dt.toString(Qt::ISODate), formatted);
|
||||
QLocale oldLocale;
|
||||
QLocale::setDefault(QLocale("en_US"));
|
||||
|
||||
QString result = datetime.toString(Qt::ISODate);
|
||||
QCOMPARE(result, expected);
|
||||
|
||||
QDateTime resultDatetime = QDateTime::fromString(result, Qt::ISODate);
|
||||
// If expecting invalid result the datetime may still be valid, i.e. year < 0 or > 9999
|
||||
if (!expected.isEmpty()) {
|
||||
QCOMPARE(resultDatetime, datetime);
|
||||
QCOMPARE(resultDatetime.date(), datetime.date());
|
||||
QCOMPARE(resultDatetime.time(), datetime.time());
|
||||
QCOMPARE(resultDatetime.timeSpec(), datetime.timeSpec());
|
||||
QCOMPARE(resultDatetime.utcOffset(), datetime.utcOffset());
|
||||
} else {
|
||||
QCOMPARE(resultDatetime, QDateTime());
|
||||
}
|
||||
|
||||
QLocale::setDefault(oldLocale);
|
||||
}
|
||||
|
||||
void tst_QDateTime::toString_textDate_data()
|
||||
{
|
||||
QTest::addColumn<QDateTime>("datetime");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("localtime") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::LocalTime)
|
||||
<< QString("Wed Jan 2 01:02:03.000 2013");
|
||||
QTest::newRow("utc") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::UTC)
|
||||
<< QString("Wed Jan 2 01:02:03.000 2013 GMT");
|
||||
QTest::newRow("offset+") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC,
|
||||
10 * 60 * 60)
|
||||
<< QString("Wed Jan 2 01:02:03.000 2013 GMT+1000");
|
||||
QTest::newRow("offset-") << QDateTime(QDate(2013, 1, 2), QTime(1, 2, 3), Qt::OffsetFromUTC,
|
||||
-10 * 60 * 60)
|
||||
<< QString("Wed Jan 2 01:02:03.000 2013 GMT-1000");
|
||||
QTest::newRow("invalid") << QDateTime()
|
||||
<< QString("");
|
||||
}
|
||||
|
||||
void tst_QDateTime::toString_textDate()
|
||||
{
|
||||
QFETCH(QDateTime, datetime);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QLocale oldLocale;
|
||||
QLocale::setDefault(QLocale("en_US"));
|
||||
|
||||
QString result = datetime.toString(Qt::TextDate);
|
||||
QCOMPARE(result, expected);
|
||||
|
||||
QDateTime resultDatetime = QDateTime::fromString(result, Qt::TextDate);
|
||||
QCOMPARE(resultDatetime, datetime);
|
||||
QCOMPARE(resultDatetime.date(), datetime.date());
|
||||
QCOMPARE(resultDatetime.time(), datetime.time());
|
||||
QCOMPARE(resultDatetime.timeSpec(), datetime.timeSpec());
|
||||
QCOMPARE(resultDatetime.utcOffset(), datetime.utcOffset());
|
||||
|
||||
QLocale::setDefault(oldLocale);
|
||||
}
|
||||
|
||||
void tst_QDateTime::toString_rfcDate_data()
|
||||
@ -1762,7 +1822,7 @@ void tst_QDateTime::fromStringDateFormat_data()
|
||||
QTest::newRow("text invalid month name") << QString::fromLatin1("Thu Jaz 1 1970 00:12:34")
|
||||
<< Qt::TextDate << invalidDateTime();
|
||||
QTest::newRow("text invalid date") << QString::fromLatin1("Thu Jan 32 1970 00:12:34")
|
||||
<< Qt::TextDate << QDateTime(invalidDate(), QTime(0, 12, 34), Qt::LocalTime);
|
||||
<< Qt::TextDate << invalidDateTime();
|
||||
QTest::newRow("text invalid day #1") << QString::fromLatin1("Thu Jan XX 1970 00:12:34")
|
||||
<< Qt::TextDate << invalidDateTime();
|
||||
QTest::newRow("text invalid day #2") << QString::fromLatin1("Thu X. Jan 00:00:00 1970")
|
||||
|
@ -575,9 +575,12 @@ void tst_QTime::fromStringDateFormat_data()
|
||||
QTest::newRow("TextDate - data1") << QString("10:12:34") << Qt::TextDate << QTime(10,12,34,0);
|
||||
QTest::newRow("TextDate - data2") << QString("19:03:54.998601") << Qt::TextDate << QTime(19, 3, 54, 999);
|
||||
QTest::newRow("TextDate - data3") << QString("19:03:54.999601") << Qt::TextDate << QTime(19, 3, 54, 999);
|
||||
QTest::newRow("TextDate - data4") << QString("10:12") << Qt::TextDate << QTime(10, 12, 0, 0);
|
||||
QTest::newRow("TextDate - invalid, minutes") << QString::fromLatin1("23:XX:00") << Qt::TextDate << invalidTime();
|
||||
QTest::newRow("TextDate - invalid, minute fraction") << QString::fromLatin1("23:00.123456") << Qt::TextDate << invalidTime();
|
||||
QTest::newRow("TextDate - invalid, seconds") << QString::fromLatin1("23:00:XX") << Qt::TextDate << invalidTime();
|
||||
QTest::newRow("TextDate - invalid, milliseconds") << QString::fromLatin1("23:01:01:XXXX") << Qt::TextDate << QTime(23, 1, 1, 0);
|
||||
QTest::newRow("TextDate - midnight 24") << QString("24:00:00") << Qt::TextDate << QTime();
|
||||
|
||||
QTest::newRow("IsoDate - valid, start of day, omit seconds") << QString::fromLatin1("00:00") << Qt::ISODate << QTime(0, 0, 0);
|
||||
QTest::newRow("IsoDate - valid, omit seconds") << QString::fromLatin1("22:21") << Qt::ISODate << QTime(22, 21, 0);
|
||||
@ -599,6 +602,7 @@ void tst_QTime::fromStringDateFormat_data()
|
||||
QTest::newRow("IsoDate - data1") << QString("10:12:34") << Qt::ISODate << QTime(10,12,34,0);
|
||||
QTest::newRow("IsoDate - data2") << QString("19:03:54.998601") << Qt::ISODate << QTime(19, 3, 54, 999);
|
||||
QTest::newRow("IsoDate - data3") << QString("19:03:54.999601") << Qt::ISODate << QTime(19, 3, 54, 999);
|
||||
QTest::newRow("IsoDate - midnight 24") << QString("24:00:00") << Qt::ISODate << QTime(0, 0, 0, 0);
|
||||
QTest::newRow("IsoDate - minute fraction midnight") << QString("24:00,0") << Qt::ISODate << QTime(0, 0, 0, 0);
|
||||
|
||||
// Test Qt::RFC2822Date format (RFC 2822).
|
||||
|
Loading…
Reference in New Issue
Block a user