I am writing current GMT time as string as follow :
const std::time_t now = std::time(nullptr);
std::stringstream ss;
ss << std::put_time(std::gmtime(&now), "%Y-%m-%d %H:%M:%S");
Later I want to do the reverse operation, reading time from the stringstream as GMT, and compare it to current timestamp :
std::tm tm = {};
ssTimestamp >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
const std::time_t&& time = std::mktime(&tm);
const double timestampDiff((std::difftime(std::time(nullptr), time)));
Something is missing in the code below, because the decoded time is never converted to GMT, thus I end up with 1 hour time difference due to my local timezone
P.S : Can use only standard libraries, and can' t change date string format
The C++20 spec has a convenient way to do this:
using namespace std::chrono;
sys_seconds tp;
ssTimestamp >> parse("%Y-%m-%d %H:%M:%S", tp);
std::time_t time = system_clock::to_time_t(tp);
No vendor has yet implemented this part of C++20, but there is an example implementation here in namespace date.
There is no library support to do this operation in C++ prior to C++20.
The best you can do using only standard libraries is to parse the fields into a tm using std::get_time (as your question shows), and then convert that {y, m, d, h, M, s} structure to a time_t using your own math, and the assumption (which is generally true) that std::time_t is Unix Time with a precision of seconds.
Here is a collection of public domain calendrical algorithms to help you do that. This is not a 3rd party library. It is a cookbook for writing your own date library.
For example:
#include <ctime>
std::time_t
to_time_t(std::tm const& tm)
{
int y = tm.tm_year + 1900;
unsigned m = tm.tm_mon + 1;
unsigned d = tm.tm_mday;
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)*86400 +
tm.tm_hour*3600 + tm.tm_min*60 + tm.tm_sec;
}
The link above has a very in-depth description of this algorithm and unit tests to make sure it works over a range of +/- millions of years.
The above to_time_t is essentially a portable version of timegm that ships on linux and bsd platforms. This function is also called _mkgmtime on Windows.
The tm struct doesn't store the timezone information, it's mktime that by default uses the local timezone.
Following this thread, the best option would be to use:
#include "time.h"
timestamp = mktime(&tm) - timezone; //or _timezone
if timezone or _timezone is available to your compiler. A comment in the linked answer warns that it may raise issues with daylight saving time, but it should not apply to GMT.
I recently tried to solve a very similar problem. I was trying to convert a string to a specific timezone regardless what is the current timezone of a computer. Here is the solution that I came up with and works as expected:
std::time_t from_time_str(std::string time_str) {
std::stringstream ss;
std::tm tm = {};
ss << time_str;
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
std::time_t t = std::mktime(&tm);
std::tm* gm_tm = std::gmtime(&t);
gm_tm->tm_isdst = false;
std::time_t gm_t = std::mktime(gm_tm);
std::time_t gm_offset = (gm_t - t);
std::time_t real_gm_t = t - gm_offset;
return real_gm_t;
}
The idea is use the function gmtime to get the gmtime of the timestamp so we could calculate the offset of the target computer's timezone. We then subtract the offset to get the GM time.
Note that the line gm_tm->tm_isdst = false; is required for any timezone that has daylight saving is enable, otherwise the gmtime is calculated with daylight saving offset (1 hour off) and this should not be the desired effect of calculating the actual GM time.
Related
How to convert timestamp string, e.g. "1997-07-16T19:20:30.45+01:00" into UTC time. The result of conversion should be timespec structure as in utimensat input arguments.
// sorry, should be get_utc_time
timespec get_local_time(const char* ts);
P.S. I need solution using either standard Linux/C/C++ facilities (whatever that means) or Boost C++ library.
Assumption: You want the "+01:00" to be subtracted from the "1997-07-16T19:20:30.45" to get a UTC timestamp and then convert that into a timespec.
Here is a C++20 solution that will automatically handle the centisecond precision and the [+/-]hh:mm UTC offset for you:
#include <chrono>
#include <ctime>
#include <sstream>
std::timespec
get_local_time(const char* ts)
{
using namespace std;
using namespace chrono;
istringstream in{ts};
in.exceptions(ios::failbit);
sys_time<nanoseconds> tp;
in >> parse("%FT%T%Ez", tp);
auto tps = floor<seconds>(tp);
return {.tv_sec = tps.time_since_epoch().count(),
.tv_nsec = (tp - tps).count()};
}
When used like this:
auto r = get_local_time("1997-07-16T19:20:30.45+01:00");
std::cout << '{' << r.tv_sec << ", " << r.tv_nsec << "}\n";
The result is:
{869077230, 450000000}
std::chrono::parse will subtract the +/-hh:mm UTC offset from the parsed local value to obtain a UTC timestamp (to up to nanosecond precision).
If the input has precision seconds, this code will handle it. If the precision is as fine as nanoseconds, this code will handle it.
If the input does not conform to this syntax, an exception will be thrown. If this is not desired, remove in.exceptions(ios::failbit);, and then you must check in.fail() to see if the parse failed.
This code will also handle dates prior to the UTC epoch of 1970-01-01 by putting a negative value into .tv_sec, and a positive value ([0, 999'999'999]) into .tv_nsec. Note that handling pre-epoch dates is normally outside of the timespec specification, and so most C utilities will not handle such a timespec value.
If you can not use C++20, or if your vendor has yet to implement this part of C++20, there exists a header-only library which implements this part of C++20, and works with C++11/14/17. I have not linked to it here as it is not in the set: "standard Linux/C/C++ facilities (whatever that means) or Boost C++ library". I'm happy to add a link if requested.
For comparison, here's how you could do this in mostly-standard C. It's somewhat cumbersome, because C's date/time support is still rather fragmented, unlike the much more complete support which C++ has, as illustrated in Howard Hinnant's answer. (Also, two of the functions I'm going to use are not specified by the C Standard, although they're present on many/most systems.)
If you have the semistandard strptime function, and if you didn't care about subseconds and explicit time zones, it would be relatively straightforward. strptime is a (partial) inverse of strftime, parsing a time string under control of a format specifier, and constructing a struct tm. Then you can call mktime to turn that struct tm into a time_t. Then you can use the time_t to populate a struct timespec.
char *inpstr = "1997-07-16T19:20:30.45+01:00";
struct tm tm;
memset(&tm, 0, sizeof(tm));
char *p = strptime(inpstr, "%Y-%m-%dT%H:%M:%S", &tm);
if(p == NULL) {
printf("strptime failed\n");
exit(1);
}
tm.tm_isdst = -1;
time_t t = mktime(&tm);
if(t == -1) {
printf("mktime failed\n");
exit(1);
}
struct timespec ts;
ts.tv_sec = t;
ts.tv_nsec = 0;
printf("%ld %ld\n", ts.tv_sec, ts.tv_nsec);
printf("%s", ctime(&ts.tv_sec));
printf("rest = %s\n", p);
In my time zone, currently UTC+4, this prints
869095230 0
Wed Jul 16 19:20:30 1997
rest = .45+01:00
But you did have subsecond information, and you did have an explicit time zone, and there's no built-in support for those in any of the basic C time-conversion functions, so you have to do things "by hand". Here's one way to do it. I'm going to use sscanf to separate out the year, month, day, hour, minute, second, and other components. I'm going to use those components to populate a struct tm, then use the semistandard timegm function to convert them straight to a UTC time. (That is, I temporarily assume that the HH:MM:SS part was UTC.) Then I'm going to manually correct for the time zone. Finally, I'm going to populate the tv_nsec field of the struct timesec with the subsecond information I extracted back in the beginning.
int y, m, d;
int H, M, S;
int ss; /* subsec */
char zs; /* zone sign */
int zh, zm; /* zone hours, minutes */
int r = sscanf(inpstr, "%d-%d-%dT%d:%d:%d.%2d%c%d:%d",
&y, &m, &d, &H, &M, &S, &ss, &zs, &zh, &zm);
if(r != 10 || (zs != '+' && zs != '-')) {
printf("parse failed\n");
exit(1);
}
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = y - 1900;
tm.tm_mon = m - 1;
tm.tm_mday = d;
tm.tm_hour = H;
tm.tm_min = M;
tm.tm_sec = S;
time_t t = timegm(&tm);
if(t == -1) {
printf("timegm failed\n");
exit(1);
}
long int z = ((zh * 60L) + zm) * 60;
if(zs == '+') /* East of Greenwich */
t -= z;
else t += z;
struct timespec ts;
ts.tv_sec = t;
ts.tv_nsec = ss * (1000000000 / 100);
printf("%ld %ld\n", ts.tv_sec, ts.tv_nsec);
printf("%s", ctime(&ts.tv_sec));
printf(".%02ld\n", ts.tv_nsec / (1000000000 / 100));
For me this prints
869077230 450000000
Wed Jul 16 14:20:30 1997
.45
The time zone and subsecond information have been honored.
This code makes no special provision for dates prior to 1970. I think it will work if mktime/timegm work.
As mentioned, two of these functions — strptime and timegm — are not specified by the ANSI/ISO C Standard and are therefore not guaranteed to be available everywhere.
I'm trying to check how many days are left for my application, one from the current time and the second one from a std::string that comes from a database, but every time I try to subtract the 2 dates using
std::chrono::duration<int>
I get "expected unqualified-d before = token", not sure what is chrono expecting below its my code
void Silo::RevisarDiasRestantes(){ // Check how many days are left, if the serial is 00 is for life
// obtain the current time with std::chrono and convert to struct tm * so it can be convert to an std::string
std::time_t now_c;
std::chrono::time_point<std::chrono::system_clock> now;
typedef std::chrono::duration<int> tiempo;
struct tm * timeinfo;
char buffer[80];
now = std::chrono::system_clock::now();
now_c = std::chrono::system_clock::to_time_t(now);
time (&now_c);
timeinfo = localtime(&now_c);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
std::string str(buffer);
Log("Tiempo usando Chrono " + QString::fromStdString(str));
for (int a{0} ; a<1000 ; ) // just an standard for
{
a++;
}
// convert std::string to std::time_t and then convert to a std::chrono
std::string i = str;
std::chrono::time_point<std::chrono::system_clock> end;
struct std::tm tm;
std::istringstream iss;
iss.str(i);
iss >> std::get_time(&tm,"%Y:%m:%d %H:%M:%S");
std::time_t time = mktime(&tm);
end = std::chrono::system_clock::from_time_t(time);
tiempo = end - now; <-------------------- heres the problem
Log( "Diferencia de tiempo: " + QString::number(tiempo.count()));
}
Edit: one thing I didn't notice until today if I try using istringstream to std::get_time the program compiles but it fails at runtime, asking for a "missing basic_istringstream" in the dynamic library, so I can't use that; is there another alternative to give the string to get_time?
Edit2: I didn't notice until JhonFilleau pointed the problem, no more working at late hours, thannks
There's two problems, one of which is pointed out by JohnFilleau in the comments.
You are assigning to a type instead of to a variable. It is as if you are coding:
int = 3;
instead of:
int i = 3;
You need something like:
tiempo t = end - now;
You are trying to implicitly convert from the precision of system_clock::time_point (typically microseconds to nanoseconds), to a precision of seconds. And chrono won't let you do that conversion implicitly because it loses precision.
But you can force it with duration_cast:
tiempo t = std::chrono::duration_cast<std::chrono::seconds>(end - now);
Finally, there's no need for
typedef std::chrono::duration<int> tiempo;
This is just another name for seconds, but stored in an int instead of something less prone to overflow. auto can be more easily used here:
auto t = std::chrono::duration_cast<std::chrono::seconds>(end - now);
and the type of t is std::chrono::duration<I> where I is a signed integral type of at least 35 bits (typically 64 bits). And there is a convenience type alias for this type named std::chrono::seconds.
If you really want this type named tiempo then I recommend:
using tiempo = std::chrono::seconds;
// ...
auto t = std::chrono::duration_cast<tiempo>(end - now);
or:
tiempo t = std::chrono::duration_cast<tiempo>(end - now);
Question
I want to parse a date-time given as string (UTC) into seconds since epoch. Example (see EpochConverter):
2019-01-15 10:00:00 -> 1547546400
Problem
The straightforward solution, which is also accepted in a very related question C++ Converting a time string to seconds from the epoch goes std::string -> std::tm -> std::time_t using std::get_time and then std::mktime:
std::tm tm;
std::stringstream ss("2019-01-15 10:00:00");
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
std::time_t epoch = std::mktime(&tm);
// 1547546400 (expected)
// 1547539200 (actual, 2 hours too early)
But std::mktime seems to mess up the hours due to timezone. I am executing the code from UTC+01:00, but we also had DST at that date, so its +2 here.
The tm shows 15 for the hour field after std::get_time. It gets messed up as soon as it enters std::mktime.
So again, the string is to be interpreted as UTC timestamp, no timezones whatsoever should be involved. But all solutions I came up with seem to interpret it as local timestamp and add offsets to it.
Restrictions
I have some restrictions for this:
C++17
platform/compiler independent
no environment variable hacking
no external libraries (like boost)
Feel free to post answers involving those for the sake of Q&A though, but I wont accept them.
Research
I found various attempts to solve this problem, but none met my requirements:
std::mktime (as mentioned above), messes up the time because it assumes local time
strptime, not available on my platform, not part of the standard
timegm (thats exactly what I would need), not platform independent
_mkgmtime, not platform independent
boost::posix_time::from_iso_string, is an external library
std::chrono::date::parse, not available with C++17
clear and reset the timezone variable with tzset, uses environment variable hacking
manually countering the offset with mktime(localtime(×tamp)) - mktime(gmtime(×tamp)), computes the wrong offset since it does not account for DST (1 hour on my platform but it would need to be 2 hours)
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.
I had the same requirement recently. I was disappointed to find that the handling of DST and timezones seemed inconsistent between writing timestamps and parsing them.
The code I came up with was this:
void time_point_from_stream(std::istream &is, system_clock::time_point &tp)
{
std::tm tm {};
is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
// unhappily, mktime thinks it's reading local time with DST adjustments
auto my_time_t = std::mktime(&tm);
my_time_t += tm.tm_gmtoff;
if (tm.tm_isdst == 1)
my_time_t -= 3600;
tp = system_clock::from_time_t(my_time_t);
if (not is)
return;
auto ch = is.peek();
if (std::isspace(ch))
return;
if (ch == '.')
{
double zz;
is >> zz;
auto zseconds = std::chrono::duration< double >(zz);
tp += chrono::duration_cast< system_clock::duration >(zseconds);
if (not is)
return;
ch = is.peek();
}
if (ch == 'Z')
is.get();
else if (not isspace(ch))
{
is.setstate(std::ios::failbit);
}
}
Essentially, the steps are:
Use std::get_time to fill a tm
use std::mktime to convert that to a time_t
reverse out timezone and DST adjustments
convert to a std::chrono::system_clock::time_point
Parse the fractional seconds and adjust the result.
I believe c++20 improves on the situation.
Howard Hinnant has also written an improved date/time library. There is also boost::posix_time which I have always found easier to use than the std offering.
I have the following integers:
int y, mon, d, h, min, s;
Their values are: 2012, 06, 27, 12, 47, 53 respectively. I want to represent the date time of "2012/06/27 12:47:53 UTC" if I have selected 'UTC' somewhere else in my application, or "2012/06/27 12:47:53 AEST" if I have selected 'AEST' somewhere else in my application.
I want to convert this into a time_t, and here's the code that I am current using to do so:
struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
//timeinfo.tm_isdst = 0; //TODO should this be set?
//TODO find POSIX or C standard way to do covert tm to time_t without in UTC instead of local time
#ifdef UNIX
return timegm(&timeinfo);
#else
return mktime(&timeinfo); //FIXME Still incorrect
#endif
So I am using a tm struct and mktime, however this is not working well, because it is always assuming my local time-zone.
What is the correct way of doing this?
So below is the solution that I have come up with so far.
It basically does one of three things:
If UNIX, simply use timegm
If not UNIX
Either, do math using the difference between UTC epoch and local epoch as an offset
Reservation: Math may be incorrect
Or, set the "TZ" environment variable to UTC temporarily
Reservation: will trip up if/ when this code needs to be multithreaded
namespace tmUtil
{
int const tm_yearCorrection = -1900;
int const tm_monthCorrection = -1;
int const tm_isdst_dontKnow = -1;
#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ) && !(defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM))
static bool isLeap(int year)
{
return
(year % 4) ? false
: (year % 100) ? true
: (year % 400) ? false
: true;
}
static int daysIn(int year)
{
return isLeap(year) ? 366 : 365;
}
#endif
}
time_t utc(int year, int mon, int day, int hour, int min, int sec)
{
struct tm time = {0};
time.tm_year = year + tmUtil::tm_yearCorrection;
time.tm_mon = mon + tmUtil::tm_monthCorrection;
time.tm_mday = day;
time.tm_hour = hour;
time.tm_min = min;
time.tm_sec = sec;
time.tm_isdst = tmUtil::tm_isdst_dontKnow;
#if defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM) //TODO remove && 00
time_t result;
result = timegm(&time);
return result;
#else
#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ)
//TODO check that math is correct
time_t fromEpochUtc = mktime(&time);
struct tm localData;
struct tm utcData;
struct tm* loc = localtime_r (&fromEpochUtc, &localData);
struct tm* utc = gmtime_r (&fromEpochUtc, &utcData);
int utcYear = utc->tm_year - tmUtil::tm_yearCorrection;
int gmtOff =
(loc-> tm_sec - utc-> tm_sec)
+ (loc-> tm_min - utc-> tm_min) * 60
+ (loc->tm_hour - utc->tm_hour) * 60 * 60
+ (loc->tm_yday - utc->tm_yday) * 60 * 60 * 24
+ (loc->tm_year - utc->tm_year) * 60 * 60 * 24 * tmUtil::daysIn(utcYear);
#ifdef UNIX
if (loc->tm_gmtoff != gmtOff)
{
StringBuilder err("loc->tm_gmtoff=", StringBuilder((int)(loc->tm_gmtoff)), " but gmtOff=", StringBuilder(gmtOff));
THROWEXCEPTION(err);
}
#endif
int resultInt = fromEpochUtc + gmtOff;
time_t result;
result = (time_t)resultInt;
return result;
#else
//TODO Find a way to do this without manipulating environment variables
time_t result;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
result = mktime(&time);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return result;
#endif
#endif
}
N.B. StringBuilder is an internal class, it doesn't matter for the purposes of this question.
More info:
I know that this can be done easily using boost, et al. But this is NOT and option. I need it to be done mathematically, or using a c or c++ standard function, or combinations thereof.
timegm appears to solve this problem, however, it doesn't appear to part of the C / POSIX standard. This code currently is compiled on multiple platforms (Linux, OSX, WIndows, iOS, Android (NDK)), so I need to find a way to make it work across all of these platforms, even if the solution involves #ifdef $PLATFORM type things.
It makes me want to throw up in my mouth a little bit, but you could convert it to a string with strftime(), replace the timezone in the string and then convert it back with strptime() and into a time_t with mktime(). In detail:
#ifdef UGLY_HACK_VOIDS_WARRANTY
time_t convert_time(const struct tm* tm)
{
const size_t BUF_SIZE=256;
char buffer[BUF_SIZE];
strftime(buffer,256,"%F %H:%M:%S %z", tm);
strncpy(&buffer[20], "+0001", 5); // +0001 is the time-zone offset from UTC in hours
struct tm newtime = {0};
strptime(buffer, "%F %H:%M:%S %z", &newtime);
return mktime(&newtime);
}
#endif
However, I would highly recommend you convince the powers that be that boost is an option after all. Boost has great support for custom timezones. There are other libraries that do this elegantly as well.
If all you want is to convert a struct tm given in UTC to a time_t then you can do it like this:
#include <time.h>
time_t utc_to_time_t(struct tm* timeinfo)
{
tzset(); // load timezone information (this can be called just once)
time_t t = mktime(timeinfo);
return t - timezone;
}
This basically converts the UTC time to time_t as if the given time was local, then applies a timezone correction to the result to bring it back to UTC.
Tested on gcc/cygwin and Visual Studio 2010.
I hope this helps!
Update: As you very well pointed out, my solution above may return time_t value that is one hour off when the daylight time savings state of the queried date is different than the one for the current time.
The solution for that problem is to have an additional function that can tell you if a date falls in the DST region or not, and use that and the current DST flag to adjust the time returned by mktime. This is actually easy to do. When you call mktime() you just have to set the tm_dst member to -1 and then the system will do its best to figure out the DST at the given time for you. Assuming we trust the system on this, then you can use this information to apply a correction:
#include <time.h>
time_t utc_to_time_t(struct tm* timeinfo)
{
tzset(); // load timezone information (this can be called just once)
timeinfo->tm_isdst = -1; // let the system figure this out for us
time_t t = mktime(timeinfo) - timezone;
if (daylight == 0 && timeinfo->tm_isdst != 0)
t += 3600;
else if (daylight != 0 && timeinfo->tm_isdst == 0)
t -= 3600;
return t;
}
If you are on Linux or other UNIx or UNIX-like system then you might have a timegm function that does what you want. The linked manual page have a portable implementation so you can make it yourself. On Windows I know of no such function.
After beating my head against this for days trying to get a timegm(1) function that works on Android (which does not ship with one), I finally discovered this simple and elegant solution, which works beautifully:
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
I don't see why this wouldn't be a suitable cross-platform solution.
I hope this helps!
time_t my_timegm2(struct tm *tm)
{
time_t ret = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 + tm->tm_yday*86400;
ret += ((time_t)31536000) * (tm->tm_year-70);
ret += ((tm->tm_year-69)/4)*86400 - ((tm->tm_year-1)/100)*86400 + ((tm->tm_year+299)/400)*86400;
return ret;
}
There seems to be a simpler solution:
#include <time64.h>
time_t timegm(struct tm* const t)
{
return (time_t)timegm64(t);
}
Actually I have not testet yet if really it works, because I still have a bit of porting to do, but it compiles.
Here's my solution:
#ifdef WIN32
# define timegm _mkgmtime
#endif
struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
return timegm(&timeinfo);
This should work both for unix and windows
What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)
So far, I have the following algorithm, which to me feels a bit ugly:
void DateTime::splitTicks(time_t time) {
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time % 24;
time /= 24;
year = DateTime::reduceDaysToYear(time);
month = DateTime::reduceDaysToMonths(time,year);
day = int(time);
}
int DateTime::reduceDaysToYear(time_t &days) {
int year;
for (year=1970;days>daysInYear(year);year++) {
days -= daysInYear(year);
}
return year;
}
int DateTime::reduceDaysToMonths(time_t &days,int year) {
int month;
for (month=0;days>daysInMonth(month,year);month++)
days -= daysInMonth(month,year);
return month;
}
you can assume that the members seconds, minutes, hours, month, day, and year all exist.
Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.
Be careful about leap years in your daysInMonth function.
If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.
A good solution is the one in the gmtime source code:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.
see: http://www.cplusplus.com/reference/clibrary/ctime/
Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.
An easy way (though different than the format you wanted):
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
Output:
Wed Sep 21 10:27:52 2011
Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:
std::string::size_type i = res.find("\n");
if (i != std::string::npos)
res.erase(i, res.length());
Taken from: http://en.cppreference.com/w/cpp/chrono/c/time
time_t t = unixTime;
cout << ctime(&t) << endl;
This code might help you.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
To convert a epoch string to UTC
string epoch_to_utc(string epoch) {
long temp = stol(epoch);
const time_t old = (time_t)temp;
struct tm *oldt = gmtime(&old);
return asctime(oldt);
}
and then it can be called as
string temp = "245446047";
cout << epoch_to_utc(temp);
outputs:
Tue Oct 11 19:27:27 1977
If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.
All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.
In short, use gmtime (and time.h functionality in general).