std::ratio<1, 1000>
(std::ratio
— это дробное значение a/b
). Тип ratio_multiply
, по сути, является функцией времени компиляции, которая представляет собой тип, получаемый в результате умножения одного типа ratio
на другой.Это может показаться непонятным, так что рассмотрим пример: команда
ratio_multiply, ratio<4, 5>>
даст результат ratio<8, 15>
, поскольку (2/3) * (4/5) = 8/15
.Полученные описания типов эквивалентны следующим описаниям:
using seconds = chrono::durationratio<1, 1>>;
using milliseconds = chrono::durationratio<1, 1000>>;
using microseconds = chrono::duration>;
После получения этих типов можно легко выполнять преобразования между ними. При наличии промежутка времени
d
с типом seconds
можно преобразовать его в тип milliseconds
, передав в конструктор другого типа — milliseconds(d)
.
Дополнительная информация
В других учебниках и книгах при преобразовании промежутков времени вы могли столкнуться с
duration_cast
. Если у нас есть промежуток времени типа chrono::milliseconds
и нужно преобразовать его к типу chrono::hours
, например, то следует написать конструкцию duration_cast(milliseconds_value)
, поскольку данные единицы измерения зависят от целочисленных типов. Преобразование точных единиц времени в менее точные приводит к потере точности, именно поэтому и нужен duration_cast
. Для продолжительностей, основанных на типах double
или float
, этого не требуется.Выполняем преобразование между абсолютными и относительными значениями с использованием std::chrono
До C++11 было довольно сложно получить физическое время и просто вывести его на экран, поскольку C++ не имела собственной библиотеки для работы с временем. Требовалось всегда вызывать функции библиотеки С, которая выглядит очень архаично, учитывая, что такие вызовы могут быть инкапсулированы в собственные классы.
Начиная с C++11, в STL можно найти библиотеку
chrono
, она значительно упрощает решение задач, связанных с временем.В этом примере мы возьмем местное время, выведем его на экран и поработаем с ним, добавляя разные смещения, что очень удобно делать с помощью библиотеки
std::chrono
.
Как это делается
В примере мы сохраним текущее время и выведем его на экран. Кроме того, наша программа будет добавлять разные смещения к сохраненному времени и выводить на экран полученные результаты.
1. Сначала идут типичные директивы
include
, затем мы объявляем об использовании по умолчанию пространства имен std
:
#include
#include
#include
using namespace std;
2. Выведем на экран абсолютные моменты времени. Они будут иметь форму шаблона типа
chrono::time_point
, поэтому просто перегрузим для него оператор выходного потока. Существуют разные способы вывести на экран дату и/или время для заданного момента. Мы применим только стандартное форматирование %c
. Можно было бы, конечно, также вывести только время, только дату, только год или что-то еще, приходящее на ум. Все преобразования между разными типами до того, как мы сможем использовать put_time
, будут выглядеть несколько «неаккуратно», но мы провернем это лишь однажды.
ostream& operator<<(ostream &os,
const chrono::time_point&t)
{
const auto tt (chrono::system_clock::to_time_t(t));
const auto loct (std::localtime(&tt));
return os << put_time(loct, "%c");
}
3. Для секунд, минут, часов и т.д. в STL существуют описания типов. Сейчас мы добавим тип
days
. Это делается легко; нужно лишь специализировать шаблон chrono::duration
, сославшись на часы и умножив их на 24, поскольку сутки насчитывают 24 часа.
using days = chrono::duration<
chrono::hours::rep,
ratio_multiply>>;
4. Чтобы наиболее элегантным способом выразить продолжительность длиной в несколько дней, можно определить собственный пользовательский литерал
days
. Теперь можно написать 3_days
, чтобы создать значение, которое представляет собой три дня.
constexpr days operator ""_days(unsigned long long h)
{
return days{h};
}
5. В самой программе сделаем снимок момента времени, который затем просто выведем на экран. Это очень легко и удобно, поскольку мы уже реализовали правильную версию перегруженного оператора.
int main()
{
auto now (chrono::system_clock::now());
cout << "The current date and time is " << now << '\n';
6. Сохранив текущее время в переменной
now
, можем добавить к нему произвольные продолжительности и также вывести их на экран. Добавим к текущему времени 12 часов и выведем результат на экран:
chrono::hours chrono_12h {12};
cout << "In 12 hours, it will be "
<< (now + chrono_12h)<< '\n';
7. Объявляя об использовании по умолчанию пространства имен
chrono_literals
, разблокируем все существующие литералы, описывающие продолжительность, для часов, секунд и т.д. Таким образом, можно изящно вывести на экран, какое время было 12 часов 15 минут назад или семь дней назад.
using namespace chrono_literals;
cout << "12 hours and 15 minutes ago, it was "
<< (now - 12h - 15min) << '\n'
<< "1 week ago, it was "
<< (now - 7_days) << '\n';
}
8. Компиляция и запуск программы дадут следующий результат. Поскольку мы использовали в качестве строки форматирования
%c
, получим довольно полное описание в конкретном формате. Поработав с разными строками формата, можем вывести время в любом формате, который нам нравится. Обратите внимание: здесь мы применяем 24-часовой формат.
$ ./relative_absolute_times
The current date and time is Fri May 5 13:20:38 2017
In 12 hours, it will be Sat May 6 01:20:38 2017
12 hours and 15 minutes ago, it was Fri May 5 01:05:38 2017
1 week ago, it was Fri Apr 28 13:20:38 2017
Как это работает
Мы получили текущий момент времени из
std::chrono::system_clock
. Этот класс часов STL единственный способен преобразовывать свои значения моментов времени в структуру time
, которая может быть отображена в виде понятной человеку строки описания.Чтобы вывести на экран такие моменты времени, мы реализовали оператор
<<
для потока вывода:
ostream& operator<<(ostream &os,
const chrono::time_point&t)
{
const auto tt (chrono::system_clock::to_time_t(t));
const auto loct (std::localtime(&tt));
return os << put_time(loct, "%c");
}
Здесь мы сначала преобразуем экземпляр типа
chrono::time_point
к типу std::time_t
. Значения этого типа можно преобразовать в локальное время, что мы делаем с помощью функции