C++ Converting a Time String to Seconds from the Epoch

C++ Converting a time string to seconds from the epoch

Using C++11 functionality we can now use streams to parse times:

The iomanip std::get_time will convert a string based on a set of format parameters and convert them into a struct tz object.

You can then use std::mktime() to convert this into an epoch value.

#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>

int main()
{
std::tm t = {};
std::istringstream ss("2010-11-04T23:23:01Z");

if (ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S"))
{
std::cout << std::put_time(&t, "%c") << "\n"
<< std::mktime(&t) << "\n";
}
else
{
std::cout << "Parse failed\n";
}
return 0;
}

Date string to epoch seconds (UTC)

Solution prior to C++20: Roll your own.

Given the right documentation, it really is much easier than it sounds, and can even be lightning fast if you don't need much error detection.

The first problem is to parse the numbers without manipulating any of them. You only need to read unsigned values of length 2 and 4 digits, so just do that bare minimum:

int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}

int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}

Now given a string, it is easy to parse out the different values you will need:

// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
...

The part that usually trips people up is how to convert the triple {y, m, d} into a count of days since/prior 1970-01-01. Here is a collection of public domain calendrical algorithms that will help you do this. This is not a 3rd party date/time library. It is a tutorial on the algorithms you will need to write your own date/time library. And these algorithms are efficient. No iteration. No large tables. That makes them very pipeline and cache friendly. And they are unit tested over a span of +/- a million years. So you don't have to worry about hitting any correctness boundaries with them. These algorithms also have a very in-depth derivation if you are interested in how they work.

So just go to the collection of public domain calendrical algorithms, pick out the algorithms you need (and customize them however you want), and roll your own converter.

For example:

#include <cstdint>
#include <limits>
#include <string>

int
days_from_civil(int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<int>(doe) - 719468;
}

int
read2(std::string const& str, int pos)
{
return (str[pos] - '0')*10 + (str[pos+1] - '0');
}

int
read4(std::string const& str, int pos)
{
return (str[pos] - '0')*1000 + (str[pos+1] - '0')*100 +
(str[pos+2] - '0')*10 + (str[pos+3] - '0');
}

// yyyy-mm-dd hh:MM:ss -> count of non-leap seconds since 1970-01-01 00:00:00 UTC
// 0123456789012345678
long long
EpochConverter(std::string const& str)
{
auto y = read4(str, 0);
auto m = read2(str, 5);
auto d = read2(str, 8);
auto h = read2(str, 11);
auto M = read2(str, 14);
auto s = read2(str, 17);
return days_from_civil(y, m, d)*86400LL + h*3600 + M*60 + s;
}

#include <iostream>

int
main()
{
std::cout << EpochConverter("2019-01-15 10:00:00") << '\n';
}

This just output for me:

1547546400

Sprinkle in whatever error detection is appropriate for your application.

Convert Epoch Time string to Time

If only you had that value in an integer instead of a string, you could just call ctime. If only there were some way to convert a string to an integer....

time_t c;
c = strtoul( "1360440555", NULL, 0 );
ctime( &c );

convert time string to epoch in milliseconds using C++

You can use the C++20 features std::chrono::parse / std::chrono::from_stream and set the timepoint to be in milliseconds.

A modified example from my error report on MSVC on this subject which uses from_stream:

#include <chrono>
#include <iostream>
#include <locale>
#include <sstream>

int main() {
std::setlocale(LC_ALL, "C");
std::istringstream stream("2022-09-25T10:07:41.123456Z");

std::chrono::sys_time<std::chrono::milliseconds> tTimePoint;
std::chrono::from_stream(stream, "%Y-%m-%dT%H:%M:%S%Z", tTimePoint);

std::cout << tTimePoint << '\n';

auto since_epoch = tTimePoint.time_since_epoch();
std::cout << since_epoch << '\n'; // 1664100461123ms

// or as 1664100461.123s
std::chrono::duration<double> fsince_epoch = since_epoch;
std::cout << std::fixed << std::setprecision(3) << fsince_epoch << '\n';
}

Demo


If you are stuck with C++11 - C++17 you can install the date library by Howard Hinnant. It's the base of what got included in C++20 so if you upgrade to C++20 later, you will not have many issues.

#include "date/date.h"

#include <chrono>
#include <iostream>
#include <sstream>

int main() {
std::istringstream stream("2022-09-25T10:07:41.123456Z");

date::sys_time<std::chrono::milliseconds> tTimePoint;
date::from_stream(stream, "%Y-%m-%dT%H:%M:%S%Z", tTimePoint);

auto since_epoch = tTimePoint.time_since_epoch();

// GMT: Sunday 25 September 2022 10:07:41.123
std::cout << since_epoch.count() << '\n'; // prints 1664100461123
}

C++ Converting a Datetime String to Epoch Cleanly

See: Date/time conversion: string representation to time_t

And: [Boost-users] [date_time] So how come there isn't a to_time_t helper func?

So, apparently something like this should work:

#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;

std::string ts("2002-01-20 23:59:59");
ptime t(time_from_string(ts));
ptime start(gregorian::date(1970,1,1));
time_duration dur = t - start;
time_t epoch = dur.total_seconds();

But I don't think it's much cleaner than Rob's suggestion: use sscanf to parse the data into a struct tm and then call mktime.

How to convert epoch time to time String

time(), localtime() and sprintf() should do the trick.

#ifdef __cplusplus
#include <cstdio>
#else
#include <stdio.h>.
#endif

#include <time.h>

static const char *Weekdays[] = {
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
};

int main() {
    time_t curSec;
    struct tm *curDate;
    char dateString[32];

    curSec = time(NULL);
    curDate = localtime(&curSec);
    sprintf(dateString,
        "%s-%02d-%02d-%02d-%02d-%02d-%d",
        Weekdays[curDate->tm_wday],
        curDate->tm_mday,
        curDate->tm_hour,
        curDate->tm_min,
        curDate->tm_sec,
        curDate->tm_mon+1,
        curDate->tm_year+1900);

    printf("%s\n",dateString);
    return 0;
}

converting string time to seconds

From http://en.wikipedia.org/wiki/Time.h:

Calendar time (also known as "broken-down time") in the C standard library is represented as the struct tm structure, consisting of the following members:

Member  Description
int tm_hour hour (0 – 23)
int tm_isdst Daylight saving time enabled (> 0), disabled (= 0), or unknown (< 0)
int tm_mday day of the month (1 – 31)
int tm_min minutes (0 – 59)
int tm_mon month (0 – 11, 0 = January)
int tm_sec seconds (0 – 60, 60 = Leap second)
int tm_wday day of the week (0 – 6, 0 = Sunday)
int tm_yday day of the year (0 – 365)
int tm_year year since 1900

ie you need to add 1900 to the year, and the months are zero-based.



Related Topics



Leave a reply



Submit