Odd behavior of mktime() - c++

Continuing on my attempt to create a DateTime class , I am trying to store the "epoch" time in my function:
void DateTime::processComponents(int month, int day, int year,
int hour, int minute, int second) {
struct tm time;
time.tm_hour = hour;
time.tm_min = minute;
time.tm_sec = second;
time.tm_mday = day;
time.tm_mon = month;
time.tm_year = year - 1900;
ticks_ = mktime(&time);
processTm(time);
}
void DateTime::processTm(struct tm time) {
second_ = time.tm_sec;
minute_ = time.tm_min;
hour_ = time.tm_hour;
weekday_ = time.tm_wday;
monthday_ = time.tm_mday;
yearday_ = time.tm_yday;
month_ = time.tm_mon;
year_ = time.tm_year + 1900;
}
For an arbitrary date, processComponents(5,5,1990,1,23,45) (June 6, 1990 1:23:45 am), it sets all values correctly and as expected.
However, upon further testing, I find that for processComponents(0,0,1970,0,0,0) (January 1, 1970, 12:00:00 am), mktime(&time) causes time to be screwed up:
time.tm_mon = 11;
time.tm_mday = 30;
time.tm_year = 69;
time.tm_hour = 23;
time.tm_min = 0;
time.tm_sec = 0;
time.tm_isdst = 0;
time.tm_gmtoff = -18000;
time.tm_zone = "EST";
time.tm_wday = 2;
time.tm_yday = 363;
Translating to a date of December 31, 1969 11:00:00 pm.
I can verify that mktime() is responsible, because by commenting out that line, it reports the date and time correctly as January 1, 1970 12:00:00 am.
Why is mktime() only messing up the epoch? And how should I fix / workaround this?
Thanks!

You're passing 0 as the day parameter and putting that into time.tm_mday. That component (and only that component) of struct tm is 1-based, not 0-based.
Don't ask me why.
To specify 01 Jan 1970, 12:00:00am you'd want to call it like so:
processComponents(0,1,1970,0,0,0);
And as sdtom mentioned, you'll want to make sure that tm_isdst is set appropriately - 0 for not in effect, positive for in effect, and negative for you don't know (in which case mktime() should try to guess).
Just to let you know, when I pass the date you have (0 Jan 1970, 00:00:00) to mktime() in MSVC 9 it returns an error (the passed in struct tm is untouched and the returned time_t value is -1).

Since it is off by one hour I would expect daylight savings time. Is the value of time.tm_isdst getting set somewhere? If you aren't setting it, it could be randomly getting set to 1 or 0 which would affect your results.

Passing all zeros to mktime() is interpreted as "Sun Jan 0 00:00:00 1900". Based on this, there needs to be some adjustments...
// the input is local time
// the output is seconds since the epoch
// The epoch is Jan 1, 1970 # 0:00 GMT
time_t mktime_wrapper( int month, int day, int year,
int hour=0, int min=0, int sec=0, bool isDST=-1
)
{
tm t;
t.tm_sec=sec, t.tm_min=min, t.tm_hour=hour, t.tm_isdst=isDST;
t.tm_mday=day, t.tm_mon=month-1, t.tm_year=year-1900;
return mktime( &t );
}

Related

Solution doesn't work for number of days between two dates

I know this question has been asked a few times, and I'm asking again because I've got issues with existing solutions on SO.
My goal is to find number of days between 1900-01-01 and a given date. The date will be in the format as yyyy-mm-dd and the type is std::string.
The solution I've followed is https://stackoverflow.com/a/14219008/2633803
And below is my version:
std::string numberOfDaysSince1900v2(std::string aDate)
{
string year, month, day;
year = aDate.substr(0, 4);
month = aDate.substr(5, 2);
day = aDate.substr(8, 2);
struct std::tm a = { 0,0,0,1,1,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month),std::stoi(year) - 1900 };
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
double difference;
if (x != (std::time_t)(-1) && y != (std::time_t)(-1))
{
difference = std::difftime(y, x) / (60 * 60 * 24) + 36526; //36526 is number of days between 1900-01-01 and 2000-01-01
}
return std::to_string(difference);
}
It worked fine until the given date comes to 2019-01-29 and 2019-02-01. In both cases, the output is 43494. And for the whole Feb, the output is 3 days less than expected. Then, when it comes to March 2019, the output is back to normal again.
Another case is 2019-09-03, the output is 43710, whilst the expected output is 43711.
Why would this happen to these specific dates? I ran the solution step by step and closely watched the variables in the memory but couldn't explain it.
Any suggestion is appreciated. Thanks.
The month should be represented as an integer between 0 and 11, not 1 and 12.
So
struct std::tm a = { 0,0,0,1,0,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month)-1,std::stoi(year) - 1900 };
I would say there are other problems with your code. You cannot reliably initialise a tm like that (the order of fields within the struct is not guaranteed). Neither does difftime necessarily return a number of seconds (which you are assuming).

Locale dependent Week of Year in C/C++

I am trying to get Week of Year. For this I am using tm * __CRTDECL localtime(const time_t * _Time) but I am not getting desired result which should be locale dependent. So I was looking for solution and some more information. I have found that there is API in JAVA Calendar.getInstance(Locale.GERMAN);.So I am just wondering whether there is such API in c/c++ as I did not find any ( not sure ). If not can anybody give me some time pointers to get Week of Year locale dependant.
The code I am using
int MyClass::getCalendarWeek(time_t time, int * p_year)
{
// Get tm structure of time parameter
tm* pCurrentTm = localtime(&time);
// determine Thursday in that week
LONG offSet = 4 - pCurrentTm->tm_wday;
if (offSet >= 4) {
offSet = -3; // Sunday
}
time += offSet * 86400L;
pCurrentTm = localtime(&time);
if (p_year) {
*p_year = pCurrentTm->tm_year + 1900;// year of current calendar week
}
return (pCurrentTm->tm_yday + 7) / 7; // current calendar week
}
Output:
11 July is in week 28 when running most locales that have monday as first day of the week, but it is week 29 for sunday as first day of week.

Date formatting - adding days to date

I have date in this format for example :
12.2.2015
I have to add days to that date. For example i have to add to that date + 1000 days so the output will not be 12 . 2 . 2015 but 14 . 3 . 2018 ( it's not accurate i did not calculate it). Is there any effective algorithm for this problem or do i have to loop it and make lots of conditions about leap year etc?
So let's say you have
int year = 2015;
int month = 2;
int day = 12;
int offset = 1000;
You can utilize <ctime> functions like mktime and localtime and belief that every day has 86400 seconds to do what you need. Something like this.
#include <ctime>
struct tm orig_date;
orig_date->tm_sec = 0;
orig_date->tm_min = 0;
orig_date->tm_hour = 12; /* midday, so we make space for non-86400 errors */
orig_date->tm_mday = day;
orig_date->tm_mon = month - 1; /* uses 0-11 range */
orig_date->tm_year = year;
orig_date->tm_isdst = 0;
time_t utc = mktime(&orig_date);
utc += (offset * 86400);
struct tm *new_date;
new_date = localtime(&utc);
printf("Old date: %d. %d. %d\n", orig_date.tm_mday, orig_date.tm_mon, orig_date.tm_year);
printf("New date: %d. %d. %d\n", new_date->tm_mday, new_date->tm_mon, new_date->tm_year);
The code will produce following (which I checked to be correct):
Old date: 12. 1. 2015
New date: 8. 10. 2017

hour, minutes,seconds to Time_t

I know the Current system time.
I know the estimated time of arrival of a place in the form of hours minutes and seconds.
I need to find the duration of travel. But the estimated time of arrival is in 12 hour format.
I have to write a program to find the time difference between these two ?
I thought of using difftime(time1,time2)
but this requires the datatype time_t. I know the time in parts. i.e. i know the hours, minutes and seconds separatley. Both current system time and Estimated time of arrival.
I need to find the time difference between the two. The ETA can be after 24 hours. then is there any way i can find out the number of days of travel. Because after 12PM time is set back. hence i'm not able to keep track of the days.
Any solution ?
I work on C++
A straight forward way using C/C++. This is not very robust, but should meet your given requirements.
#include <ctime>
tm source;
memset(&source, 0, sizeof(tm));
tm.tm_hour = hour; // 24 hour format, 0 = midnight, 23 = 11pm
tm.tm_min = min;
tm.tm_sec = sec;
tm.tm_mon = month; // 0 based, 0 = jan, 11 = dec
tm.tm_mday = 10;
tm.tm.year = year; // current - 1900
time_t src_t = mktime(&source);
time_t now = time(NULL);

C++ How can I convert a date in year-month-day format to a unix epoch format?

I need to convert a given date to an int containing the number of milliseconds since Jan 1 1970. (unix epoch)
I tried the following code:
tm lDate;
lDate.tm_sec = 0;
lDate.tm_min = 0;
lDate.tm_hour = 0;
lDate.tm_mday = 1;
lDate.tm_mon = 10;
lDate.tm_year = 2010 - 1900;
time_t lTimeEpoch = mktime(&lDate);
cout << "Epoch: " << lTimeEpoch << endl;
The result is Epoch: 1288584000 which corresponds to Mon, 01 Nov 2010 04:00:00 GMT
Edit: I was expecting Oct 01 2010, apparently tm_mon is the number of months SINCE January,
so the correct line would be lDate.tm_mon = 10 -1;
As specified in the man page, tm_mon is:
The number of months since January, in the range 0 to 11.
You are likely getting confused by timezones. I think you're missing this, from the man page:
The mktime() function converts a broken-down time structure, expressed as local time...