QDate: add conversions for std::chrono calendaring classes
std::chrono::year_month_day and related classes offer very convenient to specify dates. This patch adds implicit constructors to QDate to support this convenience, e.g.: // YYYY-MM-DD, DD-MM-YYYY, MM-DD-YYYY formats: QDate d1 = 1985y / December / 8; QDate d2 = 8d / December / 1985; QDate d3 = December / 8d / 1985; // Indexed weekday: QDate d4 = 2000y / January / Monday[0]; QDate d5 = 2000y / January / Monday[last]; and so on. These are all implemented using the conversion from the std calendaring classes to sys_days. Conversions between sys_days and QDate are also added, since they're basically "for free". I don't expect "ordinary" users to stumble upon it, but it's worthy mentioning that std::chrono::year *does* have a year zero (hence, year_month_day in year 0 or below are offset by one with the corresponding QDate). I've left a note in the documentation. [ChangeLog][QtCore][QDate] QDate (and therefore QDateTime) is now constructible using the year/month/day/week classes available in the std::chrono library. Moreover, it now features conversions from and to std::chrono::sys_days. Change-Id: I2a4f56423ac7d1469541cbb6a278a65b48878b4a Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
33c88f86b5
commit
2e29f55f76
@ -216,3 +216,23 @@ QString string = "Tuesday, 23 April 12 22:51:41";
|
|||||||
QString format = "dddd, d MMMM yy hh:mm:ss";
|
QString format = "dddd, d MMMM yy hh:mm:ss";
|
||||||
QDateTime valid = QDateTime::fromString(string, format);
|
QDateTime valid = QDateTime::fromString(string, format);
|
||||||
//! [21]
|
//! [21]
|
||||||
|
|
||||||
|
//! [22]
|
||||||
|
// 23 April 2012:
|
||||||
|
QDate date = std::chrono::year_month_day(std::chrono::year(2012),
|
||||||
|
std::chrono::month(4),
|
||||||
|
std::chrono::day(23));
|
||||||
|
|
||||||
|
// Same, under `using std::chrono` convenience:
|
||||||
|
QDate dateWithLiterals1 = 23 / April / 2012y;
|
||||||
|
QDate dateWithLiterals2 = 2012y / April / 23;
|
||||||
|
|
||||||
|
// Last day of February 2000
|
||||||
|
QDate lastDayFeb2020 = 2000y / February / last;
|
||||||
|
|
||||||
|
// First Monday of January 2020:
|
||||||
|
QDate firstMonday = 2020y / January / Monday[0];
|
||||||
|
|
||||||
|
// Last Monday of January 2020:
|
||||||
|
QDate lastMonday = 2020y / January / Monday[last];
|
||||||
|
//! [22]
|
||||||
|
@ -448,6 +448,54 @@ QDate::QDate(int y, int m, int d, QCalendar cal)
|
|||||||
*this = cal.dateFromParts(y, m, d);
|
*this = cal.dateFromParts(y, m, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QDate::QDate(std::chrono::year_month_day ymd)
|
||||||
|
\fn QDate::QDate(std::chrono::year_month_day_last ymd)
|
||||||
|
\fn QDate::QDate(std::chrono::year_month_weekday ymd)
|
||||||
|
\fn QDate::QDate(std::chrono::year_month_weekday_last ymd)
|
||||||
|
|
||||||
|
\since 6.4
|
||||||
|
|
||||||
|
Constructs a QDate representing the same date as \a ymd. This allows for
|
||||||
|
easy interoperability between the Standard Library calendaring classes and
|
||||||
|
Qt datetime classes.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_time_qdatetime.cpp 22
|
||||||
|
|
||||||
|
\note Unlike QDate, std::chrono::year and the related classes feature the
|
||||||
|
year zero. This means that if \a ymd is in the year zero or before, the
|
||||||
|
resulting QDate object will have an year one less than the one specified by
|
||||||
|
\a ymd.
|
||||||
|
|
||||||
|
\note This function requires C++20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QDate QDate::fromStdSysDays(const std::chrono::sys_days &days)
|
||||||
|
\since 6.4
|
||||||
|
|
||||||
|
Returns a QDate \a days days after January 1st, 1970 (the UNIX epoch). If
|
||||||
|
\a days is negative, the returned date will be before the epoch.
|
||||||
|
|
||||||
|
\note This function requires C++20.
|
||||||
|
|
||||||
|
\sa toStdSysDays()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn std::chrono::sys_days QDate::toStdSysDays() const
|
||||||
|
|
||||||
|
Returns the number of days between January 1st, 1970 (the UNIX epoch) and
|
||||||
|
this date, represented as a \c{std::chrono::sys_days} object. If this date
|
||||||
|
is before the epoch, the number of days will be negative.
|
||||||
|
|
||||||
|
\note This function requires C++20.
|
||||||
|
|
||||||
|
\sa fromStdSysDays(), daysTo()
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn bool QDate::isNull() const
|
\fn bool QDate::isNull() const
|
||||||
|
|
||||||
|
@ -68,6 +68,57 @@ public:
|
|||||||
constexpr QDate() : jd(nullJd()) {}
|
constexpr QDate() : jd(nullJd()) {}
|
||||||
QDate(int y, int m, int d);
|
QDate(int y, int m, int d);
|
||||||
QDate(int y, int m, int d, QCalendar cal);
|
QDate(int y, int m, int d, QCalendar cal);
|
||||||
|
#if __cpp_lib_chrono >= 201907L || defined(Q_QDOC)
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
Q_IMPLICIT QDate(std::chrono::year_month_day ymd)
|
||||||
|
{
|
||||||
|
if (!ymd.ok())
|
||||||
|
jd = nullJd();
|
||||||
|
else
|
||||||
|
*this = fromStdSysDays(ymd);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
Q_IMPLICIT QDate(std::chrono::year_month_day_last ymdl)
|
||||||
|
{
|
||||||
|
if (!ymdl.ok())
|
||||||
|
jd = nullJd();
|
||||||
|
else
|
||||||
|
*this = fromStdSysDays(ymdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
Q_IMPLICIT QDate(std::chrono::year_month_weekday ymw)
|
||||||
|
{
|
||||||
|
if (!ymw.ok())
|
||||||
|
jd = nullJd();
|
||||||
|
else
|
||||||
|
*this = fromStdSysDays(ymw);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
Q_IMPLICIT QDate(std::chrono::year_month_weekday_last ymwl)
|
||||||
|
{
|
||||||
|
if (!ymwl.ok())
|
||||||
|
jd = nullJd();
|
||||||
|
else
|
||||||
|
*this = fromStdSysDays(ymwl);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
static QDate fromStdSysDays(const std::chrono::sys_days &days)
|
||||||
|
{
|
||||||
|
const QDate epoch(unixEpochJd());
|
||||||
|
return epoch.addDays(days.time_since_epoch().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_POST_CXX17_API_IN_EXPORTED_CLASS
|
||||||
|
std::chrono::sys_days toStdSysDays() const
|
||||||
|
{
|
||||||
|
const QDate epoch(unixEpochJd());
|
||||||
|
return std::chrono::sys_days(std::chrono::days(epoch.daysTo(*this)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr bool isNull() const { return !isValid(); }
|
constexpr bool isNull() const { return !isValid(); }
|
||||||
constexpr bool isValid() const { return jd >= minJd() && jd <= maxJd(); }
|
constexpr bool isValid() const { return jd >= minJd() && jd <= maxJd(); }
|
||||||
@ -144,6 +195,7 @@ private:
|
|||||||
static constexpr inline qint64 nullJd() { return (std::numeric_limits<qint64>::min)(); }
|
static constexpr inline qint64 nullJd() { return (std::numeric_limits<qint64>::min)(); }
|
||||||
static constexpr inline qint64 minJd() { return Q_INT64_C(-784350574879); }
|
static constexpr inline qint64 minJd() { return Q_INT64_C(-784350574879); }
|
||||||
static constexpr inline qint64 maxJd() { return Q_INT64_C( 784354017364); }
|
static constexpr inline qint64 maxJd() { return Q_INT64_C( 784354017364); }
|
||||||
|
static constexpr inline qint64 unixEpochJd() { return Q_INT64_C(2440588); }
|
||||||
|
|
||||||
qint64 jd;
|
qint64 jd;
|
||||||
|
|
||||||
|
@ -199,6 +199,32 @@ void tst_QDate::isValid_data()
|
|||||||
QTest::newRow("jd latest formula") << 1400000 << 12 << 31 << qint64(513060925) << true;
|
QTest::newRow("jd latest formula") << 1400000 << 12 << 31 << qint64(513060925) << true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __cpp_lib_chrono >= 201907L
|
||||||
|
// QDate has a bigger range than year_month_date. The tests use this bigger
|
||||||
|
// range. However building a year_month_time with "out of range" data has
|
||||||
|
// unspecified results, so don't do that. See [time.cal.year],
|
||||||
|
// [time.cal.month], [time.cal.day]. Also, std::chrono::year has a year 0, so
|
||||||
|
// take that into account.
|
||||||
|
static std::optional<std::chrono::year_month_day> convertToStdYearMonthDay(int y, int m, int d)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
if (y >= int((year::min)())
|
||||||
|
&& y <= int((year::max)())
|
||||||
|
&& m >= 0
|
||||||
|
&& m <= 255
|
||||||
|
&& d >= 0
|
||||||
|
&& d <= 255)
|
||||||
|
{
|
||||||
|
if (y < 0)
|
||||||
|
++y;
|
||||||
|
return std::make_optional(year(y) / m / d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void tst_QDate::isValid()
|
void tst_QDate::isValid()
|
||||||
{
|
{
|
||||||
QFETCH(int, year);
|
QFETCH(int, year);
|
||||||
@ -218,6 +244,19 @@ void tst_QDate::isValid()
|
|||||||
QCOMPARE(d.year(), year);
|
QCOMPARE(d.year(), year);
|
||||||
QCOMPARE(d.month(), month);
|
QCOMPARE(d.month(), month);
|
||||||
QCOMPARE(d.day(), day);
|
QCOMPARE(d.day(), day);
|
||||||
|
#if __cpp_lib_chrono >= 201907L
|
||||||
|
std::optional<std::chrono::year_month_day> ymd = convertToStdYearMonthDay(year, month, day);
|
||||||
|
if (ymd) {
|
||||||
|
QDate d = *ymd;
|
||||||
|
QCOMPARE(d.year(), year);
|
||||||
|
QCOMPARE(d.month(), month);
|
||||||
|
QCOMPARE(d.day(), day);
|
||||||
|
|
||||||
|
const std::chrono::sys_days qdateSysDays = d.toStdSysDays();
|
||||||
|
const std::chrono::sys_days ymdSysDays = *ymd;
|
||||||
|
QCOMPARE(qdateSysDays, ymdSysDays);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
QCOMPARE(d.year(), 0);
|
QCOMPARE(d.year(), 0);
|
||||||
QCOMPARE(d.month(), 0);
|
QCOMPARE(d.month(), 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user