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";
|
||||
QDateTime valid = QDateTime::fromString(string, format);
|
||||
//! [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);
|
||||
}
|
||||
|
||||
/*!
|
||||
\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
|
||||
|
||||
|
@ -68,6 +68,57 @@ public:
|
||||
constexpr QDate() : jd(nullJd()) {}
|
||||
QDate(int y, int m, int d);
|
||||
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 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 minJd() { return Q_INT64_C(-784350574879); }
|
||||
static constexpr inline qint64 maxJd() { return Q_INT64_C( 784354017364); }
|
||||
static constexpr inline qint64 unixEpochJd() { return Q_INT64_C(2440588); }
|
||||
|
||||
qint64 jd;
|
||||
|
||||
|
@ -199,6 +199,32 @@ void tst_QDate::isValid_data()
|
||||
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()
|
||||
{
|
||||
QFETCH(int, year);
|
||||
@ -218,6 +244,19 @@ void tst_QDate::isValid()
|
||||
QCOMPARE(d.year(), year);
|
||||
QCOMPARE(d.month(), month);
|
||||
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 {
|
||||
QCOMPARE(d.year(), 0);
|
||||
QCOMPARE(d.month(), 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user