Home > Enterprise >  Microseconds epoch to a Date string using chrono
Microseconds epoch to a Date string using chrono

Time:10-02

I have been looking around to get what I want but I couldn't find anything hence my question (hopefully not a duplicate!)

I am looking to get a microsecond resolution epoch time (to be converted to a Date string) of the clock perhaps using chrono.

Following is what works for me for seconds resolution:

auto secondsEpochTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::cout << "Date string = " << ctime(&secondsEpochTime);

However when I change seconds to microseconds, ctime doesn't seem to reflect the correct date.

auto microSecondsEpochTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::cout << "Date string = " << ctime(&microSecondsEpochTime); // incorrect Date

CodePudding user response:

Unfortunately std::chrono is not complete to provide a full answer to your question. You will have to use parts of the C library until C 23 at least otherwise you might end up with a race-prone implementation.

The idea is to get the timestamp and convert it to an integer as microseconds since epoch (1970-01-01).

Then use localtime_r to get the local time broken down in year/month/day/hour/minute/seconds and print it to string.

Finally append the milliseconds as an int padded to 3 digits and return the entire result as an std::string.

constexpr static int64_t ONEMICROSECOND = 1000000;

static std::string nowstr() {
    auto now = std::chrono::system_clock::now();
    auto onems = std::chrono::microseconds(1);
    int64_t epochus = now.time_since_epoch()/onems;    
    time_t epoch = epochus/ONEMICROSECOND;
    struct tm tms{};
    localtime_r( &epoch, &tms );
    char buf[128];
    size_t nb = strftime( buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tms );
    nb  = ::sprintf( &buf[nb], ".d", int(epochus%ONEMICROSECOND) );
    return std::string( buf, nb );
}

If you run this as-is it will likely return the timestamp in GMT. You will heave to set your timezone programatically if not set in the environment (as it happens with compiler explorer/Godbolt.

int main() {
    setenv("TZ", "/usr/share/zoneinfo/America/New_York", 1); 
    std::cout << nowstr() << std::endl;
}

Results in

Program stdout
2022-10-01 22:51:03.988759

Compiler explorer link: https://godbolt.org/z/h88zhrr73

UPDATE: if you prefer to use boost::format (std::format is still incomplete on most compilers unfortunately) then you can do

static std::string nowstr() {
    auto now = std::chrono::system_clock::now();
    auto onems = std::chrono::microseconds(1);
    int64_t epochus = now.time_since_epoch()/onems;    
    time_t epoch = epochus/ONEMICROSECOND;
    struct tm tms{};
    localtime_r( &epoch, &tms );
    std::ostringstream ss;
    ss << boost::format( "d-d-d d:d:d.d" ) 
        % (tms.tm_year 1900) % (tms.tm_mon 1) % tms.tm_mday 
        % tms.tm_hour % tms.tm_min % tms.tm_sec 
        % (epochus%ONEMICROSECOND);
    return ss.str();
}

CodePudding user response:

You will have to use parts of the C library until C 23 at least

Umm... If your platform supports the full C 20 spec (at least with regards to format and chrono):

#include <chrono>
#include <format>
#include <iostream>

int
main()
{
    auto tp = std::chrono::system_clock::now();
    std::chrono::zoned_time zt{std::chrono::current_zone(),
        std::chrono::time_point_cast<std::chrono::microseconds>(tp)};
    std::cout << "Date string = " << std::format("{:%a %b %e %T %Y}", zt) << '\n';
}

Sample output:

Date string = Sat Oct  1 23:32:24.843844 2022
  • Related