Search code examples
c++linuxtimestamputclocaltime

UTC Timestamp comparison


I'm working on a client-server custom application (to be run on linux), and one of the frames I send include a timestamp (ie the time at which the frame is send).

In order to make my application reliable, I used gmtime to produce the time on the client. I'm in Belgium, so right now the hour of the client VM is 2 hours later than the UTC time (because of the summer hour).

In the server side, I first convert the recieved string to a time_t type. I do this to use the difftime function to see if the timestamp is not too old. Then, I generate a timestamp (in UTC time) again with gmtime, and I convert it to a time_t.

I compare then the two time_t to see the time difference.

I have got a problem with the conversion of the time at the server side. I use the same code as in the client, but the outputted gmtime is different...


Client Side : function to generate the timestamp, and export it to a string (time_str):

    std::string getTime()
    {
    time_t rawtime;
    struct tm * timeinfo;
    char buffer[80];

    time (&rawtime);              // Get time of the system
    timeinfo = gmtime(&rawtime);  // Convert it to UTC time

    strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
    std::string time_str(buffer); // Cast it into a string
    cout<<"Time Stamp now (client) : "<<time_str<<endl;

    return time_str;
    }

And it produices this (at 9h33 local time) :

Time Stamp now : 06-04-2016 07:33:30


Server Side : fucntion to retrieve the timestamp, generate the newx time stamp, and compare them :

bool checkTimeStamp(std::string TimsStamp_str, double delay)
{
    cout<<"TimeStamp recieved: "<<TimsStamp_str<<endl;
    /* Construct tm from string */
    struct tm TimeStampRecu;
    strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
    time_t t_old = mktime(&TimeStampRecu);

    /* Generate New TimeStamp */
    time_t rawtime;
    struct tm * timeinfo;
    time (&rawtime);  // Get time of the system
    timeinfo = gmtime(&rawtime);  // convert it to UTC time_t
    time_t t2 = mktime(timeinfo);  // Re-Cast it to timt_t struct
    /* Convert it into string (for output) */
        char buffer[80];
        strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
        std::string time_str(buffer); // Cast it into a string
        cout<<"Time Stamp now (server) : "<<time_str<<endl;

    /* Comparison */
    double diffSecs = difftime(t2, t_old);
    cout<<diffSecs<<endl;
    bool isTimeStampOK;
    if (diffSecs < delay)
        isTimeStampOK = true;
    else
        isTimeStampOK = false;

return isTimeStampOK;
}

And it produces this (at 9h33 in Belgium) :

TimeStamp recieved : 06-04-2016 07:33:30

Time Stamp now (server) : 06-04-2016 08:33:31


Why is the Server Time (8h33) neither in localtime (9h33), neither in UTC time (7h33) ?

Have I made a mistake in its generation ? I don't understand where, because these are the exact same code as in the client side...


Solution

  • There's a couple of errors in your code, some your fault, some not. The biggest problem here is that the C <time.h> API is so poor, confusing, incomplete and error prone that errors like this are very nearly mandatory. More on that later.

    The first problem is this line:

    struct tm TimeStampRecu;
    

    It creates an uninitialized tm and then passes that into strptime. strptime may not fill in all the fields of TimeStampRecu. You should zero-initialize TimeStampRecu like this:

    struct tm TimeStampRecu{};
    

    Next problem:

    strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
    

    The 12-hour time denoted by %I is ambiguous without an AM/PM specifier. I suspect this is just a type-o as it is the only place you use it.

    Next problem:

    gmtime  time_t -> tm  UTC to UTC
    mktime  tm -> time_t  local to UTC
    

    That is, mtkime interprets the input tm according to the local time of the computer it is running on. What you need instead is:

    timegm  tm -> time_t  UTC to UTC
    

    Unfortunately timegm isn't standard C (or C++). Fortunately it probably exists anyway on your system.

    With these changes, I think your code will run as expected.


    If you are using C++11, there is a safer date/time library to do this here (free/open-source):

    https://github.com/HowardHinnant/date

    Here is your code translated to use this higher-level library:

    std::string getTime()
    {
        using namespace std::chrono;
        auto rawtime = time_point_cast<seconds>(system_clock::now());
        auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
        std::cout<<"Time Stamp now (client) : "<<time_str<< '\n';
    
        return time_str;
    }
    
    bool checkTimeStamp(std::string TimsStamp_str, std::chrono::seconds delay)
    {
        using namespace std::chrono;
        std::cout<<"TimeStamp recieved:       "<<TimsStamp_str<< '\n';
        /* Construct tm from string */
        std::istringstream in{TimsStamp_str};
        time_point<system_clock, seconds> t_old;
        date::parse(in, "%d-%m-%Y %H:%M:%S", t_old);
    
        /* Generate New TimeStamp */
        auto rawtime = time_point_cast<seconds>(system_clock::now());
        auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
        in.str(time_str);
        time_point<system_clock, seconds> t2;
        date::parse(in, "%d-%m-%Y %H:%M:%S", t2);
        cout<<"Time Stamp now (server) : "<<time_str<<endl;
    
        /* Comparison */
        auto diffSecs = t2 - t_old;
        cout<<diffSecs.count()<<endl;
        bool isTimeStampOK;
        if (diffSecs < delay)
            isTimeStampOK = true;
        else
            isTimeStampOK = false;
    
        return isTimeStampOK;
    }