Working with Dates and Time in C++: From A to Z
Working with dates and time is one of the fundamental tasks in programming. It is needed everywhere: from event logging to calculating deadlines and building charts. In C++, this area was historically not the most convenient, but with the arrival of the C++11 standard and subsequent versions (C++14, C++17, C++20), the situation has changed dramatically. In this article, we will cover all the key aspects: from classic C functions to the modern chrono library, timers, formatting, and parsing. The material will be useful for both beginners and experienced developers looking to systematize their knowledge.
1. Classic Approach: time_t and the tm Structure
Before the advent of chrono, the main tools were functions from <ctime>. This approach remains relevant for supporting legacy code and simple tasks.
Main functions:
time()— returns the current time astime_t(number of seconds since January 1, 1970).localtime()— convertstime_tto atmstructure (local time).gmtime()— converts to UTC.strftime()— formatstminto a string.mktime()— reverse conversion:tm→time_t.
#include <iostream>#include <ctime>
int main() { // Get current time std::time_t now = std::time(nullptr);
// Convert to local time std::tm* local_tm = std::localtime(&now);
// Format into a string char buffer[80]; std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", local_tm);
std::cout << "Current time: " << buffer << std::endl; return 0;}Disadvantages of the approach:
- Only works with seconds (milliseconds and microseconds are unavailable).
- Not thread-safe in multithreaded environments (use
localtime_ron POSIX). - The
tmstructure contains fields that are easy to confuse (year offset by 1900, months starting from 0). - Lack of support for time zones and time intervals.
2. Modern C++: The chrono Library (C++11 and later)
The chrono library is a powerful and type-safe tool for working with time. It forms the basis of all modern C++ projects.
Main components:
- Clocks:
system_clock(system time),steady_clock(monotonic clock for measurements),high_resolution_clock(maximum resolution). - Time points: tied to specific clocks.
- Durations: time intervals (seconds, milliseconds, nanoseconds).
#include <iostream>#include <chrono>#include <thread>
int main() { using namespace std::chrono;
// Current time point auto now = system_clock::now();
// Convert to time_t for display std::time_t now_c = system_clock::to_time_t(now); std::cout << "Current time: " << std::ctime(&now_c);
// Measure execution time auto start = steady_clock::now(); std::this_thread::sleep_for(milliseconds(500)); // simulate work auto end = steady_clock::now();
auto duration = duration_cast<milliseconds>(end - start); std::cout << "Elapsed: " << duration.count() << " ms" << std::endl;
return 0;}Advantages of chrono:
- High precision (nanoseconds).
- Type safety (you cannot accidentally add seconds and milliseconds).
- Support for arithmetic (addition/subtraction of durations and time points).
- Built-in support for various time units.
- Thread safety.
3. Formatting Dates and Time (C++20)
With the C++20 standard, working with dates and time has become even more convenient thanks to the new formatting functions based on std::format.
#include <iostream>#include <chrono>#include <format>
int main() { auto now = std::chrono::system_clock::now(); std::cout << std::format("Current date and time: {:%Y-%m-%d %H:%M:%S}", now) << std::endl; return 0;}Format specifiers:
%Y— year (4 digits)%m— month (01-12)%d— day (01-31)%H— hours (00-23)%M— minutes (00-59)%S— seconds (00-59)
4. Parsing Dates and Time
Parsing strings into time points is a common task. In C++20, this can be done using std::chrono::parse.
#include <iostream>#include <chrono>#include <sstream>
int main() { std::string date_str = "2024-03-15 14:30:00"; std::chrono::sys_time<std::chrono::seconds> tp; std::istringstream iss(date_str); iss >> std::chrono::parse("%Y-%m-%d %H:%M:%S", tp); if (iss) { std::cout << "Parsed successfully: " << tp << std::endl; } else { std::cout << "Parse error" << std::endl; } return 0;}5. Timers and Delays
Timers are an important part of many applications. In C++, you can implement timers using std::thread and std::chrono.
#include <iostream>#include <chrono>#include <thread>#include <functional>
class Timer {private: std::thread thread; bool running = false;
public: void start(int interval_ms, std::function<void()> callback) { running = true; thread = std::thread([this, interval_ms, callback]() { while (running) { std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms)); callback(); } }); }
void stop() { running = false; if (thread.joinable()) { thread.join(); } }
~Timer() { stop(); }};
int main() { Timer timer; timer.start(1000, []() { std::cout << "Timer tick: " << std::chrono::system_clock::now() << std::endl; });
std::this_thread::sleep_for(std::chrono::seconds(5)); timer.stop();
return 0;}6. Working with Time Zones (C++20)
C++20 introduced full support for time zones through the <chrono> library.
#include <iostream>#include <chrono>#include <format>
int main() { using namespace std::chrono;
auto now = system_clock::now(); // Convert to a specific time zone auto ny_time = zoned_time{"America/New_York", now}; auto london_time = zoned_time{"Europe/London", now}; std::cout << "New York: " << std::format("{:%Y-%m-%d %H:%M:%S}", ny_time) << std::endl; std::cout << "London: " << std::format("{:%Y-%m-%d %H:%M:%S}", london_time) << std::endl; return 0;}Conclusion
Working with dates and time in C++ has come a long way from the cumbersome C functions to the powerful and convenient chrono library. Modern C++ provides everything you need: high-precision clocks, type-safe durations, convenient formatting and parsing, time zone support, and much more. Use the classic time_t approach only for maintaining legacy code; for new projects, always prefer chrono.
Useful links: