Home > other >  A class for date and time repesentation in C 11
A class for date and time repesentation in C 11

Time:01-13

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
  •  Tags:  
  • Related