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
* - 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.

View File

@ -60,8 +60,10 @@ public:
static std::pair<DateTime, TimeSpan> 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 <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 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<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.
*/
@ -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.
*/

View File

@ -8,6 +8,7 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <chrono>
#include <cmath>
#include <iostream>
@ -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<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(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);