Running dateon my server results in the correct time. But using localtime() in C(++) I'm getting the wrong time.
Running date: Fr 30. Nov 12:15:36 CET 2012
Using localtime(): Fr 30 Nov 2012 11:15:36 CET
What's wrong here?
OS: Debian 5.0.10
Some code:
struct tm* today;
today = localtime(...);
strftime(timeBuffer,50,myConnection.getMetaData().getDateFormat().c_str(),today);
disclaimer : This answer was written before any mention of strftime was added, and was a gut reaction to the 1 hour difference in the timestamps. Looking back at it now, that 1 hour difference couldn't have been due to DST (because the dates are not in summer), but is likely showing a UTC timestamp (1 hour difference between UTC and CET).
Unfortunately, the answer was accepted, and so I can't delete it. Even more unfortunate, is that the question as it stands is not answerable without additional information.
Leaving the original answer here for full transparency, but know that it does not address the question as asked :
The struct tm returned by localtime has a tm_isdst field that indicates whether daylight saving time (DST) is in effect. You need to take that field into account when formatting the time.
Try using asctime to format the time eg. :
puts(asctime(today));
I have experienced the same problem while writing a date adjustment routine. Adding 86400 seconds (= 1 day) to any given datetime value should result in incrementing the datetime value by one day. However in testing, the output value invariably added exactly one hour to the expected output. For instance, '2019-03-20 00:00:00' incremented by 86400 seconds resulted in '2019-03-21 01:00:00'. The reverse also occurred: '2019-03-21 00:00:00' decremented by -86400 resulted in '2019-03-20 01:00:00'.
The solution (inexplicably) was to subtract 3600 seconds (one hour) from the final interval before applying it to the input datetime.
The solution (thanks to helpful comments from #Lightness-Races-in-Orbit) was to set tm_isdst to -1 before calling mktime(). This tells mktime() that the DST status for the input datetime value is unknown, and that mktime() should use the system timezone databases to determine the correct timezone for the input datetime value.
The function (as corrected below) allows for any integer adjustment of days and now produces consistently correct results:
#include <stdio.h>
#include <string.h>
#include <time.h>
/*******************************************************************************
* \fn adjust_date()
*******************************************************************************/
int adjust_date(
char *original_date,
char *adjusted_date,
char *pattern_in,
char *pattern_out,
int adjustment,
size_t out_size)
{
/*
struct tm {
int tm_sec; // seconds 0-59
int tm_min; // minutes 0-59
int tm_hour; // hours 0-23
int tm_mday; // day of the month 1-31
int tm_mon; // month 0-11
int tm_year; // year minus 1900
int tm_wday; // day of the week 0-6
int tm_yday; // day in the year 0-365
int tm_isdst; // daylight saving time
};
*/
struct tm day;
time_t one_day = 86400;
// time_t interval = (one_day * adjustment) - 3600;
time_t interval = (one_day * adjustment);
strptime(original_date, pattern_in, &day);
day.tm_isdst = -1;
time_t t1 = mktime(&day);
if (t1 == -1) {
printf("The mktime() function failed");
return -1;
}
time_t t2 = t1 + interval;
struct tm *ptm = localtime(&t2);
if (ptm == NULL) {
printf("The localtime() function failed");
return -1;
}
strftime(adjusted_date, out_size, pattern_out, ptm);
return 0;
}
/*******************************************************************************
* \fn main()
*******************************************************************************/
int main()
{
char in_date[64] = "20190321000000" ,
out_date[64],
pattern_in[64] = "%Y%m%d%H%M%S",
pattern_out[64] = "%Y-%m-%d %H:%M:%S";
int day_diff = -1,
ret = 0;
size_t out_size = 64;
memset(out_date, 0, sizeof(out_date));
ret = adjust_date(in_date, out_date, pattern_in, pattern_out, day_diff, out_size);
if (ret == 0)
{
printf("Adjusted date: '%s'\n", out_date);
}
return ret;
}
Hopefully, this will be of some help to somebody. Your constructive comments are greatly appreciated.
handling date time is very error prone and usually badly tested. i always recommend using boost::date_time http://www.boost.org/doc/libs/1_52_0/doc/html/date_time.html
here are nice examples http://en.highscore.de/cpp/boost/datetime.html
Did you try this ? :
time_t rawtime;
struct tm * today;
time ( &rawtime );
today= localtime ( &rawtime );
puts(asctime (today));
Related
I tried to get the date of first Sunday in this year
int getFristSunday () {
time_t rawtime;
struct tm * timeinfo;
time( &rawtime );
timeinfo = localtime( &rawtime );
timeinfo->tm_mon = 0;
timeinfo->tm_wday = 0;
mktime( timeinfo );
return timeinfo->tm_yday ;
}
but I get the first Thursday
Result
From this mktime reference:
time->tm_wday and time->tm_yday are ignored.
You have to set timeinfo->tm_mday to 1 and then check what day it is after calling mktime, and count forward from there.
Using this free, open-source, header-only C++11/14 library:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << year_month_day{sun[1]/jan/2016} << '\n';
}
which outputs:
2016-01-03
There are year(), month() and day() accessors for the year_month_day object. And the algorithms are highly optimized (containing no iterative loops).
If you would prefer to write your own date computations, here are the public domain calendrical algorithms used in the aforementioned date library. The link goes straight to the section describing how to find the Nth day of the week of the month/year combination.
Before calling mktime() all fields, except tm_yday and tm_wday need to be set. Obviously we need to set tm_mon and tm_mday for Jan 1.
Important to set the tm_hour to midday (12) and/or tm_isdst to -1 to insure the recalculated time is not impacted by daylight saving time. Consider what would happen if the current time was near midnight and the DST setting for now was different than Jan 1. The re-calculation could push time out of Jan 1 to Jan 2 or Dec 31.
int getFirstSunday(void) {
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_mon = 0; // set to January which is 0 "Months since January"
timeinfo->tm_mday = 1; // Set to the 1st of the month
timeinfo->tm_hour = 12; // Set to avoid getting the wrong DST setting for Jan 1.
timeinfo->tm_isdst = -1; // Set to avoid getting the wrong DST setting for Jan 1.
if (mktime(timeinfo) == -1) return -1;
int DaysSinceSundayForJan1 = timeinfo->tm_wday; // days since Sunday — [0, 6]
int DaysAfterJan1toNextSunday = 7 - DaysSinceSundayForJan1;
int DaysAfterJan1toFirstSunday = DaysAfterJan1toNextSunday%7;
// Convert to "day of the month"
return DaysAfterJan1toFirstSunday + 1;
}
Does anyone know how todo math with ctime? I need to be able to get the time in sec in "time_t" (like it normally does) and then subtract a set number of seconds from it before inputting time_t into ctime to get the time and date.
so basically it would calculating the date of so many sec ago.
time_t
The most basic representation of a date and time is the type time_t. The value of a time_t variable is the number of seconds since January 1, 1970, sometimes call the Unix epoch. This is the best way to internally represent the start and end times for an event because it is easy to compare these values.
struct tm
While time_t represents a date and time as a single number, struct tm represents it as a struct with a lot of numbers:
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
};
Conversion
You can convert a time_t value to a struct tm value using the localtime function:
struct tm startTM;
time_t start;
/* ... */
startTM = *localtime(&start);
So,you can subtract subtract a set number of seconds like this
startTm.tm_sec -= somesecond;
add convert to time_t like this
struct tm startTM;
time_t start;
/* ... */
start = mktime(&startTM);
and use ctime fun to convert date
ctime(&start)
hope it can helpful!
You can try:
time_t now = time( NULL);
struct tm now_tm = *localtime( &now);
now_tm.tm_sec -= 50; // subtract 50 seconds to the time
now_tm.tm_sec +=1000; // add 1000 sec to the time
printf( "%s\n", asctime( &now_tm));
time_t is an integral type. It always represents a number of seconds, so you can freely add/subtract integers from it.
Example:
time_t now = time(nullptr);
time_t one_minute_ago = now - 60;
std::cout << ctime(&one_minute_ago) << std::endl;
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
I want to retrieve time zone from struct tm as the format below
2011-12-32 12:13:05 +0530(obtained using gtime)
I could get the first two sets but could not get the way getting the time zone value. Please let me know how it should get the time zone using c++ time.
Regards,
iSight
If you really want to use standard C library to get timezone, try using external variable 'timezone' declared in time.h. Keep in mind that its value is set after tzset() function call. Every time conversion function that depends on the timezone implicitly calls this function. As an alternative you can call tzset() explicitly.
The 'timezone' variable should be declared like this in time.h:
extern long timezone;
It contains time difference between local time and UTC in seconds.
Also you can use exern char* tzname[2] to get the symbolic timezone names for DST and non-DST periods.
You can not calculate the timezone information from struct tm directly, unless you know exactly the UTC time corresponding to time stored in that structure;
I think tm structure retrieved via time.h contains timezone information, if all you require is difference from GMT.
struct tm {
int tm_sec; /* seconds after the minute [0-60] */
int tm_min; /* minutes after the hour [0-59] */
int tm_hour; /* hours since midnight [0-23] */
int tm_mday; /* day of the month [1-31] */
int tm_mon; /* months since January [0-11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday [0-6] */
int tm_yday; /* days since January 1 [0-365] */
int tm_isdst; /* Daylight Savings Time flag */
long tm_gmtoff; /* offset from CUT in seconds */
char *tm_zone; /* timezone abbreviation */
};
Something like the following can help:
uint64_t diff;
{
time_t secs = time (NULL);
tm timeParts;
memset(&timeParts, 0, sizeof(timeParts));
tm *timeInfo = localtime_r( &secs, &timeParts );
diff = mktime( timeInfo );
memset(&timeParts, 0, sizeof(timeParts));
timeInfo = gmtime_r ( &secs, &timeParts );
diff -= mktime( timeInfo );
}
return diff;
Please note that this code does something else but it shows all the functions that you can use to retrieve information that you may require.
I'm having trouble with dates management in C++ (VS 2008).
According to MSDN specifications, time_t represents:
The number of seconds since January 1, 1970, 0:00 UTC
therefore, I've written this piece of code:
#include <stdio.h>
#include <time.h>
time_t GetDate(int year, int month, int day, int hour, int min, int sec)
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = gmtime ( &rawtime );
timeinfo->tm_year = year - 1900;
timeinfo->tm_mon = month - 1;
timeinfo->tm_mday = day;
timeinfo->tm_hour = hour;
timeinfo->tm_min = min;
timeinfo->tm_sec = sec;
timeinfo->tm_isdst = 0; // disable daylight saving time
time_t ret = mktime ( timeinfo );
return ret;
}
int main ()
{
time_t time_0 = GetDate(1970,1,1,0,0,0);
// time_0 == -1 !!!
time_t time_1 = GetDate(1970,1,1,1,0,0);
// time_1 == 0 !!!
return 0;
}
It seems to be shifted by 1 hour (i.e. zero time is January 1, 1970, 1:00 UTC).
Initially, I thought the problem could come from the DayLightSaving flag, but it doesn't change by changing it.
Am I doing something wrong ?
Thanks in advance
P.S.
In theory, I might not mind the zero time value, because it's only a reference time.
But I need to be sure about the value, because I'm porting the code to another language and I need to get exactly the same results.
EDIT:
here's the solution, thanks to Josh Kelley Answer
time_t mktimeUTC(struct tm* timeinfo)
{
// *** enter in UTC mode
char* oldTZ = getenv("TZ");
putenv("TZ=UTC");
_tzset();
// ***
time_t ret = mktime ( timeinfo );
// *** Restore previous TZ
if(oldTZ == NULL)
{
putenv("TZ=");
}
else
{
char buff[255];
sprintf(buff,"TZ=%s",oldTZ);
putenv(buff);
}
_tzset();
// ***
return ret;
}
mktime takes a struct tm giving a local time and returns the number of seconds since January 1, 1970, 0:00 UTC. Therefore, your GetDate(1970,1,1,0,0,0); call will return 0 if your local time zone is UTC but may return other values for other time zones.
Edit: For a UTC version of mktime or your GetDate, try the following (untested):
Call getenv to save the current value of the TZ environment variable (if any).
Call putenv to change the TZ environment variable to "UTC".
Call _tzset to make your changes active.
Call mktime.
Restore the old value of TZ, then call _tzset again.
Just a WAG but try the following:
timeinfo->tm_year = year - (unsigned long)1900;
timeinfo->tm_mon = month - (unsigned long)1;