Продвинутая работа с датами и временем в C++: chrono, time_t и парсинг

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему стандартная работа с временем в C++ стала сложнее (и лучше)

Долгое время работа с датами и временем в C++ сводилась к использованию функций из <ctime>: time(), localtime(), strftime(). Этого хватало для простых задач, но с ростом сложности проектов (распределенные системы, высоконагруженные серверы, финансовые приложения) потребовались более точные, безопасные и удобные инструменты.

Стандарт C++11 подарил нам библиотеку <chrono>, которая решает проблемы переполнения, путаницы с единицами измерения и платформозависимости. C++20 сделал важный шаг вперед, добавив поддержку часовых поясов, календарных типов и форматирование через std::format. В этой статье мы рассмотрим продвинутые техники, которые выходят за рамки банального получения текущего времени.

1. Библиотека chrono: от миллисекунд до наносекунд и календарей

Ядро std::chrono — это три компонента: часы (clock), продолжительность (duration) и момент времени (time_point). На продвинутом уровне важно уметь выбирать правильные часы и работать с арифметикой времени.

1.1. Выбор часов: system_clock, steady_clock, high_resolution_clock

  • system_clock — представляет системное время (календарное). Может быть скорректировано пользователем или службой синхронизации (NTP). Используется для отображения даты пользователю.
  • steady_clock — монотонные часы. Никогда не корректируются назад (no rollback). Идеальны для измерения интервалов (таймеры, профилирование).
  • high_resolution_clock — часы с наименьшим возможным тактом. На большинстве платформ это синоним steady_clock.

Продвинутый совет: Никогда не используйте high_resolution_clock для профилирования, если вам нужна стабильность. Используйте steady_clock.

1.2. Работа с duration и time_point

Продвинутая работа подразумевает не просто замер времени, а преобразование между единицами, арифметику и кастомные типы.

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

int main() {
    using namespace std::chrono_literals; // для суффиксов ms, s, us

    // Продвинутое создание duration
    auto work_time = 2h + 30min + 15s; // 2 часа 30 минут 15 секунд
    std::cout << "Рабочее время в секундах: " << work_time.count() << " с\n";

    // Преобразование с округлением (duration_cast)
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(work_time);
    std::cout << "В миллисекундах: " << ms.count() << " ms\n";

    // Замер времени выполнения функции
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(100ms); // имитация работы
    auto end = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "Функция выполнялась: " << elapsed.count() << " мкс\n";

    return 0;
}

1.3. Календарные типы (C++20)

C++20 добавил year_month_day, year_month, weekday и другие. Это позволяет безопасно манипулировать датами без работы с сырыми числами.

#include <iostream>
#include <chrono>

int main() {
    using namespace std::chrono;

    // Создание даты
    auto date = 2023y / October / 15d; // 15 октября 2023
    std::cout << "Дата: " << date << '\n';

    // Прибавление дней
    auto next_week = date + days(7);
    std::cout << "Через неделю: " << next_week << '\n';

    // Получение дня недели
    weekday wd{date};
    std::cout << "День недели: " << wd << '\n'; // выведет "Sun"

    return 0;
}

2. Парсинг дат и времени из строк

Одна из самых частых задач — преобразовать строку вида "2024-03-15 14:30:00" в объект времени. В C++20 появился std::chrono::parse, который значительно упрощает этот процесс.

2.1. Использование std::chrono::parse (C++20)

#include <iostream>
#include <chrono>
#include <sstream>

int main() {
    std::string input = "2024-03-15 14:30:00";
    std::chrono::sys_seconds tp;
    std::istringstream stream(input);
    
    // Парсинг по формату ISO 8601
    stream >> std::chrono::parse("%F %T", tp);
    
    if (stream.fail()) {
        std::cerr << "Ошибка парсинга\n";
        return 1;
    }
    
    // Преобразуем в time_t для взаимодействия со старым кодом
    std::time_t t = std::chrono::system_clock::to_time_t(tp);
    std::cout << "Распарсено: " << std::ctime(&t);
    return 0;
}

2.2. Продвинутый парсинг с часовыми поясами

Если строка содержит информацию о часовом поясе (например, "2024-03-15 14:30:00+05:00"), используйте std::chrono::parse с типом zoned_time.

#include <iostream>
#include <chrono>
#include <sstream>

int main() {
    std::string input = "2024-03-15 14:30:00+05:00";
    std::chrono::sys_seconds tp;                

Блоги

Рекомендаций по книгам