I need a Class able to store date and time information in UTC (no timezone/locale required) and I also need to perform basic arithmetic operations on those date/times (addition, subtraction) and comparison. Ideally I would like use the C 11 standard library, if that is possible.
std::chrono
seems a good candidate for my purposes. My DateTime
class could be as simple as an alias of chrono::time_point
with the desired precision. e.g.
using UTCTime = std::chrono::time_point<SomeClock>;
Defining a custom clock (SomeClock
) allows my class to be type safe, whilechrono::time_point
class provides the computational requirements.
The missing piece of the puzzle are the functions to convert to/from date and time information. chrono::system_clock
offers the methods to_time_t
and from_time_t
, which are limited by the precision of seconds, which is not good enough for me.
Does anybody have a solution for that?
CodePudding user response:
Assuming you are storing date and time information with millisecond precision, then you can always do:
time_point<system_clock, milliseconds> epoch_milli{};
auto one_million_millisecond_from_epoch = epoch_milli milliseconds(1'000'000);
CodePudding user response:
I ended up with this minimal std::chrono
based implementation:
utctime.h
#include <chrono>
#include <string>
struct UTCClock
{
typedef std::chrono::microseconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef std::chrono::time_point<UTCClock, duration> time_point;
static const bool is_steady = true;
//
// every time_point will be generated from here
//
static time_point fromDate(int year= 0, int month= 0, int day= 0,
int hour= 0, int min = 0, int sec= 0,
int usec= 0);
//
// convert time_point to a date/time representation
//
static void toDate(const time_point &tp,
int &year, int &month, int &day,
int &hour, int &min, int &sec,
int &usec);
// NOT Supported, we don't need current time. We only
// want to represent UTC DateTime
// static time_point now();
};
using UTCTime = std::chrono::time_point<UTCClock, std::chrono::microseconds>;
utctime.cpp
#include "utctime.h"
#include <ctime>
namespace chrono = std::chrono;
using chrono::duration_cast;
using chrono::time_point_cast;
namespace {
std::time_t to_time_t(const UTCClock::time_point &tp) noexcept
{
return std::time_t(
duration_cast<chrono::seconds>(tp.time_since_epoch()).count());
}
UTCClock::time_point from_time_t(std::time_t tt) noexcept
{
return time_point_cast<UTCClock::duration>(
chrono::time_point<UTCClock,chrono::seconds>(chrono::seconds(tt)));
}
} // namespace
UTCClock::time_point UTCClock::fromDate(
int year, int month, int day, int hour, int min, int sec, int usec)
{
std::tm tm = {0};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
tm.tm_isdst = -1;
std::time_t tt = timegm(&tm);
return from_time_t(tt) chrono::microseconds(usec);
}
void UTCClock::toDate(const UTCClock::time_point &tp,
int &year,
int &month,
int &day,
int &hour,
int &min,
int &sec,
int &usec)
{
std::time_t tt = to_time_t(tp);
std::tm tm;
gmtime_r(&tt, &tm);
year = tm.tm_year 1900;
month = tm.tm_mon 1;
day = tm.tm_mday;
hour = tm.tm_hour;
min = tm.tm_min;
chrono::microseconds leftover = tp - from_time_t(tt) chrono::seconds(tm.tm_sec);
sec = duration_cast<chrono::seconds>(leftover).count();
usec = (leftover-chrono::seconds(sec)).count();
}
It can be used like this:
#include "utctime.h"
#include <iostream>
using namespace std::chrono;
int main(int argc, char* argv[])
{
//
// use UTCClock::fromDate to generate a UTCTime
//
UTCTime t1 = UTCClock::fromDate(1901, 1, 1, 0, 0, 0, 0);
UTCTime t2 = UTCClock::fromDate(1901, 1, 1, 1, 0, 0, 0);
//
// Then we can make use of std::chrono algebra like
// subtracting time_point or adding a duration to
// a time_point
// We can also compare time_points
//
microseconds timeDiff = t2 -t1;
std::cout << "t2-t1 difference in microseconds " <<
timeDiff.count() << std::endl;
std::cout << "t2-t1 difference in hours " <<
duration_cast<hours>(timeDiff).count() << std::endl;
UTCTime t3 = t1 - minutes{3};
std::cout << "t3-t1 difference in minutes " <<
duration_cast<minutes>(t3-t1).count() << std::endl;
t3 = t1 milliseconds{123};
std::cout << "t3-t1 difference in milliseconds " <<
duration_cast<milliseconds>(t3-t1).count() << std::endl;
if ( t3 > t1 ) std::cout << "t3 is greater than t1" << std::endl;
//
// std::chrono is also type safe and doesn't allow to
// mix dates generated by different clocks
//
system_clock::time_point systp = system_clock::now();
// auto tx = t1 - systp; // NO: doesn't compile
// if (t1 < systp); // NO: doesn't compile
return ;
}
Output:
t2-t1 difference in microseconds 3600000000 t2-t1 difference in hours 1 t3-t1 difference in minutes -3 t3-t1 difference in milliseconds 123 t3 is greater than t1