Same function outputs different results when calculating difference between two timestamps

221 Views Asked by At

I'm currently facing a weird issue where the same function outputs a different result. The function is supposed to calculate the time difference between a provided date and the current time. Since this function is supposed to work with milliseconds, my function currently looks like this:

int calcDelay(std::string dropTime) {

    struct tm tm;
    std::istringstream iss(dropTime);
    iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
    time_t time = mktime(&tm);

    SYSTEMTIME t;
    GetSystemTime(&t);
    struct tm tm1;
    memset(&tm1, 0, sizeof(tm1));
    tm1.tm_year = t.wYear - 1900;
    tm1.tm_mon = t.wMonth - 1;
    tm1.tm_mday = t.wDay;
    tm1.tm_hour = t.wHour - 1;
    tm1.tm_min = t.wMinute;
    tm1.tm_sec = t.wSecond;
    time_t time2 = mktime(&tm1);

    //std::cout << "Input:" << dropTime << " Output:" << (int)(difftime(time, time2) * 1000) - t.wMilliseconds << std::endl;
    int retVal = (int)(difftime(time, time2) * 1000) - t.wMilliseconds;
    return retVal;
}

The provided date (dropTime) is in UTC/GMT and the WinAPI function GetSystemTime should also return the time in UTC.

I have two different threads that call this function. When the first thread calls this function, it returns the correct time difference. However, when my other thread calls this function with the exactly same input it returns a value that is exactly 3600000 ms larger - this equals the time of exactly one hour.

What's the cause of this bug?

Edit: It seems that the bug is caused by the get_time function. Even though the same string (2021-05-25T21:03:04) is used to parse the time, it sometimes adds a hour and sometimes it doesn't... Could it be that the get_time function simply cannot be used across multiple threads?

I appreciate all help.

3

There are 3 best solutions below

1
On BEST ANSWER

In C++20 your calcDelay can be greatly simplified. And there exists a preview of this functionality in a free, open-source, header-only library1 which works with C++11/14/17.

#include "date/date.h"
#include <chrono>
#include <sstream>

int calcDelay(std::string dropTime) {

    using std::chrono::milliseconds;
    date::sys_time<milliseconds> time;
    std::istringstream iss(dropTime);
    iss >> date::parse("%Y-%m-%dT%H:%M:%S", time);

    auto time2 = date::floor<milliseconds>(std::chrono::system_clock::now());
    return (time - time2).count();
}

As you state in your question, the input is UTC, and the current time is UTC. Time zones are not involved. And unlike the "C version", this version optionally supports millisecond-precision input:

std::cout << calcDelay("2021-05-26T00:41:01.568") << '\n';

Output:

12456

To port the above calcDelay to C++20:

  • Drop #include "date/date.h"
  • Change date:: to std::chrono:: (3 places)

You can also (optionally) simplify the parse string from "%Y-%m-%dT%H:%M:%S" to "%FT%T".

Also optional, you could increase type safety in the client code by returning std::chrono::milliseconds instead of int.


1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get upset if I don't fully disclose this information.

0
On

According to std::get_time API, The I/O manipulator std::get_time uses the std::time_get facet of the I/O stream's locale to convert text input to a std::tm object. And maybe all of your threads which are in the same process have the same native locale which is default behavior. So GetSystemTime(&t); has no problem.
The follwing code is API’s example:

#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
 
int main()
{
    std::tm t = {};
    std::istringstream ss("2011-Februar-18 23:12:34");
    ss.imbue(std::locale("de_DE.utf-8"));
    ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
    if (ss.fail()) {
        std::cout << "Parse failed\n";
    } else {
        std::cout << std::put_time(&t, "%c") << '\n';
    }
}
0
On

t.wHour - 1 is incorrect. Both the tm and SYSTEMTIME structures use hours from 0...23.