raneshu
raneshu

Reputation: 413

std::chrono::ceil alternative prior to c++ 17

Is there an alternative for std::chrono:ceil in a c++ version prior to c++ 17?

I want to round a time_point to it's minute ceiling (i.e. nearest minute).

https://en.cppreference.com/w/cpp/chrono/duration/ceil

std::chrono::time_point<std::chrono::system_clock> time_point_now = std::chrono::system_clock::now();

auto start_time = std::chrono::ceil<std::chrono::minutes>(time_point_now);
...

Upvotes: 6

Views: 539

Answers (2)

Arty
Arty

Reputation: 16747

One a bit verbose way of doing minutes-ceil (but using just <chrono>) is:

time_point_now += std::chrono::duration_cast<std::chrono::minutes>(
    (time_point_now + std::chrono::minutes(1)).time_since_epoch()) -
    time_point_now.time_since_epoch();

Full example with output to console:

Try it online!

#include <chrono>
#include <iostream>
#include <iomanip>

int main() {
    auto time_point_now = std::chrono::system_clock::now();

    auto Out = [&]{
        auto in_time_t = std::chrono::system_clock::to_time_t(time_point_now);
        std::cout << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X")
            << std::endl;
    };

    Out();
    time_point_now += std::chrono::duration_cast<std::chrono::minutes>(
        (time_point_now + std::chrono::minutes(1)).time_since_epoch()) -
        time_point_now.time_since_epoch();
    Out();
}

Output:

2022-01-04 12:07:23
2022-01-04 12:08:00

One may convert solution above into handy TimeCeil() templated function, that is exactly what you're looking for:

Try it online!

#include <chrono>
#include <iostream>
#include <iomanip>

template <typename Unit, typename TimePoint>
inline TimePoint TimeCeil(TimePoint const & tp) {
    return tp + std::chrono::duration_cast<Unit>(
        (tp + Unit(1)).time_since_epoch()) - tp.time_since_epoch();
}

int main() {
    auto time_point_now = std::chrono::system_clock::now();

    auto Out = [&]{
        auto in_time_t = std::chrono::system_clock::to_time_t(time_point_now);
        std::cout << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X")
            << std::endl;
    };

    Out();
    time_point_now = TimeCeil<std::chrono::minutes>(time_point_now);
    Out();
}

Output:

2022-01-15 10:31:10
2022-01-15 10:32:00

In both code snippets above ignore complex Out() lambda function, it is not needed for you, it is a way to output formatted time to console, just for example.


As noted by @HowardHinnant, if you care about strictly handling very rare case when time point is exactly equal to multiple of minute, then I did small fixup such that ceil of minute-multiple time is equal to initial time (and not plus 1 minute):

Try it online!

#include <chrono>
#include <iostream>
#include <iomanip>

template <typename Unit, typename TimePoint>
inline TimePoint TimeCeil(TimePoint const & tp) {
    auto const res = tp + std::chrono::duration_cast<Unit>(
        (tp + Unit(1)).time_since_epoch()) - tp.time_since_epoch();
    if (res == tp + Unit(1))
        return tp;
    return res;
}

int main() {
    auto time_point_now = std::chrono::system_clock::now();

    auto Out = [&]{
        auto in_time_t = std::chrono::system_clock::to_time_t(time_point_now);
        std::cout << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X")
            << std::endl;
    };

    Out();
    time_point_now = TimeCeil<std::chrono::minutes>(time_point_now);
    Out();
}

Output:

2022-01-16 11:43:00
2022-01-16 11:43:00

Upvotes: 1

Howard Hinnant
Howard Hinnant

Reputation: 219255

Yes, std::chrono::ceil was extensively field tested prior to standardization in order to prove its usefulness. That field test implementation is still available here as a free, open-source, header-only library which works with C++11/14:

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

int
main()
{
    auto time_point_now = std::chrono::system_clock::now();
    auto start_time = date::ceil<std::chrono::minutes>(time_point_now);
}

Upvotes: 4

Related Questions