CALCUL DU NOMBRE DE JOURS CONTENUS DANS L'INTERSECTION DE DEUX PÉRIODES

007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 - 15 févr. 2011 à 16:55
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 - 17 févr. 2011 à 11:03
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/52810-calcul-du-nombre-de-jours-contenus-dans-l-intersection-de-deux-periodes

007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
17 févr. 2011 à 11:03
Merci pour ce morceau de code dont chacun pourra apprécier la lisibilité. Pour ma part, je crains que la grammaire des chaînes de caractères proposée (voir le new DateTime) ait quelques difficultés à s'imposer... À moins qu'elle préfigure la reconnaissance vocale ?

Et puis, un exemple de configuration, considérée à tort par l'auteur comme sans intersection, conforterait là encore des affirmations quelques peu péremptoires...
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
16 févr. 2011 à 22:45
Encore une fois, nous n'avons rien à faire des changements d'heures pour traiter cette question ! Pour le reste, précisons simplement que mktime reste parfaitement pertinent de 1901 à 2038, ce qui nous laisse quelques délais pour aborder ces fameuses classes...

Un exemple nous permettrait néanmoins de progresser dès maintenant !
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
16 févr. 2011 à 22:04
Juste pour l'aspect didactique :
<?php

function daysIntersection($from1, $to1, $from2, $to2) {
if ($to1 >= $from2 && $to2 >= $from1) { // Intersection des deux périodes
$from = new DateTime(max($from1, $from2));
$to = new DateTime(min($to1, $to2) . ' +1day'); // On ajoute 1 jour, car PHP ne travaille pas sur des journées pleines, mais de minuit à minuit.
$interval = $from -> diff($to);
return $interval -> format('%a');
}
return 0; // Pas d'intersection des deux périodes
}

date_default_timezone_set('Europe/Paris');
echo daysIntersection('2011-01-02', '2011-01-10', '2011-01-08', '2011-01-20');

?>

Notez :
- le test d'intersection simplifié (plus juste aussi, car en tout, il y a 6 cas, l'auteur de la source n'en traite que 5, considérant à tort une configuration comme sans intersection)
- la lisibilité du code (plus facile à comprendre quand on découvre le code)
- la possibilité de formater la période grâce à DateInteval::format() (cf : http://fr.php.net/manual/fr/dateinterval.format.php )

Maintenant, je conçois parfaitement que chacun préfère continuer d'utiliser des méthodes de développement obsolètes et refusent les outils "modernes". Simplement, le code que je présente nécessite PHP5.2, qui est installé même chez Free et OVH, c'est dire si on peut considérer que cette version est la minimum à prendre en compte quand on développe aujourd'hui en PHP. Oubliez un peu le procédural et optimisez votre code, vous verrez, ça fait beaucoup de bien.
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
16 févr. 2011 à 21:17
C'est vrai, mktime sait utiliser le changement d'heure été/hiver. Il faut passer le 7ème paramètre, celui qui est justement omis dans la source.
Mais comme ce paramètre est obsolète depuis PHP 5.1, il faut utiliser le gestionnaire de fuseau horaire à la place, exactement comme cette source ne le fait pas. Ce qui provoquerait très certainement une erreur de type E_STRICT sur mon serveur.

Je me suis donc mal exprimé : cette source qui utilise mktime, ne prend pas en compte le changement d'heure été/hiver.

Là où je me suis mal exprimé également, c'est sur le fait que cette source, qui se contente de compter les jours en divisant le temps obtenu en secondes par 86400 ne donne pas un résultat pertinent.

Bref : je persiste à dire que les classes DateTime et DateInterval fournissent un résultat bien plus pertinent et avec beaucoup moins de lignes de code... Mais comme cela relèguerait la classe au statut de snippet, on admettra que cette source puisse servir d'exemple pour ce qu'il ne faut pas faire.
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
16 févr. 2011 à 20:11
Qu'est-ce que c'est que ces sornettes ?
D'abord, le calcul peut consister à inverser les périodes ou a considérer le début et la fin d'une même période si celle-ci est incluse dans l'autre.
Ensuite, depuis quand mktime ne prend plus en compte les années bissextiles ?
Pour clore le tout, le changement d'heure n'intervient pas lorsque l'on travaille seulement sur les dates à 0 heures 0 minutes 0 secondes !
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
16 févr. 2011 à 18:43
En fait, d'après l'exemple donné, ça revient à calculer le nombre de jours entre deux dates : le début de la période 2 et la fin de la période 1.
Par ailleurs, se baser sur mktime() pour se genre de calculs est, rigoureusement, incorrect et donne des valeurs qui sont, rigoureusement, fausses.
En effet, mktime ne prend pas en compte :
- le nombre de jours dans l'année (années bissextiles : rappelons qu'une année tropique conventionnelle dure 365 jours 5 heures 48 minutes 45 secondes)
- le changement d'heure été/hiver

Pour pallier ces problèmes, PHP a introduit dans sa version 5.1 (puis intégré en standard depuis 5.2) les classes DateTime, DatePeriod, DateInterval qui permettent justement de prendre en compte (sous réserve de leur spécifier le fuseau horaire adéquat) les années bissextiles et les changements d'heures.
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
16 févr. 2011 à 10:24
Encore une correction !
Les périodes étant comprises au sens large, c'est-à-dire en incluant les dates limites, il convient encore d'écarter la coïncidence des dates médianes pour affirmer l'absence de recouvrement. La fonction peut alors s'écrire

function nbJourInter($d1,$f1,$d2,$f2) {
$dat=array($d1,$f1,$d2,$f2);
asort($dat,SORT_STRING);
$key=array_keys($dat);
$dat=array_values($dat);
if ($dat[1]!=$dat[2] && ($key[0]$1)+($key[1]$1)!=2) return "Pas de recouvrement";
$arr_di = explode ('-',$dat[1]);
$arr_fi = explode ('-',$dat[2]);
$di = mktime (0,0,0,$arr_di[1],$arr_di[2],$arr_di[0]);
$fi = mktime (0,0,0,$arr_fi[1],$arr_fi[2],$arr_fi[0]);
return "du $dat[1] au $dat[2]
".(round((($fi-$di)/60/60/24))+1)." jours de recouvrement";
}
Mais l'avantage est bien mince ! Serait-il utile d'être bête ? Il convient, en toute hypothèse de ne point être étourdi.
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
15 févr. 2011 à 18:25
Il faut corriger la ligne
if (0<($key[0]-1)*($key[1]-1)) return "Intersection vide ";
en
if (0<($key[0]-1.5)*($key[1]-1.5)) return "Intersection vide ";
pour tester que les deux premières clefs sont toutes deux supérieures à 1 ou toutes deux inférieures à deux.
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
15 févr. 2011 à 17:06
Quelques corrections : Les dates se recouvrent, Choisir 4 dates au hasard et puis plutôt que de trier deux fois le tableau, il suffirait de considérer ses valeurs avec un $val=array_values($dat);
007Julien Messages postés 276 Date d'inscription mercredi 22 septembre 2010 Statut Membre Dernière intervention 8 janvier 2014 4
15 févr. 2011 à 16:55
Un problème intéressant qui devrait pouvoir être résolu, sinon plus clairement, plus élégamment...

Les périodes se recouvrement lorsque la suite des dates classées ne commence par par deux dates de la même période...

Voici une proposition, livrée avec la page de test aléatoire, qui a le mérite de fonctionner même si les dates des périodes sont inversées.

<?php
header("Content-Type: text/html; charset=iso-8859-1");

function nbJourInter($d1,$f1,$d2,$f2) {
$dat=array($d1,$f1,$d2,$f2);
asort($dat,SORT_STRING);
$key=array_keys($dat);
if (0<($key[0]-1)*($key[1]-1)) return "Intersection vide ";
sort($dat);
$arr_di = explode ('-',$dat[1]);
$arr_fi = explode ('-',$dat[2]);
$di = mktime (0,0,0,$arr_di[1],$arr_di[2],$arr_di[0]);
$fi = mktime (0,0,0,$arr_fi[1],$arr_fi[2],$arr_fi[0]);
return "du $dat[1] au $dat[2]
".(round((($fi-$di)/60/60/24))+1)." jours de recouvrement";
}
$ms=explode('|','mois|janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre');
$chnPge='<table border="0" cellspacing="0" cellpadding="5">';
for ($i=0;$i<10;$i++){
// Choisir 4 date au hazard
$chnPge.='<tr><td>';$d=array();
for ($j=0;$j<4;$j++){$jr=rand(1,365);
$dt=mktime(0,0,0,1,$jr,"2011");$d[$j]=date("Y-m-d",$dt);
$chnPge.=($j==2?' et ':' ').(($j%2)?'au':'du').' '.date("j",$dt).' '.$ms[date("n",$dt)];
}
$chnPge.='</td><td>'.nbJourInter($d[0],$d[1],$d[2],$d[3]).'</td></tr>';
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="fr">
<head>
<title>Ajouter</title>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" href="style.css" type="text/css">
<script type="text/javascript">

</script>
</head>

<?php echo $chnPge ?>

</html>
Rejoignez-nous