Search code examples
c++datetimeepoch

I'm having a hard time getting date/time components from day of year and seconds since midnight


In my data stream, I have seconds since midnight and days since January 1st...I will be manually specifying a year, so I need to be able to convert those three values to a correct date/time to output to another program. Here's my code:

int currentDay = XPLMGetDatai(gLocalDate); // days since Jan 1st
float currentTime = XPLMGetDataf(gZuluTime); // seconds since midnight
int currentYear = 2015;

struct tm t;
struct tm * ct;
time_t t_of_day;

t.tm_year = currentYear - 1900;
t.tm_yday = currentDay;
t.tm_hour = (int)(currentTime / 3600);
t.tm_min = (int)(currentTime - (t.tm_hour * 3600)) / 60;
t.tm_sec = (int)currentTime - ((t.tm_hour * 3600) + (t.tm_min * 60));

t_of_day = mktime(&t); // should convert t into a valid time_t
ct = gmtime(&t_of_day); // should give me a valid UTC from t

// Send data to Celestial Control
CCelC.SetDay(ct->tm_mday);
CCelC.SetHour(ct->tm_hour);
CCelC.SetMinute(ct->tm_min);
CCelC.SetMonth(ct->tm_mon);
CCelC.SetYear(currentYear);

The problem I seem to be having is the fact that currentDay being plugged into tm_yday is getting obliterated when the mktime(&t) gets called. So I end up with a ct->tm_mon of 0, which is incorrect in my test run of currentday 90 (April 1).

So given any year, any seconds since midnight, and any days since jan 1, how can I generate correct day(1-31), hour(0-23), min(0-59), mon(1-12), year values?


Solution

  • You can't use mktime to do this in the way you want to. From these docs

    The values of the members tm_wday and tm_yday of timeptr are ignored

    .....

    A call to this function automatically adjusts the values of the members of timeptr if they are off-range or -in the case of tm_wday and tm_yday- if they have values that do not match the date described by the other members."

    However you can take advantage of this behavior to populate the other fields correctly. If you instead set up the fields of the struct tm like so

    struct tm t = {0};
    
    t.tm_mday = currentDay + 1;
    t.tm_year = currentYear - 1900;
    
    t.tm_sec = currentTime;
    
    mktime(&t);
    

    This method works because of the automatically adjusting behavior of mktime

    As a fully working example

    #include <ctime>
    #include <iostream>
    
    int main()
    {
        int currentDay = 90;
        int currentTime = (12*3600 + 52*60 + 31);
        int currentYear = 2015;
    
        struct tm t = {0};
    
        t.tm_mday = currentDay + 1;
        t.tm_year = currentYear - 1900;
    
        t.tm_sec = currentTime;
    
        // mktime will now correctly populate
        // tm_sec, tm_min, tm_hour
        // tm_day, tm_mon, tm_year
        mktime(&t);
    
        // Print the time to make sure!
        std::cout << asctime(&t);
    }
    

    Will output

    Wed Apr  1 12:52:31 2015