diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/corelib/tools/qdatetime.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/corelib/tools/qdatetime.cpp')
-rw-r--r-- | src/corelib/tools/qdatetime.cpp | 5506 |
1 files changed, 5506 insertions, 0 deletions
diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp new file mode 100644 index 0000000..781514c --- /dev/null +++ b/src/corelib/tools/qdatetime.cpp @@ -0,0 +1,5506 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "private/qdatetime_p.h" + +#include "qdatastream.h" +#include "qset.h" +#include "qlocale.h" +#include "qdatetime.h" +#include "qregexp.h" +#include "qdebug.h" +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +#include <windows.h> +#endif +#ifndef Q_WS_WIN +#include <locale.h> +#endif + +#include <time.h> +#if defined(Q_OS_WINCE) +#include "qfunctions_wince.h" +#endif + +//#define QDATETIMEPARSER_DEBUG +#if defined (QDATETIMEPARSER_DEBUG) && !defined(QT_NO_DEBUG_STREAM) +# define QDTPDEBUG qDebug() << QString("%1:%2").arg(__FILE__).arg(__LINE__) +# define QDTPDEBUGN qDebug +#else +# define QDTPDEBUG if (false) qDebug() +# define QDTPDEBUGN if (false) qDebug +#endif + +#if defined(Q_WS_MAC) +#include <private/qcore_mac_p.h> +#endif + +QT_BEGIN_NAMESPACE + +enum { + FIRST_YEAR = -4713, + FIRST_MONTH = 1, + FIRST_DAY = 2, // ### Qt 5: make FIRST_DAY = 1, by support jd == 0 as valid + SECS_PER_DAY = 86400, + MSECS_PER_DAY = 86400000, + SECS_PER_HOUR = 3600, + MSECS_PER_HOUR = 3600000, + SECS_PER_MIN = 60, + MSECS_PER_MIN = 60000 +}; + +static inline QDate fixedDate(int y, int m, int d) +{ + QDate result(y, m, 1); + result.setDate(y, m, qMin(d, result.daysInMonth())); + return result; +} + +static uint julianDayFromDate(int year, int month, int day) +{ + if (year < 0) + ++year; + + if (year > 1582 || (year == 1582 && (month > 10 || (month == 10 && day >= 15)))) { + // Gregorian calendar starting from October 15, 1582 + // Algorithm from Henry F. Fliegel and Thomas C. Van Flandern + return (1461 * (year + 4800 + (month - 14) / 12)) / 4 + + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 + - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + + day - 32075; + } else if (year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day <= 4)))) { + // Julian calendar until October 4, 1582 + // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering + int a = (14 - month) / 12; + return (153 * (month + (12 * a) - 3) + 2) / 5 + + (1461 * (year + 4800 - a)) / 4 + + day - 32083; + } else { + // the day following October 4, 1582 is October 15, 1582 + return 0; + } +} + +static void getDateFromJulianDay(uint julianDay, int *year, int *month, int *day) +{ + int y, m, d; + + if (julianDay >= 2299161) { + // Gregorian calendar starting from October 15, 1582 + // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern + qulonglong ell, n, i, j; + ell = qulonglong(julianDay) + 68569; + n = (4 * ell) / 146097; + ell = ell - (146097 * n + 3) / 4; + i = (4000 * (ell + 1)) / 1461001; + ell = ell - (1461 * i) / 4 + 31; + j = (80 * ell) / 2447; + d = ell - (2447 * j) / 80; + ell = j / 11; + m = j + 2 - (12 * ell); + y = 100 * (n - 49) + i + ell; + } else { + // Julian calendar until October 4, 1582 + // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering + julianDay += 32082; + int dd = (4 * julianDay + 3) / 1461; + int ee = julianDay - (1461 * dd) / 4; + int mm = ((5 * ee) + 2) / 153; + d = ee - (153 * mm + 2) / 5 + 1; + m = mm + 3 - 12 * (mm / 10); + y = dd - 4800 + (mm / 10); + if (y <= 0) + --y; + } + if (year) + *year = y; + if (month) + *month = m; + if (day) + *day = d; +} + + +static const char monthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +#ifndef QT_NO_TEXTDATE +static const char * const qt_shortMonthNames[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +#endif +#ifndef QT_NO_DATESTRING +static QString fmtDateTime(const QString& f, const QTime* dt = 0, const QDate* dd = 0); +#endif + +/***************************************************************************** + QDate member functions + *****************************************************************************/ + +/*! + \since 4.5 + + \enum QDate::MonthNameType + + This enum describes the types of the string representation used + for the month name. + + \value DateFormat This type of name can be used for date-to-string formatting. + \value StandaloneFormat This type is used when you need to enumerate months or weekdays. + Usually standalone names are represented in singular forms with + capitalized first letter. +*/ + +/*! + \class QDate + \reentrant + \brief The QDate class provides date functions. + + \ingroup time + \mainclass + + A QDate object contains a calendar date, i.e. year, month, and day + numbers, in the Gregorian calendar. (see \l{QDate G and J} {Use of + Gregorian and Julian Calendars} for dates prior to 15 October + 1582). It can read the current date from the system clock. It + provides functions for comparing dates, and for manipulating + dates. For example, it is possible to add and subtract days, + months, and years to dates. + + A QDate object is typically created either by giving the year, + month, and day numbers explicitly. Note that QDate interprets two + digit years as is, i.e., years 0 - 99. A QDate can also be + constructed with the static function currentDate(), which creates + a QDate object containing the system clock's date. An explicit + date can also be set using setDate(). The fromString() function + returns a QDate given a string and a date format which is used to + interpret the date within the string. + + The year(), month(), and day() functions provide access to the + year, month, and day numbers. Also, dayOfWeek() and dayOfYear() + functions are provided. The same information is provided in + textual format by the toString(), shortDayName(), longDayName(), + shortMonthName(), and longMonthName() functions. + + QDate provides a full set of operators to compare two QDate + objects where smaller means earlier, and larger means later. + + You can increment (or decrement) a date by a given number of days + using addDays(). Similarly you can use addMonths() and addYears(). + The daysTo() function returns the number of days between two + dates. + + The daysInMonth() and daysInYear() functions return how many days + there are in this date's month and year, respectively. The + isLeapYear() function indicates whether a date is in a leap year. + + \section1 + + \target QDate G and J + \section2 Use of Gregorian and Julian Calendars + + QDate uses the Gregorian calendar in all locales, beginning + on the date 15 October 1582. For dates up to and including 4 + October 1582, the Julian calendar is used. This means there is a + 10-day gap in the internal calendar between the 4th and the 15th + of October 1582. When you use QDateTime for dates in that epoch, + the day after 4 October 1582 is 15 October 1582, and the dates in + the gap are invalid. + + The Julian to Gregorian changeover date used here is the date when + the Gregorian calendar was first introduced, by Pope Gregory + XIII. That change was not universally accepted and some localities + only executed it at a later date (if at all). QDateTime + doesn't take any of these historical facts into account. If an + application must support a locale-specific dating system, it must + do so on its own, remembering to convert the dates using the + Julian day. + + \section2 No Year 0 + + There is no year 0. Dates in that year are considered invalid. The + year -1 is the year "1 before Christ" or "1 before current era." + The day before 0001-01-01 is December 31st, 1 BCE. + + \section2 Range of Valid Dates + + The range of valid dates is from January 2nd, 4713 BCE, to + sometime in the year 11 million CE. The Julian Day returned by + QDate::toJulianDay() is a number in the contiguous range from 1 to + \e{overflow}, even across QDateTime's "date holes". It is suitable + for use in applications that must convert a QDateTime to a date in + another calendar system, e.g., Hebrew, Islamic or Chinese. + + \sa QTime, QDateTime, QDateEdit, QDateTimeEdit, QCalendarWidget +*/ + +/*! + \fn QDate::QDate() + + Constructs a null date. Null dates are invalid. + + \sa isNull(), isValid() +*/ + +/*! + Constructs a date with year \a y, month \a m and day \a d. + + If the specified date is invalid, the date is not set and + isValid() returns false. A date before 2 January 4713 B.C. is + considered invalid. + + \warning Years 0 to 99 are interpreted as is, i.e., years + 0-99. + + \sa isValid() +*/ + +QDate::QDate(int y, int m, int d) +{ + setDate(y, m, d); +} + + +/*! + \fn bool QDate::isNull() const + + Returns true if the date is null; otherwise returns false. A null + date is invalid. + + \note The behavior of this function is equivalent to isValid(). + + \sa isValid() +*/ + + +/*! + Returns true if this date is valid; otherwise returns false. + + \sa isNull() +*/ + +bool QDate::isValid() const +{ + return !isNull(); +} + + +/*! + Returns the year of this date. Negative numbers indicate years + before 1 A.D. = 1 C.E., such that year -44 is 44 B.C. + + \sa month(), day() +*/ + +int QDate::year() const +{ + int y; + getDateFromJulianDay(jd, &y, 0, 0); + return y; +} + +/*! + Returns the number corresponding to the month of this date, using + the following convention: + + \list + \i 1 = "January" + \i 2 = "February" + \i 3 = "March" + \i 4 = "April" + \i 5 = "May" + \i 6 = "June" + \i 7 = "July" + \i 8 = "August" + \i 9 = "September" + \i 10 = "October" + \i 11 = "November" + \i 12 = "December" + \endlist + + \sa year(), day() +*/ + +int QDate::month() const +{ + int m; + getDateFromJulianDay(jd, 0, &m, 0); + return m; +} + +/*! + Returns the day of the month (1 to 31) of this date. + + \sa year(), month(), dayOfWeek() +*/ + +int QDate::day() const +{ + int d; + getDateFromJulianDay(jd, 0, 0, &d); + return d; +} + +/*! + Returns the weekday (1 to 7) for this date. + + \sa day(), dayOfYear(), Qt::DayOfWeek +*/ + +int QDate::dayOfWeek() const +{ + return (jd % 7) + 1; +} + +/*! + Returns the day of the year (1 to 365 or 366 on leap years) for + this date. + + \sa day(), dayOfWeek() +*/ + +int QDate::dayOfYear() const +{ + return jd - julianDayFromDate(year(), 1, 1) + 1; +} + +/*! + Returns the number of days in the month (28 to 31) for this date. + + \sa day(), daysInYear() +*/ + +int QDate::daysInMonth() const +{ + int y, m, d; + getDateFromJulianDay(jd, &y, &m, &d); + if (m == 2 && isLeapYear(y)) + return 29; + else + return monthDays[m]; +} + +/*! + Returns the number of days in the year (365 or 366) for this date. + + \sa day(), daysInMonth() +*/ + +int QDate::daysInYear() const +{ + int y, m, d; + getDateFromJulianDay(jd, &y, &m, &d); + return isLeapYear(y) ? 366 : 365; +} + +/*! + Returns the week number (1 to 53), and stores the year in + *\a{yearNumber} unless \a yearNumber is null (the default). + + Returns 0 if the date is invalid. + + In accordance with ISO 8601, weeks start on Monday and the first + Thursday of a year is always in week 1 of that year. Most years + have 52 weeks, but some have 53. + + *\a{yearNumber} is not always the same as year(). For example, 1 + January 2000 has week number 52 in the year 1999, and 31 December + 2002 has week number 1 in the year 2003. + + \legalese + Copyright (c) 1989 The Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms are permitted + provided that the above copyright notice and this paragraph are + duplicated in all such forms and that any documentation, + advertising materials, and other materials related to such + distribution and use acknowledge that the software was developed + by the University of California, Berkeley. The name of the + University may not be used to endorse or promote products derived + from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + \sa isValid() +*/ + +int QDate::weekNumber(int *yearNumber) const +{ + if (!isValid()) + return 0; + + int year = QDate::year(); + int yday = dayOfYear() - 1; + int wday = dayOfWeek(); + if (wday == 7) + wday = 0; + int w; + + for (;;) { + int len; + int bot; + int top; + + len = isLeapYear(year) ? 366 : 365; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % 7) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - (len % 7); + if (top < -3) + top += 7; + top += len; + if (yday >= top) { + ++year; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / 7); + break; + } + --year; + yday += isLeapYear(year) ? 366 : 365; + } + if (yearNumber != 0) + *yearNumber = year; + return w; +} + +#ifndef QT_NO_TEXTDATE +/*! + \since 4.5 + + Returns the short name of the \a month for the representation specified + by \a type. + + The months are enumerated using the following convention: + + \list + \i 1 = "Jan" + \i 2 = "Feb" + \i 3 = "Mar" + \i 4 = "Apr" + \i 5 = "May" + \i 6 = "Jun" + \i 7 = "Jul" + \i 8 = "Aug" + \i 9 = "Sep" + \i 10 = "Oct" + \i 11 = "Nov" + \i 12 = "Dec" + \endlist + + The month names will be localized according to the system's locale + settings. + + \sa toString(), longMonthName(), shortDayName(), longDayName() +*/ + +QString QDate::shortMonthName(int month, QDate::MonthNameType type) +{ + if (month < 1 || month > 12) { + month = 1; + } + switch (type) { + case QDate::DateFormat: + return QLocale::system().monthName(month, QLocale::ShortFormat); + case QDate::StandaloneFormat: + return QLocale::system().standaloneMonthName(month, QLocale::ShortFormat); + default: + break; + } + return QString(); +} + +/*! + Returns the short version of the name of the \a month. The + returned name is in normal type which can be used for date formatting. + + \sa toString(), longMonthName(), shortDayName(), longDayName() + */ + +QString QDate::shortMonthName(int month) +{ + return shortMonthName(month, QDate::DateFormat); +} + +/*! + \since 4.5 + + Returns the long name of the \a month for the representation specified + by \a type. + + The months are enumerated using the following convention: + + \list + \i 1 = "January" + \i 2 = "February" + \i 3 = "March" + \i 4 = "April" + \i 5 = "May" + \i 6 = "June" + \i 7 = "July" + \i 8 = "August" + \i 9 = "September" + \i 10 = "October" + \i 11 = "November" + \i 12 = "December" + \endlist + + The month names will be localized according to the system's locale + settings. + + \sa toString(), shortMonthName(), shortDayName(), longDayName() +*/ + +QString QDate::longMonthName(int month, MonthNameType type) +{ + if (month < 1 || month > 12) { + month = 1; + } + switch (type) { + case QDate::DateFormat: + return QLocale::system().monthName(month, QLocale::LongFormat); + case QDate::StandaloneFormat: + return QLocale::system().standaloneMonthName(month, QLocale::LongFormat); + default: + break; + } + return QString(); +} + +/*! + Returns the long version of the name of the \a month. The + returned name is in normal type which can be used for date formatting. + + \sa toString(), shortMonthName(), shortDayName(), longDayName() + */ + +QString QDate::longMonthName(int month) +{ + if (month < 1 || month > 12) { + month = 1; + } + return QLocale::system().monthName(month, QLocale::LongFormat); +} + +/*! + \since 4.5 + + Returns the short name of the \a weekday for the representation specified + by \a type. + + The days are enumerated using the following convention: + + \list + \i 1 = "Mon" + \i 2 = "Tue" + \i 3 = "Wed" + \i 4 = "Thu" + \i 5 = "Fri" + \i 6 = "Sat" + \i 7 = "Sun" + \endlist + + The day names will be localized according to the system's locale + settings. + + \sa toString(), shortMonthName(), longMonthName(), longDayName() +*/ + +QString QDate::shortDayName(int weekday, MonthNameType type) +{ + if (weekday < 1 || weekday > 7) { + weekday = 1; + } + switch (type) { + case QDate::DateFormat: + return QLocale::system().dayName(weekday, QLocale::ShortFormat); + case QDate::StandaloneFormat: + return QLocale::system().standaloneDayName(weekday, QLocale::ShortFormat); + default: + break; + } + return QString(); +} + +/*! + Returns the short version of the name of the \a weekday. The + returned name is in normal type which can be used for date formatting. + + \sa toString(), longDayName(), shortMonthName(), longMonthName() + */ + +QString QDate::shortDayName(int weekday) +{ + if (weekday < 1 || weekday > 7) { + weekday = 1; + } + return QLocale::system().dayName(weekday, QLocale::ShortFormat); +} + +/*! + \since 4.5 + + Returns the long name of the \a weekday for the representation specified + by \a type. + + The days are enumerated using the following convention: + + \list + \i 1 = "Monday" + \i 2 = "Tuesday" + \i 3 = "Wednesday" + \i 4 = "Thursday" + \i 5 = "Friday" + \i 6 = "Saturday" + \i 7 = "Sunday" + \endlist + + The day names will be localized according to the system's locale + settings. + + \sa toString(), shortDayName(), shortMonthName(), longMonthName() +*/ + +QString QDate::longDayName(int weekday, MonthNameType type) +{ + if (weekday < 1 || weekday > 7) { + weekday = 1; + } + switch (type) { + case QDate::DateFormat: + return QLocale::system().dayName(weekday, QLocale::LongFormat); + case QDate::StandaloneFormat: + return QLocale::system().standaloneDayName(weekday, QLocale::LongFormat); + default: + break; + } + return QLocale::system().dayName(weekday, QLocale::LongFormat); +} + +/*! + Returns the long version of the name of the \a weekday. The + returned name is in normal type which can be used for date formatting. + + \sa toString(), shortDayName(), shortMonthName(), longMonthName() + */ + +QString QDate::longDayName(int weekday) +{ + if (weekday < 1 || weekday > 7) { + weekday = 1; + } + return QLocale::system().dayName(weekday, QLocale::LongFormat); +} +#endif //QT_NO_TEXTDATE + +#ifndef QT_NO_DATESTRING + +/*! + \fn QString QDate::toString(Qt::DateFormat format) const + + \overload + + Returns the date as a string. The \a format parameter determines + the format of the string. + + 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. An example of this formatting is + "Sat May 20 1995". + + 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-DD, where YYYY is the + year, MM is the month of the year (between 01 and 12), and DD is + the day of the month between 01 and 31. + + If the \a format is Qt::SystemLocaleShortDate or + Qt::SystemLocaleLongDate, the string format depends on the locale + settings of the system. Identical to calling + QLocale::system().toString(date, QLocale::ShortFormat) or + QLocale::system().toString(date, QLocale::LongFormat). + + If the \a format is Qt::DefaultLocaleShortDate or + Qt::DefaultLocaleLongDate, the string format depends on the + default application locale. This is the locale set with + QLocale::setDefault(), or the system locale if no default locale + has been set. Identical to calling QLocale().toString(date, + QLocale::ShortFormat) or QLocale().toString(date, + QLocale::LongFormat). + + If the date 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 shortDayName(), shortMonthName() +*/ +QString QDate::toString(Qt::DateFormat f) const +{ + if (!isValid()) + return QString(); + int y, m, d; + getDateFromJulianDay(jd, &y, &m, &d); + switch (f) { + case Qt::SystemLocaleDate: + case Qt::SystemLocaleShortDate: + case Qt::SystemLocaleLongDate: + return QLocale::system().toString(*this, f == Qt::SystemLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + case Qt::DefaultLocaleLongDate: + return QLocale().toString(*this, f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat); + 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); + } +#endif + case Qt::ISODate: + { + if (year() < 0 || year() > 9999) + return QString(); + QString month(QString::number(m).rightJustified(2, QLatin1Char('0'))); + QString day(QString::number(d).rightJustified(2, QLatin1Char('0'))); + return QString::number(y) + QLatin1Char('-') + month + QLatin1Char('-') + day; + } + } +} + +/*! + Returns the date as a string. The \a format parameter determines + the format of the result string. + + These expressions may be used: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1 to 12) + \row \i MM \i the month as number with a leading zero (01 to 12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00 to 99) + \row \i yyyy \i the year as four digit number. If the year is negative, + a minus sign is prepended in addition. + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in singlequotes will be treated as text and not be used as an + expression. Two consecutive singlequotes ("''") are replaced by a singlequote + in the output. + + Example format strings (assuming that the QDate is the 20 July + 1969): + + \table + \header \o Format \o Result + \row \o dd.MM.yyyy \o 20.07.1969 + \row \o ddd MMMM d yy \o Sun July 20 69 + \row \o 'The day is' dddd \o The day is Sunday + \endtable + + 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() + +*/ +QString QDate::toString(const QString& format) const +{ + if (year() > 9999) + return QString(); + return fmtDateTime(format, 0, this); +} +#endif //QT_NO_DATESTRING + +/*! + \obsolete + + Sets the date's year \a y, month \a m, and day \a d. + + If \a y is in the range 0 to 99, it is interpreted as 1900 to + 1999. + + Use setDate() instead. +*/ + +bool QDate::setYMD(int y, int m, int d) +{ + if (uint(y) <= 99) + y += 1900; + return setDate(y, m, d); +} + +/*! + \since 4.2 + + Sets the date's \a year, \a month, and \a day. Returns true if + the date is valid; otherwise returns false. + + If the specified date is invalid, the QDate object is set to be + invalid. Any date before 2 January 4713 B.C. is considered + invalid. + + \sa isValid() +*/ +bool QDate::setDate(int year, int month, int day) +{ + if (!isValid(year, month, day)) { + jd = 0; + } else { + jd = julianDayFromDate(year, month, day); + } + return jd != 0; +} + +/*! + \since 4.5 + + Extracts the date's year, month, and day, and assigns them to + *\a year, *\a month, and *\a day. The pointers may be null. + + \sa year(), month(), day(), isValid() +*/ +void QDate::getDate(int *year, int *month, int *day) +{ + getDateFromJulianDay(jd, year, month, day); +} + +/*! + Returns a QDate object containing a date \a ndays later than the + date of this object (or earlier if \a ndays is negative). + + \sa addMonths() addYears() daysTo() +*/ + +QDate QDate::addDays(int ndays) const +{ + QDate d; + // this is basically "d.jd = jd + ndays" with checks for integer overflow + if (ndays >= 0) + d.jd = (jd + ndays >= jd) ? jd + ndays : 0; + else + d.jd = (jd + ndays < jd) ? jd + ndays : 0; + return d; +} + +/*! + Returns a QDate object containing a date \a nmonths later than the + date of this object (or earlier if \a nmonths is negative). + + \note If the ending day/month combination does not exist in the + resulting month/year, this function will return a date that is the + latest valid date. + + \warning QDate has a date hole around the days introducing the + Gregorian calendar (the days 5 to 14 October 1582, inclusive, do + not exist). If the calculation ends in one of those days, QDate + will return either October 4 or October 15. + + \sa addDays() addYears() +*/ + +QDate QDate::addMonths(int nmonths) const +{ + if (!isValid()) + return QDate(); + if (!nmonths) + return *this; + + int old_y, y, m, d; + getDateFromJulianDay(jd, &y, &m, &d); + old_y = y; + + bool increasing = nmonths > 0; + + while (nmonths != 0) { + if (nmonths < 0 && nmonths + 12 <= 0) { + y--; + nmonths+=12; + } else if (nmonths < 0) { + m+= nmonths; + nmonths = 0; + if (m <= 0) { + --y; + m += 12; + } + } else if (nmonths - 12 >= 0) { + y++; + nmonths -= 12; + } else if (m == 12) { + y++; + m = 0; + } else { + m += nmonths; + nmonths = 0; + if (m > 12) { + ++y; + m -= 12; + } + } + } + + // was there a sign change? + if ((old_y > 0 && y <= 0) || + (old_y < 0 && y >= 0)) + // yes, adjust the date by +1 or -1 years + y += increasing ? +1 : -1; + + // did we end up in the Gregorian/Julian conversion hole? + if (y == 1582 && m == 10 && d > 4 && d < 15) + d = increasing ? 15 : 4; + + return fixedDate(y, m, d); +} + +/*! + Returns a QDate object containing a date \a nyears later than the + date of this object (or earlier if \a nyears is negative). + + \note If the ending day/month combination does not exist in the + resulting year (i.e., if the date was Feb 29 and the final year is + not a leap year), this function will return a date that is the + latest valid date (that is, Feb 28). + + \sa addDays(), addMonths() +*/ + +QDate QDate::addYears(int nyears) const +{ + if (!isValid()) + return QDate(); + + int y, m, d; + getDateFromJulianDay(jd, &y, &m, &d); + + int old_y = y; + y += nyears; + + // was there a sign change? + if ((old_y > 0 && y <= 0) || + (old_y < 0 && y >= 0)) + // yes, adjust the date by +1 or -1 years + y += nyears > 0 ? +1 : -1; + + return fixedDate(y, m, d); +} + +/*! + Returns the number of days from this date to \a d (which is + negative if \a d is earlier than this date). + + Example: + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 0 + + \sa addDays() +*/ + +int QDate::daysTo(const QDate &d) const +{ + return d.jd - jd; +} + + +/*! + \fn bool QDate::operator==(const QDate &d) const + + Returns true if this date is equal to \a d; otherwise returns + false. + +*/ + +/*! + \fn bool QDate::operator!=(const QDate &d) const + + Returns true if this date is different from \a d; otherwise + returns false. +*/ + +/*! + \fn bool QDate::operator<(const QDate &d) const + + Returns true if this date is earlier than \a d; otherwise returns + false. +*/ + +/*! + \fn bool QDate::operator<=(const QDate &d) const + + Returns true if this date is earlier than or equal to \a d; + otherwise returns false. +*/ + +/*! + \fn bool QDate::operator>(const QDate &d) const + + Returns true if this date is later than \a d; otherwise returns + false. +*/ + +/*! + \fn bool QDate::operator>=(const QDate &d) const + + Returns true if this date is later than or equal to \a d; + otherwise returns false. +*/ + +/*! + \overload + Returns the current date, as reported by the system clock. + + \sa QTime::currentTime(), QDateTime::currentDateTime() +*/ + +QDate QDate::currentDate() +{ + QDate d; +#if defined(Q_OS_WIN) + SYSTEMTIME st; + memset(&st, 0, sizeof(SYSTEMTIME)); + GetLocalTime(&st); + d.jd = julianDayFromDate(st.wYear, st.wMonth, st.wDay); +#else + // posix compliant system + time_t ltime; + time(<ime); + tm *t = 0; + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + t = localtime_r(<ime, &res); +#else + t = localtime(<ime); +#endif // !QT_NO_THREAD && _POSIX_THREAD_SAFE_FUNCTIONS + + d.jd = julianDayFromDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); +#endif + return d; +} + +#ifndef QT_NO_DATESTRING +/*! + \fn QDate QDate::fromString(const QString &string, Qt::DateFormat format) + + Returns the QDate represented by the \a string, using the + \a format given, or an invalid date if the string cannot be + parsed. + + Note for Qt::TextDate: It is recommended that you use the + 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) +{ + if (s.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; + case Qt::SystemLocaleDate: + case Qt::SystemLocaleShortDate: + case Qt::SystemLocaleLongDate: + return fromString(s, QLocale::system().dateFormat(f == Qt::SystemLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat)); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + case Qt::DefaultLocaleLongDate: + return fromString(s, QLocale().dateFormat(f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat)); + default: +#ifndef QT_NO_TEXTDATE + case Qt::TextDate: { + QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts); + + if (parts.count() != 4) { + return QDate(); + } + + QString monthName = parts.at(1); + int month = -1; + // Assume that English monthnames are the default + for (int i = 0; i < 12; ++i) { + if (monthName == QLatin1String(qt_shortMonthNames[i])) { + month = i + 1; + break; + } + } + // If English names can't be found, search the localized ones + if (month == -1) { + for (int i = 1; i <= 12; ++i) { + if (monthName == QDate::shortMonthName(i)) { + month = i; + break; + } + } + } + if (month < 1 || month > 12) { + return QDate(); + } + + bool ok; + int day = parts.at(2).toInt(&ok); + if (!ok) { + return QDate(); + } + + int year = parts.at(3).toInt(&ok); + if (!ok) { + return QDate(); + } + + return QDate(year, month, day); + } +#else + break; +#endif + } + return QDate(); +} + +/*! + \fn QDate::fromString(const QString &string, const QString &format) + + Returns the QDate represented by the \a string, using the \a + format given, or an invalid date if the string cannot be parsed. + + These expressions may be used for the format: + + \table + \header \i Expression \i Output + \row \i d \i The day as a number without a leading zero (1 to 31) + \row \i dd \i The day as a number with a leading zero (01 to 31) + \row \i ddd + \i The abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i The long localized day name (e.g. 'Monday' to 'Sunday'). + Uses QDate::longDayName(). + \row \i M \i The month as a number without a leading zero (1 to 12) + \row \i MM \i The month as a number with a leading zero (01 to 12) + \row \i MMM + \i The abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i The long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i The year as two digit number (00 to 99) + \row \i yyyy \i The year as four digit number. If the year is negative, + a minus sign is prepended in addition. + \endtable + + All other input characters will be treated as text. Any sequence + of characters that are enclosed in single quotes will also be + treated as text and will not be used as an expression. For example: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 1 + + If the format is not satisfied, an invalid QDate is returned. The + expressions that don't expect leading zeroes (d, M) will be + greedy. This means that they will use two digits even if this + will put them outside the accepted range of values and leaves too + few digits for other sections. For example, the following format + string could have meant January 30 but the M will grab two + digits, resulting in an invalid date: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 2 + + For any field that is not represented in the format the following + defaults are used: + + \table + \header \i Field \i Default value + \row \i Year \i 1900 + \row \i Month \i 1 + \row \i Day \i 1 + \endtable + + The following examples demonstrate the default values: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 3 + + \sa QDateTime::fromString(), QTime::fromString(), QDate::toString(), + QDateTime::toString(), QTime::toString() +*/ + +QDate QDate::fromString(const QString &string, const QString &format) +{ + QDate date; +#ifndef QT_BOOTSTRAPPED + QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString); + if (dt.parseFormat(format)) + dt.fromString(string, &date, 0); +#else + Q_UNUSED(string); + Q_UNUSED(format); +#endif + return date; +} +#endif // QT_NO_DATESTRING + +/*! + \overload + + Returns true if the specified date (\a year, \a month, and \a + day) is valid; otherwise returns false. + + Example: + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 4 + + \sa isNull(), setDate() +*/ + +bool QDate::isValid(int year, int month, int day) +{ + if (year < FIRST_YEAR + || (year == FIRST_YEAR && + (month < FIRST_MONTH + || (month == FIRST_MONTH && day < FIRST_DAY))) + || year == 0) // there is no year 0 in the Julian calendar + return false; + + // passage from Julian to Gregorian calendar + if (year == 1582 && month == 10 && day > 4 && day < 15) + return 0; + + return (day > 0 && month > 0 && month <= 12) && + (day <= monthDays[month] || (day == 29 && month == 2 && isLeapYear(year))); +} + +/*! + \fn bool QDate::isLeapYear(int year) + + Returns true if the specified \a year is a leap year; otherwise + returns false. +*/ + +bool QDate::isLeapYear(int y) +{ + if (y < 1582) { + return qAbs(y) % 4 == 0; + } else { + return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; + } +} + +/*! + \internal + + This function has a confusing name and shouldn't be part of the + API anyway, since we have toJulian() and fromJulian(). + ### Qt 5: remove it +*/ +uint QDate::gregorianToJulian(int y, int m, int d) +{ + return julianDayFromDate(y, m, d); +} + +/*! + \internal + + This function has a confusing name and shouldn't be part of the + API anyway, since we have toJulian() and fromJulian(). + ### Qt 5: remove it +*/ +void QDate::julianToGregorian(uint jd, int &y, int &m, int &d) +{ + getDateFromJulianDay(jd, &y, &m, &d); +} + +/*! \fn static QDate QDate::fromJulianDay(int jd) + + Converts the Julian day \a jd to a QDate. + + \sa toJulianDay() +*/ + +/*! \fn int QDate::toJulianDay() const + + Converts the date to a Julian day. + + \sa fromJulianDay() +*/ + +/***************************************************************************** + QTime member functions + *****************************************************************************/ + +/*! + \class QTime + \reentrant + + \brief The QTime class provides clock time functions. + + \ingroup time + \mainclass + + A QTime object contains a clock time, i.e. the number of hours, + minutes, seconds, and milliseconds since midnight. It can read the + current time from the system clock and measure a span of elapsed + time. It provides functions for comparing times and for + manipulating a time by adding a number of milliseconds. + + QTime uses the 24-hour clock format; it has no concept of AM/PM. + Unlike QDateTime, QTime knows nothing about time zones or + daylight savings time (DST). + + A QTime object is typically created either by giving the number + of hours, minutes, seconds, and milliseconds explicitly, or by + using the static function currentTime(), which creates a QTime + object that contains the system's local time. Note that the + accuracy depends on the accuracy of the underlying operating + system; not all systems provide 1-millisecond accuracy. + + The hour(), minute(), second(), and msec() functions provide + access to the number of hours, minutes, seconds, and milliseconds + of the time. The same information is provided in textual format by + the toString() function. + + QTime provides a full set of operators to compare two QTime + objects. One time is considered smaller than another if it is + earlier than the other. + + The time a given number of seconds or milliseconds later than a + given time can be found using the addSecs() or addMSecs() + functions. Correspondingly, the number of seconds or milliseconds + between two times can be found using secsTo() or msecsTo(). + + QTime can be used to measure a span of elapsed time using the + start(), restart(), and elapsed() functions. + + \sa QDate, QDateTime +*/ + +/*! + \fn QTime::QTime() + + Constructs a null time object. A null time can be a QTime(0, 0, 0, 0) + (i.e., midnight) object, except that isNull() returns true and isValid() + returns false. + + \sa isNull(), isValid() +*/ + +/*! + Constructs a time with hour \a h, minute \a m, seconds \a s and + milliseconds \a ms. + + \a h must be in the range 0 to 23, \a m and \a s must be in the + range 0 to 59, and \a ms must be in the range 0 to 999. + + \sa isValid() +*/ + +QTime::QTime(int h, int m, int s, int ms) +{ + setHMS(h, m, s, ms); +} + + +/*! + \fn bool QTime::isNull() const + + Returns true if the time is null (i.e., the QTime object was + constructed using the default constructor); otherwise returns + false. A null time is also an invalid time. + + \sa isValid() +*/ + +/*! + Returns true if the time is valid; otherwise returns false. For example, + the time 23:30:55.746 is valid, but 24:12:30 is invalid. + + \sa isNull() +*/ + +bool QTime::isValid() const +{ + return mds > NullTime && mds < MSECS_PER_DAY; +} + + +/*! + Returns the hour part (0 to 23) of the time. + + \sa minute(), second(), msec() +*/ + +int QTime::hour() const +{ + return ds() / MSECS_PER_HOUR; +} + +/*! + Returns the minute part (0 to 59) of the time. + + \sa hour(), second(), msec() +*/ + +int QTime::minute() const +{ + return (ds() % MSECS_PER_HOUR) / MSECS_PER_MIN; +} + +/*! + Returns the second part (0 to 59) of the time. + + \sa hour(), minute(), msec() +*/ + +int QTime::second() const +{ + return (ds() / 1000)%SECS_PER_MIN; +} + +/*! + Returns the millisecond part (0 to 999) of the time. + + \sa hour(), minute(), second() +*/ + +int QTime::msec() const +{ + return ds() % 1000; +} + +#ifndef QT_NO_DATESTRING +/*! + \overload + + Returns the time as a string. Milliseconds are not included. 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::ISODate, the string format corresponds to the + ISO 8601 extended specification for representations of dates, + which is also HH:MM:SS. (However, contrary to ISO 8601, dates + before 15 October 1582 are handled as Julian dates, not Gregorian + dates. See \l{QDate G and J} {Use of Gregorian and Julian + Calendars}. This might change in a future version of Qt.) + + If the \a format is Qt::SystemLocaleShortDate or + Qt::SystemLocaleLongDate, the string format depends on the locale + settings of the system. Identical to calling + QLocale::system().toString(time, QLocale::ShortFormat) or + QLocale::system().toString(time, QLocale::LongFormat). + + If the \a format is Qt::DefaultLocaleShortDate or + Qt::DefaultLocaleLongDate, the string format depends on the + default application locale. This is the locale set with + QLocale::setDefault(), or the system locale if no default locale + has been set. Identical to calling QLocale().toString(time, + QLocale::ShortFormat) or QLocale().toString(time, + QLocale::LongFormat). + + If the time is invalid, an empty string will be returned. +*/ + +QString QTime::toString(Qt::DateFormat format) const +{ + if (!isValid()) + return QString(); + + switch (format) { + case Qt::SystemLocaleDate: + case Qt::SystemLocaleShortDate: + case Qt::SystemLocaleLongDate: + return QLocale::system().toString(*this, format == Qt::SystemLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + case Qt::DefaultLocaleLongDate: + return QLocale().toString(*this, format == Qt::DefaultLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat); + + default: + 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')); + } +} + +/*! + Returns the time as a string. The \a format parameter determines + the format of the result string. + + These expressions may be used: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i H + \i the hour without a leading zero (0 to 23, even with AM/PM display) + \row \i HH + \i the hour with a leading zero (00 to 23, even with AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP or A + \i use AM/PM display. \e AP will be replaced by either "AM" or "PM". + \row \i ap or a + \i use am/pm display. \e ap will be replaced by either "am" or "pm". + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in singlequotes will be treated as text and not be used as an + expression. Two consecutive singlequotes ("''") are replaced by a singlequote + in the output. + + Example format strings (assuming that the QTime is 14:13:09.042) + + \table + \header \i Format \i Result + \row \i hh:mm:ss.zzz \i 14:13:09.042 + \row \i h:m:s ap \i 2:13:9 pm + \row \i H:m:s a \i 14:13:9 pm + \endtable + + If the datetime is invalid, an empty string will be returned. + + \sa QDate::toString() QDateTime::toString() +*/ +QString QTime::toString(const QString& format) const +{ + return fmtDateTime(format, this, 0); +} +#endif //QT_NO_DATESTRING +/*! + Sets the time to hour \a h, minute \a m, seconds \a s and + milliseconds \a ms. + + \a h must be in the range 0 to 23, \a m and \a s must be in the + range 0 to 59, and \a ms must be in the range 0 to 999. + Returns true if the set time is valid; otherwise returns false. + + \sa isValid() +*/ + +bool QTime::setHMS(int h, int m, int s, int ms) +{ +#if defined(Q_OS_WINCE) + startTick = NullTime; +#endif + if (!isValid(h,m,s,ms)) { + mds = NullTime; // make this invalid + return false; + } + mds = (h*SECS_PER_HOUR + m*SECS_PER_MIN + s)*1000 + ms; + return true; +} + +/*! + Returns a QTime object containing a time \a s seconds later + than the time of this object (or earlier if \a s is negative). + + Note that the time will wrap if it passes midnight. + + Example: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 5 + + \sa addMSecs(), secsTo(), QDateTime::addSecs() +*/ + +QTime QTime::addSecs(int s) const +{ + return addMSecs(s * 1000); +} + +/*! + Returns the number of seconds from this time to \a t. + If \a t is earlier than this time, the number of seconds returned + is negative. + + Because QTime measures time within a day and there are 86400 + seconds in a day, the result is always between -86400 and 86400. + + secsTo() does not take into account any milliseconds. + + \sa addSecs(), QDateTime::secsTo() +*/ + +int QTime::secsTo(const QTime &t) const +{ + return (t.ds() - ds()) / 1000; +} + +/*! + Returns a QTime object containing a time \a ms milliseconds later + than the time of this object (or earlier if \a ms is negative). + + Note that the time will wrap if it passes midnight. See addSecs() + for an example. + + \sa addSecs(), msecsTo() +*/ + +QTime QTime::addMSecs(int ms) const +{ + QTime t; + if (ms < 0) { + // % not well-defined for -ve, but / is. + int negdays = (MSECS_PER_DAY - ms) / MSECS_PER_DAY; + t.mds = (ds() + ms + negdays * MSECS_PER_DAY) % MSECS_PER_DAY; + } else { + t.mds = (ds() + ms) % MSECS_PER_DAY; + } +#if defined(Q_OS_WINCE) + if (startTick > NullTime) + t.startTick = (startTick + ms) % MSECS_PER_DAY; +#endif + return t; +} + +/*! + Returns the number of milliseconds from this time to \a t. + If \a t is earlier than this time, the number of milliseconds returned + is negative. + + Because QTime measures time within a day and there are 86400 + seconds in a day, the result is always between -86400000 and + 86400000 ms. + + \sa secsTo(), addMSecs() +*/ + +int QTime::msecsTo(const QTime &t) const +{ +#if defined(Q_OS_WINCE) + // GetLocalTime() for Windows CE has no milliseconds resolution + if (t.startTick > NullTime && startTick > NullTime) + return t.startTick - startTick; + else +#endif + return t.ds() - ds(); +} + + +/*! + \fn bool QTime::operator==(const QTime &t) const + + Returns true if this time is equal to \a t; otherwise returns false. +*/ + +/*! + \fn bool QTime::operator!=(const QTime &t) const + + Returns true if this time is different from \a t; otherwise returns false. +*/ + +/*! + \fn bool QTime::operator<(const QTime &t) const + + Returns true if this time is earlier than \a t; otherwise returns false. +*/ + +/*! + \fn bool QTime::operator<=(const QTime &t) const + + Returns true if this time is earlier than or equal to \a t; + otherwise returns false. +*/ + +/*! + \fn bool QTime::operator>(const QTime &t) const + + Returns true if this time is later than \a t; otherwise returns false. +*/ + +/*! + \fn bool QTime::operator>=(const QTime &t) const + + Returns true if this time is later than or equal to \a t; + otherwise returns false. +*/ + +/*! + \overload + + Returns the current time as reported by the system clock. + + Note that the accuracy depends on the accuracy of the underlying + operating system; not all systems provide 1-millisecond accuracy. +*/ + +QTime QTime::currentTime() +{ + QTime ct; + +#if defined(Q_OS_WIN) + SYSTEMTIME st; + memset(&st, 0, sizeof(SYSTEMTIME)); + GetLocalTime(&st); + ct.mds = MSECS_PER_HOUR * st.wHour + MSECS_PER_MIN * st.wMinute + 1000 * st.wSecond + + st.wMilliseconds; +#if defined(Q_OS_WINCE) + ct.startTick = GetTickCount() % MSECS_PER_DAY; +#endif +#elif defined(Q_OS_UNIX) + // posix compliant system + struct timeval tv; + gettimeofday(&tv, 0); + time_t ltime = tv.tv_sec; + tm *t = 0; + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + t = localtime_r(<ime, &res); +#else + t = localtime(<ime); +#endif + + ct.mds = MSECS_PER_HOUR * t->tm_hour + MSECS_PER_MIN * t->tm_min + 1000 * t->tm_sec + + tv.tv_usec / 1000; +#else + time_t ltime; // no millisecond resolution + ::time(<ime); + tm *t = 0; + localtime(<ime); + ct.mds = MSECS_PER_HOUR * t->tm_hour + MSECS_PER_MIN * t->tm_min + 1000 * t->tm_sec; +#endif + return ct; +} + +#ifndef QT_NO_DATESTRING +/*! + \fn QTime QTime::fromString(const QString &string, Qt::DateFormat format) + + Returns the time represented in the \a string as a QTime using the + \a format given, or an invalid time if this is not possible. + + Note that fromString() uses a "C" locale encoded string to convert + milliseconds to a float value. If the default locale is not "C", + this may result in two conversion attempts (if the conversion + fails for the default locale). This should be considered an + implementation detail. +*/ +QTime QTime::fromString(const QString& s, Qt::DateFormat f) +{ + if (s.isEmpty()) { + QTime t; + t.mds = NullTime; + return t; + } + + switch (f) { + case Qt::SystemLocaleDate: + case Qt::SystemLocaleShortDate: + case Qt::SystemLocaleLongDate: + return fromString(s, QLocale::system().timeFormat(f == Qt::SystemLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat)); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + case Qt::DefaultLocaleLongDate: + return fromString(s, QLocale().timeFormat(f == Qt::DefaultLocaleLongDate ? QLocale::LongFormat + : QLocale::ShortFormat)); + default: + { + bool ok = true; + const int hour(s.mid(0, 2).toInt(&ok)); + if (!ok) + return QTime(); + const int minute(s.mid(3, 2).toInt(&ok)); + if (!ok) + return QTime(); + const int second(s.mid(6, 2).toInt(&ok)); + if (!ok) + return QTime(); + const QString msec_s(QLatin1String("0.") + s.mid(9, 4)); + const float msec(msec_s.toFloat(&ok)); + if (!ok) + return QTime(); + return QTime(hour, minute, second, qMin(qRound(msec * 1000.0), 999)); + } + } +} + +/*! + \fn QTime::fromString(const QString &string, const QString &format) + + Returns the QTime represented by the \a string, using the \a + format given, or an invalid time if the string cannot be parsed. + + These expressions may be used for the format: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP + \i interpret as an AM/PM time. \e AP must be either "AM" or "PM". + \row \i ap + \i Interpret as an AM/PM time. \e ap must be either "am" or "pm". + \endtable + + All other input characters will be treated as text. Any sequence + of characters that are enclosed in single quotes will also be + treated as text and not be used as an expression. + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 6 + + If the format is not satisfied an invalid QTime is returned. + Expressions that do not expect leading zeroes to be given (h, m, s + and z) are greedy. This means that they will use two digits even if + this puts them outside the range of accepted values and leaves too + few digits for other sections. For example, the following string + could have meant 00:07:10, but the m will grab two digits, resulting + in an invalid time: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 7 + + Any field that is not represented in the format will be set to zero. + For example: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 8 + + \sa QDateTime::fromString() QDate::fromString() QDate::toString() + QDateTime::toString() QTime::toString() +*/ + +QTime QTime::fromString(const QString &string, const QString &format) +{ + QTime time; +#ifndef QT_BOOTSTRAPPED + QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString); + if (dt.parseFormat(format)) + dt.fromString(string, 0, &time); +#else + Q_UNUSED(string); + Q_UNUSED(format); +#endif + return time; +} + +#endif // QT_NO_DATESTRING + + +/*! + \overload + + Returns true if the specified time is valid; otherwise returns + false. + + The time is valid if \a h is in the range 0 to 23, \a m and + \a s are in the range 0 to 59, and \a ms is in the range 0 to 999. + + Example: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 9 +*/ + +bool QTime::isValid(int h, int m, int s, int ms) +{ + return (uint)h < 24 && (uint)m < 60 && (uint)s < 60 && (uint)ms < 1000; +} + + +/*! + Sets this time to the current time. This is practical for timing: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 10 + + \sa restart(), elapsed(), currentTime() +*/ + +void QTime::start() +{ + *this = currentTime(); +} + +/*! + Sets this time to the current time and returns the number of + milliseconds that have elapsed since the last time start() or + restart() was called. + + This function is guaranteed to be atomic and is thus very handy + for repeated measurements. Call start() to start the first + measurement, and restart() for each later measurement. + + Note that the counter wraps to zero 24 hours after the last call + to start() or restart(). + + \warning If the system's clock setting has been changed since the + last time start() or restart() was called, the result is + undefined. This can happen when daylight savings time is turned on + or off. + + \sa start(), elapsed(), currentTime() +*/ + +int QTime::restart() +{ + QTime t = currentTime(); + int n = msecsTo(t); + if (n < 0) // passed midnight + n += 86400*1000; + *this = t; + return n; +} + +/*! + Returns the number of milliseconds that have elapsed since the + last time start() or restart() was called. + + Note that the counter wraps to zero 24 hours after the last call + to start() or restart. + + Note that the accuracy depends on the accuracy of the underlying + operating system; not all systems provide 1-millisecond accuracy. + + \warning If the system's clock setting has been changed since the + last time start() or restart() was called, the result is + undefined. This can happen when daylight savings time is turned on + or off. + + \sa start(), restart() +*/ + +int QTime::elapsed() const +{ + int n = msecsTo(currentTime()); + if (n < 0) // passed midnight + n += 86400 * 1000; + return n; +} + + +/***************************************************************************** + QDateTime member functions + *****************************************************************************/ + +/*! + \class QDateTime + \reentrant + \brief The QDateTime class provides date and time functions. + + \ingroup time + \mainclass + + A QDateTime object contains a calendar date and a clock time (a + "datetime"). It is a combination of the QDate and QTime classes. + It can read the current datetime from the system clock. It + provides functions for comparing datetimes and for manipulating a + datetime by adding a number of seconds, days, months, or years. + + A QDateTime object is typically created either by giving a date + and time explicitly in the constructor, or by using the static + function currentDateTime() that returns a QDateTime object set + to the system clock's time. The date and time can be changed with + setDate() and setTime(). A datetime can also be set using the + setTime_t() function that takes a POSIX-standard "number of + seconds since 00:00:00 on January 1, 1970" value. The fromString() + function returns a QDateTime, given a string and a date format + used to interpret the date within the string. + + The date() and time() functions provide access to the date and + time parts of the datetime. The same information is provided in + textual format by the toString() function. + + QDateTime provides a full set of operators to compare two + QDateTime objects where smaller means earlier and larger means + later. + + You can increment (or decrement) a datetime by a given number of + seconds using addSecs(), or days using addDays(). Similarly you can + use addMonths() and addYears(). The daysTo() function returns the + number of days between two datetimes, and secsTo() returns the + number of seconds between two datetimes. + + QDateTime can store datetimes as \l{Qt::LocalTime}{local time} or + as \l{Qt::UTC}{UTC}. QDateTime::currentDateTime() returns a + QDateTime expressed as local time; use toUTC() to convert it to + UTC. You can also use timeSpec() to find out if a QDateTime + object stores a UTC time or a local time. Operations such as + addSecs() and secsTo() are aware of daylight saving time (DST). + + \note QDateTime does not account for leap seconds. + + \section1 + + \target QDateTime G and J + \section2 Use of Gregorian and Julian Calendars + + QDate uses the Gregorian calendar in all locales, beginning + on the date 15 October 1582. For dates up to and including 4 + October 1582, the Julian calendar is used. This means there is a + 10-day gap in the internal calendar between the 4th and the 15th + of October 1582. When you use QDateTime for dates in that epoch, + the day after 4 October 1582 is 15 October 1582, and the dates in + the gap are invalid. + + The Julian to Gregorian changeover date used here is the date when + the Gregorian calendar was first introduced, by Pope Gregory + XIII. That change was not universally accepted and some localities + only executed it at a later date (if at all). QDateTime + doesn't take any of these historical facts into account. If an + application must support a locale-specific dating system, it must + do so on its own, remembering to convert the dates using the + Julian day. + + \section2 No Year 0 + + There is no year 0. Dates in that year are considered invalid. The + year -1 is the year "1 before Christ" or "1 before current era." + The day before 0001-01-01 is December 31st, 1 BCE. + + \section2 Range of Valid Dates + + The range of valid dates is from January 2nd, 4713 BCE, to + sometime in the year 11 million CE. The Julian Day returned by + QDate::toJulianDay() is a number in the contiguous range from 1 to + \e{overflow}, even across QDateTime's "date holes". It is suitable + for use in applications that must convert a QDateTime to a date in + another calendar system, e.g., Hebrew, Islamic or Chinese. + + The Gregorian calendar was introduced in different places around + the world on different dates. QDateTime uses QDate to store the + date, so it uses the Gregorian calendar for all locales, beginning + on the date 15 October 1582. For dates up to and including 4 + October 1582, QDateTime uses the Julian calendar. This means + there is a 10-day gap in the QDateTime calendar between the 4th + and the 15th of October 1582. When you use QDateTime for dates in + that epoch, the day after 4 October 1582 is 15 October 1582, and + the dates in the gap are invalid. + + \section2 + Use of System Timezone + + QDateTime uses the system's time zone information to determine the + offset of local time from UTC. If the system is not configured + correctly or not up-to-date, QDateTime will give wrong results as + well. + + \section2 Daylight Savings Time (DST) + + QDateTime takes into account the system's time zone information + when dealing with DST. On modern Unix systems, this means it + applies the correct historical DST data whenever possible. On + Windows and Windows CE, where the system doesn't support + historical DST data, historical accuracy is not maintained with + respect to DST. + + The range of valid dates taking DST into account is 1970-01-01 to + the present, and rules are in place for handling DST correctly + until 2037-12-31, but these could change. For dates falling + outside that range, QDateTime makes a \e{best guess} using the + rules for year 1970 or 2037, but we can't guarantee accuracy. This + means QDateTime doesn't take into account changes in a locale's + time zone before 1970, even if the system's time zone database + supports that information. + + \sa QDate QTime QDateTimeEdit +*/ + +/*! + Constructs a null datetime (i.e. null date and null time). A null + datetime is invalid, since the date is invalid. + + \sa isValid() +*/ +QDateTime::QDateTime() +{ + d = new QDateTimePrivate; +} + + +/*! + Constructs a datetime with the given \a date, a valid + time(00:00:00.000), and sets the timeSpec() to Qt::LocalTime. +*/ + +QDateTime::QDateTime(const QDate &date) +{ + d = new QDateTimePrivate; + d->date = date; + d->time = QTime(0, 0, 0); +} + +/*! + Constructs a datetime with the given \a date and \a time, using + the time specification defined by \a spec. + + If \a date is valid and \a time is not, the time will be set to midnight. +*/ + +QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec) +{ + d = new QDateTimePrivate; + d->date = date; + d->time = date.isValid() && !time.isValid() ? QTime(0, 0, 0) : time; + d->spec = (spec == Qt::UTC) ? QDateTimePrivate::UTC : QDateTimePrivate::LocalUnknown; +} + +/*! + Constructs a copy of the \a other datetime. +*/ + +QDateTime::QDateTime(const QDateTime &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Destroys the datetime. +*/ +QDateTime::~QDateTime() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Makes a copy of the \a other datetime and returns a reference to the + copy. +*/ + +QDateTime &QDateTime::operator=(const QDateTime &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Returns true if both the date and the time are null; otherwise + returns false. A null datetime is invalid. + + \sa QDate::isNull(), QTime::isNull(), isValid() +*/ + +bool QDateTime::isNull() const +{ + return d->date.isNull() && d->time.isNull(); +} + +/*! + Returns true if both the date and the time are valid; otherwise + returns false. + + \sa QDate::isValid(), QTime::isValid() +*/ + +bool QDateTime::isValid() const +{ + return d->date.isValid() && d->time.isValid(); +} + +/*! + Returns the date part of the datetime. + + \sa setDate(), time(), timeSpec() +*/ + +QDate QDateTime::date() const +{ + return d->date; +} + +/*! + Returns the time part of the datetime. + + \sa setTime(), date(), timeSpec() +*/ + +QTime QDateTime::time() const +{ + return d->time; +} + +/*! + Returns the time specification of the datetime. + + \sa setTimeSpec(), date(), time(), Qt::TimeSpec +*/ + +Qt::TimeSpec QDateTime::timeSpec() const +{ + switch(d->spec) + { + case QDateTimePrivate::UTC: + return Qt::UTC; + case QDateTimePrivate::OffsetFromUTC: + return Qt::OffsetFromUTC; + default: + return Qt::LocalTime; + } +} + +/*! + Sets the date part of this datetime to \a date. + If no time is set, it is set to midnight. + + \sa date(), setTime(), setTimeSpec() +*/ + +void QDateTime::setDate(const QDate &date) +{ + detach(); + d->date = date; + if (d->spec == QDateTimePrivate::LocalStandard + || d->spec == QDateTimePrivate::LocalDST) + d->spec = QDateTimePrivate::LocalUnknown; + if (date.isValid() && !d->time.isValid()) + d->time = QTime(0, 0, 0); +} + +/*! + Sets the time part of this datetime to \a time. + + \sa time(), setDate(), setTimeSpec() +*/ + +void QDateTime::setTime(const QTime &time) +{ + detach(); + if (d->spec == QDateTimePrivate::LocalStandard + || d->spec == QDateTimePrivate::LocalDST) + d->spec = QDateTimePrivate::LocalUnknown; + d->time = time; +} + +/*! + Sets the time specification used in this datetime to \a spec. + + \sa timeSpec(), setDate(), setTime(), Qt::TimeSpec +*/ + +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; + } +} + +static uint toTime_tHelper(const QDate &utcDate, const QTime &utcTime) +{ + int days = QDate(1970, 1, 1).daysTo(utcDate); + int secs = QTime().secsTo(utcTime); + if (days < 0 || (days == 0 && secs < 0)) + return uint(-1); + + qlonglong retval = (qlonglong(days) * SECS_PER_DAY) + secs; + if (retval >= Q_INT64_C(0xFFFFFFFF)) + return uint(-1); + return uint(retval); +} + +/*! + Returns the datetime as the number of seconds that have passed + since 1970-01-01T00:00:00, Coordinated Universal Time (Qt::UTC). + + On systems that do not support time zones, this function will + behave as if local time were Qt::UTC. + + \sa setTime_t() +*/ + +uint QDateTime::toTime_t() const +{ + QDate utcDate; + QTime utcTime; + d->getUTC(utcDate, utcTime); + + return toTime_tHelper(utcDate, utcTime); +} + +/*! + \fn void QDateTime::setTime_t(uint seconds) + + Sets the date and time given 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 this function + will behave as if local time were Qt::UTC. + + \sa toTime_t() +*/ + +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().addSecs(secsSince1Jan1970UTC % SECS_PER_DAY); + d->spec = QDateTimePrivate::UTC; + + if (oldSpec != QDateTimePrivate::UTC) + d->spec = d->getLocal(d->date, d->time); +} + +#ifndef QT_NO_DATESTRING +/*! + \fn QString QDateTime::toString(Qt::DateFormat format) const + + \overload + + Returns the datetime as a string in the \a format given. + + 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". + + 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. + + If the \a format is Qt::SystemLocaleShortDate or + Qt::SystemLocaleLongDate, the string format depends on the locale + settings of the system. Identical to calling + QLocale::system().toString(datetime, QLocale::ShortFormat) or + QLocale::system().toString(datetime, QLocale::LongFormat). + + If the \a format is Qt::DefaultLocaleShortDate or + Qt::DefaultLocaleLongDate, the string format depends on the + default application locale. This is the locale set with + QLocale::setDefault(), or the system locale if no default locale + has been set. Identical to calling QLocale().toString(datetime, + QLocale::ShortFormat) or QLocale().toString(datetime, + QLocale::LongFormat). + + 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 QDate::toString() QTime::toString() Qt::DateFormat +*/ + +QString QDateTime::toString(Qt::DateFormat f) 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); + } +#ifndef QT_NO_TEXTDATE + else if (f == Qt::TextDate) { +#ifndef Q_WS_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 + QString winstr; + QT_WA({ + TCHAR out[255]; + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILDATE, out, 255); + winstr = QString::fromUtf16((ushort*)out); + } , { + char out[255]; + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILDATE, (char*)&out, 255); + winstr = QString::fromLocal8Bit(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; +} + +/*! + Returns the datetime as a string. The \a format parameter + determines the format of the result string. + + These expressions may be used for the date: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1-12) + \row \i MM \i the month as number with a leading zero (01-12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00-99) + \row \i yyyy \i the year as four digit number + \endtable + + These expressions may be used for the time: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP + \i use AM/PM display. \e AP will be replaced by either "AM" or "PM". + \row \i ap + \i use am/pm display. \e ap will be replaced by either "am" or "pm". + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in singlequotes will be treated as text and not be used as an + expression. Two consecutive singlequotes ("''") are replaced by a singlequote + in the output. + + Example format strings (assumed that the QDateTime is 21 May 2001 + 14:13:09): + + \table + \header \i Format \i Result + \row \i dd.MM.yyyy \i 21.05.2001 + \row \i ddd MMMM d yy \i Tue May 21 01 + \row \i hh:mm:ss.zzz \i 14:13:09.042 + \row \i h:m:s ap \i 2:13:9 pm + \endtable + + If the datetime is invalid, an empty string will be returned. + + \sa QDate::toString() QTime::toString() +*/ +QString QDateTime::toString(const QString& format) const +{ + return fmtDateTime(format, &d->time, &d->date); +} +#endif //QT_NO_DATESTRING + +/*! + Returns a QDateTime object containing a datetime \a ndays days + later than the datetime of this object (or earlier if \a ndays is + negative). + + \sa daysTo(), addMonths(), addYears(), addSecs() +*/ + +QDateTime QDateTime::addDays(int ndays) const +{ + return QDateTime(d->date.addDays(ndays), d->time, timeSpec()); +} + +/*! + Returns a QDateTime object containing a datetime \a nmonths months + later than the datetime of this object (or earlier if \a nmonths + is negative). + + \sa daysTo(), addDays(), addYears(), addSecs() +*/ + +QDateTime QDateTime::addMonths(int nmonths) const +{ + return QDateTime(d->date.addMonths(nmonths), d->time, timeSpec()); +} + +/*! + Returns a QDateTime object containing a datetime \a nyears years + later than the datetime of this object (or earlier if \a nyears is + negative). + + \sa daysTo(), addDays(), addMonths(), addSecs() +*/ + +QDateTime QDateTime::addYears(int nyears) const +{ + return QDateTime(d->date.addYears(nyears), d->time, timeSpec()); +} + +QDateTime QDateTimePrivate::addMSecs(const QDateTime &dt, qint64 msecs) +{ + QDate utcDate; + QTime utcTime; + dt.d->getUTC(utcDate, utcTime); + + addMSecs(utcDate, utcTime, msecs); + + return QDateTime(utcDate, utcTime, Qt::UTC).toTimeSpec(dt.timeSpec()); +} + +/*! + Adds \a msecs to utcDate and \a utcTime as appropriate. It is assumed that + utcDate and utcTime are adjusted to UTC. + + \since 4.5 + \internal + */ +void QDateTimePrivate::addMSecs(QDate &utcDate, QTime &utcTime, qint64 msecs) +{ + uint dd = utcDate.jd; + int tt = utcTime.ds(); + int sign = 1; + if (msecs < 0) { + msecs = -msecs; + sign = -1; + } + if (msecs >= int(MSECS_PER_DAY)) { + dd += sign * (msecs / MSECS_PER_DAY); + msecs %= MSECS_PER_DAY; + } + + tt += sign * msecs; + if (tt < 0) { + tt = MSECS_PER_DAY - tt - 1; + dd -= tt / MSECS_PER_DAY; + tt = tt % MSECS_PER_DAY; + tt = MSECS_PER_DAY - tt - 1; + } else if (tt >= int(MSECS_PER_DAY)) { + dd += tt / MSECS_PER_DAY; + tt = tt % MSECS_PER_DAY; + } + + utcDate.jd = dd; + utcTime.mds = tt; +} + +/*! + Returns a QDateTime object containing a datetime \a s seconds + later than the datetime of this object (or earlier if \a s is + negative). + + \sa addMSecs(), secsTo(), addDays(), addMonths(), addYears() +*/ + +QDateTime QDateTime::addSecs(int s) const +{ + return d->addMSecs(*this, qint64(s) * 1000); +} + +/*! + Returns a QDateTime object containing a datetime \a msecs miliseconds + later than the datetime of this object (or earlier if \a msecs is + negative). + + \sa addSecs(), secsTo(), addDays(), addMonths(), addYears() +*/ +QDateTime QDateTime::addMSecs(qint64 msecs) const +{ + return d->addMSecs(*this, msecs); +} + +/*! + Returns the number of days from this datetime to the \a other + datetime. If the \a other datetime is earlier than this datetime, + the value returned is negative. + + \sa addDays(), secsTo() +*/ + +int QDateTime::daysTo(const QDateTime &other) const +{ + return d->date.daysTo(other.d->date); +} + +/*! + Returns the number of seconds from this datetime to the \a other + datetime. If the \a other datetime is earlier than this datetime, + the value returned is negative. + + Before performing the comparison, the two datetimes are converted + to Qt::UTC to ensure that the result is correct if one of the two + datetimes has daylight saving time (DST) and the other doesn't. + + Example: + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 11 + + \sa addSecs(), daysTo(), QTime::secsTo() +*/ + +int QDateTime::secsTo(const QDateTime &other) const +{ + QDate date1, date2; + QTime time1, time2; + + d->getUTC(date1, time1); + other.d->getUTC(date2, time2); + + return (date1.daysTo(date2) * SECS_PER_DAY) + time1.secsTo(time2); +} + +/*! + \fn QDateTime QDateTime::toTimeSpec(Qt::TimeSpec specification) const + + Returns a copy of this datetime configured to use the given time + \a specification. + + \sa timeSpec(), toUTC(), toLocalTime() +*/ + +QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const +{ + if ((d->spec == QDateTimePrivate::UTC) == (spec == Qt::UTC)) + return *this; + + 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); + } + return ret; +} + +/*! + Returns true if this datetime is equal to the \a other datetime; + otherwise returns false. + + \sa operator!=() +*/ + +bool QDateTime::operator==(const QDateTime &other) const +{ + if (d->spec == other.d->spec && d->utcOffset == other.d->utcOffset) + return d->time == other.d->time && d->date == other.d->date; + else { + QDate date1, date2; + QTime time1, time2; + + d->getUTC(date1, time1); + other.d->getUTC(date2, time2); + return time1 == time2 && date1 == date2; + } +} + +/*! + \fn bool QDateTime::operator!=(const QDateTime &other) const + + Returns true if this datetime is different from the \a other + datetime; otherwise returns false. + + Two datetimes are different if either the date, the time, or the + time zone components are different. + + \sa operator==() +*/ + +/*! + Returns true if this datetime is earlier than the \a other + datetime; otherwise returns false. +*/ + +bool QDateTime::operator<(const QDateTime &other) const +{ + if (d->spec == other.d->spec && d->spec != QDateTimePrivate::OffsetFromUTC) { + if (d->date != other.d->date) + return d->date < other.d->date; + return d->time < other.d->time; + } else { + QDate date1, date2; + QTime time1, time2; + d->getUTC(date1, time1); + other.d->getUTC(date2, time2); + if (date1 != date2) + return date1 < date2; + return time1 < time2; + } +} + +/*! + \fn bool QDateTime::operator<=(const QDateTime &other) const + + Returns true if this datetime is earlier than or equal to the + \a other datetime; otherwise returns false. +*/ + +/*! + \fn bool QDateTime::operator>(const QDateTime &other) const + + Returns true if this datetime is later than the \a other datetime; + otherwise returns false. +*/ + +/*! + \fn bool QDateTime::operator>=(const QDateTime &other) const + + Returns true if this datetime is later than or equal to the + \a other datetime; otherwise returns false. +*/ + +/*! + Returns the current datetime, as reported by the system clock, in + the local time zone. + + \sa QDate::currentDate(), QTime::currentTime(), toTimeSpec() +*/ + +QDateTime QDateTime::currentDateTime() +{ +#if defined(Q_OS_WIN) + QDate d; + QTime t; + SYSTEMTIME st; + memset(&st, 0, sizeof(SYSTEMTIME)); + GetLocalTime(&st); + d.jd = julianDayFromDate(st.wYear, st.wMonth, st.wDay); + t.mds = MSECS_PER_HOUR * st.wHour + MSECS_PER_MIN * st.wMinute + 1000 * st.wSecond + + st.wMilliseconds; + return QDateTime(d, t); +#else +#if defined(Q_OS_UNIX) + // posix compliant system + // we have milliseconds + struct timeval tv; + gettimeofday(&tv, 0); + time_t ltime = tv.tv_sec; + tm *t = 0; + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + t = localtime_r(<ime, &res); +#else + t = localtime(<ime); +#endif + + QDateTime dt; + dt.d->time.mds = MSECS_PER_HOUR * t->tm_hour + MSECS_PER_MIN * t->tm_min + 1000 * t->tm_sec + + tv.tv_usec / 1000; +#else + time_t ltime; // no millisecond resolution + ::time(<ime); + tm *t = 0; + localtime(<ime); + dt.d->time.mds = MSECS_PER_HOUR * t->tm_hour + MSECS_PER_MIN * t->tm_min + 1000 * t->tm_sec; +#endif // Q_OS_UNIX + + dt.d->date.jd = julianDayFromDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); + dt.d->spec = t->tm_isdst > 0 ? QDateTimePrivate::LocalDST : + t->tm_isdst == 0 ? QDateTimePrivate::LocalStandard : + QDateTimePrivate::LocalUnknown; + return dt; +#endif +} + +/*! + \since 4.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). 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; +} + +/*! + \since 4.4 + \internal + + Sets the offset from UTC to \a seconds, and also sets timeSpec() to + Qt::OffsetFromUTC. + + The maximum and minimum offset is 14 positive or negative hours. If + \a seconds is larger or smaller than that, the result is undefined. + + 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. + + \sa isValid(), utcOffset() + */ +void QDateTime::setUtcOffset(int seconds) +{ + detach(); + + /* The motivation to also setting d->spec is to ensure that the QDateTime + * instance stay 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; + + /* Even if seconds is 0 we assign it to utcOffset. */ + d->utcOffset = seconds; +} + +/*! + \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, timeSpec() must be + checked. + + Likewise, if this QDateTime() is invalid or if timeSpec() isn't + Qt::OffsetFromUTC, 0 is returned. + + The UTC offset only applies if the timeSpec() is Qt::OffsetFromUTC. + + \sa isValid(), setUtcOffset() + */ +int QDateTime::utcOffset() const +{ + if(isValid() && d->spec == QDateTimePrivate::OffsetFromUTC) + return d->utcOffset; + else + return 0; +} + +#ifndef QT_NO_DATESTRING + +static int fromShortMonthName(const QString &monthName) +{ + // Assume that English monthnames are the default + for (int i = 0; i < 12; ++i) { + if (monthName == QLatin1String(qt_shortMonthNames[i])) + return i + 1; + } + // If English names can't be found, search the localized ones + for (int i = 1; i <= 12; ++i) { + if (monthName == QDate::shortMonthName(i)) + return i; + } + return -1; +} + +/*! + \fn QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) + + Returns the QDateTime represented by the \a string, using the + \a format given, or an invalid datetime if this is not possible. + + Note for Qt::TextDate: It is recommended that you use the + 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) +{ + if (s.isEmpty()) { + return QDateTime(); + } + + switch (f) { + case Qt::ISODate: { + QString tmp = s; + Qt::TimeSpec ts = Qt::LocalTime; + const QDate date = QDate::fromString(tmp.left(10), Qt::ISODate); + if (tmp.size() == 10) + return QDateTime(date); + + // Recognize UTC specifications + if (tmp.endsWith(QLatin1Char('Z'))) { + ts = Qt::UTC; + tmp.chop(1); + } + return QDateTime(date, QTime::fromString(tmp.mid(11), Qt::ISODate), ts); + } + 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); + + 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; + + month = fromShortMonthName(parts.at(1)); + if (month != -1) { + day = parts.at(2).toInt(&ok); + if (!ok) + day = -1; + } + + if (month == -1 || day == -1) { + // first variant failed, lets try the other + month = fromShortMonthName(parts.at(2)); + if (month != -1) { + QString dayStr = parts.at(1); + if (dayStr.endsWith(QLatin1Char('.'))) { + dayStr.chop(1); + day = dayStr.toInt(&ok); + if (!ok) + day = -1; + } else { + day = -1; + } + } + } + + if (month == -1 || day == -1) { + // both variants failed, give up + return QDateTime(); + } + + int year; + QStringList timeParts = parts.at(3).split(QLatin1Char(':')); + if ((timeParts.count() == 3) || (timeParts.count() == 2)) { + year = parts.at(4).toInt(&ok); + if (!ok) + return QDateTime(); + } else { + 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(); + } + + int hour = timeParts.at(0).toInt(&ok); + if (!ok) { + return QDateTime(); + } + + int minute = timeParts.at(1).toInt(&ok); + if (!ok) { + return QDateTime(); + } + + int second = (timeParts.count() > 2) ? timeParts.at(2).toInt(&ok) : 0; + if (!ok) { + return QDateTime(); + } + + QDate date(year, month, day); + QTime time(hour, minute, second); + + if (parts.count() == 5) + return QDateTime(date, time, Qt::LocalTime); + + QString tz = parts.at(5); + if (!tz.startsWith(QLatin1String("GMT"), Qt::CaseInsensitive)) + return QDateTime(); + int tzoffset = 0; + if (tz.length() > 3) { + 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); + if (!ok) + return QDateTime(); + tzoffset = (tzhour*60 + tzminute) * 60; + if (sign == QLatin1Char('-')) + tzoffset = -tzoffset; + } + return QDateTime(date, time, Qt::UTC).addSecs(-tzoffset).toLocalTime(); + } +#endif //QT_NO_TEXTDATE + } + + return QDateTime(); +} + +/*! + \fn QDateTime::fromString(const QString &string, const QString &format) + + Returns the QDateTime represented by the \a string, using the \a + format given, or an invalid datetime if the string cannot be parsed. + + These expressions may be used for the date part of the format string: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1-12) + \row \i MM \i the month as number with a leading zero (01-12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00-99) + \row \i yyyy \i the year as four digit number + \endtable + + \note Unlike the other version of this function, day and month names must + be given in the user's local language. It is only possible to use the English + names if the user's language is English. + + These expressions may be used for the time part of the format string: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i H + \i the hour without a leading zero (0 to 23, even with AM/PM display) + \row \i HH + \i the hour with a leading zero (00 to 23, even with AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP or A + \i interpret as an AM/PM time. \e AP must be either "AM" or "PM". + \row \i ap or a + \i Interpret as an AM/PM time. \e ap must be either "am" or "pm". + \endtable + + All other input characters will be treated as text. Any sequence + of characters that are enclosed in singlequotes will also be + treated as text and not be used as an expression. + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 12 + + If the format is not satisfied an invalid QDateTime is returned. + The expressions that don't have leading zeroes (d, M, h, m, s, z) will be + greedy. This means that they will use two digits even if this will + put them outside the range and/or leave too few digits for other + sections. + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 13 + + This could have meant 1 January 00:30.00 but the M will grab + two digits. + + For any field that is not represented in the format the following + defaults are used: + + \table + \header \i Field \i Default value + \row \i Year \i 1900 + \row \i Month \i 1 (January) + \row \i Day \i 1 + \row \i Hour \i 0 + \row \i Minute \i 0 + \row \i Second \i 0 + \endtable + + For example: + + \snippet doc/src/snippets/code/src_corelib_tools_qdatetime.cpp 14 + + \sa QDate::fromString() QTime::fromString() QDate::toString() + QDateTime::toString() QTime::toString() +*/ + +QDateTime QDateTime::fromString(const QString &string, const QString &format) +{ +#ifndef QT_BOOTSTRAPPED + QTime time; + QDate date; + + QDateTimeParser dt(QVariant::DateTime, QDateTimeParser::FromString); + if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) + return QDateTime(date, time); +#else + Q_UNUSED(string); + Q_UNUSED(format); +#endif + return QDateTime(QDate(), QTime(-1, -1, -1)); +} + +#endif // QT_NO_DATESTRING +/*! + \fn QDateTime QDateTime::toLocalTime() const + + Returns a datetime containing the date and time information in + this datetime, but specified using the Qt::LocalTime definition. + + \sa toTimeSpec() +*/ + +/*! + \fn QDateTime QDateTime::toUTC() const + + Returns a datetime containing the date and time information in + this datetime, but specified using the Qt::UTC definition. + + \sa toTimeSpec() +*/ + +/*! \internal + */ +void QDateTime::detach() +{ + qAtomicDetach(d); +} + +/***************************************************************************** + Date/time stream functions + *****************************************************************************/ + +#ifndef QT_NO_DATASTREAM +/*! + \relates QDate + + Writes the \a date to stream \a out. + + \sa {Format of the QDataStream operators} +*/ + +QDataStream &operator<<(QDataStream &out, const QDate &date) +{ + return out << (quint32)(date.jd); +} + +/*! + \relates QDate + + Reads a date from stream \a in into the \a date. + + \sa {Format of the QDataStream operators} +*/ + +QDataStream &operator>>(QDataStream &in, QDate &date) +{ + quint32 jd; + in >> jd; + date.jd = jd; + return in; +} + +/*! + \relates QTime + + Writes \a time to stream \a out. + + \sa {Format of the QDataStream operators} +*/ + +QDataStream &operator<<(QDataStream &out, const QTime &time) +{ + return out << quint32(time.mds); +} + +/*! + \relates QTime + + Reads a time from stream \a in into the given \a time. + + \sa {Format of the QDataStream operators} +*/ + +QDataStream &operator>>(QDataStream &in, QTime &time) +{ + quint32 ds; + in >> ds; + time.mds = int(ds); + return in; +} + +/*! + \relates QDateTime + + Writes \a dateTime to the \a out stream. + + \sa {Format of the QDataStream operators} +*/ +QDataStream &operator<<(QDataStream &out, const QDateTime &dateTime) +{ + out << dateTime.d->date << dateTime.d->time; + if (out.version() >= 7) + out << (qint8)dateTime.d->spec; + return out; +} + +/*! + \relates QDateTime + + Reads a datetime from the stream \a in into \a dateTime. + + \sa {Format of the QDataStream operators} +*/ + +QDataStream &operator>>(QDataStream &in, QDateTime &dateTime) +{ + dateTime.detach(); + + qint8 ts = (qint8)QDateTimePrivate::LocalUnknown; + in >> dateTime.d->date >> dateTime.d->time; + if (in.version() >= 7) + in >> ts; + dateTime.d->spec = (QDateTimePrivate::Spec)ts; + return in; +} +#endif // QT_NO_DATASTREAM + + +/*! + \fn QString QDate::monthName(int month) + + Use shortMonthName() instead. +*/ + +/*! + \fn QString QDate::dayName(int weekday) + + Use shortDayName() instead. +*/ + +/*! + \fn bool QDate::leapYear(int year) + + Use isLeapYear() instead. +*/ + +/*! + \fn QDate QDate::currentDate(Qt::TimeSpec spec) + + If \a spec is Qt::LocalTime, use the currentDate() overload that + takes no parameters instead; otherwise, use + QDateTime::currentDateTime(). + + \oldcode + QDate localDate = QDate::currentDate(Qt::LocalTime); + QDate utcDate = QDate::currentDate(Qt::UTC); + \newcode + QDate localDate = QDate::currentDate(); + QDate utcDate = QDateTime::currentDateTime().toUTC().date(); + \endcode + + \sa QDateTime::toUTC() +*/ + +/*! + \fn QTime QTime::currentTime(Qt::TimeSpec specification) + + Returns the current time for the given \a specification. + + To replace uses of this function where the \a specification is Qt::LocalTime, + use the currentDate() overload that takes no parameters instead; otherwise, + use QDateTime::currentDateTime() and convert the result to a UTC measurement. + + \oldcode + QTime localTime = QTime::currentTime(Qt::LocalTime); + QTime utcTime = QTime::currentTime(Qt::UTC); + \newcode + QTime localTime = QTime::currentTime(); + QTime utcTime = QTimeTime::currentDateTime().toUTC().time(); + \endcode + + \sa QDateTime::toUTC() +*/ + +/*! + \fn void QDateTime::setTime_t(uint secsSince1Jan1970UTC, Qt::TimeSpec spec) + + Use the single-argument overload of setTime_t() instead. +*/ + +/*! + \fn QDateTime QDateTime::currentDateTime(Qt::TimeSpec spec) + + Use the currentDateTime() overload that takes no parameters + instead. +*/ + +// checks if there is an unqoted '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; + } + } + } + + 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; + } + + } else if (f.startsWith(QLatin1String("yy"))) { + buf = QString::number(dd->year()).right(2).rightJustified(2, QLatin1Char('0')); + removed = 2; + } + } + 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) +{ + const QLatin1Char quote('\''); + if (f.isEmpty()) + return QString(); + if (dt && !dt->isValid()) + return QString(); + if (dd && !dd->isValid()) + return QString(); + + const bool ap = hasUnquotedAP(f); + + QString buf; + QString frm; + QChar status(QLatin1Char('0')); + + for (int i = 0; i < (int)f.length(); ++i) { + if (f.at(i) == quote) { + if (status == quote) { + if (i > 0 && f.at(i - 1) == quote) + buf += QLatin1Char('\''); + status = QLatin1Char('0'); + } else { + if (!frm.isEmpty()) { + buf += getFmtString(frm, dt, dd, ap); + frm.clear(); + } + status = quote; + } + } else if (status == quote) { + buf += f.at(i); + } else if (f.at(i) == status) { + if ((ap) && ((f.at(i) == QLatin1Char('P')) || (f.at(i) == QLatin1Char('p')))) + status = QLatin1Char('0'); + frm += f.at(i); + } else { + buf += getFmtString(frm, dt, dd, ap); + frm.clear(); + if ((f.at(i) == QLatin1Char('h')) || (f.at(i) == QLatin1Char('m')) + || (f.at(i) == QLatin1Char('H')) + || (f.at(i) == QLatin1Char('s')) || (f.at(i) == QLatin1Char('z'))) { + status = f.at(i); + frm += f.at(i); + } else if ((f.at(i) == QLatin1Char('d')) || (f.at(i) == QLatin1Char('M')) || (f.at(i) == QLatin1Char('y'))) { + status = f.at(i); + frm += f.at(i); + } else if ((ap) && (f.at(i) == QLatin1Char('A'))) { + status = QLatin1Char('P'); + frm += f.at(i); + } else if((ap) && (f.at(i) == QLatin1Char('a'))) { + status = QLatin1Char('p'); + frm += f.at(i); + } else { + buf += f.at(i); + status = QLatin1Char('0'); + } + } + } + + buf += getFmtString(frm, dt, dd, ap); + + return buf; +} +#endif // QT_NO_DATESTRING + +#ifdef Q_OS_WIN +static const int LowerYear = 1980; +#else +static const int LowerYear = 1970; +#endif +static const int UpperYear = 2037; + +static QDate adjustDate(QDate date) +{ + QDate lowerLimit(LowerYear, 1, 2); + QDate upperLimit(UpperYear, 12, 30); + + if (date > lowerLimit && date < upperLimit) + return date; + + int month = date.month(); + int day = date.day(); + + // neither 1970 nor 2037 are leap years, so make sure date isn't Feb 29 + if (month == 2 && day == 29) + --day; + + if (date < lowerLimit) + date.setDate(LowerYear, month, day); + else + date.setDate(UpperYear, month, day); + + return date; +} + +static QDateTimePrivate::Spec utcToLocal(QDate &date, QTime &time) +{ + QDate fakeDate = adjustDate(date); + + time_t secsSince1Jan1970UTC = toTime_tHelper(fakeDate, time); + tm *brokenDown = 0; + +#if defined(Q_OS_WINCE) + tm res; + FILETIME utcTime = time_tToFt(secsSince1Jan1970UTC); + FILETIME resultTime; + FileTimeToLocalFileTime(&utcTime , &resultTime); + SYSTEMTIME sysTime; + FileTimeToSystemTime(&resultTime , &sysTime); + + res.tm_sec = sysTime.wSecond; + res.tm_min = sysTime.wMinute; + res.tm_hour = sysTime.wHour; + res.tm_mday = sysTime.wDay; + res.tm_mon = sysTime.wMonth - 1; + res.tm_year = sysTime.wYear - 1900; + brokenDown = &res; +#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + brokenDown = localtime_r(&secsSince1Jan1970UTC, &res); +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + tm res; + if (!_localtime64_s(&res, &secsSince1Jan1970UTC)) + brokenDown = &res; +#else + brokenDown = localtime(&secsSince1Jan1970UTC); +#endif + if (!brokenDown) { + date = QDate(1970, 1, 1); + time = QTime(); + return QDateTimePrivate::LocalUnknown; + } else { + int deltaDays = fakeDate.daysTo(date); + date = QDate(brokenDown->tm_year + 1900, brokenDown->tm_mon + 1, brokenDown->tm_mday); + time = QTime(brokenDown->tm_hour, brokenDown->tm_min, brokenDown->tm_sec, time.msec()); + date = date.addDays(deltaDays); + if (brokenDown->tm_isdst > 0) + return QDateTimePrivate::LocalDST; + else if (brokenDown->tm_isdst < 0) + return QDateTimePrivate::LocalUnknown; + else + return QDateTimePrivate::LocalStandard; + } +} + +static void localToUtc(QDate &date, QTime &time, int isdst) +{ + if (!date.isValid()) + return; + + QDate fakeDate = adjustDate(date); + + tm localTM; + localTM.tm_sec = time.second(); + localTM.tm_min = time.minute(); + localTM.tm_hour = time.hour(); + localTM.tm_mday = fakeDate.day(); + localTM.tm_mon = fakeDate.month() - 1; + localTM.tm_year = fakeDate.year() - 1900; + localTM.tm_isdst = (int)isdst; +#if defined(Q_OS_WINCE) + time_t secsSince1Jan1970UTC = toTime_tHelper(fakeDate, time); +#else +#if defined(Q_OS_WIN) + _tzset(); +#endif + time_t secsSince1Jan1970UTC = mktime(&localTM); +#endif + tm *brokenDown = 0; +#if defined(Q_OS_WINCE) + tm res; + FILETIME localTime = time_tToFt(secsSince1Jan1970UTC); + SYSTEMTIME sysTime; + FileTimeToSystemTime(&localTime, &sysTime); + FILETIME resultTime; + LocalFileTimeToFileTime(&localTime , &resultTime); + FileTimeToSystemTime(&resultTime , &sysTime); + res.tm_sec = sysTime.wSecond; + res.tm_min = sysTime.wMinute; + res.tm_hour = sysTime.wHour; + res.tm_mday = sysTime.wDay; + res.tm_mon = sysTime.wMonth - 1; + res.tm_year = sysTime.wYear - 1900; + res.tm_isdst = (int)isdst; + brokenDown = &res; +#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of gmtime() where available + tm res; + brokenDown = gmtime_r(&secsSince1Jan1970UTC, &res); +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + tm res; + if (!_gmtime64_s(&res, &secsSince1Jan1970UTC)) + brokenDown = &res; +#else + brokenDown = gmtime(&secsSince1Jan1970UTC); +#endif // !QT_NO_THREAD && _POSIX_THREAD_SAFE_FUNCTIONS + if (!brokenDown) { + date = QDate(1970, 1, 1); + time = QTime(); + } else { + int deltaDays = fakeDate.daysTo(date); + date = QDate(brokenDown->tm_year + 1900, brokenDown->tm_mon + 1, brokenDown->tm_mday); + time = QTime(brokenDown->tm_hour, brokenDown->tm_min, brokenDown->tm_sec, time.msec()); + date = date.addDays(deltaDays); + } +} + +QDateTimePrivate::Spec QDateTimePrivate::getLocal(QDate &outDate, QTime &outTime) const +{ + outDate = date; + outTime = time; + if (spec == QDateTimePrivate::UTC) + return utcToLocal(outDate, outTime); + return spec; +} + +void QDateTimePrivate::getUTC(QDate &outDate, QTime &outTime) const +{ + outDate = date; + outTime = time; + const bool isOffset = spec == QDateTimePrivate::OffsetFromUTC; + + if (spec != QDateTimePrivate::UTC && !isOffset) + 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() << ")"; + return dbg.space(); +} + +QDebug operator<<(QDebug dbg, const QTime &time) +{ + dbg.nospace() << "QTime(" << time.toString() << ")"; + return dbg.space(); +} + +QDebug operator<<(QDebug dbg, const QDateTime &date) +{ + dbg.nospace() << "QDateTime(" << date.toString() << ")"; + return dbg.space(); +} +#endif + +#ifndef QT_BOOTSTRAPPED + +/*! + \internal + Gets the digit from a datetime. E.g. + + QDateTime var(QDate(2004, 02, 02)); + int digit = getDigit(var, Year); + // digit = 2004 +*/ + +int QDateTimeParser::getDigit(const QDateTime &t, int index) const +{ + if (index < 0 || index >= sectionNodes.size()) { +#ifndef QT_NO_DATESTRING + qWarning("QDateTimeParser::getDigit() Internal error (%s %d)", + qPrintable(t.toString()), index); +#else + qWarning("QDateTimeParser::getDigit() Internal error (%d)", index); +#endif + return -1; + } + const SectionNode &node = sectionNodes.at(index); + switch (node.type) { + case Hour24Section: case Hour12Section: return t.time().hour(); + case MinuteSection: return t.time().minute(); + case SecondSection: return t.time().second(); + case MSecSection: return t.time().msec(); + case YearSection2Digits: + case YearSection: return t.date().year(); + case MonthSection: return t.date().month(); + case DaySection: return t.date().day(); + case DayOfWeekSection: return t.date().day(); + case AmPmSection: return t.time().hour() > 11 ? 1 : 0; + + default: break; + } + +#ifndef QT_NO_DATESTRING + qWarning("QDateTimeParser::getDigit() Internal error 2 (%s %d)", + qPrintable(t.toString()), index); +#else + qWarning("QDateTimeParser::getDigit() Internal error 2 (%d)", index); +#endif + return -1; +} + +/*! + \internal + Sets a digit in a datetime. E.g. + + QDateTime var(QDate(2004, 02, 02)); + int digit = getDigit(var, Year); + // digit = 2004 + setDigit(&var, Year, 2005); + digit = getDigit(var, Year); + // digit = 2005 +*/ + +bool QDateTimeParser::setDigit(QDateTime &v, int index, int newVal) const +{ + if (index < 0 || index >= sectionNodes.size()) { +#ifndef QT_NO_DATESTRING + qWarning("QDateTimeParser::setDigit() Internal error (%s %d %d)", + qPrintable(v.toString()), index, newVal); +#else + qWarning("QDateTimeParser::setDigit() Internal error (%d %d)", index, newVal); +#endif + return false; + } + const SectionNode &node = sectionNodes.at(index); + + int year, month, day, hour, minute, second, msec; + year = v.date().year(); + month = v.date().month(); + day = v.date().day(); + hour = v.time().hour(); + minute = v.time().minute(); + second = v.time().second(); + msec = v.time().msec(); + + switch (node.type) { + case Hour24Section: case Hour12Section: hour = newVal; break; + case MinuteSection: minute = newVal; break; + case SecondSection: second = newVal; break; + case MSecSection: msec = newVal; break; + case YearSection2Digits: + case YearSection: year = newVal; break; + case MonthSection: month = newVal; break; + case DaySection: + case DayOfWeekSection: + if (newVal > 31) { + // have to keep legacy behavior. setting the + // date to 32 should return false. Setting it + // to 31 for february should return true + return false; + } + day = newVal; + break; + case AmPmSection: hour = (newVal == 0 ? hour % 12 : (hour % 12) + 12); break; + default: + qWarning("QDateTimeParser::setDigit() Internal error (%s)", + qPrintable(sectionName(node.type))); + break; + } + + if (!(node.type & (DaySection|DayOfWeekSection))) { + if (day < cachedDay) + day = cachedDay; + const int max = QDate(year, month, 1).daysInMonth(); + if (day > max) { + day = max; + } + } + if (QDate::isValid(year, month, day) && QTime::isValid(hour, minute, second, msec)) { + v = QDateTime(QDate(year, month, day), QTime(hour, minute, second, msec), spec); + return true; + } + return false; +} + + + +/*! + \ + + Returns the absolute maximum for a section +*/ + +int QDateTimeParser::absoluteMax(int s, const QDateTime &cur) const +{ + const SectionNode &sn = sectionNode(s); + switch (sn.type) { + case Hour24Section: + case Hour12Section: return 23; // this is special-cased in + // parseSection. We want it to be + // 23 for the stepBy case. + case MinuteSection: + case SecondSection: return 59; + case MSecSection: return 999; + case YearSection2Digits: + case YearSection: return 9999; // sectionMaxSize will prevent + // people from typing in a larger + // number in count == 2 sections. + // stepBy() will work on real years anyway + case MonthSection: return 12; + case DaySection: + case DayOfWeekSection: return cur.isValid() ? cur.date().daysInMonth() : 31; + case AmPmSection: return 1; + default: break; + } + qWarning("QDateTimeParser::absoluteMax() Internal error (%s)", + qPrintable(sectionName(sn.type))); + return -1; +} + +/*! + \internal + + Returns the absolute minimum for a section +*/ + +int QDateTimeParser::absoluteMin(int s) const +{ + const SectionNode &sn = sectionNode(s); + switch (sn.type) { + case Hour24Section: + case Hour12Section: + case MinuteSection: + case SecondSection: + case MSecSection: + case YearSection2Digits: + case YearSection: return 0; + case MonthSection: + case DaySection: + case DayOfWeekSection: return 1; + case AmPmSection: return 0; + default: break; + } + qWarning("QDateTimeParser::absoluteMin() Internal error (%s, %0x)", + qPrintable(sectionName(sn.type)), sn.type); + return -1; +} + +/*! + \internal + + Returns the sectionNode for the Section \a s. +*/ + +const QDateTimeParser::SectionNode &QDateTimeParser::sectionNode(int sectionIndex) const +{ + if (sectionIndex < 0) { + switch (sectionIndex) { + case FirstSectionIndex: + return first; + case LastSectionIndex: + return last; + case NoSectionIndex: + return none; + } + } else if (sectionIndex < sectionNodes.size()) { + return sectionNodes.at(sectionIndex); + } + + qWarning("QDateTimeParser::sectionNode() Internal error (%d)", + sectionIndex); + return none; +} + +QDateTimeParser::Section QDateTimeParser::sectionType(int sectionIndex) const +{ + return sectionNode(sectionIndex).type; +} + + +/*! + \internal + + Returns the starting position for section \a s. +*/ + +int QDateTimeParser::sectionPos(int sectionIndex) const +{ + return sectionPos(sectionNode(sectionIndex)); +} + +int QDateTimeParser::sectionPos(const SectionNode &sn) const +{ + switch (sn.type) { + case FirstSection: return 0; + case LastSection: return displayText().size() - 1; + default: break; + } + if (sn.pos == -1) { + qWarning("QDateTimeParser::sectionPos Internal error (%s)", qPrintable(sectionName(sn.type))); + return -1; + } + return sn.pos; +} + + +/*! + \internal helper function for parseFormat. removes quotes that are + not escaped and removes the escaping on those that are escaped + +*/ + +static QString unquote(const QString &str) +{ + const QChar quote(QLatin1Char('\'')); + const QChar slash(QLatin1Char('\\')); + const QChar zero(QLatin1Char('0')); + QString ret; + QChar status(zero); + const int max = str.size(); + for (int i=0; i<max; ++i) { + if (str.at(i) == quote) { + if (status != quote) { + status = quote; + } else if (!ret.isEmpty() && str.at(i - 1) == slash) { + ret[ret.size() - 1] = quote; + } else { + status = zero; + } + } else { + ret += str.at(i); + } + } + return ret; +} +/*! + \internal + + Parses the format \a newFormat. If successful, returns true and + sets up the format. Else keeps the old format and returns false. + +*/ + +static inline int countRepeat(const QString &str, int index, int maxCount) +{ + int count = 1; + const QChar ch(str.at(index)); + const int max = qMin(index + maxCount, str.size()); + while (index + count < max && str.at(index + count) == ch) { + ++count; + } + return count; +} + +static inline void appendSeparator(QStringList *list, const QString &string, int from, int size, int lastQuote) +{ + QString str(string.mid(from, size)); + if (lastQuote >= from) + str = unquote(str); + list->append(str); +} + + +bool QDateTimeParser::parseFormat(const QString &newFormat) +{ + const QLatin1Char quote('\''); + const QLatin1Char slash('\\'); + const QLatin1Char zero('0'); + if (newFormat == displayFormat && !newFormat.isEmpty()) { + return true; + } + + QDTPDEBUGN("parseFormat: %s", newFormat.toLatin1().constData()); + + QVector<SectionNode> newSectionNodes; + Sections newDisplay = 0; + QStringList newSeparators; + int i, index = 0; + int add = 0; + QChar status(zero); + const int max = newFormat.size(); + int lastQuote = -1; + for (i = 0; i<max; ++i) { + if (newFormat.at(i) == quote) { + lastQuote = i; + ++add; + if (status != quote) { + status = quote; + } else if (newFormat.at(i - 1) != slash) { + status = zero; + } + } else if (status != quote) { + const char sect = newFormat.at(i).toLatin1(); + switch (sect) { + case 'H': + case 'h': + if (parserType != QVariant::Date) { + const Section hour = (sect == 'h') ? Hour12Section : Hour24Section; + const SectionNode sn = { hour, i - add, countRepeat(newFormat, i, 2) }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= hour; + } + break; + case 'm': + if (parserType != QVariant::Date) { + const SectionNode sn = { MinuteSection, i - add, countRepeat(newFormat, i, 2) }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= MinuteSection; + } + break; + case 's': + if (parserType != QVariant::Date) { + const SectionNode sn = { SecondSection, i - add, countRepeat(newFormat, i, 2) }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= SecondSection; + } + break; + + case 'z': + if (parserType != QVariant::Date) { + const SectionNode sn = { MSecSection, i - add, countRepeat(newFormat, i, 3) < 3 ? 1 : 3 }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= MSecSection; + } + break; + case 'A': + case 'a': + if (parserType != QVariant::Date) { + const bool cap = (sect == 'A'); + const SectionNode sn = { AmPmSection, i - add, (cap ? 1 : 0) }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + newDisplay |= AmPmSection; + if (i + 1 < newFormat.size() + && newFormat.at(i+1) == (cap ? QLatin1Char('P') : QLatin1Char('p'))) { + ++i; + } + index = i + 1; + } + break; + case 'y': + if (parserType != QVariant::Time) { + const int repeat = countRepeat(newFormat, i, 4); + if (repeat >= 2) { + const SectionNode sn = { repeat == 4 ? YearSection : YearSection2Digits, + i - add, repeat == 4 ? 4 : 2 }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= sn.type; + } + } + break; + case 'M': + if (parserType != QVariant::Time) { + const SectionNode sn = { MonthSection, i - add, countRepeat(newFormat, i, 4) }; + newSectionNodes.append(sn); + newSeparators.append(unquote(newFormat.mid(index, i - index))); + i += sn.count - 1; + index = i + 1; + newDisplay |= MonthSection; + } + break; + case 'd': + if (parserType != QVariant::Time) { + const int repeat = countRepeat(newFormat, i, 4); + const SectionNode sn = { repeat >= 3 ? DayOfWeekSection : DaySection, i - add, repeat }; + newSectionNodes.append(sn); + appendSeparator(&newSeparators, newFormat, index, i - index, lastQuote); + i += sn.count - 1; + index = i + 1; + newDisplay |= sn.type; + } + break; + + default: + break; + } + } + } + if (newSectionNodes.isEmpty() && context == DateTimeEdit) { + return false; + } + + if ((newDisplay & (AmPmSection|Hour12Section)) == Hour12Section) { + const int max = newSectionNodes.size(); + for (int i=0; i<max; ++i) { + SectionNode &node = newSectionNodes[i]; + if (node.type == Hour12Section) + node.type = Hour24Section; + } + } + + if (index < newFormat.size()) { + appendSeparator(&newSeparators, newFormat, index, index - max, lastQuote); + } else { + newSeparators.append(QString()); + } + + displayFormat = newFormat; + separators = newSeparators; + sectionNodes = newSectionNodes; + display = newDisplay; + last.pos = -1; + +// for (int i=0; i<sectionNodes.size(); ++i) { +// QDTPDEBUG << sectionName(sectionNodes.at(i).type) << sectionNodes.at(i).count; +// } + + QDTPDEBUG << newFormat << displayFormat; + QDTPDEBUGN("separators:\n'%s'", separators.join(QLatin1String("\n")).toLatin1().constData()); + + return true; +} + +/*! + \internal + + Returns the size of section \a s. +*/ + +int QDateTimeParser::sectionSize(int sectionIndex) const +{ + if (sectionIndex < 0) + return 0; + + if (sectionIndex >= sectionNodes.size()) { + qWarning("QDateTimeParser::sectionSize Internal error (%d)", sectionIndex); + return -1; + } + if (sectionIndex == sectionNodes.size() - 1) { + return displayText().size() - sectionPos(sectionIndex) - separators.last().size(); + } else { + return sectionPos(sectionIndex + 1) - sectionPos(sectionIndex) + - separators.at(sectionIndex + 1).size(); + } +} + + +int QDateTimeParser::sectionMaxSize(Section s, int count) const +{ +#ifndef QT_NO_TEXTDATE + int mcount = 12; +#endif + + switch (s) { + case FirstSection: + case NoSection: + case LastSection: return 0; + + case AmPmSection: { + const int lowerMax = qMin(getAmPmText(AmText, LowerCase).size(), + getAmPmText(PmText, LowerCase).size()); + const int upperMax = qMin(getAmPmText(AmText, UpperCase).size(), + getAmPmText(PmText, UpperCase).size()); + return qMin(4, qMin(lowerMax, upperMax)); + } + + case Hour24Section: + case Hour12Section: + case MinuteSection: + case SecondSection: + case DaySection: return 2; + case DayOfWeekSection: +#ifdef QT_NO_TEXTDATE + return 2; +#else + mcount = 7; + // fall through +#endif + case MonthSection: + if (count <= 2) + return 2; + +#ifdef QT_NO_TEXTDATE + return 2; +#else + { + int ret = 0; + const QLocale l = locale(); + for (int i=1; i<=mcount; ++i) { + const QString str = (s == MonthSection + ? l.monthName(i, count == 4 ? QLocale::LongFormat : QLocale::ShortFormat) + : l.dayName(i, count == 4 ? QLocale::LongFormat : QLocale::ShortFormat)); + ret = qMax(str.size(), ret); + } + return ret; + } +#endif + case MSecSection: return 3; + case YearSection: return 4; + case YearSection2Digits: return 2; + + case CalendarPopupSection: + case Internal: + case TimeSectionMask: + case DateSectionMask: + qWarning("QDateTimeParser::sectionMaxSize: Invalid section %s", + sectionName(s).toLatin1().constData()); + } + return -1; +} + + +int QDateTimeParser::sectionMaxSize(int index) const +{ + const SectionNode &sn = sectionNode(index); + return sectionMaxSize(sn.type, sn.count); +} + +/*! + \internal + + Returns the text of section \a s. This function operates on the + arg text rather than edit->text(). +*/ + + +QString QDateTimeParser::sectionText(const QString &text, int sectionIndex, int index) const +{ + const SectionNode &sn = sectionNode(sectionIndex); + switch (sn.type) { + case NoSectionIndex: + case FirstSectionIndex: + case LastSectionIndex: + return QString(); + default: break; + } + + return text.mid(index, sectionSize(sectionIndex)); +} + +QString QDateTimeParser::sectionText(int sectionIndex) const +{ + const SectionNode &sn = sectionNode(sectionIndex); + switch (sn.type) { + case NoSectionIndex: + case FirstSectionIndex: + case LastSectionIndex: + return QString(); + default: break; + } + + return displayText().mid(sn.pos, sectionSize(sectionIndex)); +} + + +#ifndef QT_NO_TEXTDATE +/*! + \internal:skipToNextSection + + Parses the part of \a text that corresponds to \a s and returns + the value of that field. Sets *stateptr to the right state if + stateptr != 0. +*/ + +int QDateTimeParser::parseSection(const QDateTime ¤tValue, int sectionIndex, + QString &text, int &cursorPosition, int index, + State &state, int *usedptr) const +{ + state = Invalid; + int num = 0; + const SectionNode &sn = sectionNode(sectionIndex); + if ((sn.type & Internal) == Internal) { + qWarning("QDateTimeParser::parseSection Internal error (%s %d)", + qPrintable(sectionName(sn.type)), sectionIndex); + return -1; + } + + const int sectionmaxsize = sectionMaxSize(sectionIndex); + QString sectiontext = text.mid(index, sectionmaxsize); + int sectiontextSize = sectiontext.size(); + + QDTPDEBUG << "sectionValue for" << sectionName(sn.type) + << "with text" << text << "and st" << sectiontext + << text.mid(index, sectionmaxsize) + << index; + + int used = 0; + switch (sn.type) { + case AmPmSection: { + const int ampm = findAmPm(sectiontext, sectionIndex, &used); + switch (ampm) { + case AM: // sectiontext == AM + case PM: // sectiontext == PM + num = ampm; + state = Acceptable; + break; + case PossibleAM: // sectiontext => AM + case PossiblePM: // sectiontext => PM + num = ampm - 2; + state = Intermediate; + break; + case PossibleBoth: // sectiontext => AM|PM + num = 0; + state = Intermediate; + break; + case Neither: + state = Invalid; + QDTPDEBUG << "invalid because findAmPm(" << sectiontext << ") returned -1"; + break; + default: + QDTPDEBUGN("This should never happen (findAmPm returned %d)", ampm); + break; + } + if (state != Invalid) { + QString str = text; + text.replace(index, used, sectiontext.left(used)); + } + break; } + case MonthSection: + case DayOfWeekSection: + if (sn.count >= 3) { + if (sn.type == MonthSection) { + int min = 1; + const QDate minDate = getMinimum().date(); + if (currentValue.date().year() == minDate.year()) { + min = minDate.month(); + } + num = findMonth(sectiontext.toLower(), min, sectionIndex, §iontext, &used); + } else { + num = findDay(sectiontext.toLower(), 1, sectionIndex, §iontext, &used); + } + + if (num != -1) { + state = (used == sectiontext.size() ? Acceptable : Intermediate); + QString str = text; + text.replace(index, used, sectiontext.left(used)); + } else { + state = Intermediate; + } + break; } + // fall through + case DaySection: + case YearSection: + case YearSection2Digits: + case Hour12Section: + case Hour24Section: + case MinuteSection: + case SecondSection: + case MSecSection: { + if (sectiontextSize == 0) { + num = 0; + used = 0; + state = Intermediate; + } else { + const int absMax = absoluteMax(sectionIndex); + QLocale loc; + bool ok = true; + int last = -1; + used = -1; + + QString digitsStr(sectiontext); + for (int i = 0; i < sectiontextSize; ++i) { + if (digitsStr.at(i).isSpace()) { + sectiontextSize = i; + break; + } + } + + const int max = qMin(sectionmaxsize, sectiontextSize); + for (int digits = max; digits >= 1; --digits) { + digitsStr.truncate(digits); + int tmp = (int)loc.toUInt(digitsStr, &ok, 10); + if (ok && sn.type == Hour12Section) { + if (tmp > 12) { + tmp = -1; + ok = false; + } else if (tmp == 12) { + tmp = 0; + } + } + if (ok && tmp <= absMax) { + QDTPDEBUG << sectiontext.left(digits) << tmp << digits; + last = tmp; + used = digits; + break; + } + } + + if (last == -1) { + QChar first(sectiontext.at(0)); + if (separators.at(sectionIndex + 1).startsWith(first)) { + used = 0; + state = Intermediate; + } else { + state = Invalid; + QDTPDEBUG << "invalid because" << sectiontext << "can't become a uint" << last << ok; + } + } else { + num += last; + const FieldInfo fi = fieldInfo(sectionIndex); + const bool done = (used == sectionmaxsize); + if (!done && fi & Fraction) { // typing 2 in a zzz field should be .200, not .002 + for (int i=used; i<sectionmaxsize; ++i) { + num *= 10; + } + } + const int absMin = absoluteMin(sectionIndex); + if (num < absMin) { + state = done ? Invalid : Intermediate; + if (done) + QDTPDEBUG << "invalid because" << num << "is less than absoluteMin" << absMin; + } else if (num > absMax) { + state = Intermediate; + } else if (!done && (fi & (FixedWidth|Numeric)) == (FixedWidth|Numeric)) { + if (skipToNextSection(sectionIndex, currentValue, digitsStr)) { + state = Acceptable; + const int missingZeroes = sectionmaxsize - digitsStr.size(); + text.insert(index, QString().fill(QLatin1Char('0'), missingZeroes)); + used = sectionmaxsize; + cursorPosition += missingZeroes; + } else { + state = Intermediate;; + } + } else { + state = Acceptable; + } + } + } + break; } + default: + qWarning("QDateTimeParser::parseSection Internal error (%s %d)", + qPrintable(sectionName(sn.type)), sectionIndex); + return -1; + } + + if (usedptr) + *usedptr = used; + + return (state != Invalid ? num : -1); +} +#endif // QT_NO_TEXTDATE + +#ifndef QT_NO_DATESTRING +/*! + \internal +*/ + +QDateTimeParser::StateNode QDateTimeParser::parse(QString &input, int &cursorPosition, + const QDateTime ¤tValue, bool fixup) const +{ + const QDateTime minimum = getMinimum(); + const QDateTime maximum = getMaximum(); + + State state = Acceptable; + + QDateTime newCurrentValue; + int pos = 0; + bool conflicts = false; + const int sectionNodesCount = sectionNodes.size(); + + QDTPDEBUG << "parse" << input; + { + int year, month, day, hour12, hour, minute, second, msec, ampm, dayofweek, year2digits; + getDateFromJulianDay(currentValue.date().toJulianDay(), &year, &month, &day); + year2digits = year % 100; + hour = currentValue.time().hour(); + hour12 = -1; + minute = currentValue.time().minute(); + second = currentValue.time().second(); + msec = currentValue.time().msec(); + dayofweek = currentValue.date().dayOfWeek(); + + ampm = -1; + Sections isSet = NoSection; + int num; + State tmpstate; + + for (int index=0; state != Invalid && index<sectionNodesCount; ++index) { + if (QStringRef(&input, pos, separators.at(index).size()) != separators.at(index)) { + QDTPDEBUG << "invalid because" << input.mid(pos, separators.at(index).size()) + << "!=" << separators.at(index) + << index << pos << currentSectionIndex; + state = Invalid; + goto end; + } + pos += separators.at(index).size(); + sectionNodes[index].pos = pos; + int *current = 0; + const SectionNode sn = sectionNodes.at(index); + int used; + + num = parseSection(currentValue, index, input, cursorPosition, pos, tmpstate, &used); + QDTPDEBUG << "sectionValue" << sectionName(sectionType(index)) << input + << "pos" << pos << "used" << used << stateName(tmpstate); + if (fixup && tmpstate == Intermediate && used < sn.count) { + const FieldInfo fi = fieldInfo(index); + if ((fi & (Numeric|FixedWidth)) == (Numeric|FixedWidth)) { + const QString newText = QString(QLatin1String("%1")).arg(num, sn.count, 10, QLatin1Char('0')); + input.replace(pos, used, newText); + used = sn.count; + } + } + pos += qMax(0, used); + + state = qMin<State>(state, tmpstate); + if (state == Intermediate && context == FromString) { + state = Invalid; + break; + } + + QDTPDEBUG << index << sectionName(sectionType(index)) << "is set to" + << pos << "state is" << stateName(state); + + + if (state != Invalid) { + switch (sn.type) { + case Hour24Section: current = &hour; break; + case Hour12Section: current = &hour12; break; + case MinuteSection: current = &minute; break; + case SecondSection: current = &second; break; + case MSecSection: current = &msec; break; + case YearSection: current = &year; break; + case YearSection2Digits: current = &year2digits; break; + case MonthSection: current = &month; break; + case DayOfWeekSection: current = &dayofweek; break; + case DaySection: current = &day; num = qMax<int>(1, num); break; + case AmPmSection: current = &m; break; + default: + qWarning("QDateTimeParser::parse Internal error (%s)", + qPrintable(sectionName(sn.type))); + break; + } + if (!current) { + qWarning("QDateTimeParser::parse Internal error 2"); + return StateNode(); + } + if (isSet & sn.type && *current != num) { + QDTPDEBUG << "CONFLICT " << sectionName(sn.type) << *current << num; + conflicts = true; + if (index != currentSectionIndex || num == -1) { + continue; + } + } + if (num != -1) + *current = num; + isSet |= sn.type; + } + } + + if (state != Invalid && QStringRef(&input, pos, input.size() - pos) != separators.last()) { + QDTPDEBUG << "invalid because" << input.mid(pos) + << "!=" << separators.last() << pos; + state = Invalid; + } + + if (state != Invalid) { + if (parserType != QVariant::Time) { + if (year % 100 != year2digits) { + switch (isSet & (YearSection2Digits|YearSection)) { + case YearSection2Digits: + year = (year / 100) * 100; + year += year2digits; + break; + case ((uint)YearSection2Digits|(uint)YearSection): { + conflicts = true; + const SectionNode &sn = sectionNode(currentSectionIndex); + if (sn.type == YearSection2Digits) { + year = (year / 100) * 100; + year += year2digits; + } + break; } + default: + break; + } + } + + const QDate date(year, month, day); + const int diff = dayofweek - date.dayOfWeek(); + if (diff != 0 && state == Acceptable && isSet & DayOfWeekSection) { + conflicts = isSet & DaySection; + const SectionNode &sn = sectionNode(currentSectionIndex); + if (sn.type == DayOfWeekSection || currentSectionIndex == -1) { + // dayofweek should be preferred + day += diff; + if (day <= 0) { + day += 7; + } else if (day > date.daysInMonth()) { + day -= 7; + } + QDTPDEBUG << year << month << day << dayofweek + << diff << QDate(year, month, day).dayOfWeek(); + } + } + bool needfixday = false; + if (sectionType(currentSectionIndex) & (DaySection|DayOfWeekSection)) { + cachedDay = day; + } else if (cachedDay > day) { + day = cachedDay; + needfixday = true; + } + + if (!QDate::isValid(year, month, day)) { + if (day < 32) { + cachedDay = day; + } + if (day > 28 && QDate::isValid(year, month, 1)) { + needfixday = true; + } + } + if (needfixday) { + if (context == FromString) { + state = Invalid; + goto end; + } + if (state == Acceptable && fixday) { + day = qMin<int>(day, QDate(year, month, 1).daysInMonth()); + + const QLocale loc = locale(); + for (int i=0; i<sectionNodesCount; ++i) { + if (sectionType(i) & (DaySection|DayOfWeekSection)) { + input.replace(sectionPos(i), sectionSize(i), loc.toString(day)); + } + } + } else { + state = qMin(Intermediate, state); + } + } + } + + if (parserType != QVariant::Date) { + if (isSet & Hour12Section) { + const bool hasHour = isSet & Hour24Section; + if (ampm == -1) { + if (hasHour) { + ampm = (hour < 12 ? 0 : 1); + } else { + ampm = 0; // no way to tell if this is am or pm so I assume am + } + } + hour12 = (ampm == 0 ? hour12 % 12 : (hour12 % 12) + 12); + if (!hasHour) { + hour = hour12; + } else if (hour != hour12) { + conflicts = true; + } + } else if (ampm != -1) { + if (!(isSet & (Hour24Section))) { + hour = (12 * ampm); // special case. Only ap section + } else if ((ampm == 0) != (hour < 12)) { + conflicts = true; + } + } + + } + + newCurrentValue = QDateTime(QDate(year, month, day), QTime(hour, minute, second, msec), spec); + QDTPDEBUG << year << month << day << hour << minute << second << msec; + } + QDTPDEBUGN("'%s' => '%s'(%s)", input.toLatin1().constData(), + newCurrentValue.toString(QLatin1String("yyyy/MM/dd hh:mm:ss.zzz")).toLatin1().constData(), + stateName(state).toLatin1().constData()); + } +end: + if (newCurrentValue.isValid()) { + if (context != FromString && state != Invalid && newCurrentValue < minimum) { + const QLatin1Char space(' '); + if (newCurrentValue >= minimum) + qWarning("QDateTimeParser::parse Internal error 3 (%s %s)", + qPrintable(newCurrentValue.toString()), qPrintable(minimum.toString())); + + bool done = false; + state = Invalid; + for (int i=0; i<sectionNodesCount && !done; ++i) { + const SectionNode &sn = sectionNodes.at(i); + QString t = sectionText(input, i, sn.pos).toLower(); + if ((t.size() < sectionMaxSize(i) && (((int)fieldInfo(i) & (FixedWidth|Numeric)) != Numeric)) + || t.contains(space)) { + switch (sn.type) { + case AmPmSection: + switch (findAmPm(t, i)) { + case AM: + case PM: + state = Acceptable; + done = true; + break; + case Neither: + state = Invalid; + done = true; + break; + case PossibleAM: + case PossiblePM: + case PossibleBoth: { + const QDateTime copy(newCurrentValue.addSecs(12 * 60 * 60)); + if (copy >= minimum && copy <= maximum) { + state = Intermediate; + done = true; + } + break; } + } + case MonthSection: + if (sn.count >= 3) { + int tmp = newCurrentValue.date().month(); + // I know the first possible month makes the date too early + while ((tmp = findMonth(t, tmp + 1, i)) != -1) { + const QDateTime copy(newCurrentValue.addMonths(tmp - newCurrentValue.date().month())); + if (copy >= minimum && copy <= maximum) + break; // break out of while + } + if (tmp == -1) { + break; + } + state = Intermediate; + done = true; + break; + } + // fallthrough + default: { + int toMin; + int toMax; + + if (sn.type & TimeSectionMask) { + if (newCurrentValue.daysTo(minimum) != 0) { + break; + } + toMin = newCurrentValue.time().msecsTo(minimum.time()); + if (newCurrentValue.daysTo(maximum) > 0) { + toMax = -1; // can't get to max + } else { + toMax = newCurrentValue.time().msecsTo(maximum.time()); + } + } else { + toMin = newCurrentValue.daysTo(minimum); + toMax = newCurrentValue.daysTo(maximum); + } + const int maxChange = QDateTimeParser::maxChange(i); + if (toMin > maxChange) { + QDTPDEBUG << "invalid because toMin > maxChange" << toMin + << maxChange << t << newCurrentValue << minimum; + state = Invalid; + done = true; + break; + } else if (toMax > maxChange) { + toMax = -1; // can't get to max + } + + const int min = getDigit(minimum, i); + if (min == -1) { + qWarning("QDateTimeParser::parse Internal error 4 (%s)", + qPrintable(sectionName(sn.type))); + state = Invalid; + done = true; + break; + } + + int max = toMax != -1 ? getDigit(maximum, i) : absoluteMax(i, newCurrentValue); + int pos = cursorPosition - sn.pos; + if (pos < 0 || pos >= t.size()) + pos = -1; + if (!potentialValue(t.simplified(), min, max, i, newCurrentValue, pos)) { + QDTPDEBUG << "invalid because potentialValue(" << t.simplified() << min << max + << sectionName(sn.type) << "returned" << toMax << toMin << pos; + state = Invalid; + done = true; + break; + } + state = Intermediate; + done = true; + break; } + } + } + } + } else { + if (context == FromString) { + // optimization + Q_ASSERT(getMaximum().date().toJulianDay() == 4642999); + if (newCurrentValue.date().toJulianDay() > 4642999) + state = Invalid; + } else { + if (newCurrentValue > getMaximum()) + state = Invalid; + } + + QDTPDEBUG << "not checking intermediate because newCurrentValue is" << newCurrentValue << getMinimum() << getMaximum(); + } + } + StateNode node; + node.input = input; + node.state = state; + node.conflicts = conflicts; + node.value = newCurrentValue.toTimeSpec(spec); + text = input; + return node; +} +#endif // QT_NO_DATESTRING + +#ifndef QT_NO_TEXTDATE +/*! + \internal finds the first possible monthname that \a str1 can + match. Starting from \a index; str should already by lowered +*/ + +int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionIndex, + QString *usedMonth, int *used) const +{ + int bestMatch = -1; + int bestCount = 0; + if (!str1.isEmpty()) { + const SectionNode &sn = sectionNode(sectionIndex); + if (sn.type != MonthSection) { + qWarning("QDateTimeParser::findMonth Internal error"); + return -1; + } + + QLocale::FormatType type = sn.count == 3 ? QLocale::ShortFormat : QLocale::LongFormat; + QLocale l = locale(); + + for (int month=startMonth; month<=12; ++month) { + QString str2 = l.monthName(month, type).toLower(); + + if (str1.startsWith(str2)) { + if (used) { + QDTPDEBUG << "used is set to" << str2.size(); + *used = str2.size(); + } + if (usedMonth) + *usedMonth = l.monthName(month, type); + + return month; + } + if (context == FromString) + continue; + + const int limit = qMin(str1.size(), str2.size()); + + QDTPDEBUG << "limit is" << limit << str1 << str2; + bool equal = true; + for (int i=0; i<limit; ++i) { + if (str1.at(i) != str2.at(i)) { + equal = false; + if (i > bestCount) { + bestCount = i; + bestMatch = month; + } + break; + } + } + if (equal) { + if (used) + *used = limit; + if (usedMonth) + *usedMonth = l.monthName(month, type); + return month; + } + } + if (usedMonth && bestMatch != -1) + *usedMonth = l.monthName(bestMatch, type); + } + if (used) { + QDTPDEBUG << "used is set to" << bestCount; + *used = bestCount; + } + return bestMatch; +} + +int QDateTimeParser::findDay(const QString &str1, int startDay, int sectionIndex, QString *usedDay, int *used) const +{ + int bestMatch = -1; + int bestCount = 0; + if (!str1.isEmpty()) { + const SectionNode &sn = sectionNode(sectionIndex); + if (!(sn.type & (DaySection|DayOfWeekSection))) { + qWarning("QDateTimeParser::findDay Internal error"); + return -1; + } + const QLocale l = locale(); + for (int day=startDay; day<=7; ++day) { + const QString str2 = l.dayName(day, sn.count == 4 ? QLocale::LongFormat : QLocale::ShortFormat); + + if (str1.startsWith(str2.toLower())) { + if (used) + *used = str2.size(); + if (usedDay) { + *usedDay = str2; + } + return day; + } + if (context == FromString) + continue; + + const int limit = qMin(str1.size(), str2.size()); + bool found = true; + for (int i=0; i<limit; ++i) { + if (str1.at(i) != str2.at(i) && !str1.at(i).isSpace()) { + if (i > bestCount) { + bestCount = i; + bestMatch = day; + } + found = false; + break; + } + + } + if (found) { + if (used) + *used = limit; + if (usedDay) + *usedDay = str2; + + return day; + } + } + if (usedDay && bestMatch != -1) { + *usedDay = l.dayName(bestMatch, sn.count == 4 ? QLocale::LongFormat : QLocale::ShortFormat); + } + } + if (used) + *used = bestCount; + + return bestMatch; +} +#endif // QT_NO_TEXTDATE + +/*! + \internal + + returns + 0 if str == QDateTimeEdit::tr("AM") + 1 if str == QDateTimeEdit::tr("PM") + 2 if str can become QDateTimeEdit::tr("AM") + 3 if str can become QDateTimeEdit::tr("PM") + 4 if str can become QDateTimeEdit::tr("PM") and can become QDateTimeEdit::tr("AM") + -1 can't become anything sensible + +*/ + +int QDateTimeParser::findAmPm(QString &str, int index, int *used) const +{ + const SectionNode &s = sectionNode(index); + if (s.type != AmPmSection) { + qWarning("QDateTimeParser::findAmPm Internal error"); + return -1; + } + if (used) + *used = str.size(); + if (str.trimmed().isEmpty()) { + return PossibleBoth; + } + const QLatin1Char space(' '); + int size = sectionMaxSize(index); + + enum { + amindex = 0, + pmindex = 1 + }; + QString ampm[2]; + ampm[amindex] = getAmPmText(AmText, s.count == 1 ? UpperCase : LowerCase); + ampm[pmindex] = getAmPmText(PmText, s.count == 1 ? UpperCase : LowerCase); + for (int i=0; i<2; ++i) + ampm[i].truncate(size); + + QDTPDEBUG << "findAmPm" << str << ampm[0] << ampm[1]; + + if (str.indexOf(ampm[amindex], 0, Qt::CaseInsensitive) == 0) { + str = ampm[amindex]; + return AM; + } else if (str.indexOf(ampm[pmindex], 0, Qt::CaseInsensitive) == 0) { + str = ampm[pmindex]; + return PM; + } else if (context == FromString || (str.count(space) == 0 && str.size() >= size)) { + return Neither; + } + size = qMin(size, str.size()); + + bool broken[2] = {false, false}; + for (int i=0; i<size; ++i) { + if (str.at(i) != space) { + for (int j=0; j<2; ++j) { + if (!broken[j]) { + int index = ampm[j].indexOf(str.at(i)); + QDTPDEBUG << "looking for" << str.at(i) + << "in" << ampm[j] << "and got" << index; + if (index == -1) { + if (str.at(i).category() == QChar::Letter_Uppercase) { + index = ampm[j].indexOf(str.at(i).toLower()); + QDTPDEBUG << "trying with" << str.at(i).toLower() + << "in" << ampm[j] << "and got" << index; + } else if (str.at(i).category() == QChar::Letter_Lowercase) { + index = ampm[j].indexOf(str.at(i).toUpper()); + QDTPDEBUG << "trying with" << str.at(i).toUpper() + << "in" << ampm[j] << "and got" << index; + } + if (index == -1) { + broken[j] = true; + if (broken[amindex] && broken[pmindex]) { + QDTPDEBUG << str << "didn't make it"; + return Neither; + } + continue; + } else { + str[i] = ampm[j].at(index); // fix case + } + } + ampm[j].remove(index, 1); + } + } + } + } + if (!broken[pmindex] && !broken[amindex]) + return PossibleBoth; + return (!broken[amindex] ? PossibleAM : PossiblePM); +} + +/*! + \internal + Max number of units that can be changed by this section. +*/ + +int QDateTimeParser::maxChange(int index) const +{ + const SectionNode &sn = sectionNode(index); + switch (sn.type) { + // Time. unit is msec + case MSecSection: return 999; + case SecondSection: return 59 * 1000; + case MinuteSection: return 59 * 60 * 1000; + case Hour24Section: case Hour12Section: return 59 * 60 * 60 * 1000; + + // Date. unit is day + case DayOfWeekSection: return 7; + case DaySection: return 30; + case MonthSection: return 365 - 31; + case YearSection: return 9999 * 365; + case YearSection2Digits: return 100 * 365; + default: + qWarning("QDateTimeParser::maxChange() Internal error (%s)", + qPrintable(sectionName(sectionType(index)))); + } + + return -1; +} + +QDateTimeParser::FieldInfo QDateTimeParser::fieldInfo(int index) const +{ + FieldInfo ret = 0; + const SectionNode &sn = sectionNode(index); + const Section s = sn.type; + switch (s) { + case MSecSection: + ret |= Fraction; + // fallthrough + case SecondSection: + case MinuteSection: + case Hour24Section: + case Hour12Section: + case YearSection: + case YearSection2Digits: + ret |= Numeric; + if (s != YearSection) { + ret |= AllowPartial; + } + if (sn.count != 1) { + ret |= FixedWidth; + } + break; + case MonthSection: + case DaySection: + switch (sn.count) { + case 2: + ret |= FixedWidth; + // fallthrough + case 1: + ret |= (Numeric|AllowPartial); + break; + } + break; + case DayOfWeekSection: + if (sn.count == 3) + ret |= FixedWidth; + break; + case AmPmSection: + ret |= FixedWidth; + break; + default: + qWarning("QDateTimeParser::fieldInfo Internal error 2 (%d %s %d)", + index, qPrintable(sectionName(sn.type)), sn.count); + break; + } + return ret; +} + +/*! + \internal Get a number that str can become which is between min + and max or -1 if this is not possible. +*/ + + +QString QDateTimeParser::sectionFormat(int index) const +{ + const SectionNode &sn = sectionNode(index); + return sectionFormat(sn.type, sn.count); +} + +QString QDateTimeParser::sectionFormat(Section s, int count) const +{ + QChar fillChar; + switch (s) { + case AmPmSection: return count == 1 ? QLatin1String("AP") : QLatin1String("ap"); + case MSecSection: fillChar = QLatin1Char('z'); break; + case SecondSection: fillChar = QLatin1Char('s'); break; + case MinuteSection: fillChar = QLatin1Char('m'); break; + case Hour24Section: fillChar = QLatin1Char('H'); break; + case Hour12Section: fillChar = QLatin1Char('h'); break; + case DayOfWeekSection: + case DaySection: fillChar = QLatin1Char('d'); break; + case MonthSection: fillChar = QLatin1Char('M'); break; + case YearSection2Digits: + case YearSection: fillChar = QLatin1Char('y'); break; + default: + qWarning("QDateTimeParser::sectionFormat Internal error (%s)", + qPrintable(sectionName(s))); + return QString(); + } + if (fillChar.isNull()) { + qWarning("QDateTimeParser::sectionFormat Internal error 2"); + return QString(); + } + + QString str; + str.fill(fillChar, count); + return str; +} + + +/*! \internal Returns true if str can be modified to represent a + number that is within min and max. +*/ + +bool QDateTimeParser::potentialValue(const QString &str, int min, int max, int index, + const QDateTime ¤tValue, int insert) const +{ + if (str.isEmpty()) { + return true; + } + const int size = sectionMaxSize(index); + int val = (int)locale().toUInt(str); + const SectionNode &sn = sectionNode(index); + if (sn.type == YearSection2Digits) { + val += currentValue.date().year() - (currentValue.date().year() % 100); + } + if (val >= min && val <= max && str.size() == size) { + return true; + } else if (val > max) { + return false; + } else if (str.size() == size && val < min) { + return false; + } + + const int len = size - str.size(); + for (int i=0; i<len; ++i) { + for (int j=0; j<10; ++j) { + if (potentialValue(str + QLatin1Char('0' + j), min, max, index, currentValue, insert)) { + return true; + } else if (insert >= 0) { + QString tmp = str; + tmp.insert(insert, QLatin1Char('0' + j)); + if (potentialValue(tmp, min, max, index, currentValue, insert)) + return true; + } + } + } + + return false; +} + +bool QDateTimeParser::skipToNextSection(int index, const QDateTime ¤t, const QString &text) const +{ + Q_ASSERT(current >= getMinimum() && current <= getMaximum()); + + const SectionNode &node = sectionNode(index); + Q_ASSERT(text.size() < sectionMaxSize(index)); + + const QDateTime maximum = getMaximum(); + const QDateTime minimum = getMinimum(); + QDateTime tmp = current; + int min = absoluteMin(index); + setDigit(tmp, index, min); + if (tmp < minimum) { + min = getDigit(minimum, index); + } + + int max = absoluteMax(index, current); + setDigit(tmp, index, max); + if (tmp > maximum) { + max = getDigit(maximum, index); + } + int pos = cursorPosition() - node.pos; + if (pos < 0 || pos >= text.size()) + pos = -1; + + const bool potential = potentialValue(text, min, max, index, current, pos); + return !potential; + + /* If the value potentially can become another valid entry we + * don't want to skip to the next. E.g. In a M field (month + * without leading 0 if you type 1 we don't want to autoskip but + * if you type 3 we do + */ +} + +/*! + \internal + For debugging. Returns the name of the section \a s. +*/ + +QString QDateTimeParser::sectionName(int s) const +{ + switch (s) { + case QDateTimeParser::AmPmSection: return QLatin1String("AmPmSection"); + case QDateTimeParser::DaySection: return QLatin1String("DaySection"); + case QDateTimeParser::DayOfWeekSection: return QLatin1String("DayOfWeekSection"); + case QDateTimeParser::Hour24Section: return QLatin1String("Hour24Section"); + case QDateTimeParser::Hour12Section: return QLatin1String("Hour12Section"); + case QDateTimeParser::MSecSection: return QLatin1String("MSecSection"); + case QDateTimeParser::MinuteSection: return QLatin1String("MinuteSection"); + case QDateTimeParser::MonthSection: return QLatin1String("MonthSection"); + case QDateTimeParser::SecondSection: return QLatin1String("SecondSection"); + case QDateTimeParser::YearSection: return QLatin1String("YearSection"); + case QDateTimeParser::YearSection2Digits: return QLatin1String("YearSection2Digits"); + case QDateTimeParser::NoSection: return QLatin1String("NoSection"); + case QDateTimeParser::FirstSection: return QLatin1String("FirstSection"); + case QDateTimeParser::LastSection: return QLatin1String("LastSection"); + default: return QLatin1String("Unknown section ") + QString::number(s); + } +} + +/*! + \internal + For debugging. Returns the name of the state \a s. +*/ + +QString QDateTimeParser::stateName(int s) const +{ + switch (s) { + case Invalid: return QLatin1String("Invalid"); + case Intermediate: return QLatin1String("Intermediate"); + case Acceptable: return QLatin1String("Acceptable"); + default: return QLatin1String("Unknown state ") + QString::number(s); + } +} + +#ifndef QT_NO_DATESTRING +bool QDateTimeParser::fromString(const QString &t, QDate *date, QTime *time) const +{ + QDateTime val(QDate(1900, 1, 1), QDATETIMEEDIT_TIME_MIN); + QString text = t; + int copy = -1; + const StateNode tmp = parse(text, copy, val, false); + if (tmp.state != Acceptable || tmp.conflicts) { + return false; + } + if (time) { + const QTime t = tmp.value.time(); + if (!t.isValid()) { + return false; + } + *time = t; + } + + if (date) { + const QDate d = tmp.value.date(); + if (!d.isValid()) { + return false; + } + *date = d; + } + return true; +} +#endif // QT_NO_DATESTRING + +QDateTime QDateTimeParser::getMinimum() const +{ + return QDateTime(QDATETIMEEDIT_DATE_MIN, QDATETIMEEDIT_TIME_MIN, spec); +} + +QDateTime QDateTimeParser::getMaximum() const +{ + return QDateTime(QDATETIMEEDIT_DATE_MAX, QDATETIMEEDIT_TIME_MAX, spec); +} + +QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const +{ + if (ap == AmText) { + return (cs == UpperCase ? QLatin1String("AM") : QLatin1String("am")); + } else { + return (cs == UpperCase ? QLatin1String("PM") : QLatin1String("pm")); + } +} + +/* + \internal + + I give arg2 preference because arg1 is always a QDateTime. +*/ + +bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2) +{ + return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count); +} + + +#endif // QT_BOOTSTRAPPED + +QT_END_NAMESPACE |