Petit défi : déterminer l'année d'une date à partir de sa valeur entière
par
, 31/12/2020 à 21h35 (2099 Affichages)
Objectif : réaliser un petit défi consistant à déterminer une année à partir d'un entier correspondant à une date en utilisant uniquement des opérations arithmétiques et des comparaisons.
Les dates sont stockées sous forme de numériques, dont la partie entière représente la date et la partie décimale l'heure. En fait cet entier est égal au nombre de jours écoulés depuis une date de référence.
On se propose d'écrire une fonction permettant d'obtenir l'année d'une date à partir de sa valeur entière en effectuant des divisions entières successives.
Pour cela, on aura besoin de comprendre les règles de notre calendrier grégorien :
En résumé, l'année n’est bissextile (avec 366 jours) que dans l’un des deux cas suivants :Le calendrier grégorien reste un calendrier solaire, qui se fonde non sur la révolution de la Terre autour du Soleil (hypothèse non validée à l'époque), mais sur le retour du Soleil au point vernal à chaque printemps, permettant le calcul du début de l'année quelques jours après le solstice d'hiver, en 365,2421898 jours de 24 heures. Le calendrier grégorien donne un temps moyen de l'année de 365,2425 jours.
Pour assurer un nombre entier de jours à l'année, on y ajoute tous les 4 ans (années dont le millésime est divisible par 4) un jour intercalaire, le 29 février (voir année bissextile), à l'exception des années séculaires, qui ne sont bissextiles que si leur millésime est divisible par 400.
On considéra donc comme années communes (années de 365 jours) les millésimes qui sont multiples de 100 sans être multiples de 400. Ainsi 1600 et 2000 furent bissextiles, mais pas 1700, 1800, 1900 qui furent communes. De même, 2100, 2200, 2300 seront communes, alors que 2400 sera une année bissextile.
- si l'année est divisible par 4 et non divisible par 100 ;
- si l'année est divisible par 400 (« divisible » signifie que la division donne un nombre entier, sans reste).
Découpage du calendrier en blocs de 400 années :
Les groupes de 400 années commençant par une année bissextile multiple de 400 (exemple l'an 1600) comportent donc en particulier 3 années non bissextiles débutant les 3 derniers siècles de ce groupe.
Dans les blocs de 100 années contenus dans les blocs de 400 années, on à donc une première centaine comportant 25*(366+3*365) = 36525 jours, et les 3 autres centaines contenant 1 jour de moins, soit 25*(366+3*365) - 1 = 36524 jours.
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 '----------------------------------------------------Groupe de 400-------------------------------------------------- '-----------1er groupe de 100-------2ème groupe de 100-----------3ème groupe de 100-----------4ème groupe de 100---- TotalJours = 25*(366 + 3*365) + (4*365 + 24*(366 + 3*365)) + (4*365 + 24*(366 + 3*365)) + (4*365 + 24*(366 + 3*365))
Par conséquent, ces groupes de 400 années débutant le 1er janvier d'une année divisible par 400 contiennent :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part 100*(366 + 3*365) - 3 = 146097 jours
Si on divise le nombre total de jours contenus dans ces groupes par 400, on obtient bien :
146097 / 400 = 365,2425 qui représente le nombre moyen de jours par an dans le calendrier grégorien.
Cependant, comme il s'agit d'une approximation, il est très compliqué d'obtenir, même en arrondissant, le nombre exact d'années écoulées, depuis par exemple le 1er janvier de l'an 1600, à partir du nombre de jours passés depuis cette date et du nombre moyen de jours par an.
C'est pourquoi, on propose pour obtenir ce résultat d'utiliser plutôt des divisions entières successives.
Déroulé des différentes étapes :
- détermination du nombre de jours écoulés depuis le 1er jour de l'année de référence multiple de 400 ;
- découpage de ce nombre de jours en blocs de 400 années ;
- découpage du reste des jours obtenu en groupes de 100 années ;
- division à nouveau du reste en groupes de 4 années ;
- enfin, découpage du reste des jours en groupe de 365 jours.
L'addition des années contenues dans nos différents groupes nous permettra finalement d'obtenir le nombre d'années écoulées depuis le 1er jour de l'année de référence.
Description de la fonction Annee :
Commençons par déterminer le nombre de jours écoulés depuis le 1er janvier de l'an 0 jusqu'à la date dt passée en argument de la fonction :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part nbJours = dt - #1/1/400# + 4*146097 ' nombre de jours écoulés entre le 1er jour de l'an 0 (1er janvier 1600 - 1600 ans ou 4*146097 jours) et le jour passé en argument
On part de l'an 0 qui n'existe en réalité pas dans les calendriers chrétiens, pour éviter d'avoir à gérer des valeurs négatives pour les dates antérieures à l'année 1600 (cf. calendrier grégorien proleptique).
Evaluons ensuite le nombre de groupes de 400 années depuis le 1er janvier de l'an 0 en réalisant une division entière du nombre de jours écoulés par le nombre de jours contenu dans un groupe de 400 :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part nbGroupes400annees = (nbJours \ 146097) ' groupe de 400 années comportant 100* (366 + 3* 365) - 3 = 146097 jours
Puis, utilisons le modulo pour obtenir le reste des jours après division :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part nbJours = (nbJours Mod 146097) ' nombre de jours restant après division
Divisons encore ce reste en groupes de 100 années si le nombre de jours restant est supérieur ou égal à un siècle :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 ' division en groupes de 100 années If nbJours >= 36525 Then ' si le nombre des jours restant est supérieur ou égal au 1er siècle suivant les groupes de 400 années ' on ôte un jour au reste pour découper le nombre de jours restant en groupes de 100 années avec le même nombre de jours nbGroupes100annees = ((nbJours - 1) \ 36524) ' nombre de groupes de 100 années comportant 36524 jours nbJours = ((nbJours - 1) Mod 36524) ' nombre de jours restant, après avoir soustrait les groupes de 100 années If nbJours >= 365 Then ' si le nombre de jours restant est supérieur ou égal à 365 jours ' on ajoute ensuite un jour pour permettre de diviser le nombre de jours restant en groupes constants de 365 jours ou de 4 années commençant par une année bissextile : (366+3*365)=1461 nbJours = nbJours + 1 End If End If ...
Puis, découpons encore le reste des jours en groupes de 4 années, si celui-ci est supérieur ou égal à 4 années :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 ' division en groupes de 4 années If (nbJours >= 1461) Then ' si le reste des jours est supérieur ou égal à un groupe de 4 années, commençant lui-même par une année bissextile et comportant (366 + 3*365)=1461 jours nbGroupes4annees = (nbJours \ 1461) ' nombre de groupes de 4 années nbJours = (nbJours Mod 1461) ' nombre de jours restant, après avoir soustrait les groupes de 4 années End If
Enfin, si le reste des jours est supérieur à une année, découpons le encore en blocs de 365 jours pour obtenir le reste des années :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 If (nbJours >= 366) Then ' si le reste des jours commence par une année bissextile ' on ôte un jour au reste pour diviser le nombre de jours restant en groupes constants de 365 jours nbAnnees = ((nbJours - 1) \ 365) ' nombre d'années restantes 'nbJours = ((nbJours - 1) Mod 365) ' nombre de jours restant, après avoir soustrait les dernières années End If
Et renvoyons le résultat final de notre fonction Annee en additionnant les années de nos différents groupes :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 ' on additionne le nombre d'années des groupes de 400, de 100, de 4 avec reste des années obtenu à la fin Annee = nbGroupes400annees * 400 + nbGroupes100annees * 100 + nbGroupes4annees * 4 + nbAnnees
Code complet de la fonction :
Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 Public Function Annee(ByVal dt As Date) As Long Dim nbJours As Long ' variable contenant le nombre de jours Dim nbGroupes400annees As Long ' variable contenant le nombre de groupes de 400 années comportant 100 * (366 + 3 * 365) - 1 = 146097 jours Dim nbGroupes100annees As Long ' variable contenant le nombre de groupes de 100 années comportant 25 * (366 + 3 * 365) = 36525 jours ou 36524 jours Dim nbGroupes4annees As Long ' variable contenant le nombre de groupes de 4 années avec (366 + 3 * 365) = 1461 jours Dim nbAnnees As Long ' nombre d'années restante après les divisions successives nbJours = dt - #1/1/1600# + 4 * 146097 ' nombre de jours écoulés entre le 1er jour de l'an 0 (1er janvier 1600 - 1600 ans ou 4*146097 jours) et le jour passé en argument nbGroupes400annees = (nbJours \ 146097) ' groupe de 400 années comportant 100* (366 + 3* 365) - 3 = 146097 jours nbJours = (nbJours Mod 146097) ' nombre de jours restant, après avoir retranché les groupes de 400 années ' division en groupes de 100 années If nbJours >= 36525 Then ' si le nombre des jours restant est supérieur ou égal au 1er siècle suivant les groupes de 400 années ' on ôte un jour au reste pour découper le nombre de jours restant en groupes de 100 années avec le même nombre de jours nbGroupes100annees = ((nbJours - 1) \ 36524) ' nombre de groupes de 100 années comportant 36524 jours nbJours = ((nbJours - 1) Mod 36524) ' nombre de jours restant, après avoir soustrait les groupes de 100 années If nbJours >= 365 Then ' si le nombre de jours restant est supérieur ou égal à 365 jours ' on ajoute ensuite un jour pour permettre de diviser le nombre de jours restant en groupes constants de 365 jours ou de 4 années commençant par une année bissextile : (366+3*365)=1461 nbJours = nbJours + 1 End If End If ' division en groupes de 4 années If (nbJours >= 1461) Then ' si le reste des jours est supérieur ou égal à un groupe de 4 années, commençant lui-même par une année bissextile et comportant (366 + 3*365)=1461 jours nbGroupes4annees = (nbJours \ 1461) ' nombre de groupes de 4 années nbJours = (nbJours Mod 1461) ' nombre de jours restant, après avoir soustrait les groupes de 4 années End If If (nbJours >= 366) Then ' si le reste des jours commence par une année bissextile ' on ôte un jour au reste pour diviser le nombre de jours restant en groupes constants de 365 jours nbAnnees = ((nbJours - 1) \ 365) ' nombre d'années restantes nbJours = ((nbJours - 1) Mod 365) ' nombre de jours restant, après avoir soustrait les dernières années End If ' on additionne le nombre d'années des groupes de 400, de 100, de 4 avec reste des années obtenu à la fin Annee = nbGroupes400annees * 400 + nbGroupes100annees * 100 + nbGroupes4annees * 4 + nbAnnees End Function
Le code peut bien sûr être encore optimisé, mais j'ai voulu le garder le plus lisible possible pour mieux montrer l'enchainement des divisions successives.
Cette méthode permet aussi d'obtenir le reste des jours après avoir retranché les années.
Voilà, il doit certainement exister d'autres façons de faire, mais j'ai trouvé celle-ci intéressante.
Bon réveillon et bonne année à toutes et tous![]()