Search code examples
c++ctime

strftime returns incorrect day of the week on iOS


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);
}

Solution

  • There were two problems leading to the behaviour in the question.

    1. 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.

    2. 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);