Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I compare system_clock::now() to a local time in c++20?

I am testing a library like follows:

std::chrono::system_clock::time_point FromDateTime(const DateTime& dateTime)
{
    std::tm tm{};
    tm.tm_year = dateTime.Year - 1900;
    tm.tm_mon = dateTime.Month - 1;
    tm.tm_mday = dateTime.Day;
    tm.tm_hour = dateTime.Hour;
    tm.tm_min = dateTime.Minute;
    tm.tm_sec = dateTime.Second;

    const auto& time = std::mktime(&tm);
    const auto& timePoint = std::chrono::system_clock::from_time_t(time);

    return timePoint;
}

TEST_F(MyLibraryFixture, GetDateTime)
{
    // Arrange
    const auto& now = std::chrono::system_clock::now();

    // Act
    const auto& actual = _testee->GetDateTime();

    // Assert
    const auto& timeDiscrepanceInSec = std::chrono::duration_cast<std::chrono::seconds>(FromDateTime(actual) - now);
    ASSERT_TRUE(timeDiscrepanceInSec.count() < 10);
}

The DateTime type is just a raw struct with the above used fields. The call

_testee->GetDateTime();

gives a DateTime struct back and the date time is actually local time. For some reason inherent to the library I am testing, I need to compare the local time with the time it is now. My code above seems to be working fine, I "hope" it will still work when we get back to Summer time.

Now, c++20 exposes a lot of date time utilities. I have browsed the documentation, I have tried some stuff, but I was not able to come up with a reliable counterpart to the above snippet. Is it any possible (I mean, without using std::mktime and the strange std::tm)?

The closest I was able to come is this, but it is still wrong (I get a discrepancy of one hour):

std::chrono::time_point<std::chrono::local_t, std::chrono::seconds> FromDateTime(const DateTime& dateTime)
{
    const auto& d = std::chrono::year_month_day(std::chrono::year(dateTime.Year), std::chrono::month(dateTime.Month), std::chrono::day(dateTime.Day));
    const auto& t = std::chrono::hours(dateTime.Hour) + std::chrono::minutes(dateTime.Minute) + std::chrono::seconds(dateTime.Second);
    const auto& dt = std::chrono::local_days(d) + t;

    return dt;
}

TEST_F(MyLibraryFixture, GetDateTime)
{
    // Arrange
    const auto& now = std::chrono::utc_clock::now();

    // Act
    const auto& actual = _testee->GetDateTime();

    // Assert
    const auto& timeDiscrepanceInSec = std::chrono::duration_cast<std::chrono::seconds>(FromDateTime(actual).time_since_epoch() - now.time_since_epoch());
    ASSERT_TRUE(timeDiscrepanceInSec.count() < 10);
}
like image 236
Laurent Michel Avatar asked Feb 02 '26 14:02

Laurent Michel


1 Answers

Here's the equivalent C++20 code to your first version of FromDateTime:

std::chrono::system_clock::time_point
FromDateTime(const DateTime& dateTime)
{
    using namespace std::chrono;
    auto local_tp = local_days{year{dateTime.Year}/dateTime.Month/dateTime.Day} +
        hours{dateTime.Hour} + minutes{dateTime.Minute} + seconds{dateTime.Second};
    zoned_time zt{current_zone(), local_tp};
    return zt.get_sys_time();
}

You were on the right track with creating a local_time using local_days.

There's no need to explicitly name the year_month_day type. This is generated for you using the / notation.

Once you have the local time, you can associate it with your computer's local time zone by constructing a zoned_time with current_zone() and the local_time.

Then you can extract the sys_time out of the zoned_time. The sys_time is a time_point<system_clock, seconds> (seconds-precision system_clock::time_point), which is UTC (excluding leap seconds). And this implicitly converts to the return type: system_clock::time_point.

If desired, you could interpret the local_time with respect to some other time zone, rather than your computer's local time zone. For example:

    zoned_time zt{"America/New_York", local_tp};

In general, zoned_time is a convenience type used to translate between local_time and sys_time.

like image 192
Howard Hinnant Avatar answered Feb 04 '26 02:02

Howard Hinnant



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!