HowardHinnant / date

A date and time library based on the C++11/14/17 <chrono> header
Other
3.14k stars 677 forks source link

system_clock::now returns a wrong value #721

Closed YvesZHI closed 2 years ago

YvesZHI commented 2 years ago

https://stackoverflow.com/questions/70577774/why-does-the-function-about-getting-current-time-return-a-wrong-time-point

I just got a very weird bug.

As mentioned in the link, it seems that system_clock::now() gave me a time point of 1200 days ago. But I have no idea why.

My machine

OS:  Ubuntu 16.04.4 LTS.
lscpu:
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                32
On-line CPU(s) list:   0-31
Thread(s) per core:    2
Core(s) per socket:    8
Socket(s):             2
NUMA node(s):          2
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 85
Model name:            Intel(R) Xeon(R) Silver 4110 CPU @ 2.10GHz
Stepping:              4
CPU MHz:               2100.000
BogoMIPS:              4201.35
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              1024K
L3 cache:              11264K

What I've done I defined some functions as below:

template <typename T = std::chrono::milliseconds>
using Clock = std::chrono::time_point<std::chrono::system_clock, T>;

// get current time point
template <typename T = std::chrono::milliseconds>
inline Clock<T> getCurrentTimePoint(int8_t timeZone = 0) {
    return std::chrono::time_point_cast<T>(std::chrono::system_clock::now()) +
        std::chrono::hours {timezone};
}

std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> getCurrentTimePoint1(int8_t timeZone = 0) {
    return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()) + std::chrono::hours {timeZone};
}

std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> getCurrentTimePoint2(int8_t timeZone = 0) { // OK
    return std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()) + std::chrono::hours {timeZone};
}

After testing, I got this:

  1. the return value of time(nullptr) is always correct;
  2. the return value of std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()); is always correct;
  3. the return value of getCurrentTimePoint() is always wrong;
  4. the return value of getCurrentTimePoint2() is always correct;
  5. the return value of getCurrentTimePoint1() has been wrong for about one day but after testing many times, it becomes correct.

I'm developing a web server, with a coroutine framwork. I don't know all of details of the framework, all I know is that it's similar with grpc and it has some mechanism to do the time synchronization. I developed a simple demo without the framework and this issue is just gone. So for now it seems that the framework caused this issue.

HowardHinnant commented 2 years ago

Do you have a type-o above? getCurrentTimePoint1 and getCurrentTimePoint2 are identical.

HowardHinnant commented 2 years ago

In any event, I recommend looking at the implementation of system_clock::now(). See what it calls, and see if you can replicate your results calling that directly. system_clock::now() is typically a thin wrapper around clock_gettime or gettimeofday.

For example: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc#L51-L74

HowardHinnant commented 2 years ago

Also double-check the types of nanoseconds::rep, milliseconds::rep and seconds::rep. (https://stackoverflow.com/a/20170989/576911)

They should all be signed 64 bits. If they aren't chrono is likely overflowing.

YvesZHI commented 2 years ago

Do you have a type-o above? getCurrentTimePoint1 and getCurrentTimePoint2 are identical.

You see that's why I said it was so weird. The two functions are exactly the same except their names. But they returns the different values.

I will do what you proposed here and reply immediately when I figure it out.

YvesZHI commented 2 years ago

Well, this is a very stupid mistake.

template <typename T = std::chrono::milliseconds>
inline Clock<T> getCurrentTimePoint(int8_t timeZone = 0) {
    return std::chrono::time_point_cast<T>(std::chrono::system_clock::now()) +
        std::chrono::hours {timezone}; // typo error! timeZone, instead of timezone
}

The variable timezone has been defined (man7.org/linux/man-pages/man3/tzset.3.html) and been included by chrono. That's why the compiler didn't generate any error.

HowardHinnant commented 2 years ago

I think modules may fix this, not positive. Looking forward to that!