Элегантная печать времени на С++ 11

Это то, что я хотел бы сделать в С++ 11: учитывая два момента времени (например, класс времени) как std::chrono::steady_clock::now(), напечатайте их разницу во времени элегантно, например:

1 day 4 hours 3 minutes 45 seconds

или

32 minutes 54 seconds 345 milliseconds

Обратите внимание, что мне не интересно просто использовать put_time, так как я хочу печатать, начиная с наиболее значимой единицы времени. Я знаю, это решение просто распечатать разницу, но это не очень: я ищу элегантное решение:)

Ура!

Ответ 1

template<typename T>
void print_time_diff(std::ostream& out, T prior, T latter)
{
    namespace sc = std::chrono;
    auto diff = sc::duration_cast<sc::milliseconds>(latter - prior).count();
    auto const msecs = diff % 1000;
    diff /= 1000;
    auto const secs = diff % 60;
    diff /= 60;
    auto const mins = diff % 60;
    diff /= 60;
    auto const hours = diff % 24;
    diff /= 24;
    auto const days = diff;

    bool printed_earlier = false;
    if (days >= 1) {
        printed_earlier = true;
        out << days << (1 != days ? " days" : " day") << ' ';
    }
    if (printed_earlier || hours >= 1) {
        printed_earlier = true;
        out << hours << (1 != hours ? " hours" : " hour") << ' ';
    }
    if (printed_earlier || mins >= 1) {
        printed_earlier = true;
        out << mins << (1 != mins ? " minutes" : " minute") << ' ';
    }
    if (printed_earlier || secs >= 1) {
        printed_earlier = true;
        out << secs << (1 != secs ? " seconds" : " second") << ' ';
    }
    if (printed_earlier || msecs >= 1) {
        printed_earlier = true;
        out << msecs << (1 != msecs ? " milliseconds" : " millisecond");
    }
}

http://ideone.com/bBNHQp

Ответ 2

Продолжительность может выполняться арифметикой.

#include <chrono>
#include <iostream>
#include <thread>

int main(){
    using namespace std::chrono;
    using day_t = duration<long, std::ratio<3600 * 24>>;
    auto start = system_clock::now();
    std::this_thread::sleep_for(seconds(1));
    auto end = system_clock::now();
    auto dur = end - start;
    auto d = duration_cast<day_t>(dur);
    auto h = duration_cast<hours>(dur -= d);
    auto m = duration_cast<minutes>(dur -= h);
    auto s = duration_cast<seconds>(dur -= m);
    auto ms = duration_cast<seconds>(dur -= s);
    std::cout << d.count() << " days, "
        << h.count() << " hours, "
        << m.count() << " minutes, "
        << s.count() << " seconds, "
        << ms.count() << " milliseconds\n";

    return 0;
}

Вывод:

enter image description here

Возможный дубликат: Извлечь год/месяц/день и т.д. из std:: chrono:: time_point в С++

Ответ 3

Здесь легко расширяемое решение с использованием вариативных шаблонов и рекурсии. Он определяет ostream& operator<<(ostream&, const duration&) для удобства использования.

#include <chrono>
#include <iostream>
#include <tuple>

using day_t = std::chrono::duration<long long, std::ratio<3600 * 24>>;

template<typename> struct duration_traits {};

#define DURATION_TRAITS(Duration, Singular, Plural) \
template<> struct duration_traits<Duration> { \
    constexpr static const char* singular = Singular; \
    constexpr static const char* plural = Plural; \
}

DURATION_TRAITS(std::chrono::milliseconds, "millisecond", "milliseconds");
DURATION_TRAITS(std::chrono::seconds, "second", "seconds");
DURATION_TRAITS(std::chrono::minutes, "minute", "minutes");
DURATION_TRAITS(std::chrono::hours, "hour", "hours");
DURATION_TRAITS(day_t, "day", "days");

using divisions = std::tuple<std::chrono::milliseconds, 
                             std::chrono::seconds, 
                             std::chrono::minutes, 
                             std::chrono::hours, 
                             day_t>;

namespace detail {
template<typename...> struct print_duration_impl_ {};

template<typename Head, typename... Tail>
struct print_duration_impl_<Head, Tail...> {
    template <typename Duration>
    static bool print(std::ostream& os, Duration& dur) {
        const auto started_printing = print_duration_impl_<Tail...>::print(os, dur);

        const auto n = std::chrono::duration_cast<Head>(dur);
        const auto count = n.count();

        if (count == 0) {
            return started_printing;
        }

        if (started_printing) {
            os << ' ';
        }

        using traits = duration_traits<Head>;
        os << count << ' ' << (count == 1 ? traits::singular : traits::plural);
        dur -= n;

        return true;
    }
};

template<>
struct print_duration_impl_<> {
    template <typename Duration>
    static bool print(std::ostream& os, Duration& dur) {
        return false;
    }
};

template<typename...> struct print_duration {};

template<typename... Args>
struct print_duration<std::tuple<Args...>> {
    template<typename Duration>
    static void print(std::ostream& os, Duration dur) {
        print_duration_impl_<Args...>::print(os, dur);
    }
};
}

template<typename Rep, typename Period>
std::ostream& operator<<(std::ostream& os, const std::chrono::duration<Rep, Period>& dur) {
    detail::print_duration<divisions>::print(os, dur);
    return os;
}

Новые продолжительности добавляются специализацией duration_traits и введением типа в правильное положение в подразделениях. Например, добавление типа jiffy 10 мс будет включать в себя:

using jiffy_t = std::chrono::duration<long long, std::centi>;
DURATION_TRAITS(jiffy_t, "jiffy", "jiffies");

using divisions = std::tuple<std::chrono::milliseconds, 
                             jiffy_t,
                             std::chrono::seconds, 
                             std::chrono::minutes, 
                             std::chrono::hours, 
                             day_t>;

Неплохо для трех строк кода!

Пример в реальном времени на ideone.com.