Improve DateTime

* Improve documentation about time zone handling
* Add DateTime::toTimeStamp()
* Add experimental DateTime::fromChronoTimePoint()
  and DateTime::fromChronoTimePointGmt()
This commit is contained in:
Martchus 2019-11-17 22:30:51 +01:00
parent 83180119c2
commit 085ec2feca
3 changed files with 49 additions and 10 deletions

View File

@ -39,12 +39,13 @@ template <typename num1, typename num2, typename num3> constexpr bool inRangeExc
* \remarks * \remarks
* - Time values are measured in 100-nanosecond units called ticks, * - 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, * 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 * 0001 A.D. (C.E.) in the Gregorian Calendar (excluding ticks that would be added by leap seconds).
* 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
* - There is no time zone information associated. Hence different time zones are * be done by keeping an additional TimeSpan around which represents the delta to GMT or by simply using GMT everywhere
* not taken into account when comparing two instances. For instance, the * in the program.
* expression (DateTime::now() - DateTime::gmtNow()) returns one hour in Germany during winter * - When constructing an instance via DateTime::fromTimeStamp(), DateTime::fromChronoTimePoint() or DateTime::fromIsoStringLocal()
* time (instead of zero). * 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 * \todo
* - Add method for parsing custom string formats. * - Add method for parsing custom string formats.
* - Add method for printing to custom string formats. * - Add method for printing to custom string formats.

View File

@ -60,8 +60,10 @@ public:
static std::pair<DateTime, TimeSpan> fromIsoString(const char *str); static std::pair<DateTime, TimeSpan> fromIsoString(const char *str);
static DateTime fromIsoStringGmt(const char *str); static DateTime fromIsoStringGmt(const char *str);
static DateTime fromIsoStringLocal(const char *str); static DateTime fromIsoStringLocal(const char *str);
static DateTime fromTimeStamp(time_t timeStamp); static DateTime fromTimeStamp(std::time_t timeStamp);
constexpr static DateTime fromTimeStampGmt(time_t timeStamp); constexpr static DateTime fromTimeStampGmt(std::time_t timeStamp);
template <typename TimePoint> static DateTime fromChronoTimePoint(TimePoint timePoint);
template <typename TimePoint> constexpr static DateTime fromChronoTimePointGmt(TimePoint timePoint);
constexpr std::uint64_t &ticks(); constexpr std::uint64_t &ticks();
constexpr std::uint64_t totalTicks() const; constexpr std::uint64_t totalTicks() const;
@ -84,6 +86,7 @@ public:
std::string toString(DateTimeOutputFormat format = DateTimeOutputFormat::DateAndTime, bool noMilliseconds = false) const; std::string toString(DateTimeOutputFormat format = DateTimeOutputFormat::DateAndTime, bool noMilliseconds = false) const;
void toString(std::string &result, 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; std::string toIsoString(TimeSpan timeZoneDelta = TimeSpan()) const;
constexpr std::time_t toTimeStamp() const;
static const char *printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation = false); static const char *printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation = false);
static constexpr DateTime eternity(); static constexpr DateTime eternity();
@ -220,6 +223,26 @@ constexpr inline DateTime DateTime::fromTimeStampGmt(std::time_t timeStamp)
return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(timeStamp) * TimeSpan::ticksPerSecond); return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(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 <typename TimePoint> 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 <typename TimePoint> 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. * \brief Returns a mutable reference to the total ticks.
*/ */
@ -396,6 +419,14 @@ inline std::string DateTime::toString(DateTimeOutputFormat format, bool noMillis
return result; 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. * \brief Constructs a new instance of the DateTime class with the maximal number of ticks.
*/ */

View File

@ -8,6 +8,7 @@
#include <cppunit/TestFixture.h> #include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/HelperMacros.h>
#include <chrono>
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
@ -114,11 +115,17 @@ void ChronoTests::testDateTime()
CPPUNIT_ASSERT(!test1.isSameDay(test1 + TimeSpan::fromHours(9))); CPPUNIT_ASSERT(!test1.isSameDay(test1 + TimeSpan::fromHours(9)));
CPPUNIT_ASSERT_EQUAL("Wed 2012-02-29 15:34:20.033"s, test1.toString(DateTimeOutputFormat::DateTimeAndShortWeekday)); CPPUNIT_ASSERT_EQUAL("Wed 2012-02-29 15:34:20.033"s, test1.toString(DateTimeOutputFormat::DateTimeAndShortWeekday));
// test fromTimeStamp() // test fromTimeStamp()/toTimeStamp()
const auto fromTimeStampGmt = DateTime::fromTimeStampGmt(1453840331), fromTimeStamp = DateTime::fromTimeStamp(1453840331); const auto timeStamp = static_cast<time_t>(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_EQUAL("Tue 2016-01-26 20:32:11"s, fromTimeStampGmt.toString(DateTimeOutputFormat::DateTimeAndShortWeekday));
CPPUNIT_ASSERT(fabs((fromTimeStamp - fromTimeStampGmt).totalDays()) <= 1.0); CPPUNIT_ASSERT(fabs((fromTimeStamp - fromTimeStampGmt).totalDays()) <= 1.0);
CPPUNIT_ASSERT_EQUAL(DateTime(), DateTime::fromTimeStamp(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 // test whether ConversionException() is thrown when invalid values are specified
CPPUNIT_ASSERT_THROW(DateTime::fromDate(0, 1, 1), ConversionException); CPPUNIT_ASSERT_THROW(DateTime::fromDate(0, 1, 1), ConversionException);