¿Cómo podemos ayudar?
¿Cómo gestionar automáticamente el horario de verano en la HMI mediante una macro?
En este post, le mostraremos cómo gestionar automáticamente el horario de verano en el HMI mediante una macro. El cambio de hora se aplicó por primera vez en 1916. Desde 2002, el cambio de hora se realiza de manera uniforme en todos los Estados miembros de la Unión Europea:
- el cambio al horario de verano tiene lugar la noche del último sábado de marzo
- el cambio al horario de invierno tiene lugar la noche del último sábado de octubre
Como recordatorio, los países de la UE están divididos actualmente en tres husos horarios:
- Europa Occidental (GMT): Irlanda y Portugal
- Europa Central (GMT 1): Alemania, Austria, Bélgica, Croacia, Dinamarca, Eslovaquia, Eslovenia, España, Francia, Hungría, Italia, Luxemburgo, Malta, Países Bajos, Polonia, República Checa y Suecia
- Europa del Este (GMT 2): Bulgaria, Chipre, Estonia, Finlandia, Grecia, Letonia, Lituania y Rumanía.
Con la herramienta HMI

En el Editor de macros, añada una nueva macro y cópiela.
#include "MacroInit.h"
// Returns the day-of-month of the last Sunday of a given month/year
// Uses the Tomohiko Sakamoto-inspired method (no division by 7 needed)
int LastSundayOfMonth(int month, int year)
{
// Day of week of the last day of the month (0=Sunday ... 6=Saturday)
// Zeller-like formula for day-of-week of day 1, then offset to last day
int y = year;
int m = month;
int daysInMonth;
switch(m)
{
case 1: daysInMonth = 31; break;
case 2:
if ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0))
daysInMonth = 29;
else
daysInMonth = 28;
break;
case 3: daysInMonth = 31; break;
case 4: daysInMonth = 30; break;
case 5: daysInMonth = 31; break;
case 6: daysInMonth = 30; break;
case 7: daysInMonth = 31; break;
case 8: daysInMonth = 31; break;
case 9: daysInMonth = 30; break;
case 10: daysInMonth = 31; break;
case 11: daysInMonth = 30; break;
case 12: daysInMonth = 31; break;
default: daysInMonth = 30; break;
}
// Compute day-of-week of the last day of the month
// Using Zeller's formula (0=Sunday, 1=Monday ... 6=Saturday)
if (m < 3) { m += 12; y -= 1; }
int k = y % 100;
int j = y / 100;
int dow = (daysInMonth + (13*(m+1))/5 + k + k/4 + j/4 - 2*j) % 7;
// Zeller returns: 0=Saturday, 1=Sunday, 2=Monday ... 6=Friday
// Normalize to: 0=Sunday, 1=Monday ... 6=Saturday
dow = (dow + 6) % 7;
// Subtract offset to reach last Sunday (dow==0)
return daysInMonth - dow;
}
void Macro_main(IN *p)
{
MarcoInit
// Automatic Daylight Saving Time (DST) management
// <!> This script must be called every minute
// DST start : last Sunday of March - 02:00 -> 03:00 (clock forward +1h)
// DST end : last Sunday of October - 03:00 -> 02:00 (clock back -1h)
// LocalBit[50000]: 0 = summer time, 1 = winter time (backed up on power failure)
// Catchup window: up to 6 days after the DST Sunday (HMI was powered off)
int minute = BCD2BIN(LocalWord[60001]); // Current minute
int hour = BCD2BIN(LocalWord[60002]); // Current hour
int day = BCD2BIN(LocalWord[60003]); // Day of month
int month = BCD2BIN(LocalWord[60004]); // Month
int year = BCD2BIN(LocalWord[60005]); // Year (needed for LastSundayOfMonth)
int dayOfWeek = BCD2BIN(LocalWord[60006]); // Monday=1 ... Sunday=7
// --- DST START : last Sunday of March at 02:00 -> 03:00 ---
// Window: from Sunday at 02:00 until the following Saturday at 23:59 (6 days catchup)
int lastSunMarch = LastSundayOfMonth(3, year);
int daysSinceDSTStart = day - lastSunMarch; // Positive if we are past the last Sunday
int dstStartWindow = (daysSinceDSTStart == 0 && hour >= 2) || // Sunday from 02:00
(daysSinceDSTStart >= 1 && daysSinceDSTStart <= 6); // Mon to Sat catchup
if ((LocalBit[50000] == 1) &&
(month == 3) &&
dstStartWindow)
{
LocalWord[60002] = BIN2BCD(hour + 1); // Shift clock forward by 1 hour
LocalBit[50000] = 0; // Flag: now summer time
}
// --- DST END : last Sunday of October at 03:00 -> 02:00 ---
// Window: from Sunday at 03:00 until the following Saturday at 23:59 (6 days catchup)
int lastSunOctober = LastSundayOfMonth(10, year);
int daysSinceDSTEnd = day - lastSunOctober;
int dstEndWindow = (daysSinceDSTEnd == 0 && hour >= 3) || // Sunday from 03:00
(daysSinceDSTEnd >= 1 && daysSinceDSTEnd <= 6); // Mon to Sat catchup
if ((LocalBit[50000] == 0) &&
(month == 10) &&
dstEndWindow)
{
LocalWord[60002] = BIN2BCD(hour - 1); // Shift clock back by 1 hour
LocalBit[50000] = 1; // Flag: now winter time
}
}
Añade la macro en la lista «Macro global» y establece su frecuencia de ejecución en 60000 ms (1 minuto) para actualizar el reloj cada minuto.

Cómo funciona
El programa se ejecuta cada minuto y lee la hora actual (minuto, hora, día, mes, año, día de la semana) de los registros.
Todos los valores de la hora se almacenan en formato BCD, por lo que primero se convierten a decimal.
Una función de ayuda calcula el número de días del mes actual, incluidos los años bisiestos.
Para detectar el último domingo del mes, el código comprueba: dayOfWeek == 7 (domingo) y day 7 > daysInMonth.
Si es el último domingo de marzo exactamente a las 03:00, el reloj se adelanta a las 04:00 (horario de verano).
Se fija un bit de bandera (LocalBit[65535]) para indicar el horario de verano.
Si es el último domingo de octubre exactamente a las 03:00, el reloj se adelanta a las 02:00 (horario de invierno).
El bit de bandera se conmuta para indicar el horario de invierno, evitando correcciones repetidas.
Esto evita bucles infinitos en los que la misma hora se procesaría varias veces.
Por tanto, el sistema mantiene la hora local sincronizada con las reglas oficiales del horario de verano de forma automática.
Puede sustituir LB65535 por LB51999 para respaldar el valor(tipos de registro HMI: LB, LW, RWI y registro de retención/guardado)
Véase también Cómo ajustar y sincronizar el reloj del ACE PLC con la HMI mediante una macro