I have the following code:
#include <iostream>
#include <ctime>
using namespace std;
int main() {
tm _tm;
strptime("2017-04-17", "%Y-%m-%d", &_tm);
char buf[16];
strftime(buf, sizeof(buf), "%A", &_tm);
cout << buf << endl;
}
On Ideone, it correctly outputs "Monday" (today's day of the week). When I compile and run the same code on iOS, it returns "Sunday". What gives?!
Edit: For all those people who can't understand how this is also a C question, here's the C code. The question still stands:
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm _tm;
strptime("2017-04-17", "%Y-%m-%d", &_tm);
char buf[16];
strftime(buf, sizeof(buf), "%A", &_tm);
printf(buf);
}
There were two problems leading to the behaviour in the question.
strptime
seems to unexpectedly take local timezone into effect when parsing a date (but only for setting the day of the week!). It seems like when it parses "2017-04-17", at least for the day of the week, it's treating it as something like midnight UTC -- meaning in a negative UTC offset the day of the week at that "time" is one day earlier.
Basically strptime
was always giving me tm_wday
of one less than the correct value. It would return 0
(Sunday) for tm_wday
when parsing "2017-04-17" (a Monday) and 1
(Monday) for "2017-04-18" (a Tuesday).
What's very strange is that it wouldn't fill in other timezone data like tm_isdst
or tm_gmtoff
. It would leave those unchanged -- just choose a bad value for tm_wday
.
I was able to fix this by calling mktime
on the generated tm
struct returned from strptime
. I didn't use the time_t
returned from mktime
, but the act of calling mktime
on the tm
struct properly set the value of tm_wday
, in addition to correctly setting values for tm_isdst
, tm_gmtoff
, and so on.
I hadn't zero-initialized the tm
struct. This only really came into play once I started calling mktime
. Without proper initialization, mktime
mangles the date because of garbage values for tm_gmtoff
. After the proper initialization, calling mktime
gave a well-formed date with the correct tm_wday
set.
So a working example would be something like this:
tm _tm = {};
strptime("2017-04-17", "%Y-%m-%d", &_tm); // Incorrect tm_wday after this call
mktime(&_tm); // Correct tm_wday after this call
char buf[16];
strftime(buf, sizeof(buf), "%A", &_tm);