Search code examples
c++timezonec++20c++-chrono

Convert tm of another timezone into tm of the GMT timezone


I'm using chrono and c++20.

I have a tm struct for EST timezone, but can't figure out how to obtain the corresponding time in the GMT zone.

Here is what I've been thinking:

tm timeInAmerica = {0};
timeInAmerica.tm_year = 75;//1975
timeInAmerica.tm_month = 0;//January
timeInAmerica.tm_mday = 31;
timeInAmerica.tm_hour = 23;
timeInAmerica.tm_minute = 23;
timeInAmerica.tm_second = 23;

auto timeZone = std::chrono::locate_zone("America/New_York");
auto sysTime = timeZone->to_sys( /*needs local_time */ );

...I don't know how to convert that tm into local_time so that I can feed it into to_sys().

I also can't figure out how to convert the returned sysTime value back into tm (which will allow me to inspect the GMT year, month, day, hour, minute).


Solution

  • using namespace std::chrono;
    
    tm timeInAmerica = {0};
    timeInAmerica.tm_year = 75;//1975
    timeInAmerica.tm_mon = 0;//January
    timeInAmerica.tm_mday = 31;
    timeInAmerica.tm_hour = 23;
    timeInAmerica.tm_min = 23;
    timeInAmerica.tm_sec = 23;
    
    auto lt = local_days{year{timeInAmerica.tm_year+1900}
                        /month(timeInAmerica.tm_mon+1)
                        /timeInAmerica.tm_mday}
              + hours{timeInAmerica.tm_hour}
              + minutes{timeInAmerica.tm_min}
              + seconds{timeInAmerica.tm_sec};
    
    auto timeZone = locate_zone("America/New_York");
    auto sysTime = timeZone->to_sys(lt);
    
    auto sysDay = floor<days>(sysTime);
    year_month_day ymd = sysDay;
    hh_mm_ss hms{sysTime - sysDay};
    
    int y = int{ymd.year()};
    int m = unsigned{ymd.month()};
    int d = unsigned{ymd.day()};
    int h = hms.hours().count();
    int M = hms.minutes().count();
    int s = hms.seconds().count();
    

    I've issued a using namespace std::chrono just to keep the verbosity down to a low roar. If you would prefer to put std::chrono:: in all the right places, that's fine too.

    lt is the local_time<seconds> (or just local_seconds) needed to represent the local time. Careful on the biases (1900 and 1) when converting from a tm.

    To convert sysTime back into a {year, month, day, hour, minute, second} structure, first truncate sysTime to a days-precision time_point. Then that days-precision time_point can be converted to a {year, month, day} data structure.

    The time of day is simply the date_time minus the date. This can be converted into an {hours, minutes, seconds} data structure: hh_mm_ss.

    Both year_month_day and hh_mm_ss have getters to get the strongly typed fields. And then each strongly typed field has conversions to integral as shown above. When converting back to a tm, don't forget about the biases (1900 and 1).

    Additionally, everything has a streaming operator. This makes it very convenient for debugging:

    cout << "lt      = " << lt << '\n';       // 1975-01-31 23:23:23
    cout << "sysTime = " << sysTime << '\n';  // 1975-02-01 04:23:23
    cout << "sysDay  = " << sysDay << '\n';   // 1975-02-01
    cout << "ymd     = " << ymd << '\n';      // 1975-02-01
    cout << "hms     = " << hms << '\n';      // 04:23:23