简体   繁体   中英

Converting tm to time_t on windows in UTC - _mkgmtime returning -1

I am trying to parse UTC ECMA date format strings into a std::chrono::system_clock::time_point on Windows.

My plan:

  1. Use scanf to load the string into a struct tm
  2. Use _mkgmtime to turn that into a time_t (I am using _mkgmtime for this, because it uses UTC time, unlike something like mktime , which uses local time).
  3. Use std::chrono::system_clock::from_time_t to convert to a time_point

However, step 2 is always returning -1 for me, giving me bogus values for step 3.

The tm struct seems valid, so I'm not sure what I'm doing wrong.

#include <chrono>
#include <iostream>

using namespace std;

int main() {
  string timeStr = "2020-12-01T06:06:48Z";

  struct tm tm = {0};
  float s;
  if (sscanf_s(timeStr.c_str(), "%d-%d-%dT%d:%d:%fZ", &tm.tm_year, &tm.tm_mon,
               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &s) == 6) {
    tm.tm_sec = static_cast<int>(s);

    time_t t = _mkgmtime(&tm);
    cout << "YEAR: " << tm.tm_year << endl;  // 2020
    cout << "MON:  " << tm.tm_mon << endl;   // 12
    cout << "MDAY: " << tm.tm_mday << endl;  // 1
    cout << "HOUR: " << tm.tm_hour << endl;  // 6
    cout << "MIN:  " << tm.tm_min << endl;   // 6
    cout << "SEC:  " << tm.tm_sec << endl;   // 48
    cout << "TIME: " << t << endl;           // -1

    // TODO once this is solved:
    auto tp = chrono::system_clock::from_time_t(t);
  }
}

Compiler is MSVC 2019, C++17, x64. I cannot upgrade to C++20, or else I would use the new chrono tools for this.

In a tm structure the member tm_year is the number of years since 1900 . Feeding in 2020 means you're actually providing year 3920.

It looks like _mkgmtime hates that year. On my system _mkgmtime64 doesn't appear to work either. It should fit in a 64 bit time_t , but definitely out of range for the 32 bit version. I can't find a clear-cut, documented reason for 3920 to be rejected or a cut-off point after which the function gives up. Make sure you have a 64 bit time_t .

But none of that matters in this case since we want the year 2020. Adding

tm.tm_year -= 1900;
tm.tm_mon -= 1; // valid range 0 to 11, not 1 to 12

before converting will fix things up.

Here is how it looks in C++20:

#include <chrono>
#include <iostream>
#include <sstream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    string timeStr = "2020-12-01T06:06:48Z";
    istringstream in{timeStr};
    system_clock::time_point tp;
    in >> parse("%FT%TZ", tp);
    cout << tp << '\n';  // 2020-12-01 06:06:48.000000
}

Using the C++20 <chrono> preview library , just add #include "date/date.h" and using namespace date; and it now works with C++11/14/17.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM