diff options
Diffstat (limited to 'src/corelib/tools/qdatetime.cpp')
-rw-r--r-- | src/corelib/tools/qdatetime.cpp | 1592 |
1 files changed, 940 insertions, 652 deletions
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index d8e3a78cdf..ab5a516e8a 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -158,10 +158,135 @@ static const char monthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, static const char * const qt_shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +int qt_monthNumberFromShortName(const QString &shortName) +{ + for (unsigned int i = 0; i < sizeof(qt_shortMonthNames) / sizeof(qt_shortMonthNames[0]); ++i) { + if (shortName == QLatin1String(qt_shortMonthNames[i])) + return i + 1; + } + return -1; +} #endif + #ifndef QT_NO_DATESTRING -static QString fmtDateTime(const QString& f, const QTime* dt = 0, const QDate* dd = 0); +static void rfcDateImpl(const QString &s, QDate *dd = 0, QTime *dt = 0, int *utfcOffset = 0); #endif +static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time); +static void utcToOffset(QDate *date, QTime *time, qint32 offset); +static QDate adjustDate(QDate date); + +// Return offset in [+-]HH:MM format +// Qt::ISODate puts : between the hours and minutes, but Qt:TextDate does not +static QString toOffsetString(Qt::DateFormat format, int offset) +{ + QString result; + if (format == Qt::TextDate) + result = QStringLiteral("%1%2%3"); + else // Qt::ISODate + result = QStringLiteral("%1%2:%3"); + + return result.arg(offset >= 0 ? QLatin1Char('+') : QLatin1Char('-')) + .arg(qAbs(offset) / SECS_PER_HOUR, 2, 10, QLatin1Char('0')) + .arg((offset / 60) % 60, 2, 10, QLatin1Char('0')); +} + +// Parse offset in [+-]HH[:]MM format +static int fromOffsetString(const QString &offsetString, bool *valid) +{ + *valid = false; + + const int size = offsetString.size(); + if (size < 2 || size > 6) + return 0; + + // First char must be + or - + const QChar sign = offsetString.at(0); + if (sign != QLatin1Char('+') && sign != QLatin1Char('-')) + return 0; + + // Split the hour and minute parts + QStringList parts = offsetString.split(QLatin1Char(':')); + if (parts.count() == 1) { + // [+-]HHMM format + parts.append(parts.at(0).mid(3)); + parts[0] = parts.at(0).left(3); + } + + bool ok = false; + const int hour = parts.at(0).toInt(&ok); + if (!ok) + return 0; + + const int minute = parts.at(1).toInt(&ok); + if (!ok || minute < 0 || minute > 59) + return 0; + + *valid = true; + return ((hour * 60) + minute) * 60; +} + +#if !defined(Q_OS_WINCE) +// Calls the platform variant of mktime for the given date and time, +// and updates the date, time, spec and abbreviation with the returned values +// If the date falls outside the 1970 to 2037 range supported by mktime / time_t +// then null date/time will be returned, you should call adjustDate() first if +// you need a guaranteed result. +static time_t qt_mktime(QDate *date, QTime *time, QDateTimePrivate::Spec *spec, + QString *abbreviation, bool *ok) +{ + if (ok) + *ok = false; + int yy, mm, dd; + date->getDate(&yy, &mm, &dd); + tm local; + local.tm_sec = time->second(); + local.tm_min = time->minute(); + local.tm_hour = time->hour(); + local.tm_mday = dd; + local.tm_mon = mm - 1; + local.tm_year = yy - 1900; + local.tm_wday = 0; + local.tm_yday = 0; + local.tm_isdst = -1; +#if defined(Q_OS_WIN) + _tzset(); +#else + tzset(); +#endif // Q_OS_WIN + const time_t secsSinceEpoch = mktime(&local); + if (secsSinceEpoch != time_t(-1)) { + *date = QDate(local.tm_year + 1900, local.tm_mon + 1, local.tm_mday); + *time = QTime(local.tm_hour, local.tm_min, local.tm_sec, time->msec()); + if (local.tm_isdst == 1) { + if (spec) + *spec = QDateTimePrivate::LocalDST; + if (abbreviation) + *abbreviation = QString::fromLocal8Bit(tzname[1]); + } else if (local.tm_isdst == 0) { + if (spec) + *spec = QDateTimePrivate::LocalStandard; + if (abbreviation) + *abbreviation = QString::fromLocal8Bit(tzname[0]); + } else { + if (spec) + *spec = QDateTimePrivate::LocalUnknown; + if (abbreviation) + *abbreviation = QString::fromLocal8Bit(tzname[0]); + } + if (ok) + *ok = true; + } else { + *date = QDate(); + *time = QTime(); + if (spec) + *spec = QDateTimePrivate::LocalUnknown; + if (abbreviation) + *abbreviation = QString(); + } + return secsSinceEpoch; +} +#endif // !Q_OS_WINCE /***************************************************************************** QDate member functions @@ -543,8 +668,8 @@ int QDate::weekNumber(int *yearNumber) const \li 12 = "Dec" \endlist - The month names will be localized according to the system's default - locale settings. + The month names will be localized according to the system's + locale settings, i.e. using QLocale::system(). Returns an empty string if the date is invalid. @@ -590,8 +715,8 @@ QString QDate::shortMonthName(int month, QDate::MonthNameType type) \li 12 = "December" \endlist - The month names will be localized according to the system's default - locale settings. + The month names will be localized according to the system's + locale settings, i.e. using QLocale::system(). Returns an empty string if the date is invalid. @@ -632,8 +757,8 @@ QString QDate::longMonthName(int month, MonthNameType type) \li 7 = "Sun" \endlist - The day names will be localized according to the system's default - locale settings. + The day names will be localized according to the system's + locale settings, i.e. using QLocale::system(). Returns an empty string if the date is invalid. @@ -674,8 +799,8 @@ QString QDate::shortDayName(int weekday, MonthNameType type) \li 7 = "Sunday" \endlist - The day names will be localized according to the system's default - locale settings. + The day names will be localized according to the system's + locale settings, i.e. using QLocale::system(). Returns an empty string if the date is invalid. @@ -712,7 +837,7 @@ QString QDate::longDayName(int weekday, MonthNameType type) If the \a format is Qt::TextDate, the string is formatted in the default way. QDate::shortDayName() and QDate::shortMonthName() are used to generate the string, so the day and month names will - be localized names using the default locale from the system. An + be localized names using the system locale, i.e. QLocale::system(). An example of this formatting is "Sat May 20 1995". If the \a format is Qt::ISODate, the string format corresponds @@ -735,6 +860,10 @@ QString QDate::longDayName(int weekday, MonthNameType type) QLocale::ShortFormat) or QLocale().toString(date, QLocale::LongFormat). + If the \a format is Qt::RFC2822Date, the string is formatted in + an \l{RFC 2822} compatible way. An example of this formatting is + "20 May 1995". + If the date is invalid, an empty string will be returned. \warning The Qt::ISODate format is only valid for years in the @@ -743,43 +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::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')); } } @@ -795,18 +923,18 @@ QString QDate::toString(Qt::DateFormat f) const \row \li dd \li the day as number with a leading zero (01 to 31) \row \li ddd \li the abbreviated localized day name (e.g. 'Mon' to 'Sun'). - Uses QDate::shortDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li dddd \li the long localized day name (e.g. 'Monday' to 'Sunday'). - Uses QDate::longDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li M \li the month as number without a leading zero (1 to 12) \row \li MM \li the month as number with a leading zero (01 to 12) \row \li MMM \li the abbreviated localized month name (e.g. 'Jan' to 'Dec'). - Uses QDate::shortMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li MMMM \li the long localized month name (e.g. 'January' to 'December'). - Uses QDate::longMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li yy \li the year as two digit number (00 to 99) \row \li yyyy \li the year as four digit number. If the year is negative, a minus sign is prepended in addition. @@ -829,18 +957,12 @@ QString QDate::toString(Qt::DateFormat f) const If the datetime is invalid, an empty string will be returned. - \warning The Qt::ISODate format is only valid for years in the - range 0 to 9999. This restriction may apply to locale-aware - formats as well, depending on the locale settings. - - \sa QDateTime::toString(), QTime::toString() + \sa QDateTime::toString(), QTime::toString(), QLocale::toString() */ QString QDate::toString(const QString& format) const { - if (year() > 9999) - return QString(); - return fmtDateTime(format, 0, this); + return QLocale::system().toString(*this, format); } #endif //QT_NO_DATESTRING @@ -1099,39 +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(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; @@ -1150,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) { + bool ok = false; + int year = parts.at(3).toInt(&ok); + if (!ok) return QDate(); - } - int year = parts.at(3).toInt(&ok); - if (!ok) { + 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(year, month, day); - } -#else - break; -#endif } return QDate(); } @@ -1190,18 +1304,18 @@ QDate QDate::fromString(const QString& s, Qt::DateFormat f) \row \li dd \li The day as a number with a leading zero (01 to 31) \row \li ddd \li The abbreviated localized day name (e.g. 'Mon' to 'Sun'). - Uses QDate::shortDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li dddd \li The long localized day name (e.g. 'Monday' to 'Sunday'). - Uses QDate::longDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li M \li The month as a number without a leading zero (1 to 12) \row \li MM \li The month as a number with a leading zero (01 to 12) \row \li MMM \li The abbreviated localized month name (e.g. 'Jan' to 'Dec'). - Uses QDate::shortMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li MMMM \li The long localized month name (e.g. 'January' to 'December'). - Uses QDate::longMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li yy \li The year as two digit number (00 to 99) \row \li yyyy \li The year as four digit number. If the year is negative, a minus sign is prepended in addition. @@ -1474,15 +1588,15 @@ int QTime::msec() const /*! \overload - Returns the time as a string. Milliseconds are not included. The - \a format parameter determines the format of the string. + Returns the time as a string. The \a format parameter determines + the format of the string. - If \a format is Qt::TextDate, the string format is HH:MM:SS; e.g. 1 - second before midnight would be "23:59:59". + If \a format is Qt::TextDate, the string format is HH:MM:SS.zzz; + e.g. 1 second before midnight would be "23:59:59.000". If \a format is Qt::ISODate, the string format corresponds to the - ISO 8601 extended specification for representations of dates, - which is also HH:MM:SS. + ISO 8601 extended specification (with decimal fractions) for + representations of dates; also HH:MM:SS.zzz. If the \a format is Qt::SystemLocaleShortDate or Qt::SystemLocaleLongDate, the string format depends on the locale @@ -1498,7 +1612,13 @@ int QTime::msec() const QLocale::ShortFormat) or QLocale().toString(time, QLocale::LongFormat). + If the \a format is Qt::RFC2822Date, the string is formatted in + an \l{RFC 2822} compatible way. An example of this formatting is + "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 @@ -1509,22 +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); - - default: + 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')); case Qt::ISODate: case Qt::TextDate: - 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::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')); } } @@ -1574,11 +1697,11 @@ QString QTime::toString(Qt::DateFormat format) const If the time is invalid, an empty string will be returned. If \a format is empty, the default format "hh:mm:ss" is used. - \sa QDate::toString(), QDateTime::toString() + \sa QDate::toString(), QDateTime::toString(), QLocale::toString() */ QString QTime::toString(const QString& format) const { - return fmtDateTime(format, this, 0); + return QLocale::system().toString(*this, format); } #endif //QT_NO_DATESTRING /*! @@ -1759,99 +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::TextDate: - case Qt::ISODate: - { - bool ok = true; - const int hour(s.mid(0, 2).toInt(&ok)); - if (!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(); - const int minute(s.mid(3, 2).toInt(&ok)); + // 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(); - 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); - } - } - - const int second(s.mid(6, 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(); - 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); - } + 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); } - return QTime(hour, minute, second, qMin(qRound(msec * 1000.0), 999)); } + + if (format == Qt::ISODate && hour == 24 && minute == 0 && second == 0 && msec == 0) { + if (isMidnight24) + *isMidnight24 = true; + hour = 0; } - Q_UNREACHABLE(); - return QTime(); -} -} + return QTime(hour, minute, second, msec); +} /*! \fn QTime QTime::fromString(const QString &string, Qt::DateFormat format) @@ -1865,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); + } } /*! @@ -2129,6 +2243,20 @@ int QTime::elapsed() const time zone before 1970, even if the system's time zone database supports that information. + \section2 Offset From UTC + + A Qt::TimeSpec of Qt::OffsetFromUTC is also supported. This allows you + to define a QDateTime relative to UTC at a fixed offset of a given number + of seconds from UTC. For example, an offset of +3600 seconds is one hour + ahead of UTC and is usually written in ISO standard notation as + "UTC+01:00". Daylight Savings Time never applies with this TimeSpec. + + There is no explicit size restriction to the offset seconds, but there is + an implicit limit imposed when using the toString() and fromString() + methods which use a format of [+|-]hh:mm, effectively limiting the range + to +/- 99 hours and 59 minutes and whole minutes only. Note that currently + no time zone lies outside the range of +/- 14 hours. + \sa QDate, QTime, QDateTimeEdit */ @@ -2150,10 +2278,8 @@ QDateTime::QDateTime() */ QDateTime::QDateTime(const QDate &date) - : d(new QDateTimePrivate) + : d(new QDateTimePrivate(date, QTime(0, 0, 0), Qt::LocalTime, 0)) { - d->date = date; - d->time = QTime(0, 0, 0); } /*! @@ -2161,14 +2287,72 @@ QDateTime::QDateTime(const QDate &date) the time specification defined by \a spec. If \a date is valid and \a time is not, the time will be set to midnight. + + If \a spec is Qt::OffsetFromUTC then it will be set to Qt::UTC, i.e. an + offset of 0 seconds. To create a Qt::OffsetFromUTC datetime use the + correct constructor. */ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec) - : d(new QDateTimePrivate) + : d(new QDateTimePrivate(date, time, spec, 0)) { - d->date = date; - d->time = date.isValid() && !time.isValid() ? QTime(0, 0, 0) : time; - d->spec = (spec == Qt::UTC) ? QDateTimePrivate::UTC : QDateTimePrivate::LocalUnknown; +} + +/*! + \since 5.2 + + Constructs a datetime with the given \a date and \a time, using + the time specification defined by \a spec and \a offsetSeconds seconds. + + If \a date is valid and \a time is not, the time will be set to midnight. + + If the \a spec is not Qt::OffsetFromUTC then \a offsetSeconds will be ignored. + + If the \a spec is Qt::OffsetFromUTC and \a offsetSeconds is 0 then the + timeSpec() will be set to Qt::UTC, i.e. an offset of 0 seconds. +*/ + +QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec, int offsetSeconds) + : d(new QDateTimePrivate(date, time, spec, offsetSeconds)) +{ +} + +/*! + \internal + \since 5.2 + + Private. + + Create a datetime with the given \a date, \a time, \a spec and \a offsetSeconds +*/ + +QDateTimePrivate::QDateTimePrivate(const QDate &toDate, const QTime &toTime, Qt::TimeSpec toSpec, + int offsetSeconds) +{ + date = toDate; + + if (!toTime.isValid() && toDate.isValid()) + time = QTime(0, 0, 0); + else + time = toTime; + + m_offsetFromUtc = 0; + + switch (toSpec) { + case Qt::UTC : + spec = QDateTimePrivate::UTC; + break; + case Qt::OffsetFromUTC : + if (offsetSeconds == 0) { + spec = QDateTimePrivate::UTC; + } else { + spec = QDateTimePrivate::OffsetFromUTC; + m_offsetFromUtc = offsetSeconds; + } + break; + case Qt::LocalTime : + spec = QDateTimePrivate::LocalUnknown; + } } /*! @@ -2271,6 +2455,77 @@ Qt::TimeSpec QDateTime::timeSpec() const } /*! + \since 5.2 + + Returns the current Offset From UTC in seconds. + + If the timeSpec() is Qt::OffsetFromUTC this will be the value originally set. + + If the timeSpec() is Qt::LocalTime this will be the difference between the + Local Time and UTC including any Daylight Saving Offset. + + If the timeSpec() is Qt::UTC this will be 0. + + \sa setOffsetFromUtc() +*/ + +int QDateTime::offsetFromUtc() const +{ + switch (d->spec) { + case QDateTimePrivate::OffsetFromUTC: + return d->m_offsetFromUtc; + case QDateTimePrivate::UTC: + return 0; + default: // Any Qt::LocalTime + const QDateTime fakeDate(d->date, d->time, Qt::UTC); + return (fakeDate.toMSecsSinceEpoch() - toMSecsSinceEpoch()) / 1000; + } +} + +/*! + \since 5.2 + + Returns the Time Zone Abbreviation for the datetime. + + If the timeSpec() is Qt::UTC this will be "UTC". + + If the timeSpec() is Qt::OffsetFromUTC this will be in the format + "UTC[+-]00:00". + + If the timeSpec() is Qt::LocalTime then the host system is queried for the + correct abbreviation. + + Note that abbreviations may or may not be localized. + + Note too that the abbreviation is not guaranteed to be a unique value, + i.e. different time zones may have the same abbreviation. + + \sa timeSpec() +*/ + +QString QDateTime::timeZoneAbbreviation() const +{ + switch (d->spec) { + case QDateTimePrivate::UTC: + return QStringLiteral("UTC"); + case QDateTimePrivate::OffsetFromUTC: + return QLatin1String("UTC") + toOffsetString(Qt::ISODate, d->m_offsetFromUtc); + default: { // Any Qt::LocalTime +#if defined(Q_OS_WINCE) + // TODO Stub to enable compilation on WinCE + return QString(); +#else + QDate dt = adjustDate(d->date); + QTime tm = d->time; + QString abbrev; + qt_mktime(&dt, &tm, 0, &abbrev, 0); + return abbrev; +#endif // !Q_OS_WINCE + } + } +} + +/*! Sets the date part of this datetime to \a date. If no time is set, it is set to midnight. @@ -2307,6 +2562,9 @@ void QDateTime::setTime(const QTime &time) Sets the time specification used in this datetime to \a spec. The datetime will refer to a different point in time. + If \a spec is Qt::OffsetFromUTC then the timeSpec() will be set + to Qt::UTC, i.e. an effective offset of 0. + Example: \snippet code/src_corelib_tools_qdatetime.cpp 19 @@ -2317,17 +2575,43 @@ void QDateTime::setTimeSpec(Qt::TimeSpec spec) { detach(); - switch(spec) - { - case Qt::UTC: - d->spec = QDateTimePrivate::UTC; - break; - case Qt::OffsetFromUTC: - d->spec = QDateTimePrivate::OffsetFromUTC; - break; - default: - d->spec = QDateTimePrivate::LocalUnknown; - break; + d->m_offsetFromUtc = 0; + switch (spec) { + case Qt::UTC: + case Qt::OffsetFromUTC: + d->spec = QDateTimePrivate::UTC; + break; + default: + d->spec = QDateTimePrivate::LocalUnknown; + break; + } +} + +/*! + \since 5.2 + + Sets the timeSpec() to Qt::OffsetFromUTC and the offset to \a offsetSeconds. + The datetime will refer to a different point in time. + + The maximum and minimum offset is 14 positive or negative hours. If + \a offsetSeconds is larger or smaller than that, then the result is + undefined. + + If \a offsetSeconds is 0 then the timeSpec() will be set to Qt::UTC. + + \sa isValid(), offsetFromUtc() +*/ + +void QDateTime::setOffsetFromUtc(int offsetSeconds) +{ + detach(); + + if (offsetSeconds == 0) { + d->spec = QDateTimePrivate::UTC; + d->m_offsetFromUtc = 0; + } else { + d->spec = QDateTimePrivate::OffsetFromUTC; + d->m_offsetFromUtc = offsetSeconds; } } @@ -2410,8 +2694,6 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) { detach(); - QDateTimePrivate::Spec oldSpec = d->spec; - qint64 ddays = msecs / MSECS_PER_DAY; msecs %= MSECS_PER_DAY; if (msecs < 0) { @@ -2422,10 +2704,11 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) d->date = QDate(1970, 1, 1).addDays(ddays); d->time = QTime(0, 0, 0).addMSecs(msecs); - d->spec = QDateTimePrivate::UTC; - if (oldSpec != QDateTimePrivate::UTC) - d->spec = d->getLocal(d->date, d->time); + if (d->spec == QDateTimePrivate::OffsetFromUTC) + utcToOffset(&d->date, &d->time, d->m_offsetFromUtc); + else if (d->spec != QDateTimePrivate::UTC) + utcToLocal(d->date, d->time); } /*! @@ -2443,14 +2726,13 @@ void QDateTime::setTime_t(uint secsSince1Jan1970UTC) { detach(); - QDateTimePrivate::Spec oldSpec = d->spec; - d->date = QDate(1970, 1, 1).addDays(secsSince1Jan1970UTC / SECS_PER_DAY); d->time = QTime(0, 0, 0).addSecs(secsSince1Jan1970UTC % SECS_PER_DAY); - d->spec = QDateTimePrivate::UTC; - if (oldSpec != QDateTimePrivate::UTC) - d->spec = d->getLocal(d->date, d->time); + if (d->spec == QDateTimePrivate::OffsetFromUTC) + utcToOffset(&d->date, &d->time, d->m_offsetFromUtc); + else if (d->spec != QDateTimePrivate::UTC) + utcToLocal(d->date, d->time); } #ifndef QT_NO_DATESTRING @@ -2464,16 +2746,17 @@ void QDateTime::setTime_t(uint secsSince1Jan1970UTC) If the \a format is Qt::TextDate, the string is formatted in the default way. QDate::shortDayName(), QDate::shortMonthName(), and QTime::toString() are used to generate the string, so the - day and month names will be localized names. An example of this - formatting is "Wed May 20 03:40:13 1998". + day and month names will be localized names using the system locale, + i.e. QLocale::system(). An example of this formatting is + "Wed May 20 03:40:13.456 1998". If the \a format is Qt::ISODate, the string format corresponds - to the ISO 8601 extended specification for representations of - dates and times, taking the form YYYY-MM-DDTHH:MM:SS[Z|[+|-]HH:MM], - depending on the timeSpec() of the QDateTime. If the timeSpec() - is Qt::UTC, Z will be appended to the string; if the timeSpec() is - Qt::OffsetFromUTC, the offset in hours and minutes from UTC will - be appended to the string. + to the ISO 8601 extended specification (with decimal fractions) for + representations of dates and times, taking the form + YYYY-MM-DDTHH:MM:SS.zzz[Z|[+|-]HH:MM], depending on the timeSpec() + of the QDateTime. If the timeSpec() is Qt::UTC, Z will be appended + to the string; if the timeSpec() is Qt::OffsetFromUTC, the offset + in hours and minutes from UTC will be appended to the string. If the \a format is Qt::SystemLocaleShortDate or Qt::SystemLocaleLongDate, the string format depends on the locale @@ -2489,6 +2772,9 @@ void QDateTime::setTime_t(uint secsSince1Jan1970UTC) QLocale::ShortFormat) or QLocale().toString(datetime, QLocale::LongFormat). + If the \a format is Qt::RFC2822Date, the string is formatted + following \l{RFC 2822}. + If the datetime is invalid, an empty string will be returned. \warning The Qt::ISODate format is only valid for years in the @@ -2498,13 +2784,64 @@ 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) { + 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; + if (timeSpec() == Qt::LocalTime) { + QDateTime utc = toUTC(); + utc.setTimeSpec(timeSpec()); + utcOffset = utc.secsTo(*this); + } + + const int offset = qAbs(utcOffset); + buf += QLatin1Char((offset == utcOffset) ? '+' : '-'); + + const int hour = offset / 3600; + if (hour < 10) + buf += QLatin1Char('0'); + buf += QString::number(hour); + + const int min = (offset - (hour * 3600)) / 60; + if (min < 10) + buf += QLatin1Char('0'); + buf += QString::number(min); + return buf; + } + default: +#ifndef QT_NO_TEXTDATE + 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 + case Qt::ISODate: buf = d->date.toString(Qt::ISODate); if (buf.isEmpty()) return QString(); // failed to convert @@ -2514,61 +2851,14 @@ QString QDateTime::toString(Qt::DateFormat f) const case QDateTimePrivate::UTC: buf += QLatin1Char('Z'); break; - case QDateTimePrivate::OffsetFromUTC: { - int sign = d->utcOffset >= 0 ? 1: -1; - buf += QString::fromLatin1("%1%2:%3"). - arg(sign == 1 ? QLatin1Char('+') : QLatin1Char('-')). - arg(d->utcOffset * sign / SECS_PER_HOUR, 2, 10, QLatin1Char('0')). - arg((d->utcOffset / 60) % 60, 2, 10, QLatin1Char('0')); + case QDateTimePrivate::OffsetFromUTC: + buf += toOffsetString(Qt::ISODate, d->m_offsetFromUtc); break; - } default: break; } + return buf; } -#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()); - } -#endif - buf += QLatin1Char(' '); - buf += d->time.toString(); - buf += QLatin1Char(' '); - buf += QString::number(d->date.year()); - } -#endif - else { - buf = d->date.toString(f); - if (buf.isEmpty()) - return QString(); // failed to convert - buf += QLatin1Char(' '); - buf += d->time.toString(f); - } - - return buf; } /*! @@ -2583,18 +2873,18 @@ QString QDateTime::toString(Qt::DateFormat f) const \row \li dd \li the day as number with a leading zero (01 to 31) \row \li ddd \li the abbreviated localized day name (e.g. 'Mon' to 'Sun'). - Uses QDate::shortDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li dddd \li the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). - Uses QDate::longDayName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li M \li the month as number without a leading zero (1-12) \row \li MM \li the month as number with a leading zero (01-12) \row \li MMM \li the abbreviated localized month name (e.g. 'Jan' to 'Dec'). - Uses QDate::shortMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li MMMM \li the long localized month name (e.g. 'January' to 'December'). - Uses QDate::longMonthName(). + Uses the system locale to localize the name, i.e. QLocale::system(). \row \li yy \li the year as two digit number (00-99) \row \li yyyy \li the year as four digit number \endtable @@ -2607,16 +2897,21 @@ QString QDateTime::toString(Qt::DateFormat f) const \li the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) \row \li hh \li the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \li H + \li the hour without a leading zero (0 to 23, even with AM/PM display) + \row \li HH + \li the hour with a leading zero (00 to 23, even with AM/PM display) \row \li m \li the minute without a leading zero (0 to 59) \row \li mm \li the minute with a leading zero (00 to 59) \row \li s \li the second without a leading zero (0 to 59) \row \li ss \li the second with a leading zero (00 to 59) \row \li z \li the milliseconds without leading zeroes (0 to 999) \row \li zzz \li the milliseconds with leading zeroes (000 to 999) - \row \li AP - \li use AM/PM display. \e AP will be replaced by either "AM" or "PM". - \row \li ap - \li use am/pm display. \e ap will be replaced by either "am" or "pm". + \row \li AP or A + \li use AM/PM display. \e A/AP will be replaced by either "AM" or "PM". + \row \li ap or a + \li use am/pm display. \e a/ap will be replaced by either "am" or "pm". + \row \li t \li the timezone (for example "CEST") \endtable All other input characters will be ignored. Any sequence of characters that @@ -2637,11 +2932,11 @@ QString QDateTime::toString(Qt::DateFormat f) const If the datetime is invalid, an empty string will be returned. - \sa QDate::toString(), QTime::toString() + \sa QDate::toString(), QTime::toString(), QLocale::toString() */ QString QDateTime::toString(const QString& format) const { - return fmtDateTime(format, &d->time, &d->date); + return QLocale::system().toString(*this, format); } #endif //QT_NO_DATESTRING @@ -2655,7 +2950,10 @@ QString QDateTime::toString(const QString& format) const QDateTime QDateTime::addDays(qint64 ndays) const { - return QDateTime(d->date.addDays(ndays), d->time, timeSpec()); + QDateTime dt(*this); + dt.detach(); + dt.d->date = d->date.addDays(ndays); + return dt; } /*! @@ -2668,7 +2966,10 @@ QDateTime QDateTime::addDays(qint64 ndays) const QDateTime QDateTime::addMonths(int nmonths) const { - return QDateTime(d->date.addMonths(nmonths), d->time, timeSpec()); + QDateTime dt(*this); + dt.detach(); + dt.d->date = d->date.addMonths(nmonths); + return dt; } /*! @@ -2681,7 +2982,10 @@ QDateTime QDateTime::addMonths(int nmonths) const QDateTime QDateTime::addYears(int nyears) const { - return QDateTime(d->date.addYears(nyears), d->time, timeSpec()); + QDateTime dt(*this); + dt.detach(); + dt.d->date = d->date.addYears(nyears); + return dt; } QDateTime QDateTimePrivate::addMSecs(const QDateTime &dt, qint64 msecs) @@ -2692,10 +2996,13 @@ QDateTime QDateTimePrivate::addMSecs(const QDateTime &dt, qint64 msecs) QDate utcDate; QTime utcTime; dt.d->getUTC(utcDate, utcTime); - addMSecs(utcDate, utcTime, msecs); + QDateTime utc(utcDate, utcTime, Qt::UTC); - return QDateTime(utcDate, utcTime, Qt::UTC).toTimeSpec(dt.timeSpec()); + if (dt.timeSpec() == Qt::OffsetFromUTC) + return utc.toOffsetFromUtc(dt.d->m_offsetFromUtc); + else + return utc.toTimeSpec(dt.timeSpec()); } /*! @@ -2846,12 +3153,14 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const + static_cast<qint64>(selfTime.msecsTo(otherTime)); } - /*! - \fn QDateTime QDateTime::toTimeSpec(Qt::TimeSpec specification) const + \fn QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const Returns a copy of this datetime converted to the given time - \a specification. + \a spec. + + If \a spec is Qt::OffsetFromUTC then it is set to Qt::UTC. To set to a + spec of Qt::OffsetFromUTC use toOffsetFromUtc(). Example: \snippet code/src_corelib_tools_qdatetime.cpp 16 @@ -2861,19 +3170,41 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const { - if ((d->spec == QDateTimePrivate::UTC) == (spec == Qt::UTC)) - return *this; + if (spec == Qt::UTC || spec == Qt::OffsetFromUTC) { + QDate date; + QTime time; + d->getUTC(date, time); + return QDateTime(date, time, Qt::UTC, 0); + } QDateTime ret; - if (spec == Qt::UTC) { - d->getUTC(ret.d->date, ret.d->time); - ret.d->spec = QDateTimePrivate::UTC; - } else { - ret.d->spec = d->getLocal(ret.d->date, ret.d->time); - } + ret.d->spec = d->getLocal(ret.d->date, ret.d->time); return ret; } + +/*! + \since 5.2 + + \fn QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const + + Returns a copy of this datetime converted to a spec of Qt::OffsetFromUTC + with the given \a offsetSeconds. + + If the \a offsetSeconds equals 0 then a UTC datetime will be returned + + \sa setOffsetFromUtc(), offsetFromUtc(), toTimeSpec() +*/ + +QDateTime QDateTime::toOffsetFromUtc(int offsetSeconds) const +{ + QDate date; + QTime time; + d->getUTC(date, time); + d->addMSecs(date, time, offsetSeconds * 1000); + return QDateTime(date, time, Qt::OffsetFromUTC, offsetSeconds); +} + /*! Returns true if this datetime is equal to the \a other datetime; otherwise returns false. @@ -2883,7 +3214,7 @@ QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const bool QDateTime::operator==(const QDateTime &other) const { - if (d->spec == other.d->spec && d->utcOffset == other.d->utcOffset) + if (d->spec == other.d->spec && d->m_offsetFromUtc == other.d->m_offsetFromUtc) return d->time == other.d->time && d->date == other.d->date; else { QDate date1, date2; @@ -3160,16 +3491,32 @@ qint64 QDateTime::currentMSecsSinceEpoch() Q_DECL_NOTHROW Returns a datetime whose date and time are the number of \a seconds that have passed since 1970-01-01T00:00:00, Coordinated Universal - Time (Qt::UTC). On systems that do not support time zones, the time - will be set as if local time were Qt::UTC. + Time (Qt::UTC) and converted to Qt::LocalTime. On systems that do not + support time zones, the time will be set as if local time were Qt::UTC. \sa toTime_t(), setTime_t() */ QDateTime QDateTime::fromTime_t(uint seconds) { - QDateTime d; - d.setTime_t(seconds); - return d; + return fromMSecsSinceEpoch((qint64)seconds * 1000, Qt::LocalTime); +} + +/*! + \since 5.2 + + Returns a datetime whose date and time are the number of \a seconds + that have passed since 1970-01-01T00:00:00, Coordinated Universal + Time (Qt::UTC) and converted to the given \a spec. + + If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be + ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 + then the spec will be set to Qt::UTC, i.e. an offset of 0 seconds. + + \sa toTime_t(), setTime_t() +*/ +QDateTime QDateTime::fromTime_t(uint seconds, Qt::TimeSpec spec, int offsetSeconds) +{ + return fromMSecsSinceEpoch((qint64)seconds * 1000, spec, offsetSeconds); } /*! @@ -3177,8 +3524,8 @@ QDateTime QDateTime::fromTime_t(uint seconds) Returns a datetime whose date and time are the number of milliseconds, \a msecs, that have passed since 1970-01-01T00:00:00.000, Coordinated Universal - Time (Qt::UTC). On systems that do not support time zones, the time - will be set as if local time were Qt::UTC. + Time (Qt::UTC), and converted to Qt::LocalTime. On systems that do not + support time zones, the time will be set as if local time were Qt::UTC. Note that there are possible values for \a msecs that lie outside the valid range of QDateTime, both negative and positive. The behavior of this @@ -3188,67 +3535,79 @@ QDateTime QDateTime::fromTime_t(uint seconds) */ QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs) { - QDateTime d; - d.setMSecsSinceEpoch(msecs); - return d; + return fromMSecsSinceEpoch(msecs, Qt::LocalTime); } /*! - \since 4.4 - \internal + \since 5.2 - Sets the offset from UTC to \a seconds, and also sets timeSpec() to - Qt::OffsetFromUTC. + Returns a datetime whose date and time are the number of milliseconds \a msecs + that have passed since 1970-01-01T00:00:00.000, Coordinated Universal + Time (Qt::UTC) and converted to the given \a spec. - The maximum and minimum offset is 14 positive or negative hours. If - \a seconds is larger or smaller than that, the result is undefined. + Note that there are possible values for \a msecs that lie outside the valid + range of QDateTime, both negative and positive. The behavior of this + function is undefined for those values. - 0 as offset is identical to UTC. Therefore, if \a seconds is 0, the - timeSpec() will be set to Qt::UTC. Hence the UTC offset always - relates to UTC, and can never relate to local time. + If the \a spec is not Qt::OffsetFromUTC then the \a offsetSeconds will be + ignored. If the \a spec is Qt::OffsetFromUTC and the \a offsetSeconds is 0 + then the spec will be set to Qt::UTC, i.e. an offset of 0 seconds. - \sa isValid(), utcOffset() - */ -void QDateTime::setUtcOffset(int seconds) + \sa fromTime_t() +*/ +QDateTime QDateTime::fromMSecsSinceEpoch(qint64 msecs, Qt::TimeSpec spec, int offsetSeconds) { - detach(); - - /* The motivation to also setting d->spec is to ensure that the QDateTime - * instance stays in well-defined states all the time; instead of that, - * we instruct the user to ensure it. */ - if(seconds == 0) - d->spec = QDateTimePrivate::UTC; - else - d->spec = QDateTimePrivate::OffsetFromUTC; + QDate newDate = QDate(1970, 1, 1); + QTime newTime = QTime(0, 0, 0); + QDateTimePrivate::addMSecs(newDate, newTime, msecs); - /* Even if seconds is 0 we assign it to utcOffset. */ - d->utcOffset = seconds; + switch (spec) { + case Qt::UTC: + return QDateTime(newDate, newTime, Qt::UTC); + case Qt::OffsetFromUTC: + utcToOffset(&newDate, &newTime, offsetSeconds); + return QDateTime(newDate, newTime, Qt::OffsetFromUTC, offsetSeconds); + default: + utcToLocal(newDate, newTime); + return QDateTime(newDate, newTime, Qt::LocalTime); + } } +#if QT_DEPRECATED_SINCE(5, 2) /*! - \since 4.4 - \internal - - Returns the UTC offset in seconds. If the timeSpec() isn't - Qt::OffsetFromUTC, 0 is returned. However, since 0 is a valid UTC - offset, the return value of this function cannot be used to determine - whether a utcOffset() is used or is valid; in that case, timeSpec() must be - checked. + \since 4.4 + \internal + \obsolete - Likewise, if this QDateTime() is invalid or if timeSpec() isn't - Qt::OffsetFromUTC, 0 is returned. + This method was added in 4.4 but never documented as public. It was replaced + in 5.2 with public method setOffsetFromUtc() for consistency with QTimeZone. - The UTC offset only applies if the timeSpec() is Qt::OffsetFromUTC. + This method should never be made public. - \sa isValid(), setUtcOffset() + \sa setOffsetFromUtc() */ +void QDateTime::setUtcOffset(int seconds) +{ + setOffsetFromUtc(seconds); +} + +/*! + \since 4.4 + \internal + \obsolete + + This method was added in 4.4 but never documented as public. It was replaced + in 5.1 with public method offsetFromUTC() for consistency with QTimeZone. + + This method should never be made public. + + \sa offsetFromUTC() +*/ int QDateTime::utcOffset() const { - if(isValid() && d->spec == QDateTimePrivate::OffsetFromUTC) - return d->utcOffset; - else - return 0; + return offsetFromUtc(); } +#endif // QT_DEPRECATED_SINCE #ifndef QT_NO_DATESTRING @@ -3277,144 +3636,169 @@ 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) { + 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(string, &date, &time, &utcOffset); + + if (!date.isValid() || !time.isValid()) + return QDateTime(); + + QDateTime dateTime(date, time, Qt::UTC); + dateTime.setOffsetFromUtc(utcOffset); + return dateTime; + } 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); + const int size = string.size(); + if (size < 10) + return QDateTime(); - tmp = tmp.mid(11); + QString isoString = string; + Qt::TimeSpec spec = Qt::LocalTime; - // Recognize UTC specifications - if (tmp.endsWith(QLatin1Char('Z'))) { - ts = Qt::UTC; - tmp.chop(1); - } + QDate date = QDate::fromString(isoString.left(10), Qt::ISODate); + if (!date.isValid()) + return QDateTime(); + if (size == 10) + return QDateTime(date); - // Recognize timezone specifications - QRegExp rx(QLatin1String("[+-]")); - if (tmp.contains(rx)) { - int idx = tmp.indexOf(rx); - QString tmp2 = tmp.mid(idx); - tmp = tmp.left(idx); - bool ok = true; - int ntzhour = 1; - int ntzminute = 3; - if ( tmp2.indexOf(QLatin1Char(':')) == 3 ) - ntzminute = 4; - const int tzhour(tmp2.mid(ntzhour, 2).toInt(&ok)); - const int tzminute(tmp2.mid(ntzminute, 2).toInt(&ok)); - QTime tzt(tzhour, tzminute); - int utcOffset = (tzt.hour() * 60 + tzt.minute()) * 60; - if ( utcOffset != 0 ) { - ts = Qt::OffsetFromUTC; - QDateTime dt(date, QTime::fromString(tmp, Qt::ISODate), ts); - dt.setUtcOffset( utcOffset * (tmp2.startsWith(QLatin1Char('-')) ? -1 : 1) ); - return dt; + 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; } } - 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. + // 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, ts); + return QDateTime(date, time, spec, offset); } - 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)); #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(); - } - 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 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(); } + 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 = (timeParts.count() > 2) ? timeParts.at(2).toInt(&ok) : 0; - if (!ok) { - return QDateTime(); + int second = 0; + int millisecond = 0; + if (timeParts.count() > 2) { + QStringList secondParts = timeParts.at(2).split(QLatin1Char('.')); + if (secondParts.size() > 2) { + return QDateTime(); + } + + second = secondParts.first().toInt(&ok); + if (!ok) { + return QDateTime(); + } + + if (secondParts.size() > 1) { + millisecond = secondParts.last().toInt(&ok); + if (!ok) { + return QDateTime(); + } + } } - QDate date(year, month, day); - QTime time(hour, minute, second); + QTime time(hour, minute, second, millisecond); + if (!time.isValid()) + return QDateTime(); if (parts.count() == 5) return QDateTime(date, time, Qt::LocalTime); @@ -3422,26 +3806,15 @@ QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f) QString tz = parts.at(5); if (!tz.startsWith(QLatin1String("GMT"), Qt::CaseInsensitive)) return QDateTime(); - QDateTime dt(date, time, Qt::UTC); - if (tz.length() > 3) { - int tzoffset = 0; - QChar sign = tz.at(3); - if ((sign != QLatin1Char('+')) - && (sign != QLatin1Char('-'))) { - return QDateTime(); - } - int tzhour = tz.mid(4, 2).toInt(&ok); - if (!ok) - return QDateTime(); - int tzminute = tz.mid(6).toInt(&ok); + tz.remove(0, 3); + if (!tz.isEmpty()) { + int offset = fromOffsetString(tz, &ok); if (!ok) return QDateTime(); - tzoffset = (tzhour*60 + tzminute) * 60; - if (sign == QLatin1Char('-')) - tzoffset = -tzoffset; - dt.setUtcOffset(tzoffset); + return QDateTime(date, time, Qt::OffsetFromUTC, offset); + } else { + return QDateTime(date, time, Qt::UTC); } - return dt.toLocalTime(); } #endif //QT_NO_TEXTDATE } @@ -3691,7 +4064,7 @@ QDataStream &operator>>(QDataStream &in, QTime &time) */ QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) { - if (out.version() == 13) { + if (out.version() == QDataStream::Qt_5_0) { if (dateTime.isValid()) { // This approach is wrong and should not be used again; it breaks // the guarantee that a deserialised local datetime is the same time @@ -3704,8 +4077,12 @@ QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) out << (qint8)dateTime.timeSpec(); } else { out << dateTime.d->date << dateTime.d->time; - if (out.version() >= 7) + if (out.version() >= QDataStream::Qt_4_0) out << (qint8)dateTime.d->spec; + if (out.version() >= QDataStream::Qt_5_2 + && dateTime.d->spec == QDateTimePrivate::OffsetFromUTC) { + out << qint32(dateTime.offsetFromUtc()); + } } return out; } @@ -3724,220 +4101,98 @@ QDataStream &operator>>(QDataStream &in, QDateTime &dateTime) in >> dateTime.d->date >> dateTime.d->time; - if (in.version() == 13) { + if (in.version() == QDataStream::Qt_5_0) { qint8 ts = 0; in >> ts; if (dateTime.isValid()) { - // We always store the datetime as UTC in 13 onwards. + // We incorrectly stored the datetime as UTC in Qt_5_0. dateTime.d->spec = QDateTimePrivate::UTC; dateTime = dateTime.toTimeSpec(static_cast<Qt::TimeSpec>(ts)); } } else { qint8 ts = (qint8)QDateTimePrivate::LocalUnknown; - if (in.version() >= 7) + if (in.version() >= QDataStream::Qt_4_0) in >> ts; + qint32 offset = 0; + if (in.version() >= QDataStream::Qt_5_2 && ts == qint8(QDateTimePrivate::OffsetFromUTC)) + in >> offset; dateTime.d->spec = (QDateTimePrivate::Spec)ts; + dateTime.d->m_offsetFromUtc = offset; } return in; } #endif // QT_NO_DATASTREAM - -// checks if there is an unquoted 'AP' or 'ap' in the string -static bool hasUnquotedAP(const QString &f) -{ - const QLatin1Char quote('\''); - bool inquote = false; - const int max = f.size(); - for (int i=0; i<max; ++i) { - if (f.at(i) == quote) { - inquote = !inquote; - } else if (!inquote && f.at(i).toUpper() == QLatin1Char('A')) { - return true; - } - } - return false; -} - -#ifndef QT_NO_DATESTRING /***************************************************************************** Some static function used by QDate, QTime and QDateTime *****************************************************************************/ -// Replaces tokens by their value. See QDateTime::toString() for a list of valid tokens -static QString getFmtString(const QString& f, const QTime* dt = 0, const QDate* dd = 0, bool am_pm = false) -{ - if (f.isEmpty()) - return QString(); - - QString buf = f; - int removed = 0; - - if (dt) { - if (f.startsWith(QLatin1String("hh")) || f.startsWith(QLatin1String("HH"))) { - const bool hour12 = f.at(0) == QLatin1Char('h') && am_pm; - if (hour12 && dt->hour() > 12) - buf = QString::number(dt->hour() - 12).rightJustified(2, QLatin1Char('0'), true); - else if (hour12 && dt->hour() == 0) - buf = QLatin1String("12"); - else - buf = QString::number(dt->hour()).rightJustified(2, QLatin1Char('0'), true); - removed = 2; - } else if (f.at(0) == QLatin1Char('h') || f.at(0) == QLatin1Char('H')) { - const bool hour12 = f.at(0) == QLatin1Char('h') && am_pm; - if (hour12 && dt->hour() > 12) - buf = QString::number(dt->hour() - 12); - else if (hour12 && dt->hour() == 0) - buf = QLatin1String("12"); - else - buf = QString::number(dt->hour()); - removed = 1; - } else if (f.startsWith(QLatin1String("mm"))) { - buf = QString::number(dt->minute()).rightJustified(2, QLatin1Char('0'), true); - removed = 2; - } else if (f.at(0) == (QLatin1Char('m'))) { - buf = QString::number(dt->minute()); - removed = 1; - } else if (f.startsWith(QLatin1String("ss"))) { - buf = QString::number(dt->second()).rightJustified(2, QLatin1Char('0'), true); - removed = 2; - } else if (f.at(0) == QLatin1Char('s')) { - buf = QString::number(dt->second()); - } else if (f.startsWith(QLatin1String("zzz"))) { - buf = QString::number(dt->msec()).rightJustified(3, QLatin1Char('0'), true); - removed = 3; - } else if (f.at(0) == QLatin1Char('z')) { - buf = QString::number(dt->msec()); - removed = 1; - } else if (f.at(0).toUpper() == QLatin1Char('A')) { - const bool upper = f.at(0) == QLatin1Char('A'); - buf = dt->hour() < 12 ? QLatin1String("am") : QLatin1String("pm"); - if (upper) - buf = buf.toUpper(); - if (f.size() > 1 && f.at(1).toUpper() == QLatin1Char('P') && - f.at(0).isUpper() == f.at(1).isUpper()) { - removed = 2; - } else { - removed = 1; - } +#ifndef QT_NO_DATESTRING +static void rfcDateImpl(const QString &s, QDate *dd, QTime *dt, int *utcOffset) +{ + int day = -1; + int month = -1; + int year = -1; + int hour = -1; + int min = -1; + int sec = -1; + int hourOffset = 0; + int minOffset = 0; + bool positiveOffset = false; + + // Matches "Wdy, DD Mon YYYY HH:MM:SS ±hhmm" (Wdy, being optional) + QRegExp rex(QStringLiteral("^(?:[A-Z][a-z]+,)?[ \\t]*(\\d{1,2})[ \\t]+([A-Z][a-z]+)[ \\t]+(\\d\\d\\d\\d)(?:[ \\t]+(\\d\\d):(\\d\\d)(?::(\\d\\d))?)?[ \\t]*(?:([+-])(\\d\\d)(\\d\\d))?")); + if (s.indexOf(rex) == 0) { + if (dd) { + day = rex.cap(1).toInt(); + month = qt_monthNumberFromShortName(rex.cap(2)); + year = rex.cap(3).toInt(); } - } - - if (dd) { - if (f.startsWith(QLatin1String("dddd"))) { - buf = dd->longDayName(dd->dayOfWeek()); - removed = 4; - } else if (f.startsWith(QLatin1String("ddd"))) { - buf = dd->shortDayName(dd->dayOfWeek()); - removed = 3; - } else if (f.startsWith(QLatin1String("dd"))) { - buf = QString::number(dd->day()).rightJustified(2, QLatin1Char('0'), true); - removed = 2; - } else if (f.at(0) == QLatin1Char('d')) { - buf = QString::number(dd->day()); - removed = 1; - } else if (f.startsWith(QLatin1String("MMMM"))) { - buf = dd->longMonthName(dd->month()); - removed = 4; - } else if (f.startsWith(QLatin1String("MMM"))) { - buf = dd->shortMonthName(dd->month()); - removed = 3; - } else if (f.startsWith(QLatin1String("MM"))) { - buf = QString::number(dd->month()).rightJustified(2, QLatin1Char('0'), true); - removed = 2; - } else if (f.at(0) == QLatin1Char('M')) { - buf = QString::number(dd->month()); - removed = 1; - } else if (f.startsWith(QLatin1String("yyyy"))) { - const int year = dd->year(); - buf = QString::number(qAbs(year)).rightJustified(4, QLatin1Char('0')); - if(year > 0) - removed = 4; - else - { - buf.prepend(QLatin1Char('-')); - removed = 5; + if (dt) { + if (!rex.cap(4).isEmpty()) { + hour = rex.cap(4).toInt(); + min = rex.cap(5).toInt(); + sec = rex.cap(6).toInt(); } - - } else if (f.startsWith(QLatin1String("yy"))) { - buf = QString::number(dd->year()).right(2).rightJustified(2, QLatin1Char('0')); - removed = 2; + positiveOffset = (rex.cap(7) == QStringLiteral("+")); + hourOffset = rex.cap(8).toInt(); + minOffset = rex.cap(9).toInt(); } - } - if (removed == 0 || removed >= f.size()) { - return buf; - } - - return buf + getFmtString(f.mid(removed), dt, dd, am_pm); -} - -// Parses the format string and uses getFmtString to get the values for the tokens. Ret -static QString fmtDateTime(const QString& f, const QTime* dt, const QDate* dd) -{ - QString buf; - - if (f.isEmpty()) - return buf; - if (dt && !dt->isValid()) - return buf; - if (dd && !dd->isValid()) - return buf; - - const bool ap = hasUnquotedAP(f); - - QString frm; - uint status = '0'; - - for (int i = 0, n = f.length(); i < n; ++i) { - const QChar c = f.at(i); - const uint cc = c.unicode(); - if (cc == '\'') { - if (status == cc) { - if (i > 0 && f.at(i - 1).unicode() == cc) - buf += c; - status = '0'; - } else { - if (!frm.isEmpty()) { - buf += getFmtString(frm, dt, dd, ap); - frm.clear(); - } - status = cc; + if (utcOffset) + *utcOffset = ((hourOffset * 60 + minOffset) * (positiveOffset ? 60 : -60)); + } else { + // Matches "Wdy Mon DD HH:MM:SS YYYY" + QRegExp rex(QStringLiteral("^[A-Z][a-z]+[ \\t]+([A-Z][a-z]+)[ \\t]+(\\d\\d)(?:[ \\t]+(\\d\\d):(\\d\\d):(\\d\\d))?[ \\t]+(\\d\\d\\d\\d)[ \\t]*(?:([+-])(\\d\\d)(\\d\\d))?")); + if (s.indexOf(rex) == 0) { + if (dd) { + month = qt_monthNumberFromShortName(rex.cap(1)); + day = rex.cap(2).toInt(); + year = rex.cap(6).toInt(); } - } else if (status == '\'') { - buf += c; - } else if (c == status) { - if (ap && (cc == 'P' || cc == 'p')) - status = '0'; - frm += c; - } else { - buf += getFmtString(frm, dt, dd, ap); - frm.clear(); - if (cc == 'h' || cc == 'm' || cc == 'H' || cc == 's' || cc == 'z') { - status = cc; - frm += c; - } else if (cc == 'd' || cc == 'M' || cc == 'y') { - status = cc; - frm += c; - } else if (ap && cc == 'A') { - status = 'P'; - frm += c; - } else if (ap && cc == 'a') { - status = 'p'; - frm += c; - } else { - buf += c; - status = '0'; + if (dt) { + if (!rex.cap(3).isEmpty()) { + hour = rex.cap(3).toInt(); + min = rex.cap(4).toInt(); + sec = rex.cap(5).toInt(); + } + positiveOffset = (rex.cap(7) == QStringLiteral("+")); + hourOffset = rex.cap(8).toInt(); + minOffset = rex.cap(9).toInt(); } + if (utcOffset) + *utcOffset = ((hourOffset * 60 + minOffset) * (positiveOffset ? 60 : -60)); } } - buf += getFmtString(frm, dt, dd, ap); - - return buf; + if (dd) + *dd = QDate(year, month, day); + if (dt) + *dt = QTime(hour, min, sec); } #endif // QT_NO_DATESTRING + #ifdef Q_OS_WIN static const int LowerYear = 1980; #else @@ -3968,6 +4223,7 @@ static QDate adjustDate(QDate date) return date; } +// Convert passed in UTC datetime into LocalTime and return spec static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time) { QDate fakeDate = adjustDate(date); @@ -4021,6 +4277,7 @@ static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time) } } +// Convert passed in LocalTime datetime into UTC static void localToUtc(QDate &date, QTime &time, int isdst) { if (!date.isValid()) @@ -4093,44 +4350,75 @@ static void localToUtc(QDate &date, QTime &time, int isdst) } } +// Convert passed in OffsetFromUTC datetime and offset into UTC +static void offsetToUtc(QDate *outDate, QTime *outTime, qint32 offset) +{ + QDateTimePrivate::addMSecs(*outDate, *outTime, -(qint64(offset) * 1000)); +} + +// Convert passed in UTC datetime and offset into OffsetFromUTC +static void utcToOffset(QDate *outDate, QTime *outTime, qint32 offset) +{ + QDateTimePrivate::addMSecs(*outDate, *outTime, (qint64(offset) * 1000)); +} + +// Get current date/time in LocalTime and put result in outDate and outTime QDateTimePrivate::Spec QDateTimePrivate::getLocal(QDate &outDate, QTime &outTime) const { outDate = date; outTime = time; if (spec == QDateTimePrivate::UTC) return utcToLocal(outDate, outTime); + if (spec == QDateTimePrivate::OffsetFromUTC) { + offsetToUtc(&outDate, &outTime, m_offsetFromUtc); + return utcToLocal(outDate, outTime); + } return spec; } +// Get current date/time in UTC and put result in outDate and outTime void QDateTimePrivate::getUTC(QDate &outDate, QTime &outTime) const { outDate = date; outTime = time; - const bool isOffset = spec == QDateTimePrivate::OffsetFromUTC; - if (spec != QDateTimePrivate::UTC && !isOffset) + if (spec == QDateTimePrivate::OffsetFromUTC) + offsetToUtc(&outDate, &outTime, m_offsetFromUtc); + else if (spec != QDateTimePrivate::UTC) localToUtc(outDate, outTime, (int)spec); - - if (isOffset) - addMSecs(outDate, outTime, -(qint64(utcOffset) * 1000)); } #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) QDebug operator<<(QDebug dbg, const QDate &date) { - dbg.nospace() << "QDate(" << date.toString() << ')'; + dbg.nospace() << "QDate(" << date.toString(QStringLiteral("yyyy-MM-dd")) << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QTime &time) { - dbg.nospace() << "QTime(" << time.toString() << ')'; + dbg.nospace() << "QTime(" << time.toString(QStringLiteral("HH:mm:ss.zzz")) << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QDateTime &date) { - dbg.nospace() << "QDateTime(" << date.toString() << ')'; + QString spec; + switch (date.d->spec) { + case QDateTimePrivate::UTC : + spec = QStringLiteral(" Qt::UTC"); + break; + case QDateTimePrivate::OffsetFromUTC : + spec = QString::fromUtf8(" Qt::OffsetFromUTC %1s").arg(date.offsetFromUtc()); + break; + case QDateTimePrivate::LocalDST : + case QDateTimePrivate::LocalStandard : + case QDateTimePrivate::LocalUnknown : + default : + spec = QStringLiteral(" Qt::LocalTime"); + } + QString output = date.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz t")) + spec; + dbg.nospace() << "QDateTime(" << output << ')'; return dbg.space(); } #endif |