- Congruencia de Zeller
-
La congruencia de Zeller es un algoritmo ideado por Julius Christian Johannes Zeller para calcular el día de la semana de cualquier fecha del calendario.
Contenido
Fórmula
Para el calendario gregoriano la congruencia de Zeller es
para el calendario juliano es
donde h es el día de la semana (0 = sábado, 1 = domingo, 2 = lunes,...), q es el día del mes, m es el mes, J es la centuria (es realidad ⌊año / 100⌋) y K el año de la centuria (año mod 100). Enero y febrero se cuentan como meses 13 y 14 del año anterior. Es oportuno recordar que la función mod es el residuo que queda de la división de dos números.
En las implementaciones informáticas en las que el módulo de un número negativo es negativo, la manera más sencilla de obtener un resultado entre 0 y 6 es reemplazar - 2 J por + 5 J y - J por + 6 J.
Análisis
Estas fórmulas se basan en la observación de que el día de la semana progresa de una manera predecible basada en cada subparte de esa fecha. Cada término de la fórmula se usa para calcular el desplazamiento necesario para obtener el día correcto de la semana.
Por tanto, para el calendario gregoriano, las diversas partes de esta fórmula pueden entenderse así:
- q representa la progresión del día de la semana basada en el día del mes, dado que cada día sucesivo resulta en un desplazamiento adicional de 1 en el día de la semana.
- K representa la progresión del día de la semana basada en el año. Suponiendo que cada año tiene 365 días, la misma fecha de cada año sucesivo será desplazada por un valor de .
- Como hay 366 días en cada año bisiesto, esto de debe tener en cuenta añadiendo un día adicional al valor de desplazamiento del día de la semana. Esto se logra añadiendo al desplazamiento. Este término se calcula como un resultado entero. Cualquier resto que pueda haber es descartado.
- Usando una lógica similar, se puede calcular la progresión del día de la semana para cada centuria observando que hay 36524 días en una centuria normal, y 36525 en cada centuria divisible por 400. Dado que y , el término: refleja esto (de nuevo usando división entera y descartando cualquier resto fraccional). Para evitar los números negativos, este término se puede reemplazar por con un resultado equivalente.
- El término se puede explicar de la siguiente manera. Zeller observó que, al iniciar cada año el 1 de marzo, el día de la semana de cada mes sucesivo progresaba multiplicando el mes por un valor constante y descartando el resto fraccional.
- La función global, , normaliza el resultado para que se encuentre en el intervalo de 0 a 6, lo que da el índice del día de la semana correcto para la fecha analizada.
La razón por la que la fórmula difiere para el calendario juliano es que este calendario no tiene una regla aparte para las centurias bisiestas y está desplazado con respecto al calendario gregoriano un número fijo de días. Ambas diferencias se pueden tener en cuenta reemplazando el término por el término 5 − J, o 5 + 6J para evitar números negativos.
Dado que el calendario gregoriano fue adoptado en diferentes momentos en diferentes partes del mundo, la ubicación de un evento es significativa a la hora de determinar el día de la semana correcto de una fecha que tuvo lugar durante este periodo de transición.
Algoritmo
Algoritmo Z(y, m, d) Entrada: El año y, mes m (1 ≤ m ≤ 12) y día d (1 ≤ d ≤ 31). Salida: El día de la semana. t ← (0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4) n ← (domingo, lunes, martes, miércoles, jueves, viernes, sábado) if m < 3 y ← y - 1 w ← (y + ⌊y/4⌋ - ⌊y/100⌋ + ⌊y/400⌋ + tm-1 + d) mod 7 devolver nw
Una forma más fácil de ver el mismo desarrollo es la siguiente
a = (14 - Mes) / 12 y = Año - a m = Mes + 12 * a - 2 Para el calendario Juliano: d = (5 + dia + y + y/4 + (31*m)/12) mod 7 Para el calendario Gregoriano: d = (día + y + y/4 - y/100 + y/400 + (31*m)/12) mod 7 El resultado es un cero (0) para el domingo, 1 para el lunes… 6 para el sábado --------------------------------------------------------------- Ejemplo, ¿En qué día de la semana cae el 2 de agosto de 1953?? ' a = (14 - 8) / 12 = 0 ' y = 1953 - 0 = 1953 ' m = 8 + 12 * 0 - 2 = 6 ' d = (2 + 1953 + 1953 / 4 - 1953 / 100 + 1953 / 400 + (31 * 6) / 12) Mod 7 ' = (2 + 1953 + 488 - 19 + 4 + 15 ) mod 7 ' = 2443 mod 7 ' = 0 ' El valor cero(0) corresponde al domingo.
Un ejemplo de código
A continuación se muestra una función excrita en Visual Basic .NET (Version 9.0 - .NET Framework 3.5) que implementa el algoritmo de Zeller
''' <summary> ''' Devuelve un numero que representa el día de la semana en la que cae una fecha. ''' Por ejemplo, el día 1 de enero de 2010 fue 5 (Viernes) ''' </summary> ''' <param name="año">el año</param> ''' <param name="mes">el mes</param> ''' <param name="dia">el dia</param> ''' <returns>Un numero (entre 0 y 6) que representa el día de ''' la semana con los siguientes valores ''' <para> 0 = domingo</para> ''' <para> 1 = lunes</para> ''' <para> 2 = martes</para> ''' <para> 3 = miércoles</para> ''' <para> 4 = jueves</para> ''' <para> 5 = viernes</para> ''' <para> 6 = sábado</para> ''' </returns> ''' <remarks> ''' <para>Lenguaje: (.NET Framework 3.5) - Visual Basic .NET (Version 9.0) - 2010</para> ''' <example> ''' [wikipedia] Congruencia de Zeller ''' <code>http://es.wikipedia.org/wiki/Congruencia_de_Zeller</code> ''' </example> ''' <autor> Joaquin medina Serrano: joaquin@medina.name</autor> ''' <rights> Licencia Creative Commons Compartir-Igual 3.0</rights> ''' </remarks> Public Shared Function DayOfWeek( _ ByVal año As Integer, ByVal mes As Integer, ByVal dia As Integer) _ As Integer ' '--------------------------------------------------------------- ' ATENCION no hay control de datos de entrada ' La fecha que se recibe se supone que es correcta ' No se realiza ninguna comprobación de fechas. '--------------------------------------------------------------- ' '--------------------------------------------------------------- ' Apunte Táctico: Congruencia de Zeller. ' La congruencia de Zeller es un algoritmo que permite obtener, ' a partir de una fecha, el día de la semana que le corresponde '--------------------------------------------------------------- ' a = (14 - Mes) / 12 ' y = Año - a ' m = Mes + 12 * a - 2 ' Para el calendario Juliano: ' d = (5 + dia + y + y / 4 + (31 * m) / 12) Mod 7 ' Para el calendario Gregoriano: ' d = (día + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) Mod 7 ' El resultado es un cero (0) para el domingo, 1 para el lunes… 6 para el sábado '--------------------------------------------------------------- ' Ejemplo, ¿En qué día de la semana cae el 2 de agosto de 1953?? ' a = (14 - 8) / 12 = 0 ' y = 1953 - 0 = 1953 ' m = 8 + 12 * 0 - 2 = 6 ' d = (2 + 1953 + 1953 / 4 - 1953 / 100 + 1953 / 400 + (31 * 6) / 12) Mod 7 ' = (2 + 1953 + 488 - 19 + 4 + 15 ) mod 7 ' = 2443 mod 7 ' = 0 ' El valor cero(0) corresponde al domingo. '--------------------------------------------------------------- '' ' cociente= Math.DivRem(dividendo, divisor, resto) ' Variable resto (valor de retorno) contiene el resto de la división, no se emplea ' Utilizo la función [Math.DivRem] porque a la hora de dividir daban ' problemas los redondeos y el resultado no era correcto '' Dim resto As Integer = 0 ' no se emplea ' Dim a As Integer a = Math.DivRem((14 - mes), 12, resto) Dim y As Integer = año - a Dim m As Integer = mes + 12 * a - 2 Dim d As Integer = 0 '------------------------------------------------------------------------- ' auxDia + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) Mod 7) Dim y4 As Integer = Math.DivRem(y, 4, resto) Dim y100 As Integer = Math.DivRem(y, 100, resto) Dim y400 As Integer = Math.DivRem(y, 400, resto) Dim m12 As Integer = Math.DivRem((31 * m), 12, resto) ' d = dia + y + y4 - y100 + y400 + m12 d = d Mod 7 ' Return d End Function
Véase también
La norma ISO 8601:2004
La norma ISO 8601:2004, en su apartado 3.2.2 define el código de los días de la semana así como los nombres de los días de la semana (evidentemente están en inglés) pero lo que interesa resaltar es que la numeración que propone el estándar no coincide con la numeración que proporciona el algoritmo de Zeller (y por tanto, la función [DayOfWeek]) la diferencia está en la forma de contar, el algoritmo de Zeller proporciona valores del 0 (domingo) 1 lunes… hasta al 6(sábado), mientras que la norma ISO 8601, dice que los valores van desde el 1 lunes al 7 domingo
Un ejemplo de código
A continuación se muestra una función excrita en Visual Basic .NET (Version 9.0 - .NET Framework 3.5) que realiza la transformación de valores
''' <summary> ''' Numero del día de la semana según la norma ISO8601 ''' domingo=7, lunes=1, martes=2,..., sábado=6 ''' </summary> ''' <returns>Un numero que indica el día de la semana, ''' el lunes tiene el valor 1 y el domingo el valor 7</returns> ''' <remarks> ''' <para>Lenguaje: (.NET Framework 3.5) - Visual Basic .NET (Version 9.0) - 2010</para> ''' <para>PROBLEMA No concuerdan los conceptos</para> ''' <para> ''' La enumeración DayOfWeek representa el día de la semana ''' en los calendarios que tienen siete días por semana. ''' El valor de las constantes en esta enumeración varía ''' de DayOfWeek.Sunday a DayOfWeek.Saturday. ''' Si se convierte en un entero, su valor varía de cero ''' (que indica DayOfWeek.Sunday) a seis (que indica DayOfWeek.Saturday). ''' </para> ''' <para>Problema</para> ''' <para> ''' La norma ISO 8601 indica que los valores son 1 para el lunes, ''' 2 martes, etc. 6 sábado y 7 domingo ''' </para> ''' <para> -------------------------------------------- </para> ''' <para> Nombre Descripción DayOfWeek ISO8601 </para> ''' <para> -------------------------------------------- </para> ''' <para> Sunday domingo. 0 7 </para> ''' <para> Monday lunes. 1 1 </para> ''' <para> Tuesday martes. 2 2 </para> ''' <para> Wednesday miércoles. 3 3 </para> ''' <para> Thursday jueves. 4 4 </para> ''' <para> Friday viernes. 5 5 </para> ''' <para> Saturday sábado. 6 6 </para> ''' <para> -------------------------------------------- </para> ''' <autor> Joaquin medina Serrano: joaquin@medina.name</autor> ''' <rights> Licencia Creative Commons Compartir-Igual 3.0</rights> '''</remarks> Public ReadOnly Property ISO8601DayOfWeek( _ ByVal unaFecha As DateTimeOffset) As Integer Get ' Utilizar el algoritmo de Zeller Dim resultado As Integer = DayOfWeek(unaFecha) If resultado = 0 Then ' DayOfWeek.Sunday resultado = 7 End If Return resultado End Get End Property
Enlaces externos
- The Calendrical Works of Rektor Chr. Zeller: The Day-of-Week and Easter Formulae
- Algoritmo de Zeller y prueba interactiva en la Web Mates y +.
Referencias
- Paul E. Black, Zeller's congruence en el Diccionario de algoritmos y estructuras de datos del NIST.
- Christian Zeller, "Kalender-Formeln", Acta Mathematica 9 (1886) 131-6.
Categorías:- Algoritmos
- Calendario
Wikimedia foundation. 2010.