Las fechas y horas en C++ se manejan con <chrono>, que desde C++20 trae calendarios, zonas horarias y formato. Aquí tienes lo que se usa a diario: la fecha actual, crear y formatear fechas, sumar días y meses sin crear fechas imposibles, diferencias, zonas horarias, leer fechas de un texto y medir tiempos.
También te puede interesar
Todo lo de fechas y horas en C++ está en <chrono>. Desde C++20 incluye calendarios, zonas horarias y formato con std::format, así que ya no hace falta recurrir a <ctime> ni a bibliotecas externas. Los ejemplos usan sobre todo fechas fijas, para que la salida que ves sea la misma que obtendrás tú.
La fecha y la hora actuales
#include <chrono>
#include <print>
int main()
{
using namespace std::chrono;
auto ahora = system_clock::now();
auto hoy = floor<days>(ahora);
std::println("hoy, en UTC: {:%Y-%m-%d}", hoy);
std::println("año {}", year_month_day{hoy}.year());
}salidahoy, en UTC: 2026-10-30 año 2026
system_clock::now() da el instante actual en UTC, con precisión de nanosegundos en Linux. Aquí lo redondeamos al día con floor<days>; con {:%Y-%m-%d %H:%M:%S} tendrías también la hora. La salida es la del día en que se compilaron estos ejemplos.
Crear fechas y formatearlas
#include <array>
#include <chrono>
#include <print>
int main()
{
using namespace std::chrono;
constexpr std::array dias{"domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"};
constexpr std::array meses{"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio",
"agosto", "septiembre", "octubre", "noviembre", "diciembre"};
year_month_day fecha = 2026y / October / 30;
sys_seconds momento = sys_days{fecha} + 15h + 30min;
std::println("{:%d/%m/%Y %H:%M}", momento);
std::println("{:%F %T}", momento);
std::println("{:%A %d %B}", momento);
weekday ds{sys_days{fecha}};
std::println("{} {} de {} de {}", dias[ds.c_encoding()], static_cast<unsigned>(fecha.day()),
meses[static_cast<unsigned>(fecha.month()) - 1], static_cast<int>(fecha.year()));
}salida30/10/2026 15:30 2026-10-30 15:30:00 Friday 30 October viernes 30 de octubre de 2026
La sintaxis 2026y / October / 30 construye un year_month_day y es difícil de confundir: el año lleva sufijo y y el mes tiene nombre. Los especificadores de formato son los de strftime (%d, %m, %Y, %H…). Los nombres de días y meses salen en inglés porque std::format usa el idioma «C» si no le pasas otro, y muchos servidores no tienen instalados los idiomas: lo más fiable es tener tus propias tablas, como aquí.
Sumar días y meses: la trampa del 31
#include <chrono>
#include <print>
int main()
{
using namespace std::chrono;
year_month_day fecha = 2026y / January / 31;
year_month_day mas_10_dias{sys_days{fecha} + days{10}};
std::println("31 de enero + 10 días = {}", mas_10_dias);
year_month_day mas_1_mes = fecha + months{1};
std::println("31 de enero + 1 mes = {} (¿válida? {})", mas_1_mes, mas_1_mes.ok());
// dos formas de arreglarlo: último día del mes o desbordar hacia el mes siguiente
auto ultimo = year_month_day_last{mas_1_mes.year(), month_day_last{mas_1_mes.month()}};
std::println("ajustada al último día del mes: {}", year_month_day{ultimo});
std::println("desbordando: {}", year_month_day{sys_days{mas_1_mes}});
}salida31 de enero + 10 días = 2026-02-10 31 de enero + 1 mes = 2026-02-31 is not a valid date (¿válida? false) ajustada al último día del mes: 2026-02-28 desbordando: 2026-03-03
Sumar días hay que hacerlo sobre sys_days, que es un número de días y siempre da una fecha válida. Sumar meses a un year_month_day solo cambia el mes, y el 31 de febrero no existe: ok() devuelve false y tienes que decidir qué quieres. Es el mismo dilema de cualquier calendario de pagos: ¿el 31 de enero más un mes es el 28 de febrero o el 3 de marzo?
Diferencia entre dos fechas y duraciones
#include <chrono>
#include <print>
int main()
{
using namespace std::chrono;
sys_days inicio = 2026y / March / 15;
sys_days fin = 2026y / October / 30;
std::println("días entre las dos fechas: {}", (fin - inicio).count());
auto reunion = 2h + 45min;
std::println("la reunión dura {} minutos", duration_cast<minutes>(reunion).count());
std::println("o {:%H:%M} en formato de reloj", reunion);
hh_mm_ss partes{seconds{9876}};
std::println("9876 s = {} h, {} min, {} s", partes.hours().count(),
partes.minutes().count(), partes.seconds().count());
}salidadías entre las dos fechas: 229 la reunión dura 165 minutos o 02:45 en formato de reloj 9876 s = 2 h, 44 min, 36 s
Restar dos instantes da una duración, con su unidad en el tipo: no hay que recordar si un número son segundos o milisegundos. duration_cast convierte entre unidades cuando se pierde precisión; las conversiones sin pérdida, como de horas a minutos, son automáticas.
Zonas horarias
#include <chrono>
#include <print>
int main()
{
using namespace std::chrono;
sys_seconds utc = sys_days{2026y / October / 30} + 15h;
for (const char* zona : {"America/Mexico_City", "America/Bogota", "Europe/Madrid", "Asia/Tokyo"}) {
zoned_time local{zona, utc};
std::println("{:<20} {:%d/%m %H:%M %Z}", zona, local);
}
// del horario de verano al de invierno en Madrid, el 25 de octubre de 2026
for (auto dia : {2026y / October / 24, 2026y / October / 26}) {
zoned_time m{"Europe/Madrid", sys_days{dia} + 12h};
std::println("Madrid {}: UTC{:%z}", dia, m);
}
std::println("base de zonas horarias: {}", get_tzdb().version);
}salidaAmerica/Mexico_City 30/10 09:00 CST America/Bogota 30/10 10:00 -05 Europe/Madrid 30/10 16:00 CET Asia/Tokyo 31/10 00:00 JST Madrid 2026-10-24: UTC+0200 Madrid 2026-10-26: UTC+0100 base de zonas horarias: 2026c
zoned_time combina un instante con una zona de la base de datos de la IANA, la misma que usan los sistemas operativos, y aplica los cambios de horario de verano. Desde 2022 México no cambia la hora (salvo en algunos municipios de la frontera), y la base de datos ya lo recoge; por eso Ciudad de México sale en UTC-6 todo el año, mientras que Madrid pasa de UTC+2 a UTC+1 a finales de octubre. Si tu programa calcula horas locales, mantén actualizada la base de zonas horarias del sistema: los gobiernos cambian sus reglas con frecuencia.
Leer una fecha escrita como texto
#include <chrono>
#include <print>
#include <sstream>
int main()
{
using namespace std::chrono;
for (const char* texto : {"2026-10-30 15:30", "2026-02-30 10:00", "30/10/2026"}) {
std::istringstream in(texto);
sys_time<minutes> t;
in >> parse("%Y-%m-%d %H:%M", t);
if (in.fail()) {
std::println("«{}»: no es una fecha válida", texto);
} else {
std::println("«{}»: {:%d/%m/%Y a las %H:%M}", texto, t);
}
}
}salida«2026-10-30 15:30»: 30/10/2026 a las 15:30 «2026-02-30 10:00»: no es una fecha válida «30/10/2026»: no es una fecha válida
std::chrono::parse lee una fecha con el mismo formato que se usa para escribirla y rechaza las fechas imposibles, como el 30 de febrero. Es mucho más seguro que partir el texto a mano.
Medir cuánto tarda algo
#include <chrono>
#include <print>
#include <thread>
int main()
{
using namespace std::chrono;
auto t0 = steady_clock::now();
std::this_thread::sleep_for(120ms); // aquí iría el trabajo que quieres medir
auto t1 = steady_clock::now();
auto ms = duration_cast<milliseconds>(t1 - t0);
std::println("tardó al menos 120 ms: {}", ms >= 120ms);
}salidatardó al menos 120 ms: true
Para medir intervalos usa steady_clock, nunca system_clock: el reloj del sistema puede saltar hacia delante o hacia atrás cuando se sincroniza con un servidor de hora o cuando alguien lo cambia, y tu medición saldría negativa o enorme. steady_clock solo avanza. (El ejemplo imprime si tardó al menos 120 ms y no el número exacto, que cambia en cada ejecución.)
Errores típicos con fechas
- ErrorConsecuenciaSolución
- Medir con
system_clockMediciones negativas si el reloj se ajustasteady_clock - Sumar meses sin comprobarFechas como el 31 de febreroMirar
ok()y decidir cómo ajustar - Guardar horas localesAmbigüedades en el cambio de horarioGuardar en UTC y convertir al mostrar
- Calcular husos horarios a manoFallan cuando un país cambia sus reglas
zoned_timecon la base de la IANA - Partir fechas con
substrAcepta fechas imposiblesstd::chrono::parse
Con <chrono>, el problema del año 2038 de time_t de 32 bits no te afecta: system_clock usa enteros de 64 bits, con margen para cientos de años.
Todo el código de esta página se compiló con GCC 15.3 (C++23, -std=c++23 -Wall -Wextra, sin avisos) y se ejecutó en Linux, en un contenedor limpio sin acceso a la red, antes de publicarla; las salidas están copiadas de esa ejecución.