Desarrollo

Las 02:30 que pasan dos veces: el cambio de hora por dentro

Mañana a las tres vuelven a ser las dos, y la hora que va de las 02:00 a las 02:59 pasa dos veces. PHP y JavaScript resuelven la misma fecha con una hora de diferencia, el domingo tiene veinticinco horas, cron lo lleva mejor de lo que se dice y la base de datos tz va detrás de gobiernos que avisan con tres días.

Mañana, domingo 25 de octubre, a las tres de la madrugada vuelven a ser las dos. En toda la Unión Europea el cambio ocurre en el mismo instante —la directiva 2000/84/CE lo fija a la 01:00 UTC del último domingo de octubre—, así que en la península se retrasa de 03:00 a 02:00 y en Canarias, de 02:00 a 01:00.

Para nosotros es una hora más de sueño. Para un ordenador es una hora en la que «las 02:30» deja de identificar un instante.

Dos despertadores digitales sobre una mesa marcan las 02:30, el de la izquierda en ámbar con la etiqueta UTC+2 y el de la derecha en verde con UTC+1, unidos por dos flechas luminosas que forman un bucle

Las mismas 02:30, primero en horario de verano y una hora después en el de invierno. Ilustración generada con IA para este artículo.

Una hora con dos 02:30

Entre las 02:00 y las 02:59 de mañana, cada minuto del reloj de pared pasa dos veces: primero en horario de verano (CEST, UTC+2) y una hora después en horario de invierno (CET, UTC+1). Lo he ejecutado en el contenedor PHP de esta web, recorriendo la noche en UTC de media en media hora:

00:00 UTC  →  02:00 CEST
00:30 UTC  →  02:30 CEST
01:00 UTC  →  02:00 CET
01:30 UTC  →  02:30 CET

Cuatro instantes distintos, dos lecturas del reloj. En marzo pasa lo contrario: el 28 de marzo de 2027 las 02:30 no existen, porque a las dos el reloj salta a las tres.

Y aquí viene la parte friki. Si a un programa le das la cadena 2026-10-25 02:30 en hora de Madrid, tiene que elegir cuál de las dos es. No todos eligen la misma:

$tz = new DateTimeZone('Europe/Madrid');
echo (new DateTimeImmutable('2026-10-25 02:30', $tz))->format('H:i T');
// PHP 8.3:  02:30 CET   → la segunda, 01:30 UTC
// Node 24, con TZ=Europe/Madrid
new Date(2026, 9, 25, 2, 30).toISOString();
// '2026-10-25T00:30:00.000Z'  → la primera, la de verano

La misma hora local, dos lenguajes, una hora de diferencia. Ninguno de los dos está mal: es la hora local la que no dice lo suficiente. Una fecha local sin desfase es un número sin unidad, que es exactamente lo que perdió el Mars Climate Orbiter. Ahí ya dejé caer la pregunta de «¿y cuando cambia la hora en octubre?». Pues es mañana.

El domingo tiene veinticinco horas

La otra trampa es más tonta y más común: dar por hecho que un día son veinticuatro horas. El domingo 25 tiene veinticinco. Salida real, también en PHP 8.3:

$sabado = new DateTimeImmutable('2026-10-24 12:00', $tz);

$sabado->modify('+1 day');                  // Sun 12:00 CET
$sabado->modify('+24 hours');               // Sun 12:00 CET   ← ojo
$sabado->add(new DateInterval('PT24H'));    // Sun 11:00 CET

Las dos primeras dan el mediodía del domingo: veinticinco horas reales después. Solo la tercera suma veinticuatro horas de reloj de verdad. La de +24 hours es la que engaña: se lee como una duración y se comporta como un salto de calendario.

En JavaScript la frontera se ve igual: setDate(getDate() + 1) te deja a las 12:00, y sumar 24 * 3600 * 1000 milisegundos te deja a las 11:00. Lo que cuenta es tener claro cuál de las dos cosas quieres:

  • «Mañana a la misma hora» es calendario. El recordatorio diario, la reserva, la cita. Se suma un día.
  • «Dentro de 24 horas» es duración. La caducidad de un token, el plazo de un SLA, el TTL de una caché. Se suman segundos.

Muchos fallos del cambio de hora son una de las dos disfrazada de la otra.

Lo que hace cron (mejor de lo que yo dije)

A cuenta de Gremlins y la norma de la medianoche escribí que en el cambio de hora de octubre «el proceso nocturno se ejecuta dos veces». Al preparar este artículo he ido a leer el manual, y con el cron de Debian o el de Red Hat, no es verdad.

El cron de Debian, heredero del de Paul Vixie, y cronie, el de la familia Red Hat, tratan aparte los saltos de reloj de menos de tres horas:

  • Si el reloj se adelanta (marzo), las tareas que caían en la hora que desaparece se ejecutan nada más producirse el salto.
  • Si se atrasa (octubre), las tareas de la hora repetida no se vuelven a ejecutar.
  • Esto solo vale para las tareas a hora fija. Las que llevan * en el minuto o en la hora se ejecutan según la hora nueva, sin más.
  • Un salto de más de tres horas se interpreta como una corrección del reloj, no como un cambio de horario.

En FreeBSD, en cambio, ese tratamiento es una opción del demonio (-s) que hay que activar.

Pero la garantía es de ese cron, no de «cron» como idea. En cuanto la programación vive en tu aplicación, depende de cómo esté escrita. Así que he ido a mirar el scheduler de esta misma web, que es casero: un cron del sistema lo despierta cada minuto y él compara la hora de pared de Madrid con la expresión de cada tarea. Si hubiera una tarea a las 30 2 * * *, el domingo se ejecutaría dos veces, y el 28 de marzo, ninguna. No la hay: las tareas están a las 04:00, a las 08:00, a las 08:15 y cada minuto. Y así se van a quedar.

Europe/Madrid, y no «CET»

Todo esto funciona porque existe una base de datos que lleva la cuenta de la hora oficial de cada sitio del mundo desde 1970 —y bastante de antes—: la tz database, o tzdata, o «la de Olson». La empezó Arthur David Olson, hoy la coordinan Paul Eggert y Tim Parenti con un puñado de voluntarios, la aloja la IANA y el procedimiento para mantenerla está escrito en una RFC, la 6557. Es de dominio público, y la usan tu móvil, tu servidor y tu navegador.

Sus identificadores tienen la forma Area/Location —Europe/Madrid, Atlantic/Canary— y usan ciudades, no países, porque las fronteras y los gobiernos cambian más que las ciudades.

¿Por qué no basta con escribir «CET»? Porque una abreviatura no dice las reglas:

  • CST es la hora central de Norteamérica, la de China y la de Cuba. Las tres están en la propia base de datos.
  • CET es lo que usa Madrid en invierno, y también lo que usa Argelia todo el año, sin horario de verano.
  • Un desfase fijo como +01:00 sirve para el invierno de Madrid, pero no para su verano.

Y dentro de Europe/Madrid va la historia entera. Leída en el fichero fuente, es esta:

  • Hasta el 1 de enero de 1901, hora solar media de Madrid: 14 minutos y 44 segundos por detrás de Greenwich.
  • De 1901 a 1940, hora de Greenwich, la que nos corresponde por meridiano.
  • El 16 de marzo de 1940 a las 23:00, una orden ministerial adelanta la hora legal sesenta minutos. No se ha deshecho: desde entonces vamos con la hora de Berlín.

Canarias, por cierto, fue una hora por detrás de Greenwich hasta el 30 de septiembre de 1946. Esa es la segunda razón para no usar abreviaturas: si mañana tienes clientes allí, su hora repetida es la 01:00, no las 02:00.

Los gobiernos avisan tarde

La base de datos no se actualiza sola: alguien tiene que enterarse de que un país ha cambiado sus reglas, escribirlo y publicar una versión nueva. La IANA pide que cualquier cambio se anuncie al menos con un año de antelación. Los gobiernos no siempre lo hacen:

  • Samoa se cambió de lado de la línea internacional de cambio de fecha a finales de 2011. Para sus relojes, al 29 de diciembre a las 23:59:59 le siguió el 31 de diciembre a las 00:00. El día 30 de 2011 no existe en Samoa, y PHP lo sabe.
  • Egipto había anunciado que recuperaba el horario de verano el 7 de julio de 2016. El gobierno lo canceló tres días antes, y la versión 2016f de tzdata salió el 5 de julio.
  • Marruecos aprobó el 26 de octubre de 2018 quedarse en horario de verano permanente, cuando el cambio tocaba el 28. La versión 2018g salió esa misma noche, hora de California.

Y este año ha sido especialmente movido: Marruecos volvió a la hora de Greenwich el 20 de septiembre, y varias provincias y territorios de Canadá han dejado de cambiar la hora. La última, Manitoba, que tenía que retrasar el reloj una semana después que nosotros, lo anunció el 17 de septiembre: seis semanas de aviso. Cuando escribo esto van cinco versiones de tzdata en 2026, de la 2026a a la 2026e.

Ahora la parte incómoda. Que haya una versión nueva no significa que la tengas. Tiene que llegar al sistema operativo, al runtime del lenguaje, al móvil. PHP, por ejemplo, lleva su propia copia compilada dentro en su versión estándar, y su documentación remite a la extensión PECL timezonedb para tenerla al día. El contenedor de desarrollo de esta web ni siquiera tiene tzdata instalado en el sistema, y su PHP dice esto:

echo timezone_version_get();   // 2026.1  (la 2026a, de marzo)

Con esa versión, a las 12:00 UTC de hoy, Casablanca le sale a las 13:00. Desde el 20 de septiembre son las 12:00. El dato correcto se publicó en julio y aquí no ha llegado.

No es un problema solo mío. En octubre de 2010 las alarmas recurrentes de los iPhone sonaron una hora tarde en Europa el lunes después del cambio de hora. El fallo se había visto semanas antes en Australia, que cambia en otra fecha, y Apple ya había reconocido el error y prometido arreglarlo. La fecha de Europa se sabía con años de antelación. Saber que algo va a pasar no es lo mismo que haber desplegado el arreglo.

¿Y no lo iba a quitar la UE?

En 2018 la Comisión Europea abrió una consulta pública: 4,6 millones de respuestas, un 84 % a favor de acabar con el cambio y una participación muy desigual —respondió el 3,79 % de la población alemana y el 0,19 % de la española—. Presentó la propuesta ese mismo año, el Parlamento Europeo la aprobó el 26 de marzo de 2019 y el último cambio iba a ser en 2021.

Y ahí se quedó. Hace falta el Consejo, y el Consejo no se pone de acuerdo: cada país tendría que elegir si se queda en verano o en invierno para siempre, y nada impide que dos vecinos elijan distinto. La ficha del propio Parlamento marca hoy el expediente como bloqueado; en octubre de 2025 se volvió a debatir en el pleno, y la Comisión ha decidido hacer un estudio más.

No tengo una conclusión cómoda sobre esto. Como usuario quiero que se acabe. Como programador, me da igual qué decidan siempre que lo anuncien con tiempo, porque el día que se acabe el cambio de hora seguiremos necesitando la base de datos: todas las marcas de tiempo de 2026 seguirán siendo de verano o de invierno, y alguien tendrá que saber cuál.

Feliz cambio de hora

Cuatro cosas para hacer hoy, antes de que pasen las dos de la madrugada dos veces:

  1. Distingue instantes de citas. Lo que ya ha pasado —un pedido, un log, un acceso— es un instante: se guarda en UTC y se convierte a Europe/Madrid al pintarlo. Lo que se repite a una hora local —«todos los días a las 08:00 en Madrid»— es calendario: se guarda la hora local y la zona, y se calcula cada vez. Nunca un +01:00 fijo ni un «CET» escrito a mano.
  2. Revisa el crontab y el scheduler de tu aplicación y busca cualquier tarea local entre las 02:00 y las 02:59 —entre la 01:00 y la 01:59 si hay algo en Canarias—. Si la hay, muévela.
  3. Mira qué versión de tzdata usa de verdad tu runtime, no la del sistema: php -r 'echo timezone_version_get();'. Si es de antes de julio de 2026, Marruecos ya te sale mal.
  4. Añade dos fechas a tus tests: 2026-10-25 y 2027-03-28. Y en cada suma, pregúntate si querías un día o veinticuatro horas.

(Por eso este artículo sale a las 02:30 del sábado y no del domingo: la del sábado solo pasa una vez.)

Preguntas frecuentes

¿Qué ocurre con la hora entre las 02:00 y las 03:00 el 25 de octubre de 2026?

En la España peninsular y Baleares, a las 03:00 de horario de verano (CEST, UTC+2) los relojes vuelven a las 02:00 de horario de invierno (CET, UTC+1). Toda la franja de 02:00 a 02:59 se vive dos veces, de modo que una hora local como 02:30 corresponde a dos instantes distintos: 00:30 y 01:30 UTC. En Canarias la franja repetida va de 01:00 a 01:59. El cambio se produce en toda la Unión Europea a la vez, a la 01:00 UTC del último domingo de octubre, según la Directiva 2000/84/CE.

¿Se ejecuta dos veces una tarea de cron programada a las 02:30 el día del cambio de hora?

Depende del programa que la ejecute. El cron de Debian, derivado del de Paul Vixie, y cronie, usado en la familia Red Hat, tratan los saltos de reloj de menos de tres horas de forma especial: en otoño no repiten las tareas a hora fija de la franja duplicada y en primavera ejecutan inmediatamente las que caían en la hora suprimida. Las tareas con comodines en el minuto o la hora siguen la hora nueva. En FreeBSD ese tratamiento requiere la opción -s. Un planificador propio que compare la hora local minuto a minuto ejecutaría la tarea dos veces en octubre y ninguna en marzo.

¿Por qué se recomienda usar Europe/Madrid en lugar de CET como zona horaria?

Las abreviaturas no identifican reglas, solo un desfase y a veces ni eso: CST designa a la vez la hora central de Norteamérica, la de China y la de Cuba, y CET la usan tanto Madrid en invierno como Argelia durante todo el año, sin horario de verano. Un identificador de la base de datos tz como Europe/Madrid contiene las reglas y los cambios históricos registrados para esa zona, incluidos los horarios de verano, y se actualiza cuando cambian las decisiones legales.

¿Qué es la base de datos tz y quién la mantiene?

La tz database, también llamada tzdata o base de datos de Olson, recopila las reglas de hora oficial de cada región del mundo y su historia. La inició Arthur David Olson, la coordinan Paul Eggert y Tim Parenti con voluntarios, la aloja la IANA y su procedimiento de mantenimiento está descrito en la RFC 6557. Es de dominio público y la utilizan sistemas operativos, móviles, navegadores y lenguajes de programación. Publica versiones nuevas cuando un país cambia sus reglas, y la IANA recomienda que esos cambios se anuncien con al menos un año de antelación.

¿Por qué no se ha eliminado el cambio de hora en la Unión Europea?

La Comisión Europea lo propuso en 2018, tras una consulta pública con 4,6 millones de respuestas en la que el 84 % se mostró a favor de suprimirlo, y el Parlamento Europeo aprobó su posición el 26 de marzo de 2019 por 410 votos a favor, 192 en contra y 51 abstenciones. La directiva necesita también el acuerdo del Consejo, que no se ha alcanzado: los Estados pidieron una evaluación de impacto y coordinar qué horario conservaría cada uno. El Parlamento mantiene el expediente como bloqueado y el asunto se volvió a debatir en el pleno en octubre de 2025.

Otras noticias

El blog, día a día

96 artículos publicados 25 abr 2026 — 24 abr 2027

¿Te ha resultado útil o quieres comentar algo?

Hablemos →