Недостатки C ++ 20 std :: chrono :: длительности форматирования - PullRequest
1 голос
/ 20 января 2020

Мне нужно было напечатать std::chrono::duration, которое может быть больше 24 часов в легко читаемом виде, например "2 d. 09:10:11".

C ++ 20-х std::chrono::time_point и std::chrono::duration форматирование (на основе библиотеки дат Говарда Хиннанта ) содержит тонны полезных спецификаторов формата, но не имеет их для числа дней в std::chrono::duration. Ближайшим является %j, но он дополняется нулями. Также нет указателя количества часов в течение дня. Ближайшими являются %H (но он включает в себя также целые дни) и %OH (но он падает при утверждении, что количество часов меньше 24).

Я что-то упустил?

Имеет ли смысл добавлять больше спецификаторов в C ++ 20 std::format?

Пока я использую обходную функцию :

#include <chrono>
#include <cstring> // std::strncmp
#include <date.h> // use Howard Hinnant's date library until C++20 is supported by compilers
#include <string>

// Workaround for C++20 std::chrono::format's inability to produce number of days in std::chrono::duration.
// Temporary format specifier for this is `%J`. It must be in the beginning of the format string.
template<typename Rep, typename Period>
std::string duration_to_str(const char* format, const std::chrono::duration<Rep, Period>& duration)
{
    if (std::strncmp(format, "%J", 2))
        return date::format(format, duration);
    static const auto one_day = std::chrono::hours(24);
    const size_t days = std::chrono::duration_cast<std::chrono::hours>(duration).count() / 24;
    return std::to_string(days) + date::format(format + 2, duration - days * one_day);
}

Test :

#include <iostream>

template<typename Rep, typename Period>
void test_dur_to_str(const std::chrono::duration<Rep, Period>& duration)
{
    std::cout << duration_to_str("%T", duration) << ":\n"
              << duration_to_str("%j d. %T", duration) << '\n'
              << duration_to_str("%J d. %T", duration) << "\n\n";
}

int main()
{
    test_dur_to_str(std::chrono::seconds(24 * 60 * 60 - 1));
    test_dur_to_str(std::chrono::seconds(24 * 60 * 60));
    test_dur_to_str(std::chrono::seconds(91297));
    test_dur_to_str(std::chrono::seconds(2 * 24 * 60 * 60 - 1));
    test_dur_to_str(std::chrono::seconds(2 * 24 * 60 * 60));
    test_dur_to_str(std::chrono::seconds(2 * 24 * 60 * 60 + 1));
    test_dur_to_str(std::chrono::seconds(13 * 24 * 60 * 60 + 5));
    test_dur_to_str(std::chrono::seconds(365 * 24 * 60 * 60 + 5));
    test_dur_to_str(std::chrono::seconds(366 * 24 * 60 * 60 + 5));
    test_dur_to_str(std::chrono::seconds(367 * 24 * 60 * 60 + 5));
}

Вывод:

23:59:59:
000 d. 23:59:59
0 d. 23:59:59

24:00:00:
001 d. 24:00:00
1 d. 00:00:00

25:21:37:
001 d. 25:21:37
1 d. 01:21:37

47:59:59:
001 d. 47:59:59
1 d. 23:59:59

48:00:00:
002 d. 48:00:00
2 d. 00:00:00

48:00:01:
002 d. 48:00:01
2 d. 00:00:01

312:00:05:
013 d. 312:00:05
13 d. 00:00:05

8760:00:05:
365 d. 8760:00:05
365 d. 00:00:05

8784:00:05:
366 d. 8784:00:05
366 d. 00:00:05

8808:00:05:
367 d. 8808:00:05
367 d. 00:00:05

Ответы [ 2 ]

1 голос
/ 21 января 2020

Я хочу показать упрощенную версию функции обхода, как предложил Говард Хиннант в своем комментарии, для тех, кто может найти ее полезной:

#include <chrono>
#include <cstring> // std::strncmp
#include <date.h> // use Howard Hinnant's date library until C++20 is supported by compilers
#include <string>

template<typename Rep, typename Period>
std::string duration_to_str(const char* format, const std::chrono::duration<Rep, Period>& duration)
{
    if (std::strncmp(format, "%J", 2))
        return date::format(format, duration);
    const auto days = std::chrono::duration_cast<date::days>(duration);
    return std::to_string(days.count()) + date::format(format + 2, duration - days);
}
0 голосов
/ 20 января 2020

Вы можете просто сделать от duration_cast до std::chrono::days:

template<typename Rep, typename Period>
std::string duration_to_str(const char* format, const std::chrono::duration<Rep, Period>& duration)
{
    if (std::strncmp(format, "%J", 2))
        return date::format(format, duration);

    auto num_days = std::chrono::duration_cast<date::days>(duration);
    return std::to_string(num_days.count()) + date::format(format + 2, duration - num_days);
}

Что касается того, почему он не существует, цель форматирования даты / времени chrono заключается в форматировании дат и раз. Форматирование даты, генерирующей год и количество дней в этом году, является операцией, подобной дате. Форматирование даты, генерирующей год, месяц и количество дней в этом месяце, также является операцией, подобной дате. Однако число дней в пределах продолжительности не является операцией форматирования, подобной дате.

Это то же самое, что разница между получением количества часов в пределах продолжительности и 24-часового времени, в течение которого продолжительность содержит. Первое сделано без учета какого-либо временного контекста; вы просто получаете количество часов в продолжительности. Для также нет поля для форматирования .

...