007Julien
Messages postés276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és2480Date d'inscriptionjeudi 30 novembre 2006StatutMembreDernière intervention14 janvier 201119 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
}
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és2480Date d'inscriptionjeudi 30 novembre 2006StatutMembreDernière intervention14 janvier 201119 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és2480Date d'inscriptionjeudi 30 novembre 2006StatutMembreDernière intervention14 janvier 201119 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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és276Date d'inscriptionmercredi 22 septembre 2010StatutMembreDernière intervention 8 janvier 20144 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.
17 févr. 2011 à 11:03
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...
16 févr. 2011 à 22:45
Un exemple nous permettrait néanmoins de progresser dès maintenant !
16 févr. 2011 à 22:04
<?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.
16 févr. 2011 à 21:17
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.
16 févr. 2011 à 20:11
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 !
16 févr. 2011 à 18:43
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.
16 févr. 2011 à 10:24
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.
15 févr. 2011 à 18:25
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.
15 févr. 2011 à 17:06
15 févr. 2011 à 16:55
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>