I have a time represented as the number of seconds elapsed since midnight, January 1, 1970, UTC (the results of an earlier call to time()). How do I add one day to this time?
Adding 24 * 60 * 60 works in most cases, but fails if the daylight saving time comes on or off in between. In other words, I mostly want to add 24 hours, but sometimes 23 or 25 hours.
To illustrate - the program:
#include <time.h>
#include <iostream>
int main()
{
time_t base = 1142085600;
for(int i = 0; i < 4; ++i) {
time_t time = base + i * 24 * 60 * 60;
std::cout << ctime(&time);
}
return 0;
}
Produces:
Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006
I want the times for March 12, 13, ... to also be 8 AM.
The answer provided by FigBug pointed me in the right direction. But I had to use localtime instead of gmtime.
int main()
{
time_t base = 1142085600;
for(int i = 0; i < 4; ++i) {
struct tm* tm = localtime(&base);
tm->tm_mday += i;
std::cout << asctime(tm);
}
return 0;
}
Give me:
Sat Mar 11 08:00:00 2006
Sat Mar 12 08:00:00 2006
Sat Mar 13 08:00:00 2006
Sat Mar 14 08:00:00 2006
Which is what I want. Using gmtime gives me the times at 14:00:00
However, note that all days are Sat. Also, it goes to March 32, 33, etc. If I throw in the mktime function I am back where I started:
#include <time.h>
#include <iostream>
int main()
{
time_t base = 1142085600;
for(int i = 0; i < 4; ++i) {
struct tm* tm = localtime(&base);
tm->tm_mday += i;
time_t time = mktime(tm);
std::cout << asctime(tm);
}
return 0;
}
Gives me:
Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006
What am I missing???
OK, I have tried out FigBug's latest suggestion that is to use:
std::cout << ctime(&time);
instead of asctime, but I get the same results. So I guess that my library and/or compiler is messed up. I am using g++ 3.4.4 on cygwin. I copied the files over to Solaris 5.8 and used g++ 3.3 there to compile. I get the correct results there! In fact I get the correct results whether I use ctime or asctime for output:
Sat Mar 11 08:00:00 2006
Sun Mar 12 08:00:00 2006
Mon Mar 13 08:00:00 2006
Tue Mar 14 08:00:00 2006
I also get the correct results (with both output functions) on Red Hut Linux with g++ 3.4.6.
So I guess that I have come across a Cygwin bug.
Thank you for all your help and advice....
-
I always had the best result with keeping the timestamps UTC and convert them to the specified timezone (including daylight saving) when you want to display the values.
This saves a lot of hassle like this (and makes your program independent of time zones.
Andrew Stein : The timestamps are UTC. -
use gmtime() to convert the time_t to a struct tm
add one to the day (tm_mday)
use mktime() to convert the struct tm back to a time_t
see time.h for more info
Edit:
I just tried it, this works:
int main() { time_t base = 1142085600; for(int i = 0; i < 4; ++i) { struct tm* tm = localtime(&base); tm->tm_mday += i; time_t next = mktime(tm); std::cout << ctime(&next); } return 0; }
FigBug : Can words with an underscore not be in italics?Jimmy : use some_word_with_underscores instead of *word*Jonathan Leffler : Or put a backslash in front of the underscore.Jonathan Leffler : Doesn't using gmtime() instead of localtime() mean that you ignore the very problem the question asks about? In effect, you are always going to add 86400, and never 90000 or 82800?Jonathan Leffler : Corollary: the algorithm using localtime() in place of gmtime() should work - if localtime() is sane.Andrew Stein : loacltime gives me what I want. I have posted this separately. Thanks...FigBug : Whoops, yes localtime() is correct.Andrew Stein : Just noticed that I don't get what I want. I have edited the posting accordingly. -
Just add 24*60*60. It shouldn't fail during DST, since UTC won't ever use DST.
If it is failing, then you are not using UTC somewhere in your code. Remove the timezone dependence.
Andrew Stein : The point is that adding one day to say 3/31/2008 8:00:00 AM local time should give me 4/1/2008 8:00:00 AM local time even if DST changes locally overnight on 3/31.Pyrolistical : I don't understand, are you using unix timestamp in UTC or not? Unix time stamp has nothing to do with DST.Mark Ransom : The conversion from UTC to local time is where the DST hour should be added or subtracted - the UTC shouldn't be affected, as Pyrolistical says.Andrew Stein : I have added an example showing what I mean (local for me is US Central time zone),Pyrolistical : Then do what FigBug suggests below, but use localtime() insteadAndrew Stein : Just posted that... ThanksAndrew Stein : Just noticed that I don't get what I want. I have edited the posting accordingly. -
FigBug's solution will work almost every time, but it needs DST fix: tm->tm_isdst = -1
A positive or 0 value for tm_isdst causes mktime() to presume initially that Daylight Savings Time, respectively, is or is not in effect for the specified time. A negative value for tm_isdst causes mktime() to attempt to determine whether Daylight Saving Time is in effect for the specified time.
(quoted from mktime spec)
int main() { time_t base = 1142085600; for(int i = 0; i < 4; ++i) { struct tm* tm = localtime(&base); tm->tm_mday += i; tm->tm_isdst = -1; // don't know if DST is in effect, please determine // this for me time_t next = mktime(tm); std::cout << ctime(&next); } return 0; }
Otherwise there will be a bug (example for Moscow Daylight Saving Time which starts 29 March 2009 01:59:59):
int main() { // 28 March 2009 05:00:00 GMT ( local - 08:00 (MSK) ) time_t base = 1238216400; std::time_t start_date_t = base; std::time_t end_date_t = base; std::tm start_date = *std::localtime(&start_date_t); std::tm end_date = *std::localtime(&end_date_t); end_date.tm_mday += 1; // end_date.tm_isdst = -1; std::time_t b = mktime(&start_date); std::time_t e = mktime(&end_date); std::string start_date_str(ctime(&b)); std::string stop_date_str(ctime(&e)); cout << " begin (MSK) (DST is not active): " << start_date_str; cout << " end (MSD) (DST is active): " << stop_date_str; }
Output:
begin (MSK) (DST is not active): Sat Mar 28 08:00:00 2009 end (MSD) (DST is active): Sun Mar 29 09:00:00 2009
0 comments:
Post a Comment