0

In my C++ project, there is a very old function, which used the system function of Linux to do some calculation about time.

Here is a picec of code:

struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t seqId = (tv.tv_sec % 86400)*10000 + tv.tv_usec / 100;

* 10000 and / 100 are not wrong, because this piece of code is about to generate a seqId.

Now, I'm trying to use std::chrono of C++11 to replace it. Here is my code:

std::chrono::high_resolution_clock::duration duration_since_midnight() {
    auto now = std::chrono::high_resolution_clock::now();
    std::time_t tnow = std::chrono::high_resolution_clock::to_time_t(now);
    tm *date = std::localtime(&tnow);
    date->tm_hour = 0;
    date->tm_min = 0;
    date->tm_sec = 0;
    auto midnight = std::chrono::system_clock::from_time_t(std::mktime(date));
    return now - midnight;
}
auto res = duration_since_midnight();
auto sec = std::chrono::duration_cast<std::chrono::seconds>(res);
auto mil = std::chrono::duration_cast<std::chrono::milliseconds>(res - sec);
std::cout << sec.count() * 10000 + mil.count() << std::endl;

However, the result is always kind of wrong. For example, the old version may give me 360681491 but my version would give me 360680149. You see they are not exactly the same.

I don't know why. Or it's not possible to do so with std::chrono?

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
Yves
  • 11,597
  • 17
  • 83
  • 180

1 Answers1

0

You do not have to stick to the duration types that the library provides. You can define your own that has the representation that you need:

using tenthmillis = duration<long, ratio<1,10000>>;

Then you can follow up with the following calculation.

auto now = high_resolution_clock::now().time_since_epoch();
auto oneDay = duration_cast<tenthmillis>(days{1});
auto sinceMidnight = duration_cast<tenthmillis>(now) % oneDay.count();

cout << sinceMidnight.count();

Note: Add namespace qualifications as required yourself.

[Update: Instead of high_resolution_clock, choose a clock that uses gettimeofday in its implementation to get results that are comparable to your gettimeofday implementation.]

j6t
  • 9,150
  • 1
  • 15
  • 35
  • It's not compilable. `now` is a time_point, which can't be converted to a `duration` immediately, right? So I used `(std::chrono::time_point_cast(now).time_since_epoch() % oneDay).count();`. – Yves Nov 21 '21 at 05:01
  • If I'm right, could you please update your answer later? – Yves Nov 21 '21 at 05:02
  • Thanks for the tip. I could come up with an even simpler procedure. – j6t Nov 21 '21 at 11:08
  • Just one nitpick: `high_resolution_clock` doesn't necessarily have an epoch that starts on a day boundary. With gcc it does, but nowhere else. `system_clock` is the right tool for this job. – Howard Hinnant Nov 21 '21 at 17:14
  • @HowardHinnant The solution is system-dependent anyway. The requirement is "produce the same result that `gettimeofday` gives", so OP has to check which of the clocks uses `gettimeofday` or an equivalent behind the scenes. – j6t Nov 22 '21 at 07:28
  • `gettimeofday` returns the number of seconds and microseconds since 1970-01-01 00:00:00 UTC. In C++11/14/17 the epoch of `system_clock` is unspecified, but all implementations use the same epoch as `gettimeofday`. In C++20 this existing practice is standardized: http://eel.is/c++draft/time.clock.system#overview-1 In all versions of C++, the epoch of `high_resolution_clock` is unspecified, and different implementations have different epochs. Linux/gcc is the only implementation that makes `high_resolution_clock` a type alias for `system_clock`. `system_clock` is the right tool for this job. – Howard Hinnant Nov 22 '21 at 12:29
  • @HowardHinnant I'm not questioning your expertise. Note though that (as far as I understood the question) "since midnight" is just a red herring. "Same as `gettimeofday`" is the important part, hence, whichever clock uses that behind the scenes is the right tool for this particular job. – j6t Nov 22 '21 at 13:38