From 085ec2feca85c68c3f0ed3d98546d79f8781047c Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 17 Nov 2019 22:30:51 +0100 Subject: [PATCH] Improve DateTime * Improve documentation about time zone handling * Add DateTime::toTimeStamp() * Add experimental DateTime::fromChronoTimePoint() and DateTime::fromChronoTimePointGmt() --- chrono/datetime.cpp | 13 +++++++------ chrono/datetime.h | 35 +++++++++++++++++++++++++++++++++-- tests/chronotests.cpp | 11 +++++++++-- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/chrono/datetime.cpp b/chrono/datetime.cpp index 51cd68b..eacc289 100644 --- a/chrono/datetime.cpp +++ b/chrono/datetime.cpp @@ -39,12 +39,13 @@ template constexpr bool inRangeExc * \remarks * - Time values are measured in 100-nanosecond units called ticks, * and a particular date is the number of ticks since 12:00 midnight, January 1, - * 0001 A.D. (C.E.) in the GregorianCalendar calendar (excluding ticks that would - * be added by leap seconds). - * - There is no time zone information associated. Hence different time zones are - * not taken into account when comparing two instances. For instance, the - * expression (DateTime::now() - DateTime::gmtNow()) returns one hour in Germany during winter - * time (instead of zero). + * 0001 A.D. (C.E.) in the Gregorian Calendar (excluding ticks that would be added by leap seconds). + * - There is no time zone information associated. You need to keep track of the used time zone separately. That can + * be done by keeping an additional TimeSpan around which represents the delta to GMT or by simply using GMT everywhere + * in the program. + * - When constructing an instance via DateTime::fromTimeStamp(), DateTime::fromChronoTimePoint() or DateTime::fromIsoStringLocal() + * the time zone deltas are "baked into" the DateTime instance. For instance, the expression (DateTime::now() - DateTime::gmtNow()) + * returns one hour in Germany during winter time (and *not* zero although both instances represent the current time). * \todo * - Add method for parsing custom string formats. * - Add method for printing to custom string formats. diff --git a/chrono/datetime.h b/chrono/datetime.h index ead4a25..8d252fd 100644 --- a/chrono/datetime.h +++ b/chrono/datetime.h @@ -60,8 +60,10 @@ public: static std::pair fromIsoString(const char *str); static DateTime fromIsoStringGmt(const char *str); static DateTime fromIsoStringLocal(const char *str); - static DateTime fromTimeStamp(time_t timeStamp); - constexpr static DateTime fromTimeStampGmt(time_t timeStamp); + static DateTime fromTimeStamp(std::time_t timeStamp); + constexpr static DateTime fromTimeStampGmt(std::time_t timeStamp); + template static DateTime fromChronoTimePoint(TimePoint timePoint); + template constexpr static DateTime fromChronoTimePointGmt(TimePoint timePoint); constexpr std::uint64_t &ticks(); constexpr std::uint64_t totalTicks() const; @@ -84,6 +86,7 @@ public: std::string toString(DateTimeOutputFormat format = DateTimeOutputFormat::DateAndTime, bool noMilliseconds = false) const; void toString(std::string &result, DateTimeOutputFormat format = DateTimeOutputFormat::DateAndTime, bool noMilliseconds = false) const; std::string toIsoString(TimeSpan timeZoneDelta = TimeSpan()) const; + constexpr std::time_t toTimeStamp() const; static const char *printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation = false); static constexpr DateTime eternity(); @@ -220,6 +223,26 @@ constexpr inline DateTime DateTime::fromTimeStampGmt(std::time_t timeStamp) return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast(timeStamp) * TimeSpan::ticksPerSecond); } +/*! + * \brief Constructs a new DateTime object with the local time from the specified std::chrono::time_point. + * \remarks Works only with time points of std::chrono::system_clock so far. C++20 will fix this. Until then this function + * should be considered experimental. + */ +template inline DateTime DateTime::fromChronoTimePoint(TimePoint timePoint) +{ + return DateTime::fromTimeStamp(decltype(timePoint)::clock::to_time_t(timePoint)); +} + +/*! + * \brief Constructs a new DateTime object with the GMT time from the specified std::chrono::time_point. + * \remarks Works only with time points of std::chrono::system_clock so far. C++20 will fix this. Until then this function + * should be considered experimental. + */ +template constexpr DateTime DateTime::fromChronoTimePointGmt(TimePoint timePoint) +{ + return DateTime::fromTimeStampGmt(decltype(timePoint)::clock::to_time_t(timePoint)); +} + /*! * \brief Returns a mutable reference to the total ticks. */ @@ -396,6 +419,14 @@ inline std::string DateTime::toString(DateTimeOutputFormat format, bool noMillis return result; } +/*! + * \brief Returns the UNIX timestamp for the current instance. + */ +constexpr std::time_t DateTime::toTimeStamp() const +{ + return (totalTicks() - DateTime::unixEpochStart().totalTicks()) / TimeSpan::ticksPerSecond; +} + /*! * \brief Constructs a new instance of the DateTime class with the maximal number of ticks. */ diff --git a/tests/chronotests.cpp b/tests/chronotests.cpp index 94c7d79..528d2ea 100644 --- a/tests/chronotests.cpp +++ b/tests/chronotests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -114,11 +115,17 @@ void ChronoTests::testDateTime() CPPUNIT_ASSERT(!test1.isSameDay(test1 + TimeSpan::fromHours(9))); CPPUNIT_ASSERT_EQUAL("Wed 2012-02-29 15:34:20.033"s, test1.toString(DateTimeOutputFormat::DateTimeAndShortWeekday)); - // test fromTimeStamp() - const auto fromTimeStampGmt = DateTime::fromTimeStampGmt(1453840331), fromTimeStamp = DateTime::fromTimeStamp(1453840331); + // test fromTimeStamp()/toTimeStamp() + const auto timeStamp = static_cast(1453840331); + const auto fromTimeStampGmt = DateTime::fromTimeStampGmt(timeStamp), fromTimeStamp = DateTime::fromTimeStamp(timeStamp); CPPUNIT_ASSERT_EQUAL("Tue 2016-01-26 20:32:11"s, fromTimeStampGmt.toString(DateTimeOutputFormat::DateTimeAndShortWeekday)); CPPUNIT_ASSERT(fabs((fromTimeStamp - fromTimeStampGmt).totalDays()) <= 1.0); CPPUNIT_ASSERT_EQUAL(DateTime(), DateTime::fromTimeStamp(0)); + CPPUNIT_ASSERT_EQUAL(timeStamp, fromTimeStampGmt.toTimeStamp()); + + // test fromChronoTimePointGmt() + const auto fromChronoTimePointGmt = DateTime::fromChronoTimePointGmt(chrono::system_clock::from_time_t(timeStamp)); + CPPUNIT_ASSERT_EQUAL("Tue 2016-01-26 20:32:11"s, fromChronoTimePointGmt.toString(DateTimeOutputFormat::DateTimeAndShortWeekday)); // test whether ConversionException() is thrown when invalid values are specified CPPUNIT_ASSERT_THROW(DateTime::fromDate(0, 1, 1), ConversionException);