A stopwatch measurement, an event timestamp and a calendar date are different values. A timer should not depend on a wall clock that an administrator can adjust. A calendar date should not be treated as an arbitrary number of seconds without explaining its calendar meaning. Chrono uses typed durations and clock-specific time points to make these distinctions visible.
This guide requires C++20 for its calendar example. Core clocks and durations have been available since C++11. You will convert a fixed duration, advance across a leap day and test an exact deadline without a real sleep. Time-zone facilities depend on library support and available zone data, so they are discussed as a separate deployment concern.
Duration includes a unit in its type
A chrono duration combines a count representation with a period. Seconds and milliseconds are different duration types, so an expression can preserve units rather than passing an unqualified integer called timeout. Adding compatible durations uses appropriate common units under the type rules.
Duration_cast explicitly changes representation and units. Converting an integral number of milliseconds to integral seconds discards the fractional part toward zero. That is not automatically the correct billing or rounding policy. Floor, ceil and round provide other conversions under supported standards when those meanings are needed. Choose the conversion from the requirement and keep range in mind: a duration's count representation can still overflow. Typed units help prevent mixing milliseconds with seconds, but do not guarantee every magnitude fits.
Choose a clock from the operation
Steady_clock is suitable for elapsed timing and deadlines because its time points do not go backward under its clock contract. System_clock represents system wall time and is useful for timestamps, but its observed value can be adjusted. High_resolution_clock is implementation-defined and may alias another clock, so its name alone does not promise monotonic behavior.
A time_point belongs to a clock and contains a duration relative to that clock's epoch. Do not subtract unrelated clock types as though their epochs were interchangeable. For elapsed work, take two points from the same steady clock and subtract them. For an audit timestamp, use the appropriate wall-clock representation and formatting contract. Storing a steady-clock deadline as a permanent cross-process date is generally not meaningful.
Calendar dates need validation
C++20 year_month_day represents calendar components and offers ok to check whether they form a valid date. A value can hold an invalid combination such as February thirty, so constructing the object alone is not validation. Check before converting it into a concrete day-based representation under the intended contract.
Sys_days is a system-clock time point with day precision and can support date arithmetic after a valid date is established. Adding one day across a leap-day boundary follows the calendar representation. Adding one month to a day-of-month value can create an invalid date in a shorter month. Decide whether the business rule rejects, clamps or otherwise adjusts that result; the library cannot choose the correct scheduling policy for every application.
A date and local time are not a complete instant
A local date and clock reading require an offset or regional time zone to identify an instant. A named zone includes rules that can skip or repeat local times during transitions. A fixed offset does not describe all those rules for future dates.
C++20 includes time-zone facilities, but implementation availability and the zone database should be verified for the project's compiler and standard library. An application needs a policy for ambiguous and nonexistent local times, plus a configured supported zone identifier. Do not claim every local day is exactly twenty-four elapsed hours. Keep plain calendar rules separate from time-zone conversion until the feature actually needs a concrete instant.
Measure without promising exact timings
A real benchmark can record steady_clock before and after work, but the measured count depends on hardware, scheduler activity, optimization and the workload. Printing a fixed “expected milliseconds” would mislead learners. Report the unit and state that the value is environment-dependent.
Very short work may require repeated measurement and an appropriate benchmark harness. Make sure the compiler cannot eliminate the measured work and separate warm-up from the question being measured. For application correctness tests, prefer a replaceable clock or explicit time arguments. Use real-time waiting only when the feature genuinely needs an integration test of scheduling behavior, and allow for platform timing tolerance.
Keep serialization and arithmetic contracts visible
A raw duration count without its unit is ambiguous when written to a file or passed between services. Name fields such as timeoutMilliseconds or use a documented format. A raw time-point count also needs a clock and epoch contract. Avoid assuming a library implementation's internal count is a universal timestamp.
Validate imported magnitudes before constructing durations used for allocation, retries or waits. An excessively large timeout can be an application failure even when the type represents it. Test zero duration, negative input if possible, the allowed maximum and the exact expiration point. Calendar tests should include leap years and invalid dates independently from elapsed-time tests.
Official references
C++ Chrono Clocks, Durations and Dates Examples
Each example shows the code, the output it produces and what each part does.
Duration conversion and a leap-day boundary
Compile this complete program with C++20 support. It uses fixed values so the output is deterministic.
The conversion loses the remaining half second because the destination count is integral. The date output deliberately prints components without requiring a particular chrono formatting implementation.
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
milliseconds elapsed{1500};
std::cout << "Milliseconds: " << elapsed.count() << '\n';
std::cout << "Whole seconds: "
<< duration_cast<seconds>(elapsed).count() << '\n';
year_month_day leap_day{year{2028}, February, day{29}};
std::cout << std::boolalpha
<< "Valid date: " << leap_day.ok() << '\n';
if (!leap_day.ok())
return 1;
year_month_day next{sys_days{leap_day} + days{1}};
std::cout << int(next.year()) << '-'
<< unsigned(next.month()) << '-'
<< unsigned(next.day()) << '\n';
}Milliseconds: 1500
Whole seconds: 1
Valid date: true
2028-3-1C++ Chrono Clocks, Durations and Dates in Real Projects
Worked exercise: test a deadline at its boundary
The task is to declare an operation expired when now is at or after its deadline. Receive both time points as arguments so a test can supply controlled values without sleeping. Use steady-clock time points because this is elapsed-operation behavior, not a stored calendar date.
#include <chrono>
#include <iostream>
bool expired(std::chrono::steady_clock::time_point now,
std::chrono::steady_clock::time_point deadline) {
return now >= deadline;
}
int main() {
using Clock = std::chrono::steady_clock;
using namespace std::chrono;
Clock::time_point started{};
auto deadline = started + seconds{5};
std::cout << std::boolalpha;
std::cout << expired(started + seconds{4}, deadline) << '\n';
std::cout << expired(started + seconds{5}, deadline) << '\n';
std::cout << expired(started + seconds{6}, deadline) << '\n';
}false
true
trueThe default-constructed starting point is a controlled fixture value. A real operation would receive a point from Clock::now. The exact-equality case proves the inclusive rule. An arbitrary sleep would make the test slower and would not establish the boundary as clearly.
C++ Chrono Clocks, Durations and Dates FAQ
Why use steady_clock for a stopwatch?
Its contract is appropriate for monotonic elapsed timing, so wall-clock adjustments do not define the measurement. System_clock is useful for timestamps but can change with synchronization or manual settings. Use two points from the same clock and subtract them. High_resolution_clock's name does not independently promise monotonic behavior, because it can alias another implementation clock.
Does duration_cast round to the nearest unit?
For integral destination counts it truncates toward zero. A positive fifteen-hundred-millisecond duration becomes one whole second. If the requirement needs floor, ceiling or nearest rounding, use an appropriate operation under the target standard. Keep rounding policy explicit and review count range, especially when converting large inputs to a finer unit.
Is constructing year_month_day enough to validate a date?
No. It can hold an invalid component combination. Check ok before treating it as a valid calendar date. A February-thirty value can be representable as components without being an actual date. Business validation follows calendar validation: a real date may still be outside a booking window or forbidden by a scheduling rule.
Why not sleep to test expiration?
A sleep depends on scheduling and introduces unnecessary delay. Passing controlled time points tests before, at and after the boundary precisely. Real waiting belongs in a separate scheduling integration test when needed. A replaceable clock or explicit now argument keeps ordinary deadline logic deterministic and makes the inclusive or exclusive policy clear.
C++ Chrono Clocks, Durations and Dates Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A deadline expires at equality. Which test set establishes that rule?
C++ Chrono Clocks, Durations and Dates Quiz
2 questions to check what you learned.
Which clock is the ordinary choice for elapsed-operation deadlines?
- System_clock solely because it prints dates.
- Steady_clock.
- Any clock named high_resolution without checking its contract.
- System_clock, assuming wall-clock adjustments cannot affect elapsed comparisons.
What does integral duration_cast from 1500 milliseconds to seconds produce?
- 2 because it always rounds upward.
- 1.5 as an integral count.
- 1 by truncating the fractional part.
- 1500 because units are ignored.