Je vous propose une fonction VBA qui détermine la date du Dimanche de Pâques pour toute année (de 325 à 9999) dont découlent les jours fériés mobiles en France.
Elle complète également la fonction fournie dans la FAQ Access qui n’est valide qu’entre 1982 et 2048.
La fonction VBA
L’objectif est la performance et non l’aspect didactique des calculs…
'Calcul de la date du dimanche de Pâques à partir de l'année 325
'Performance par million d'appel :
' - Entre 325 et 1582 et entre 1900 et 2099 => 1/4 de seconde
' - Année supérieure à 1582 hors 1900 - 2099 => 1/2 de seconde
'Philben - v1.0 - Free to use
Dim a As Integer, b As Integer, c As Integer, d As Integer, e As Integer, f As Integer
If an < 10000 Then 'Limite supérieure des dates sous Access (31 décembre 9999)
Select Case an
Case 1900 To 2099 'Algorithme de Carter
a = (204 - 11 * (an Mod 19)) Mod 30 + 22
Paques = DateSerial(an, 3, a + 6 + (a > 49) - (an + an \ 4 + a + (a > 49)) Mod 7)
Case Is > 1582 'Proposé en 1876 dans la revue Nature (dérivé de l'algorithme de Delambre)
a = an Mod 19: b = an \ 100: c = an Mod 100
d = (19 * a + b - b \ 4 - (b - (b + 8) \ 25 + 1) \ 3 + 15) Mod 30
e = (32 + 2 * (b Mod 4) + 2 * (c \ 4) - d - c Mod 4) Mod 7
f = d + e - 7 * ((a + 11 * d + 22 * e) \ 451) + 114
Paques = DateSerial(an, f \ 31, f Mod 31 + 1)
Case Is > 324 'Algorithme de Oudin pour les dates juliennes < 1583 décrit par Claus Tondering
a = (19 * (an Mod 19) + 15) Mod 30
Paques = DateSerial(an, 3, 28 + a - (an + an \ 4 + a) Mod 7)
End Select
End If
End Function
Pourquoi trois périodes de temps ?
La date de Pâques (Easter en anglais) a été fixée au concile de Nicée en 325 ce qui explique l’année de départ de la fonction. De l’an 325 à 1582, on utilise l’algorithme de Oudin adapté au calendrier julien puis l’algorithme de la revue Nature pour le calendrier grégorien actuel (réforme du 15 octobre 1582). Enfin, l’algorithme de Carter est utilisé pour la période actuelle (1900 à 2099) compte tenu de sa rapidité de calcul.
Performance
Pour un million d’appels de la fonction :
- Compter 1/4 de seconde pour la période entre 325 et 1582 et entre 1900 et 2099;
- Compter 1/2 seconde pour les années supérieures à 1582 (hors 1900 à 2099).
Vérification des résultats
La méthode retenue est de comparer les résultats avec d’autres algorithmes.
Concernant le calendrier julien j’ai utilisé l’algorithme de Delambre versus Oudin :
Dim a As Integer, b As Integer, c As Integer, d As Integer, e As Integer, an As Integer, bErr As Boolean
Dim d1 As Date, d2 As Date
Debug.Print "Test calendrier julien..."
For an = 325 To 1582
'Delambre
a = an Mod 19
b = an Mod 7
c = an Mod 4
d = (19 * a + 15) Mod 30
e = (2 * c + 4 * b - d + 34) Mod 7
d1 = DateSerial(an, 3, 22 + d + e)
d2 = Paques(an)
If d1 <> d2 Then
Debug.Print vbTab & "-> Date différente pour l'année " & an & " entre Delambre (" & d1 & ") et la fonction Paques (" & d2 & ")"
bErr = True
Exit For
End If
Next an
Debug.Print "Fin - " & IIf(bErr, "Ecart constaté !", "Pas d'écart constaté")
End Function
Pour le calendrier grégorien, j’ai utilisé l’algorithme de Oudin versus nos deux algorithmes (Carter et Nature) :
Dim c As Integer, g As Integer, k As Integer, i As Integer, j As Integer, l As Integer, m As Integer
Dim an As Integer, bErr As Boolean, d1 As Date, d2 As Date
Debug.Print "Test calendrier grégorien..."
For an = 1583 To 9999
'Oudin - http://www.smart.net/~mmontes/oudin.html
g = an Mod 19
c = an \ 100
k = (c - 17) \ 25
i = (c - c \ 4 - (c - k) \ 3 + 19 * g + 15) Mod 30
i = i - (i \ 28) * (1 - (i \ 28) * (29 \ (i + 1)) * ((21 - g) \ 11))
j = (an + an \ 4 + i + 2 - c + c \ 4) Mod 7
l = i - j
m = 3 + (l + 40) \ 44
d1 = DateSerial(an, m, l + 28 - 31 * (m \ 4))
d2 = Paques(an)
If d1 <> d2 Then
Debug.Print vbTab & "-> Date différente pour l'année " & an & " entre Oudin (" & d1 & ") et la fonction Paques (" & d2 & ")"
bErr = True
Exit For
End If
Next an
Debug.Print "Fin - " & IIf(bErr, "Ecart constaté !", "Pas d'écart constaté")
End Function
On peut également vérifier que la date de Pâques calculée soit comprise entre le 22 mars et le 25 avril et qu’elle soit bien un dimanche. Comme la fonction VBA Weekday() n’est adaptée qu’au calendrier grégorien, il faut donc ajouter un algorithme qui détermine les jours de semaine du comput julien (années < 1583 dans notre cas).
La fonction de test et JourSemaineJulien() :
'Quelque soit l'année, vérifier que la date de Pâques est bien :
' - comprise entre le 22 mars et 25 avril
' - un dimanche
Const cMinAn As Integer = 325
Const cMaxAn As Integer = 9999
Const cMaxMoisJour As Integer = 425
Const cMinMoisJour As Integer = 322
Const cMinCalendrierGregorien As Integer = 1583
Dim p As Date, i As Integer, md As Integer, j As Integer
For i = cMinAn To cMaxAn
p = Paques(i)
md = Month(p) * 100 + Day(p)
If i < cMinCalendrierGregorien Then j = JourSemaineJulien(p) Else j = Weekday(p)
If j <> vbSunday Then
Debug.Print "Erreur ! Une date de Pâques n'est pas un dimanche : ", p
Exit For
End If
If md < cMinMoisJour Then
Debug.Print "Erreur ! Une date de Pâques est inférieure au 22 mars : ", p
Exit For
ElseIf md > cMaxMoisJour Then
Debug.Print "Erreur ! Une date de Pâques est supérieure au 25 avril : ", p
Exit For
End If
Next i
If i > cMaxAn Then Debug.Print "Aucune erreur détectée entre " & cMinAn & " et " & cMaxAn
End Function
Public Function JourSemaineJulien(ByVal LaDate As Date) As VbDayOfWeek
'Formule de Zeller - http://www.vendredi13.us/ZellerMethode.html
Dim a As Integer, m As Integer, s As Integer, y As Integer, z As Integer
y = Year(LaDate): m = Month(LaDate)
If m < 3 Then m = 12 + m: y = y - 1
s = y \ 100: a = y - s * 100
'Comput julien : de 100 (1ère date Access : 1/1/100) à 1582
z = (Day(LaDate) + Int(2.6 * (m + 1)) + a + a \ 4 + 5 - s) Mod 7
'comput grégorien : > 1582
'z = (Day(LaDate) + Int(2.6 * (m + 1)) + a + a \ 4 + s \ 4 - 2 * s) Mod 7
'If z < 0 Then z = 7 + (z Mod 7) 'inutile entre 100 et 1582
If z = 0 Then z = vbSaturday 'alignement sur VbDayOfWeek
JourSemaineJulien = z
End Function
J'ai également comparé les résultats d'un échantillon d'années avec ceux de ce site web très intéressant sans constater d’erreur.
Exemples
A copier dans la fenêtre ‘Exécution’ de VBE puis mettre le focus sur la ligne à exécuter et taper sur ‘Entrée’.
?Paques(Year(Date())) 'pour 2012 -> 08/04/2012
'An 1000 - Calendrier julien
?Paques(1000) '31/03/1000
?Paques(3000) '13/04/3000
Remarque
Bien que l’année grégorienne (365.2425 jours en moyenne) soit plus courte que l’année julienne (365.25 jours en moyenne) , elle est encore trop longue par rapport à l’année tropique (365.2422 jours en moyenne). De plus, le raccourcissement de l’année tropique et l’allongement du jour au fil des millénaires nécessiteront des ajustements qui rendront caduques les algorithmes utilisés…
Références
Cette présentation en français au format PDF de P. Rocher (Observatoire de Paris) fait un point détaillé sur les dates de Pâques, les méthodes de calcul et présente l’algorithme de la revue Nature (page 15).
Le site vendredi13.us dont le design pique un peu les yeux (!) est d’une richesse exceptionnelle sur le vendredi 13 et les dates de Pâques, le tout en français ! Il expose très clairement de nombreuses méthodes de calcul du jour de Pâques dont Gauss, Delambre, Oudin, Reints, Zeller…
@+
Philippe