Les dates de tete
Beaucoup de gens detestent manipuler dates et nombres de tete, c'est
pourtant une chose simple, la plupart des calculs sur les dates peuvent
se faire en quelques operations courtes sur des nombres a deux ou
quatre chiffres.
Les jours, une histoire de modulos
On appellera par la suite "modulo de a et b", le reste de la
division de a par b. Ainsi, le modulo de 8 par 7 est 1. Tracez un
cercle, vous y placez B nombres : {0 1 2 3 ... b-1}, et bien si vous
placez a sur ce cercle, il sera a la meme place que a modulo b. On note
l'operation modulo "%".
Pour la suite, on s'interessera a un cercle de 7 nombres, 0 pour le lundi, et 6 pour le dimanche.
Nous avons globalement represente un "decalage" en "jours de la semaine", a travers une seule operation.
Si nous sommes un mardi, le D du mois M, quel jours seront nous le D du mois M+1 ?
Il s'agit de placer le nombre de jours du mois M sur le cercle :
28 7 * 4 | 28 % 7 029 7 * 4 + 1 | 29 % 7 130 7 * 4 + 2 | 30 % 7 231 7 * 4 + 3 | 31 % 7 3
Si le mois fait 28 jours, alors nous serons toujours un mardi, si le
mois fait 29 jours, alors nous passerons un mercredi, etc... Il faut le
voir comme un decalage. On note que pour les annees, ca donne :
365 % 7 = 1
Quel jours serons nous le D, du mois M, annee Y ?
Le premier janvier 2000 etait un samedi, on a donc une base :
calcul(D1, M1, Y1) = 5 (5 pour samedi). Le but du jeu, c'est d'exprimer
calcul(D, M, Y) en fonction de calcul(D1, M1, Y1), D1, M1, Y1, D, M et
Y. Il semble evident que prendre des donnnees numerique facilite
toujours les calculs... On prendra donc janvier 0, lundi 0, et le
premier du mois = 0.
dY = Y - Y1 // de combien d'annees on a avancedM M - M1 // on doit toujours faire en sorte que ca soit positif... en general, on prend M1 janvier
dD = D - D1 // idem, ca doit rester positif
pour les contraintes presentes ci dessus, il semble natuel de
prendre D1, M1, Y1 en debut de siecle, ca facilite grandement les
calculs...
calcul(D, M, Y) = calcul(D1, M1, Y1) + dD + (le decalage engendre par le mois dM) + (le decalage engendre par l'annee dY)
pour calculer les deux precedents decalages, il n'est plus seulement
naturel, mais il est necessaire de prendre D1, M1, Y1 en debut de
millenaire.
calcul(D, M, Y) = calcul(D1, M1, Y1) + dD + mois[dM] + dY + dY/4 -dY/100 + dY / 1000
les /4 et /1000 s'expliquent a cause des annees bisextiles. On a
aussi besoin d'un tableau mois, a 12 entrees, qui contiendrait les
decalages cummules correspondant aux mois.
Pour une annee bisextile :
indice |
mois |
nombre de jours du mois |
%7 |
cummules % 7 |
----
0,
janvier,
31,
3,
3,
----
1,
fevrier,
29,
1,
4,
----
2,
mars,
31,
3,
0,
----
3,
avril,
30,
2,
2,
----
4,
mai,
31,
3,
5,
----
5,
juin,
30,
2,
0,
----
6,
juillet,
31,
3,
3,
----
7,
aout,
31,
3,
6,
----
8,
septembre,
30,
2,
1,
----
9,
octobre,
31,
3,
4,
----
10,
novembre,
30,
2,
6,
----
11,
decembre,
31,
3,
2
Pour une annee bisextile :
indice |
mois |
nombre de jours du mois |
%7 |
cummules % 7 |
----
0,
janvier,
31,
3,
3,
----
1,
fevrier,
28,
0,
3,
----
2,
mars,
31,
3,
6,
----
3,
avril,
30,
2,
1,
----
4,
mai,
31,
3,
4,
----
5,
juin,
30,
2,
6,
----
6,
juillet,
31,
3,
2,
----
7,
aout,
31,
3,
5,
----
8,
septembre,
30,
2,
0,
----
9,
octobre,
31,
3,
3,
----
10,
novembre,
30,
2,
5,
----
11,
decembre,
31,
3,
1
en pratique, on s'arrange pour n'avoir besoin que du second tableau.
<?php
function
isBissextile
(
$Y
){
return
$Y
%
4
===
0
&& (
$Y
%
100
!==
0
||
$Y
%
1000
=
0
);
}
function
calcul
(
$D
,
$M
,
$Y
){
$Y
--;
$D
--;
$M
--;
$mois_nombre
=array(
0
,
3
,
3
,
6
,
1
,
4
,
6
,
2
,
5
,
0
,
3
,
5
);
return (
3
+
$D
+
$mois_nombre
[
$M
] +
$Y
+
intval
(
$Y
/
4
) -
intval
(
$Y
/
100
) +
intval
(
$Y
/
1000
)
+ (
isBissextile
(
$Y
+
1
) &&
$M
>
1
)
) %
7
;
}
echo
calcul
(
1
,
7
,
1988
);
// exemple
?>
1les -1, c'est parce-qu'on ne doit pas compter les decalages
du mois en cours, ou de l'annee en cours, sauf si l'annee est
bissextile et que le mois est au delas de fevrier.
Trouver le premier mercredi d'un mois precis
<?php
function calcul2($M, $Y, $toD){
$d=calcul(1, $M, $Y);
if ($d < $toD){
return $toD-$d+1;
}else if ($d==$toD){
return 1;
}else{
return $toD+8-$d;
}
}
echo calcul2(8, 2008, 2); // mercredi, c'est le jours 2
?>
1