diff options
Diffstat (limited to 'src/corelib/time/qdatetime.cpp')
-rw-r--r-- | src/corelib/time/qdatetime.cpp | 690 |
1 files changed, 437 insertions, 253 deletions
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index ecc7e85610..8b1665e7dd 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -73,6 +73,9 @@ #include <private/qcore_mac_p.h> #endif +#include "qcalendar.h" +#include "qgregoriancalendar_p.h" + QT_BEGIN_NAMESPACE /***************************************************************************** @@ -94,78 +97,24 @@ enum { QDate static helper functions *****************************************************************************/ -static inline QDate fixedDate(int y, int m, int d) +static inline QDate fixedDate(QCalendar::YearMonthDay &&parts, QCalendar cal) { - QDate result(y, m, 1); - result.setDate(y, m, qMin(d, result.daysInMonth())); - return result; -} - -/* - Division, rounding down (rather than towards zero). - - From C++11 onwards, integer division is defined to round towards zero, so we - can rely on that when implementing this. This is only used with denominator b - > 0, so we only have to treat negative numerator, a, specially. - */ -static inline qint64 floordiv(qint64 a, int b) -{ - return (a - (a < 0 ? b - 1 : 0)) / b; -} - -static inline int floordiv(int a, int b) -{ - return (a - (a < 0 ? b - 1 : 0)) / b; -} - -static inline qint64 julianDayFromDate(int year, int month, int day) -{ - // Adjust for no year 0 - if (year < 0) - ++year; + if ((parts.year < 0 && !cal.isProleptic()) || (parts.year == 0 && !cal.hasYearZero())) + return QDate(); -/* - * Math from The Calendar FAQ at http://www.tondering.dk/claus/cal/julperiod.php - * This formula is correct for all julian days, when using mathematical integer - * division (round to negative infinity), not c++11 integer division (round to zero) - */ - int a = floordiv(14 - month, 12); - qint64 y = (qint64)year + 4800 - a; - int m = month + 12 * a - 3; - return day + floordiv(153 * m + 2, 5) + 365 * y + floordiv(y, 4) - floordiv(y, 100) + floordiv(y, 400) - 32045; + parts.day = qMin(parts.day, cal.daysInMonth(parts.month, parts.year)); + return cal.dateFromParts(parts); } -struct ParsedDate +static inline QDate fixedDate(QCalendar::YearMonthDay &&parts) { - int year, month, day; -}; - -// prevent this function from being inlined into all 10 users -Q_NEVER_INLINE -static ParsedDate getDateFromJulianDay(qint64 julianDay) -{ -/* - * Math from The Calendar FAQ at http://www.tondering.dk/claus/cal/julperiod.php - * This formula is correct for all julian days, when using mathematical integer - * division (round to negative infinity), not c++11 integer division (round to zero) - */ - qint64 a = julianDay + 32044; - qint64 b = floordiv(4 * a + 3, 146097); - int c = a - floordiv(146097 * b, 4); - - int d = floordiv(4 * c + 3, 1461); - int e = c - floordiv(1461 * d, 4); - int m = floordiv(5 * e + 2, 153); - - int day = e - floordiv(153 * m + 2, 5) + 1; - int month = m + 3 - 12 * floordiv(m, 10); - int year = 100 * b + d - 4800 + floordiv(m, 10); - - // Adjust for no year 0 - if (year <= 0) - --year ; - - return { year, month, day }; + if (parts.year) { + parts.day = qMin(parts.day, QGregorianCalendar::monthLength(parts.month, parts.year)); + qint64 jd; + if (QGregorianCalendar::julianFromParts(parts.year, parts.month, parts.day, &jd)) + return QDate::fromJulianDay(jd); + } + return QDate(); } /***************************************************************************** @@ -189,7 +138,7 @@ static int qt_monthNumberFromShortName(QStringRef shortName) static int qt_monthNumberFromShortName(const QString &shortName) { return qt_monthNumberFromShortName(QStringRef(&shortName)); } -static int fromShortMonthName(const QStringRef &monthName) +static int fromShortMonthName(const QStringRef &monthName, int year) { // Assume that English monthnames are the default int month = qt_monthNumberFromShortName(monthName); @@ -197,7 +146,7 @@ static int fromShortMonthName(const QStringRef &monthName) return month; // If English names can't be found, search the localized ones for (int i = 1; i <= 12; ++i) { - if (monthName == QLocale::system().monthName(i, QLocale::ShortFormat)) + if (monthName == QCalendar().monthName(QLocale::system(), i, year, QLocale::ShortFormat)) return i; } return -1; @@ -303,12 +252,6 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) noexcep } #endif // datestring -static constexpr int daysInUsualMonth(int month) // (February isn't usual.) -{ - // Long if odd up to July = 7, or if even from 8 = August onwards: - return Q_ASSERT(month != 2 && month > 0 && month <= 12), 30 | ((month & 1) ^ (month >> 3)); -} - /***************************************************************************** QDate member functions *****************************************************************************/ @@ -385,7 +328,7 @@ static constexpr int daysInUsualMonth(int month) // (February isn't usual.) for technical reasons limited to between -784350574879 and 784354017364, which means from before 2 billion BCE to after 2 billion CE. - \sa QTime, QDateTime, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget + \sa QTime, QDateTime, QCalendar, QDateTime::YearRange, QDateEdit, QDateTimeEdit, QCalendarWidget */ /*! @@ -399,19 +342,24 @@ static constexpr int daysInUsualMonth(int month) // (February isn't usual.) /*! 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 \c false. + The date is understood in terms of the Gregorian calendar. If the specified + date is invalid, the date is not set and isValid() returns \c false. \warning Years 1 to 99 are interpreted as is. Year 0 is invalid. - \sa isValid() + \sa isValid(), QCalendar::dateFromParts() */ QDate::QDate(int y, int m, int d) { - setDate(y, m, d); + if (!QGregorianCalendar::julianFromParts(y, m, d, &jd)) + jd = nullJd(); } +QDate::QDate(int y, int m, int d, QCalendar cal) +{ + *this = cal.dateFromParts(y, m, d); +} /*! \fn bool QDate::isNull() const @@ -429,29 +377,61 @@ QDate::QDate(int y, int m, int d) Returns \c true if this date is valid; otherwise returns \c false. - \sa isNull() + \sa isNull(), QCalendar::isDateValid() */ /*! - Returns the year of this date. Negative numbers indicate years - before 1 CE, such that year -44 is 44 BCE. + Returns the year of this date. - Returns 0 if the date is invalid. + Uses \a cal as calendar, if supplied, else the Gregorian calendar. - \sa month(), day() + Returns 0 if the date is invalid. For some calendars, dates before their + first year may all be invalid. + + If using a calendar which has a year 0, check using isValid() if the return + is 0. Such calendars use negative year numbers in the obvious way, with + year 1 preceded by year 0, in turn preceded by year -1 and so on. + + Some calendars, despite having no year 0, have a conventional numbering of + the years before their first year, counting backwards from 1. For example, + in the proleptic Gregorian calendar, successive years before 1 CE (the first + year) are identified as 1 BCE, 2 BCE, 3 BCE and so on. For such calendars, + negative year numbers are used to indicate these years before year 1, with + -1 indicating the year before 1. + + \sa month(), day(), QCalendar::hasYearZero(), QCalendar::isProleptic() */ -int QDate::year() const +int QDate::year(QCalendar cal) const { - if (isNull()) - return 0; + if (isValid()) { + const auto parts = cal.partsFromDate(*this); + if (parts.isValid()) + return parts.year; + } + return 0; +} + +/*! + \overload + */ - return getDateFromJulianDay(jd).year; +int QDate::year() const +{ + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.year; + } + return 0; } /*! - Returns the number corresponding to the month of this date, using - the following convention: + Returns the month-number for the date. + + Numbers the months of the year starting with 1 for the first. Uses \a cal + as calendar if supplied, else the Gregorian calendar, for which the month + numbering is as follows: \list \li 1 = "January" @@ -468,105 +448,186 @@ int QDate::year() const \li 12 = "December" \endlist - Returns 0 if the date is invalid. + Returns 0 if the date is invalid. Note that some calendars may have more + than 12 months in some years. \sa year(), day() */ -int QDate::month() const +int QDate::month(QCalendar cal) const { - if (isNull()) - return 0; + if (isValid()) { + const auto parts = cal.partsFromDate(*this); + if (parts.isValid()) + return parts.month; + } + return 0; +} + +/*! + \overload + */ - return getDateFromJulianDay(jd).month; +int QDate::month() const +{ + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.month; + } + return 0; } /*! - Returns the day of the month (1 to 31) of this date. + Returns the day of the month for this date. - Returns 0 if the date is invalid. + Uses \a cal as calendar if supplied, else the Gregorian calendar (for which + the return ranges from 1 to 31). Returns 0 if the date is invalid. \sa year(), month(), dayOfWeek() */ -int QDate::day() const +int QDate::day(QCalendar cal) const { - if (isNull()) - return 0; + if (isValid()) { + const auto parts = cal.partsFromDate(*this); + if (parts.isValid()) + return parts.day; + } + return 0; +} + +/*! + \overload + */ - return getDateFromJulianDay(jd).day; +int QDate::day() const +{ + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return parts.day; + } + return 0; } /*! Returns the weekday (1 = Monday to 7 = Sunday) for this date. - Returns 0 if the date is invalid. + Uses \a cal as calendar if supplied, else the Gregorian calendar. Returns 0 + if the date is invalid. Some calendars may give special meaning + (e.g. intercallary days) to values greater than 7. \sa day(), dayOfYear(), Qt::DayOfWeek */ -int QDate::dayOfWeek() const +int QDate::dayOfWeek(QCalendar cal) const { if (isNull()) return 0; - if (jd >= 0) - return (jd % 7) + 1; - else - return ((jd + 1) % 7) + 7; + return cal.dayOfWeek(*this); } /*! - Returns the day of the year (1 to 365 or 366 on leap years) for - this date. + \overload + */ - Returns 0 if the date is invalid. +int QDate::dayOfWeek() const +{ + return isValid() ? QGregorianCalendar::weekDayOfJulian(jd) : 0; +} + +/*! + Returns the day of the year (1 for the first day) for this date. + + Uses \a cal as calendar if supplied, else the Gregorian calendar. + Returns 0 if either the date or the first day of its year is invalid. \sa day(), dayOfWeek() */ -int QDate::dayOfYear() const +int QDate::dayOfYear(QCalendar cal) const { - if (isNull()) - return 0; + if (isValid()) { + QDate firstDay = cal.dateFromParts(year(cal), 1, 1); + if (firstDay.isValid()) + return firstDay.daysTo(*this) + 1; + } + return 0; +} + +/*! + \overload + */ - return jd - julianDayFromDate(year(), 1, 1) + 1; +int QDate::dayOfYear() const +{ + if (isValid()) { + qint64 first; + if (QGregorianCalendar::julianFromParts(year(), 1, 1, &first)) + return jd - first + 1; + } + return 0; } /*! - Returns the number of days in the month (28 to 31) for this date. + Returns the number of days in the month for this date. - Returns 0 if the date is invalid. + Uses \a cal as calendar if supplied, else the Gregorian calendar (for which + the result ranges from 28 to 31). Returns 0 if the date is invalid. \sa day(), daysInYear() */ -int QDate::daysInMonth() const +int QDate::daysInMonth(QCalendar cal) const { - if (isNull()) - return 0; + if (isValid()) { + const auto parts = cal.partsFromDate(*this); + if (parts.isValid()) + return cal.daysInMonth(parts.month, parts.year); + } + return 0; +} - const ParsedDate pd = getDateFromJulianDay(jd); - if (pd.month == 2) - return isLeapYear(pd.year) ? 29 : 28; +/*! + \overload + */ - return daysInUsualMonth(pd.month); +int QDate::daysInMonth() const +{ + if (isValid()) { + const auto parts = QGregorianCalendar::partsFromJulian(jd); + if (parts.isValid()) + return QGregorianCalendar::monthLength(parts.month, parts.year); + } + return 0; } /*! - Returns the number of days in the year (365 or 366) for this date. + Returns the number of days in the year for this date. - Returns 0 if the date is invalid. + Uses \a cal as calendar if supplied, else the Gregorian calendar (for which + the result is 365 or 366). Returns 0 if the date is invalid. \sa day(), daysInMonth() */ -int QDate::daysInYear() const +int QDate::daysInYear(QCalendar cal) const { if (isNull()) return 0; - return isLeapYear(getDateFromJulianDay(jd).year) ? 366 : 365; + return cal.daysInYear(year(cal)); +} + +/*! + \overload + */ + +int QDate::daysInYear() const +{ + return isValid() ? QGregorianCalendar::leapTest(year()) ? 366 : 365 : 0; } /*! @@ -593,6 +654,7 @@ int QDate::weekNumber(int *yearNumber) const if (!isValid()) return 0; + // This could be replaced by use of QIso8601Calendar, once we implement it. // The Thursday of the same week determines our answer: QDate thursday(addDays(4 - dayOfWeek())); int year = thursday.year(); @@ -904,9 +966,11 @@ QString QDate::shortMonthName(int month, QDate::MonthNameType type) { switch (type) { case QDate::DateFormat: - return QLocale::system().monthName(month, QLocale::ShortFormat); + return QCalendar().monthName(QLocale::system(), month, + QCalendar::Unspecified, QLocale::ShortFormat); case QDate::StandaloneFormat: - return QLocale::system().standaloneMonthName(month, QLocale::ShortFormat); + return QCalendar().standaloneMonthName(QLocale::system(), month, + QCalendar::Unspecified, QLocale::ShortFormat); } return QString(); } @@ -947,9 +1011,11 @@ QString QDate::longMonthName(int month, MonthNameType type) { switch (type) { case QDate::DateFormat: - return QLocale::system().monthName(month, QLocale::LongFormat); + return QCalendar().monthName(QLocale::system(), month, + QCalendar::Unspecified, QLocale::LongFormat); case QDate::StandaloneFormat: - return QLocale::system().standaloneMonthName(month, QLocale::LongFormat); + return QCalendar().standaloneMonthName(QLocale::system(), month, + QCalendar::Unspecified, QLocale::LongFormat); } return QString(); } @@ -1034,24 +1100,32 @@ QString QDate::longDayName(int weekday, MonthNameType type) #if QT_CONFIG(datestring) #if QT_CONFIG(textdate) +static QString toStringTextDate(QDate date, QCalendar cal) +{ + if (date.isValid()) { + const auto parts = cal.partsFromDate(date); + if (parts.isValid()) { + const QLatin1Char sp(' '); + return QLocale::system().dayName(cal.dayOfWeek(date), QLocale::ShortFormat) + sp + + cal.monthName(QLocale::system(), parts.month, parts.year, QLocale::ShortFormat) + + sp + QString::number(parts.day) + sp + QString::number(parts.year); + } + } + return QString(); +} + static QString toStringTextDate(QDate date) { - const ParsedDate pd = getDateFromJulianDay(date.toJulianDay()); - static const QLatin1Char sp(' '); - return QLocale::system().dayName(date.dayOfWeek(), QLocale::ShortFormat) + sp - + QLocale::system().monthName(pd.month, QLocale::ShortFormat) + sp - + QString::number(pd.day) + sp - + QString::number(pd.year); + return toStringTextDate(date, QCalendar()); } #endif // textdate -static QString toStringIsoDate(qint64 jd) +static QString toStringIsoDate(const QDate &date) { - const ParsedDate pd = getDateFromJulianDay(jd); - if (pd.year >= 0 && pd.year <= 9999) - return QString::asprintf("%04d-%02d-%02d", pd.year, pd.month, pd.day); - else - return QString(); + const auto parts = QCalendar().partsFromDate(date); + if (parts.isValid() && parts.year >= 0 && parts.year <= 9999) + return QString::asprintf("%04d-%02d-%02d", parts.year, parts.month, parts.day); + return QString(); } /*! @@ -1124,7 +1198,7 @@ QString QDate::toString(Qt::DateFormat format) const #endif case Qt::ISODate: case Qt::ISODateWithMs: - return toStringIsoDate(jd); + return toStringIsoDate(*this); } } @@ -1198,6 +1272,47 @@ QString QDate::toString(const QString &format) const } #endif +QString QDate::toString(Qt::DateFormat format, QCalendar cal) const +{ + if (!isValid()) + return QString(); + + switch (format) { + case Qt::SystemLocaleDate: + case Qt::SystemLocaleShortDate: + return QLocale::system().toString(*this, QLocale::ShortFormat, cal); + case Qt::SystemLocaleLongDate: + return QLocale::system().toString(*this, QLocale::LongFormat, cal); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + return QLocale().toString(*this, QLocale::ShortFormat, cal); + case Qt::DefaultLocaleLongDate: + return QLocale().toString(*this, QLocale::LongFormat, cal); + case Qt::RFC2822Date: + return QLocale::c().toString(*this, QStringView(u"dd MMM yyyy"), cal); + default: +#ifndef QT_NO_TEXTDATE + case Qt::TextDate: + return toStringTextDate(*this, cal); +#endif + case Qt::ISODate: + case Qt::ISODateWithMs: + return toStringIsoDate(*this); + } +} + +QString QDate::toString(QStringView format, QCalendar cal) const +{ + return QLocale::system().toString(*this, format, cal); // QLocale::c() ### Qt6 +} + +#if QT_STRINGVIEW_LEVEL < 2 +QString QDate::toString(const QString &format, QCalendar cal) const +{ + return toString(qToStringViewIgnoringNull(format), cal); +} +#endif + #endif // datestring /*! @@ -1217,21 +1332,36 @@ QString QDate::toString(const QString &format) const /*! \since 4.2 - Sets the date's \a year, \a month, and \a day. Returns \c true if - the date is valid; otherwise returns \c false. - - If the specified date is invalid, the QDate object is set to be - invalid. + Sets this to represent the date, in the Gregorian calendar, with the given + \a year, \a month and \a day numbers. Returns true if the resulting date is + valid, otherwise it sets this to represent an invalid date and returns + false. - \sa isValid() + \sa isValid(), QCalendar::dateFromParts() */ bool QDate::setDate(int year, int month, int day) { - if (isValid(year, month, day)) - jd = julianDayFromDate(year, month, day); - else - jd = nullJd(); + if (QGregorianCalendar::julianFromParts(year, month, day, &jd)) + return true; + jd = nullJd(); + return false; +} + +/*! + \since 5.14 + + Sets this to represent the date, in the given calendar \a cal, with the + given \a year, \a month and \a day numbers. Returns true if the resulting + date is valid, otherwise it sets this to represent an invalid date and + returns false. + + \sa isValid(), QCalendar::dateFromParts() +*/ + +bool QDate::setDate(int year, int month, int day, QCalendar cal) +{ + *this = QDate(year, month, day, cal); return isValid(); } @@ -1245,20 +1375,21 @@ bool QDate::setDate(int year, int month, int day) \note In Qt versions prior to 5.7, this function is marked as non-\c{const}. - \sa year(), month(), day(), isValid() + \sa year(), month(), day(), isValid(), QCalendar::partsFromDate() */ void QDate::getDate(int *year, int *month, int *day) const { - ParsedDate pd = { 0, 0, 0 }; + QCalendar::YearMonthDay parts; // invalid by default if (isValid()) - pd = getDateFromJulianDay(jd); + parts = QGregorianCalendar::partsFromJulian(jd); + const bool ok = parts.isValid(); if (year) - *year = pd.year; + *year = ok ? parts.year : 0; if (month) - *month = pd.month; + *month = ok ? parts.month : 0; if (day) - *day = pd.day; + *day = ok ? parts.day : 0; } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -1296,96 +1427,129 @@ QDate QDate::addDays(qint64 ndays) const 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. + Uses \a cal as calendar, if supplied, else the Gregorian calendar. + + \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 + in the selected month. \sa addDays(), addYears() */ -QDate QDate::addMonths(int nmonths) const +QDate QDate::addMonths(int nmonths, QCalendar cal) const { if (!isValid()) return QDate(); - if (!nmonths) + + if (nmonths == 0) return *this; - int old_y, y, m, d; - { - const ParsedDate pd = getDateFromJulianDay(jd); - y = pd.year; - m = pd.month; - d = pd.day; + auto parts = cal.partsFromDate(*this); + + if (!parts.isValid()) + return QDate(); + Q_ASSERT(parts.year || cal.hasYearZero()); + + parts.month += nmonths; + while (parts.month <= 0) { + if (--parts.year || cal.hasYearZero()) + parts.month += cal.monthsInYear(parts.year); } - 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; - } - } + int count = cal.monthsInYear(parts.year); + while (parts.month > count) { + parts.month -= count; + count = (++parts.year || cal.hasYearZero()) ? cal.monthsInYear(parts.year) : 0; } - // 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; + return fixedDate(std::move(parts), cal); +} + +/*! + \overload +*/ + +QDate QDate::addMonths(int nmonths) const +{ + if (isNull()) + return QDate(); + + if (nmonths == 0) + return *this; + + auto parts = QGregorianCalendar::partsFromJulian(jd); + + if (!parts.isValid()) + return QDate(); + Q_ASSERT(parts.year); - return fixedDate(y, m, d); + parts.month += nmonths; + while (parts.month <= 0) { + if (--parts.year) // skip over year 0 + parts.month += 12; + } + while (parts.month > 12) { + parts.month -= 12; + if (!++parts.year) // skip over year 0 + ++parts.year; + } + + return fixedDate(std::move(parts)); } /*! 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). + Uses \a cal as calendar, if supplied, else the Gregorian calendar. + + \note If the ending day/month combination does not exist in the resulting + year (e.g., for the Gregorian calendar, 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 in the given month (in the example, Feb 28). \sa addDays(), addMonths() */ -QDate QDate::addYears(int nyears) const +QDate QDate::addYears(int nyears, QCalendar cal) const { if (!isValid()) return QDate(); - ParsedDate pd = getDateFromJulianDay(jd); + auto parts = cal.partsFromDate(*this); + if (!parts.isValid()) + return QDate(); - int old_y = pd.year; - pd.year += nyears; + int old_y = parts.year; + parts.year += nyears; - // was there a sign change? - if ((old_y > 0 && pd.year <= 0) || - (old_y < 0 && pd.year >= 0)) - // yes, adjust the date by +1 or -1 years - pd.year += nyears > 0 ? +1 : -1; + // If we just crossed (or hit) a missing year zero, adjust year by +/- 1: + if (!cal.hasYearZero() && ((old_y > 0) != (parts.year > 0) || !parts.year)) + parts.year += nyears > 0 ? +1 : -1; - return fixedDate(pd.year, pd.month, pd.day); + return fixedDate(std::move(parts), cal); +} + +/*! + \overload +*/ + +QDate QDate::addYears(int nyears) const +{ + if (isNull()) + return QDate(); + + auto parts = QGregorianCalendar::partsFromJulian(jd); + if (!parts.isValid()) + return QDate(); + + int old_y = parts.year; + parts.year += nyears; + + // If we just crossed (or hit) a missing year zero, adjust year by +/- 1: + if ((old_y > 0) != (parts.year > 0) || !parts.year) + parts.year += nyears > 0 ? +1 : -1; + + return fixedDate(std::move(parts)); } /*! @@ -1505,7 +1669,7 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format) if (!ok || !day) return QDate(); - const int month = fromShortMonthName(parts.at(1)); + const int month = fromShortMonthName(parts.at(1), year); if (month == -1) // Month name matches no English or localised name. return QDate(); @@ -1531,6 +1695,10 @@ 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. + Uses \a cal as calendar if supplied, else the Gregorian calendar. Ranges of + values in the format descriptions below are for the latter; they may be + different for other calendars. + These expressions may be used for the format: \table @@ -1590,55 +1758,61 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format) QLocale::toDate() */ -QDate QDate::fromString(const QString &string, const QString &format) +QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal) { QDate date; #if QT_CONFIG(datetimeparser) - QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString); + QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString, cal); // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, &date, 0); #else Q_UNUSED(string); Q_UNUSED(format); + Q_UNUSED(cal); #endif return date; } + +/*! + \overload +*/ + +QDate QDate::fromString(const QString &string, const QString &format) +{ + return fromString(string, format, QCalendar()); +} #endif // datestring /*! \overload - Returns \c true if the specified date (\a year, \a month, and \a - day) is valid; otherwise returns \c false. + Returns \c true if the specified date (\a year, \a month, and \a day) is + valid in the Gregorian calendar; otherwise returns \c false. Example: \snippet code/src_corelib_tools_qdatetime.cpp 4 - \sa isNull(), setDate() + \sa isNull(), setDate(), QCalendar::isDateValid() */ bool QDate::isValid(int year, int month, int day) { - // There is no year 0 in the Gregorian calendar. - return year && day > 0 && month > 0 && month <= 12 && - day <= (month == 2 ? isLeapYear(year) ? 29 : 28 : daysInUsualMonth(month)); + return QGregorianCalendar::validParts(year, month, day); } /*! \fn bool QDate::isLeapYear(int year) - Returns \c true if the specified \a year is a leap year; otherwise - returns \c false. + Returns \c true if the specified \a year is a leap year in the Gregorian + calendar; otherwise returns \c false. + + \sa QCalendar::isLeapYear() */ bool QDate::isLeapYear(int y) { - // No year 0 in Gregorian calendar, so -1, -5, -9 etc are leap years - if ( y < 1) - ++y; - - return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; + return QGregorianCalendar::leapTest(y); } /*! \fn static QDate QDate::fromJulianDay(qint64 jd) @@ -2311,7 +2485,7 @@ QTime QTime::fromString(const QString &string, const QString &format) { QTime time; #if QT_CONFIG(datetimeparser) - QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString); + QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString, QCalendar()); // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, 0, &time); @@ -4686,12 +4860,10 @@ static inline uint msecsFromDecomposed(int hour, int minute, int sec, int msec = QDate QDate::currentDate() { - QDate d; SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetLocalTime(&st); - d.jd = julianDayFromDate(st.wYear, st.wMonth, st.wDay); - return d; + return QDate(st.wYear, st.wMonth, st.wDay); } QTime QTime::currentTime() @@ -4706,24 +4878,22 @@ QTime QTime::currentTime() QDateTime QDateTime::currentDateTime() { - QDate d; QTime t; SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetLocalTime(&st); - d.jd = julianDayFromDate(st.wYear, st.wMonth, st.wDay); + QDate d(st.wYear, st.wMonth, st.wDay); t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); return QDateTime(d, t); } QDateTime QDateTime::currentDateTimeUtc() { - QDate d; QTime t; SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetSystemTime(&st); - d.jd = julianDayFromDate(st.wYear, st.wMonth, st.wDay); + QDate d(st.wYear, st.wMonth, st.wDay); t.mds = msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); return QDateTime(d, t, Qt::UTC); } @@ -4733,10 +4903,10 @@ qint64 QDateTime::currentMSecsSinceEpoch() noexcept SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetSystemTime(&st); + const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); return msecsFromDecomposed(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds) + - qint64(julianDayFromDate(st.wYear, st.wMonth, st.wDay) - - julianDayFromDate(1970, 1, 1)) * Q_INT64_C(86400000); + daysAfterEpoch * Q_INT64_C(86400000); } qint64 QDateTime::currentSecsSinceEpoch() noexcept @@ -4744,10 +4914,10 @@ qint64 QDateTime::currentSecsSinceEpoch() noexcept SYSTEMTIME st; memset(&st, 0, sizeof(SYSTEMTIME)); GetSystemTime(&st); + const qint64 daysAfterEpoch = QDate(1970, 1, 1).daysTo(QDate(st.wYear, st.wMonth, st.wDay)); return st.wHour * SECS_PER_HOUR + st.wMinute * SECS_PER_MIN + st.wSecond + - qint64(julianDayFromDate(st.wYear, st.wMonth, st.wDay) - - julianDayFromDate(1970, 1, 1)) * Q_INT64_C(86400); + daysAfterEpoch * Q_INT64_C(86400); } #elif defined(Q_OS_UNIX) @@ -5126,13 +5296,13 @@ QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) return QDateTime(); // Next try month then day - month = fromShortMonthName(parts.at(1)); + month = fromShortMonthName(parts.at(1), year); if (month) day = parts.at(2).toInt(&ok); // If failed, try day then month if (!ok || !month || !day) { - month = fromShortMonthName(parts.at(2)); + month = fromShortMonthName(parts.at(2), year); if (month) { QStringRef dayStr = parts.at(1); if (dayStr.endsWith(QLatin1Char('.'))) { @@ -5213,6 +5383,10 @@ 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 the string cannot be parsed. + Uses the calendar \a cal if supplied, else Gregorian. The illustrative + values and ranges below are given for the latter; other calendars may have + different ranges or values. + These expressions may be used for the date part of the format string: \table @@ -5315,23 +5489,33 @@ QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format) QLocale::toDateTime() */ -QDateTime QDateTime::fromString(const QString &string, const QString &format) +QDateTime QDateTime::fromString(const QString &string, const QString &format, QCalendar cal) { #if QT_CONFIG(datetimeparser) QTime time; QDate date; - QDateTimeParser dt(QVariant::DateTime, QDateTimeParser::FromString); + QDateTimeParser dt(QVariant::DateTime, QDateTimeParser::FromString, cal); // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) return QDateTime(date, time); #else Q_UNUSED(string); Q_UNUSED(format); + Q_UNUSED(cal); #endif return QDateTime(); } +/* + \overload +*/ + +QDateTime QDateTime::fromString(const QString &string, const QString &format) +{ + return fromString(string, format, QCalendar()); +} + #endif // datestring /*! \fn QDateTime QDateTime::toLocalTime() const |