skip to Main Content

2038 is coming…I have to think date time in 64 bit! I’m trying to understand how to do this in C++, with chrono library, but I’m failing miserably so far.

I will have to communicate a date time format to a device that will receive 8 bytes to set its date and will return me the same 8 bytes when requested.

These my experiments so far:

#include <iostream>
#include <chrono>
#include <ratio>
#include <ctime>

using namespace std::chrono;

int main()
{

    typedef std::chrono::duration<int64_t, std::ratio<1,1>> dur_s;
    dur_s time_since_start(0x000000FFFFFFFFFF);
    std::cout << "time_since_start: " << time_since_start.count() << std::endl;

    time_point <system_clock, dur_s> time_point_since_start(time_since_start);
    std::cout << "time_point_since_start: " << 
    time_point_since_start.time_since_epoch().count() << std::endl;

    std::time_t date_since_start = system_clock::to_time_t(time_point_since_start); 

    std::cout << "date_since_start 0: " << date_since_start << std::endl;
    std::cout << "date_since_start 1: " << ctime(&date_since_start) << std::endl;

    return 0;
}

This is the output:

  time_since_start: 1099511627775
  time_point_since_start: 1099511627775
  date_since_start 0: -7293016647
  date_since_start 1: Sun Nov 23 02:52:29 1738

My intent is that to create a duration stored in an int64_t, with the precision of seconds (don’t need any more resolution). From that I want to define a time_point and finally represent it as time_t. For my purpose I’m defining a duration that is bigger than 32bit, but something is not working. When I do the system_clock::to_time_t the time_point gets truncated and the result is a date back in time.

I’m missing something obvious here.

I’m working on a recent Linux Mint on a 64bit VM. Code is with standard C++ 17 (can move up to 20 if needed). Compiler is gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0.

2

Answers


  1. Your code looks correct except that your sample time stamp (0x0000'00FF'FFFF'FFFF) is in the year 36,812. And that is likely blowing your implementation of ctime out of the water. Try removing a leading F:

    dur_s time_since_start(0x000'0000F'FFFF'FFFF);
    

    This gets you down to the year 4147. That is still ridiculously far in the future, but your ctime is more likely to be able to handle it.

    Also, if you want, you can replace:

    typedef std::chrono::duration<int64_t, std::ratio<1,1>> dur_s;
    

    with:

    using dur_s = std::chrono::seconds;
    

    It won’t change anything, but you might find it more readable.

    If you upgrade to C++20, you can also:

    std::cout << "date_since_start 2: " << time_point_since_start << 'n';
    

    Which would output:

    date_since_start 2: 4147-08-20 07:32:15
    

    (with 0x000'0000F'FFFF'FFFF)

    Printing out (and telling us) sizeof(std::time_t) would be assuring.


    If you are dealing with a buggy implementation of system_clock::to_time_t, you can replace:

    std::time_t date_since_start = system_clock::to_time_t(time_point_since_start); 
    

    with:

    std::time_t date_since_start = time_point_since_start.time_since_epoch().count();
    

    While the above replacement is correct, this does not represent a libstdc++ bug. It is an overflow caused by the fact that to_time_t will do an intermediate conversion of units to system_clock::duration. And on libstdc++ this is nanoseconds. And 64 bit nanoseconds will overflow on 2262-04-11 23:47:16.854775807.

    Login or Signup to reply.
  2. system_clock::time_point is generally implemented as the number of nanoseconds since the unix epoch stored in a 64-bit integer. 0x0000'00FF'FFFF'FFFF seconds is 0x3B'9AC9'FFFF'C465'3600 nanoseconds which overflows a signed 64-bit integer, you’ll need to stick to seconds values less than around 0x2'25C1'7D04 to avoid overflows.

    https://godbolt.org/z/xfc1oE3x5

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search