Введение
До C++11 работа с временем в стандартной библиотеке ограничивалась C-API (time_t, struct tm, difftime) — без типобезопасности единиц измерения и с неудобной работой с датами. std::chrono ввела строго типизированные длительности (duration) и точки во времени (time_point), а C++20 добавила полноценную поддержку календаря и таймзон, закрыв исторический пробел.
Концепция
Три ключевых сущности std::chrono:
duration<Rep, Period>— промежуток времени с типобезопасной единицей измерения (std::chrono::seconds,milliseconds,nanoseconds, и т.д.). Компилятор не позволит случайно сложить секунды с миллисекундами без явного преобразования.time_point<Clock, Duration>— конкретный момент времени относительно эпохи определённых часов (Clock). Основные часы:system_clock(календарное время, может быть скорректировано NTP),steady_clock(монотонные, не идут назад — для измерения интервалов),high_resolution_clock.- C++20:
std::chrono::year_month_day,zoned_time, таймзоны (std::chrono::current_zone(),locate_zone()) — полноценная работа с календарными датами и переводом времени между часовыми поясами без сторонних библиотек (ранее это требовало Howard Hinnant’s date library, которая фактически и стала основой стандарта).
Пример кода
#include <chrono>
#include <iostream>
#include <thread>
void measureDuration() {
using Clock = std::chrono::steady_clock;
auto start = Clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(150));
auto end = Clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Elapsed: " << elapsed.count() << " msn";
// Явные преобразования между единицами -- типобезопасно
auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed);
std::cout << "Rounded to seconds: " << elapsedSeconds.count() << " sn";
}
void durationArithmetic() {
using namespace std::chrono_literals; // C++14: суффиксы 1s, 500ms, ...
auto a = 2h; // 2 часа
auto b = 30min; // 30 минут
auto total = a + b; // std::chrono::minutes -> общий тип
std::cout << "Total: " << total.count() << " minutesn";
auto inSeconds = std::chrono::duration_cast<std::chrono::seconds>(total);
std::cout << "In seconds: " << inSeconds.count() << "n";
}
#if __cpp_lib_chrono >= 201907L
void calendarAndTimezones() {
using namespace std::chrono;
// Конкретная календарная дата (без времени суток)
year_month_day date = 2026y / June / 22;
std::cout << "Date: " << static_cast<int>(date.year()) << "-"
<< static_cast<unsigned>(date.month()) << "-"
<< static_cast<unsigned>(date.day()) << "n";
// Текущий момент в системных часах
auto now = system_clock::now();
try {
// Перевод текущего момента в конкретную таймзону
zoned_time zonedMoscow{"Europe/Moscow", now};
zoned_time zonedNewYork{"America/New_York", now};
std::cout << "Moscow time: " << zonedMoscow << "n";
std::cout << "New York time: " << zonedNewYork << "n";
} catch (const std::exception& e) {
std::cout << "Timezone database not available: " << e.what() << "n";
}
}
#endif
int main() {
measureDuration();
durationArithmetic();
#if __cpp_lib_chrono >= 201907L
calendarAndTimezones();
#else
std::cout << "Calendar/timezone API requires full C++20 chrono support "
"(check your standard library version).n";
#endif
return 0;
}
Пояснения к коду
measureDuration использует steady_clock — это специально подобранные часы для измерения интервалов: они монотонны (не «прыгают» назад из-за корректировки системного времени или перехода на летнее время) в отличие от system_clock, который отражает календарное время и может скорректироваться. Вычитание двух time_point даёт duration, который явно приводится через duration_cast к нужной единице — компилятор не позволит просто «сложить секунды с миллисекундами» без явного преобразования, что исключает целый класс ошибок единиц измерения.
durationArithmetic демонстрирует литералы длительностей (2h, 30min) из std::chrono_literals (C++14) — они делают код, работающий с временем, читаемым почти как обычный текст, без магических чисел вида 1800 (секунд).
calendarAndTimezones — пример возможностей C++20: year_month_day позволяет конструировать календарные даты через выразительный синтаксис 2026y / June / 22, а zoned_time привязывает момент времени (system_clock::now()) к конкретной IANA-таймзоне («Europe/Moscow», «America/New_York»), автоматически учитывая переход на летнее время и смещения. Этот функционал требует наличия базы данных таймзон в реализации стандартной библиотеки (например, в libstdc++ для полной поддержки может требоваться системная tzdata или явная линковка с библиотекой, поставляющей tz-данные) — поэтому код оборачивает обращение к zoned_time в try/catch.
Подводные камни и советы
1. Используйте steady_clock для измерения длительностей (тестов производительности, таймаутов) и system_clock — для отображения «настенного времени» (даты, времени в UI). Использование system_clock для замера интервалов может дать отрицательную или скачкообразную длительность при корректировке системных часов. 2. Поддержка таймзон в C++20 (<chrono> с zoned_time, tzdb) не всегда полностью реализована во всех версиях компиляторов и стандартных библиотек на момент написания кода — проверяйте конкретную версию GCC/Clang/MSVC и при необходимости используйте библиотеку Howard Hinnant’s date (которая стала основой стандарта) как fallback. 3. duration_cast усекает (не округляет) при сужающем преобразовании — duration_cast<seconds>(milliseconds(1999)) даст 1, а не 2. Если нужно округление, реализуйте его явно. 4. Не сравнивайте time_point разных часов — system_clock::time_point и steady_clock::time_point несовместимы напрямую, у них разные эпохи и гарантии. 5. Для хранения времени в файлах/БД/сетевых протоколах сериализуйте в int64_t с явно зафиксированной единицей (например, миллисекунды с Unix-эпохи через time_point_cast и time_since_epoch()), а не сохраняйте внутреннее представление chrono-типов, которое может отличаться между платформами и компиляторами.