He investigado este error, en realidad hay tres síntomas distintos que comparten una sola y misma causa.
La causa
El front utiliza dayjs para todos sus cálculos de fechas. Una caja del panel de control (EDF Tempo) cargaba el plugin timezone de dayjs para leer la hora de París.
El problema: dayjs.extend() modifica globalmente dayjs, para toda la aplicación. Y la biblioteca del calendario (react-big-calendar) contiene esta prueba:
// si el plugin de zona horaria está cargado, entonces usa la versión con zona horaria
const dayjs = dayjsLib.tz ? dayjsLib.tz : dayjsLib;
Tan pronto como el plugin está presente, el calendario cambia todos sus cálculos a dayjs.tz, que se comporta de manera diferente:
interpreta ciertas fechas como UTC → la cuadrícula horaria se desliza 2 horas
add(1, ‹ día ›) añade 24 horas absolutas, mientras que el día del cambio al horario de invierno dura 25 → se vuelve a las 23h del mismo día, de ahí la fecha mostrada dos veces
startOf(‹ mes ›) se desplaza una hora, lo que falsea la comparación de los meses → el sombreado se invierte
Punto importante: esta caja se importaba estáticamente con todas las demás, por lo que el plugin se cargaba incluso si nunca usabas EDF Tempo.
La corrección
En lugar de parchear el calendario, eliminé la causa: la caja EDF Tempo ahora utiliza Intl.DateTimeFormat (nativo, sin efectos secundarios) para leer la hora de París. El comportamiento del negocio es idéntico — las horas punta/valle siguen definidas en hora francesa sin importar tu zona horaria.
Queda una corrección en el calendario, para un error independiente de este: el día del cambio de hora mostraba 26 líneas horarias en octubre y 22 en marzo en lugar de 24, y los eventos estaban mal posicionados (una cita a las 14h se mostraba a las 13:26).