Compensación automática del horario de verano para sus proyectos de reloj
Muy temprano en el desarrollo de mi reloj de ping-pong, se me ocurrió la idea de la compensación automática de la luz del día. Es una característica interesante, pero es un lujo, así que pensé que podría agregarla como una mejora futura. Ahora es el momento y estoy informando sobre lo que he aprendido y cómo puede agregar esto a sus propios proyectos.
Hay dos cosas a considerar antes de agregar esta función. ¿Vale la pena el esfuerzo y hace que el reloj sea más confuso que fácil de usar?
Sobre el último, si es responsable de establecer la hora inicialmente, pero usted no es responsable de poner a cero el reloj cuando caemos hacia atrás o saltamos hacia adelante, ¿causará confusión? Quizás al principio, pero el RTC respaldado por batería que utilicé en mi proyecto debería significar que lo configuró una vez y nunca tendrá que reiniciarlo. La única excepción es el horario de verano y lo compensé.
Ya sea que valga la pena o no, es difícil responder hasta después del hecho. Debe tener en cuenta que las reglas de DST no están escritas en piedra, cambian de vez en cuando. Agregue a eso el hecho de que no todas las partes del mundo siguen la práctica. Esto significa que no solo debe implementar la compensación, sino que debe agregar un método para activarla y desactivarla y también cambiar las reglas para cuando se observe.
Únase a mí después del descanso para aprender el método y el código que utilizo para hacer ajustes de tiempo automáticamente dos veces al año.
Implementación
Hay dos partes que intervienen en la implementación de la compensación DST. La primera parte es averiguar si actualmente estamos observando el horario de verano o si la hora estándar es válida. La segunda parte del problema es desarrollar un método para realizar la compensación sin preocuparse por cómo funciona el reloj o cómo está configurado.
Esta publicación abordará solo el primer problema; cómo decidir cuándo entrará en vigor el horario de verano. Estoy planeando una segunda publicación para detallar la mecánica necesaria para usar esa información.
El problema
Fuente de la imagen: Wikimedia Commons
El horario de verano no comienza en un día específico, como el 15 de abril de cada año. En cambio, comienza en un día específico de la semana. Estados Unidos está observando actualmente el inicio del horario de verano a las 2:00 am del segundo domingo de abril. El problema se agrava por el hecho de que esta norma legislativa cambia de vez en cuando, la última vez que se modificaron las normas estadounidenses en 2007. Para cumplir con la compensación, debemos ser capaces de responder a la pregunta: ¿cuál es la fecha exacta en que comienza el horario de verano? ?
Necesitaremos un algoritmo que tome las reglas basadas en el día de la semana (DOW) y las traduzca en una respuesta de fecha precisa. ¿Cuál es la cantidad mínima de información de entrada requerida para que el algoritmo siga funcionando? Vamos a averiguar.
Elaborándolo
Si pudiera encontrar la fecha en que comienza el horario de verano, ¿cómo lo haría? Dado que el horario de verano comienza el segundo domingo de abril, ¿cuál fue la fecha en que comenzó el año pasado (2011)? Naturalmente, miraría un calendario de abril de 2011 y contaría los martes hasta llegar al segundo. Eso es exactamente lo que hará nuestro algoritmo, excepto que no necesita tener un calendario antes. Para resolver nuestro problema, establezcamos explícitamente el método que queremos usar:
- Encuentra el día de la semana del primer día del mes dado.
- Aumente la fecha hasta que alcance el día objetivo de la semana.
- aumentar las semanas hasta llegar a la fecha objetivo.
La única información útil que obtenemos al mirar un calendario es en qué día de la semana comenzó el mes. Afortunadamente, esto es fácil de calcular y solo tiene que hacer una búsqueda rápida en Google.
Cálculo del día de la semana para cualquier fecha
Wikipedia tiene un artículo muy bueno sobre cómo calcular el día de la semana. Gran parte del tiempo empleado en este cálculo se utiliza para establecer si el año es bisiesto o no. El resto del cálculo involucra tablas de búsqueda para obtener la respuesta final. Afortunadamente, los cálculos DOW son un problema común, por lo que hay varios algoritmos optimizados disponibles para la tarea. Elegí usar el algoritmo de Sakamoto porque ya está escrito en C y es bastante simple.
int dow(int y, int m, int d) { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; }
Explicar exactamente cómo funciona esto es un ejercicio que puede reservar para un día lluvioso. Cuando intente resolverlo, asegúrese de buscar las reglas de prioridad. Tenga en cuenta que "m <3" se evaluará primero, devolviendo 0 si es falso y 1 si es verdadero. También nos gustaría señalar que las operaciones de tres divisiones en la tercera línea de código se utilizan para ajustar los años bisiestos.
Encuentra el día de inicio correcto
Ahora que sabemos el día de la semana del primer día del mes, necesitamos encontrar la fecha de la primera ocasión de nuestro día objetivo. Si asignamos nuestro día objetivo a la variable "DOW", un día de la semana el primer día del mes a "firstDOW", es simple usar un ciclo para extender hasta que alcancemos nuestro objetivo:
char targetDate = 1; while (firstDOW != DOW){ firstDOW = (firstDOW+1)%7; targetDate++; }
Este bucle de código saldrá cuando la variable "targetDate" coincida con la primera ocasión de nuestro día objetivo. Pero eso solo resuelve parte del problema. También debemos identificar la fecha para la segunda, tercera u otra ocasión de ese día en el mes.
Ajustar la fecha objetivo para calcular semanas
Si buscamos el segundo domingo de un mes determinado, podemos asignar el número 2 a la variable "NthWeek":
targetDate += (NthWeek-1)*7;
Este fragmento rápido agregará siete a la fecha de cada semana después de la primera ocasión de nuestro día objetivo. Como resto uno antes de multiplicar por 7 (el número de días en una semana) no se sumará nada si buscamos el primer domingo del mes.
Poniendolo todo junto
Si envolvemos todo nuestro código en un paquete agradable, terminaremos con una función que devuelve la fecha en función de la información de entrada. Para mantener el enfoque en el problema, primero definí la información que se transmitirá en la función y lo que planeo recibir de ella:
- Entradas: año, mes, día de la semana (por ejemplo, domingo = 0), semana del mes (por ejemplo, segundo domingo del mes = 2)
- Salida: número entero que representa la fecha (por ejemplo: 15 sería el día 15 del mes)
Debido a que nuestro primer paso depende de otro algoritmo, debe incluirse en nuestro paquete. Lo cambié solo ligeramente reemplazando tantos tipos de datos int como sea posible con char. Dependiendo del compilador que esté utilizando, esto puede terminar ahorrando en RAM.
#include <stdio.h> char buff[50]; int myYear = 2011; char myMonth = 04; char myDOW = 0; char myNthWeek = 2; /*-------------------------------------------------------------------------- FUNC: 6/11/11 - Returns day of week for any given date PARAMS: year, month, date RETURNS: day of week (0-7 is Sun-Sat) NOTES: Sakamoto's Algorithm http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week#Sakamoto.27s_algorithm Altered to use char when possible to save microcontroller ram --------------------------------------------------------------------------*/ char dow(int y, char m, char d) { static char t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; } /*-------------------------------------------------------------------------- FUNC: 6/11/11 - Returns the date for Nth day of month. For instance, it will return the numeric date for the 2nd Sunday of April PARAMS: year, month, day of week, Nth occurence of that day in that month RETURNS: date NOTES: There is no error checking for invalid inputs. --------------------------------------------------------------------------*/ char NthDate(int year, char month, char DOW, char NthWeek){ char targetDate = 1; char firstDOW = dow(year,month,targetDate); while (firstDOW != DOW){ firstDOW = (firstDOW+1)%7; targetDate++; } //Adjust for weeks targetDate += (NthWeek-1)*7; return targetDate; } int main(void){ //Used to test on a computer sprintf(buff,"%i",NthDate(myYear,myMonth,myDOW,myNthWeek)); printf("%sn",buff); }
Escribí esto con la intención de usarlo en un microcontrolador, pero solo para probar, incluí algunas E / S para la retroalimentación de la computadora. Puede eliminar las dos primeras líneas y toda la función principal e insertarla en su proyecto. En la caja de Linux, compílelo con GCC usando: "gcc -o dst dst.c" y luego ejecútelo con "./dst".
Conclusión
Esta es una cantidad bastante pequeña de código que no requiere mucha potencia de procesamiento para su uso. Si puede agregar la opción para establecer la fecha actual a su proyecto de reloj, esto es rápido para desplazarse al código. Lo único en lo que debe trabajar es en cómo configurar y almacenar el tiempo. Esto será diferente dependiendo de si el horario de verano es válido o no.
Como nota al margen. Terminé reutilizando este código para una de las preguntas del Proyecto Euler. Supongo que el trabajo ya está dando sus frutos.
Recursos
Código de muestra
Jeff Epler dice:
Para un proyecto de reloj GPS sin terminar, escribí un código para convertir la información de la zona horaria de Unix en una tabla adecuada para incluirla en el firmware de mi reloj; Una tabla de 78 entradas (624 bytes) es suficiente para mantener el segundo de cada cambio de horario de verano desde 2000 hasta el final de la era similar a Unix; Es poco probable que las reglas de DST en la jurisdicción de su área cambien en los próximos 20 años de todos modos (o que se traslade a una jurisdicción con una compensación o regla de DST diferente en un período de tiempo similar).
Una de las ventajas de WWVB es que lleva dos bits que ayudan al reloj a aplicar el horario de verano en los momentos adecuados. En los Estados Unidos, el usuario del reloj "atómico" de WWVB sólo necesita indicar si se observa el horario de verano en esta jurisdicción y la compensación del horario de verano de invierno. Dado que hay 4 zonas horarias en las 48 inferiores, puede lograr esto con 3 interruptores DIP o puentes. (No creo que WWVB se reciba en Alaska, Hawái o Puerto Rico, pero podría estar equivocado; en ese caso, necesitará 4 interruptores DIP)
selvkumarv dice:
Cómo encontrar la factura del horario de verano del último domingo de marzo. Cómo dar entrada para el último domingo.
char NthDate (año int, mes char, char DOW, char NthWeek)
¿Cómo represento el último domingo de "NthWeek"?
Ej .: En octubre de 2011, hay 5 domingos.Arduino dice:
Ah, sí, la regla de la UE :-): ¿por qué hacerlo más fácil si podemos complicar las cosas?
Alex Clockman dice:
También estoy interesado en algún lugar (el último domingo) de un mes de fiesta como Na Week. ¿Usted o alguien más entendió esto?
kiwi dice:
Puede seguir estos pasos si necesita "el último domingo" de un mes determinado:
// 1. Descubra qué día de la semana es el comienzo del mes
dow_march = dow (pequeño_año, MARZO, MES_DÍA_1);
dow_october = dow (little_year, OCTUBRE, MONTH_DAY_1);// 2. Averigua cuántos domingos habrá en marzo y octubre de ese año
// Válido para Mar y Okto porque ambos tienen 31 días
para (día_i = DÍA_1_EL MES; día_i 6) dow_walk = 0;// —-
if (dow_october == DOW_SUNDAY) how_of_October ++;
if (++ dow_october> 6) dow_october = 0;
}// 3. Usa la rutina de arriba para saber la fecha de finalización
// PD: También puede mantener el último día del mes registrado cuando se encontró el último domingo en el paso no. 2 y omita el paso núm. 3
Gregmac dice:
Tengo un reloj que lo configura yo mismo a través de WWVB y tiene un botón debajo que le permite cambiar el horario de verano. Esto resuelve varios problemas:
* Permite un ajuste sencillo ("un toque") de DST por parte del usuario sin tener que desplazarse para establecer la hora normalmente
* Si no respeta el horario de verano, simplemente no toque el botón
* Si las reglas de DST cambian, no importa, porque el usuario tiene que presionar el botón en el momento adecuado de todos modos
* El reloj no necesita saber la fecha.
* El reloj no necesita conocer las reglas de DST
* El reloj no necesita una forma de actualizar las reglas de DST (interfaz de usuario compleja o conectividad de red)Si bien es preferible no tener que tocarlo en absoluto que incluso un botón de un solo toque, la molestia adicional de ajustar las reglas del horario de verano realmente no vale la pena cuando solo se puede agregar un botón simple. Si tiene conectividad de red de todos modos, asegúrese de actualizar las reglas automáticamente (aunque tenga en cuenta que cualquier infraestructura que utilice para hacerlo debe continuar existiendo durante la vida útil del reloj).
Realmente me gusta el botón como una solución simple, elegante y básicamente nunca desactualizada.
Nuevo comentario1283 dice:
Nunca he sido fanático de ningún tipo de horario de verano en ningún sentido de la palabra, ¿eso me hace decidir sobre esto? ¿Problema de horario de verano? partia? lolz jes ...
Cualquiera, creo que el aspecto del reloj de ping pong es increíble.
Sugeriría usar una ventana de pantalla de color que mejore el contraste (no difusa) ...
y hazlo "abrible" para un "brillo de luz nocturna adicional 🙂 ... o brillo automático con un interruptor de reemplazo 🙂Geoffrey dice:
Buen trabajo.
En los Estados Unidos, el horario de verano comienza el segundo domingo de marzo y termina el primer domingo de noviembre.Chetchez dice:
Es hora de verano, no hora de verano
Mike Szczys dice:
Así es, gracias por señalarlo. Reparado.
eco delta dice:
¡No hay nada de ahorro en eso! Durante más de 30 años, Indiana ha demostrado que utilizamos más energía que si nos dejaran solos lo suficiente. Este reloj no puede mostrar UTC. El reloj de cristal en frente de esta publicación puede. Esta es la forma de jugar golf jugando a gatos gordos en Washington y otros tipos de élite que juegan este viejo juego en un mundo 24/7.
JB dice:
Si. Todo se trata de control. No hay necesidad de esa estúpida regla. Las estadísticas también muestran que hay más accidentes automovilísticos cuando ocurre el "cambio".
Roger Wilco dice:
El horario de verano no está actualizado y deberíamos eliminarlo. Las personas que puedan necesitar adaptarse pueden hacerlo (levantarse más temprano o más tarde según la época del año). No hay razón para que todo un país cambie la hora, lo que genera confusión.
JB dice:
Así es. El horario de verano también causa más accidentes automovilísticos. Si alguien necesita levantarse más temprano, hágalo, pero no moleste al resto del país. Todo lo que diga "ahorro" se elimina si tienes que levantarte más temprano y encender las luces porque todavía está oscuro afuera. ¡Reglas estúpidas!
Arduino dice:
Totalmente de acuerdo. No hay ahorros. Incluso obstaculiza muchas profesiones. Las vacas quieren ser ordeñadas en un momento determinado. No puedes decirles que tienen que esperar una hora porque de repente es invierno otra vez
Ceniza dice:
¡Gran trabajo! En una nota diferente: ¿la luz del día realmente ahorra energía?
Alex dice:
hola, gran trabajo, pero ¿y si lo incluyo en el boceto de arduino?
SuperFabius dice:
Hola a todos
usando algo de aritmética modular es posible optimizar el algoritmo de Sakamoto para una MCU de 8 bits, usando solo una variable de byte y limitando un año en el rango. [2000-2099]:dow = (yy1 + (yy1 + 3) / 4 + t[month-1] + día) MOD 7
donde: yy1 [5-104] = año 1995 (año 2000 a 2099) o: yy1 [5-104] = año + 5 (año de 00 a 99)
mes [1-12]día [1-31]
/ es la división entera
dow [0-6] (Dom = 0, lun = 1,… sáb = 6)A continuación, se muestra un ejemplo de una implementación de ensamblaje para PIC de rango medio 16F:
; ******************************************
Aporte:
; día [1-31] -> RTC1_SYSday
; mes [1-12] -> RTC1_SYSmth
; año [0-99] -> RTC1_SYSyar
;
;
; Salida: día de la semana [0-6] -> W
; ******************************************
RTC1_dow_var UDATARTC1_SYSday res 1
RTC1_SYSmth res 1
RTC1_SYSyar res 1
RTC1_yy1 res 1
RTC1_yy2 res 1RTC1_dow_code CÓDIGO
Sub_RTC1_dow
calcular yy1 = año + 5
banksel RTC1_yy1
5 ′ movlw
addwf RTC1_SYSyar, w; W = año + 5
movwf RTC1_yy1; yy1 = año + 5mes yy1 = yy1-1
movf RTC1_SYSmth, w
añadir -3
btfss ESTADO, C; mes> = 3 (C = 1)?
decf RTC1_yy1, f; ne, yy1 = yy1-1
Sí continuarcalcular yy2 = (yy1 + 3) / 4 (nota: / es un div entero)
movf RTC1_yy1, w
addlw d'3 ′; W = yy1 + 3 (nota: yy1 [7-104], entonces siempre C = 0)
movwf RTC1_yy2; yy2 = yy1 + 3
rrf RTC1_yy2, f; yy2 = (yy1 + 3) / 2
bcf ESTADO, C; malbar C
rrf RTC1_yy2, f; yy2 = (yy1 + 3) / 2/2 = (yy1 + 3) / 4calcular W = t (mes-1)
llamar RTC1_dow_t; W = t (mes-1)
agregar todos los términos
addwf RTC1_SYSday, w; W = t (mes-1) + día
addwf RTC1_yy2, w; W = t (mes-1) + día + yy2
addwf RTC1_yy1, w; W = t (mes-1) + día + yy2 + yy1calcular el modo W 7
RTC1_dow_Wmod7
addlw -d'7 ′; W = W-7
btfsc ESTADO, C; W> = 7 (C = 1)?
vaya a RTC1_dow_Wmod7; sí
; no, continuar (W = W- (W / 7) * 7-7)
adicional de 7 ′; W = W- (W / 7) * 7-7 + 7 = W- (W / 7) * 7 = W mod 7
regresarRTC1_dow_t; calcular W = t (mes-1)
movlw HIGH RTC1_dow_tab
movwf PCLATH; PCLATH = RTC1_dow_tab (b12-b8)
decf RTC1_SYSmth, w; W = mes-1 (nota: mes-1 [0-11])
addlw LOW RTC1_dow_tab; W = mes-1 + RTC1_dow_tab (b7-b0)
btfsc STATUS, C; 0xFF ¿borde cruzado (C = 1)?
incf PCLATH, f; sí, ajuste PCLATH
; no, sigue
movwf PCL; saltar a la tabla RTC1_dow_tab (retlw); t (mes-1) tabla
RTC1_dow_tab dt 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
; ******************************************Hola.
SuperFabius dice:
Olvidé en la fórmula dow decir que si mes <3 ol yy1 = yy1-1.
Un ejemplo de montaje ya está considerando esto.Hola.
Chris Gorman dice:
Gracias por tu código de muestra. Lo incorporé a mi programa de reloj digital AVR. Quería comunicarme con usted para asegurarme de que aprueba que su algoritmo se utilice en mi proyecto. Usé su función NthDate para determinar el día del primer domingo de noviembre y el segundo domingo de marzo, por lo que el reloj no tendrá que actualizarse manualmente. Mi proyecto es un reloj digital GPLv3 AVR, que puede consultar en mi sitio web.