Calcul de dates avec PHP [Résolu]

Messages postés
6
Date d'inscription
vendredi 27 août 2010
Dernière intervention
4 octobre 2010
- - Dernière réponse : neigedhiver
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
- 5 oct. 2010 à 09:32
Bonjour,


Pour un site web que je suis en train de développer, j'ai besoin de lister toutes les dates comprises dans une période. Sur la première page l'utilisateur saisi une date de début et une date de fin et valide le formulaire. Sur la page de résultat les dates allant de "date_deb" à "date"fin" inclus sont affichées avec un retour chariot entre chaque. (bon en réalité c'est beaucoup plus compliqué, mais je simplifie). Les dates doivent être saisies et affichées au format "Y-m-d" pour être compatible avec ma base SQL.

Mon problème est que certaines dates sont affichées en double dans mon résultat et d'autres manquent. Et bien sur ce ne serait pas drole si le problème était fixe : c'est totalement aléatoire :

Exemple 1 : je demande les dates du 2010-01-01 au 2010-12-31 : aucun problème, j'ai bien 365 dates toutes différentes.
Exemple 2 : je demande les dates du 2010-08-17 au 2010-09-14 : aucun problème, j'ai bien 29 dates toutes différentes
Exemple 3 : je demande les dates du 2011-06-01 au 2011-12-20 : j'ai bien 203 dates, ce qui pourrait paraitre normal, mais en regardant de plus près, je me rend compte que la liste s'arrete au 2011-12-19 et que le 2011-30-10 apparait 2 fois.
Exemple 4 : je demande les dates du 2010-06-01 au 2010-12-31 : même chose que l'exemple 3, mais c'est le 2010-12-31 qui manque et le 2010-10-31 qui est en double.

Voici le code de ma page de résultat :

<?php
include("enteteshtml.php") ;

echo '


';

$date_deb = $_POST['date_deb'];
$date_fin = $_POST['date_fin'];

$date_debunix = strtotime($date_deb);
$date_finunix = strtotime($date_fin);

for($aunix = $date_debunix; $aunix <= $date_finunix; $aunix = $aunix+86400)
{
$a = date('Y-m-d', $aunix);
echo $a.'
';
}
echo '




</html>
';
?>


Je vous ai même mis le bouzin en ligne pour tester par vous même si ça peut vous aider à m'aider : http://macqueron.fr/help/

Est-ce que quelqu'un a une idée d'où cela peut venir ? Je ne pense pas avoir fait d'erreur dans le code et je n'ai trouvé aucun bug recensé sur le web qui parle de ça. Bref, je patauge...

--
Rénald
Afficher la suite 

Votre réponse

11 réponses

Meilleure réponse
Messages postés
6
Date d'inscription
vendredi 27 août 2010
Dernière intervention
4 octobre 2010
3
Merci
Me revoilà une dernière fois : j'ai trouvé tout seul finalement et le problème vient des deux changements d'heure annuels. J'ai donc mis une rustine à mon code mais ça ne me plait pas beaucoup :

<?php
include("avantcorps.php") ;

echo '


';

$date_deb = $_POST['date_deb'];
$date_fin = $_POST['date_fin'];

$date_debunix = strtotime($date_deb)+43200;
$date_finunix = strtotime($date_fin)+43200;

for($aunix = $date_debunix; $aunix <= $date_finunix+3600; $aunix = $aunix+86400)
{
$a = date('Y-m-d H:i', $aunix);
echo $a.' = '.$aunix.'
';
}
echo '




</html>
';
?>


Si quelqu'un a quelque-chose de mieux à me proposer, je suis intéressé.

--
Rénald

Dire « Merci » 3

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources a aidé 101 internautes ce mois-ci

Commenter la réponse de Renald689
Messages postés
6
Date d'inscription
vendredi 27 août 2010
Dernière intervention
4 octobre 2010
0
Merci
Re-bonjour,


J'avance dans l'analyse de mon problème, et j'ai eut l'idée de modifier mon code comme ceci pour voir ce qui se passe exactement :

<?php
include("avantcorps.php") ;

echo '


';

$date_deb = $_POST['date_deb'];
$date_fin = $_POST['date_fin'];

$date_debunix = strtotime($date_deb);
$date_finunix = strtotime($date_fin);

for($aunix = $date_debunix; $aunix <= $date_finunix; $aunix = $aunix+86400)
{
$a = date('Y-m-d H:i', $aunix);
echo $a.' = '.$aunix.'
';
}
echo '




</html>
';
?>


On se rend donc bien compte que la date Unix est bien incrémentée de 86400 à chaque fois, y compris quand le problème se pose, mais il semblerait que ce soit la conversion au format "Y-m-d" qui pose problème... Je relis la page de manuel PHP sur la fonction "date" mais je ne vois rien qui me mette sur la voix
Commenter la réponse de Renald689
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
0
Merci
Salut,

Depuis PHP5.1, ne pas préciser le fuseau horaire provoque une erreur.
As-tu essayé de le préciser ? Spécifier le fuseau horaire permet de prendre en compte, dans les calculs de date et heure, le changement d'heure été/hiver (DST).

Exemple :
<?php
date_default_timezone_set('Europe/Paris');
$debut = strtotime('10/31/2010 01:00');
$fin = strtotime('10/31/2010 04:00');
$diff = $fin - $debut;
echo $diff;
?>

Ce code affichera 14400, en secondes, soit 4h. Ce qui est bien le temps véritablement écoulé, puisqu'à 3h il sera de nouveau 2h...

--
Neige

Souvent la réponse à votre question se trouve dans la doc. Commencez par là ;)
Commenter la réponse de neigedhiver
Messages postés
341
Date d'inscription
mercredi 17 juillet 2002
Dernière intervention
14 juillet 2011
0
Merci
Bonjour,

Personnellement, je trouve plus simple d'utiliser la classe "DateTime" qui existe depuis PHP 5.1.
Essaie ce code :
<?php
  function calculDate($deb, $fin)
  {
    $tableauDate = array();
    $dateDeb = new DateTime($deb);
    while($deb <= $fin)
    {
      $tableauDate[] = $deb;
      $dateDeb -> modify('+ 1 day');
      $deb = $dateDeb -> format('Y-m-d');
    }
    return $tableauDate;
  }
  //----------------------------------------------------------------------------
  //----------------------------------------------------------------------------
  //----------------------------------------------------------------------------
  if(isset($_POST['btn']))
  {
    echo implode('
', calculDate($_POST['dateDeb'], $_POST['dateFin']));
  }
  else
  {
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <script type="text/javascript"></script>
    <style type="text/css"></style>
  </head>
  
    <form name="date" method="post" action="">
      Date début (AAAA-MM-JJ) 

      Date fin (AAAA-MM-JJ) 

      
    </form>
  
</html>
<?php } ?>

C'est un tout petit peu plus long à l'exécution (l'écart se situe au niveau du millième de seconde), mais pas besoin de se préoccuper du fuseau horaire ni des changements d'heure ni des années bissextiles (dans ce cas particulier, qui ne réclame qu'une liste de jours).

Cordialement.

JC
Commenter la réponse de cs_jeca
Messages postés
2382
Date d'inscription
lundi 4 février 2002
Dernière intervention
29 décembre 2012
0
Merci
Comme Jeca.. on goute une fois aux DateTime, on se prend la tête dessus qqs fois mais fini les questions existencielles sur la gestion et autres fonctions..
S.
Commenter la réponse de syndrael
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
0
Merci
@jeca : faux. Avec ton code, j'obtiens :

Fatal error: Uncaught exception 'Exception' with message 'DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function.


C'est bien quand on n'affiche pas les erreurs, mais on ne les voit pas...

Conclusion : il faut TOUJOURS se préoccuper du fuseau horaire, sinon PHP ne sait pas comment calculer les heures.

--
Neige

Souvent la réponse à votre question se trouve dans la doc. Commencez par là ;)
Commenter la réponse de neigedhiver
Messages postés
6
Date d'inscription
vendredi 27 août 2010
Dernière intervention
4 octobre 2010
0
Merci
Bonjour,

Merci pour l'info sur le fuseau horaire neigedhiver, mais celà ne resoud pas le problème : j'ai fait un test en enlevant ma rustine et en mettant le fuseau horaire, et le problème reste le même.

Je garde l'idée du DateTime pour la fin de mon projet, au moment de l'optimisation, parce que pour l'instant ça me parait un peu compliqué à mettre en oeuvre. Donc je reste avec ma rustine pour l'instant.

--
Rénald
Commenter la réponse de Renald689
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
0
Merci
L'origine du problème est assez simple à identifier : c'est le passage à l'heure d'hiver.
A partir de là, décortiquons ce qui se passe.
Je ne sais pas dans quel format tu passes la date de début et de fin, mais je suppose, puisque tu souhaites compter des jours, que tu passes une date sans heure. Donc, par défaut, PHP considère que c'est à 00h00:00.
Au passage à l'heure d'hiver en 2010, le 30 octobre, à 3h, il sera de nouveau 2H. La journée du 30 octobre ne compte pas 24h, mais 25. Donc si tu ajoutes 24h à minuit, il est au final... 23h, toujours le 30 octobre 2010. Donc, tu as 2 fois la même date.
Et au final, ta comparaison dans ta boucle ne sera pas satisfaite la fois qui manque (il manquerait 3600 pour que la boucle soit exécutée une fois de plus). Donc, il te manque la dernière date.

La conclusion, c'est que pour calculer des dates, il faut utiliser... des dates. Et pas des secondes.
Au lieu d'ajouter 86400 secondes, il convient d'ajouter des jours... Tu commences avec un compteur à 0 et tu recalcules la date avec strtotime() en ajoutant litéralement le nombre de jours à la date (puisque PHP est capable de calculer ça). Voici un exemple :

<?php

date_default_timezone_set('Europe/Paris');
$deb_date = '2010-10-01';
$deb = strtotime($deb_date);
$fin = strtotime('2010-11-30');
$curr = $deb;
for ($i = 1; $curr < $fin; $i++) {
echo date('d-m-Y', $curr), '
';
$curr = strtotime($deb_date.' +'.$i.'days');
}

?>


Une autre solution consiste à prendre la date de début à midi (mais c'est quand même moins propre).

Pour ce qui est de l'utilisation de l'objet DateTime, voici un exemple commenté :

<?php

$deb = '2010-10-01';
$fin = '2010-11-30';

// On isntancie un nouvel objet DateTime, avec la date de début et le fuseau horaire CET.
$d = new DateTime($deb , new DateTimeZone('Europe/Paris'));

// On crée un objet DateInterval qui correspond à un intervalle d'une journée
// P1D : P pour Period, 1D pour 1 day.
// Cf la liste des paramètres : http://fr2.php.net/manual/fr/dateinterval.construct.php
$di = new DateInterval('P1D');

//On boucle jusqu'à ce que la date de $d soit celle de $fin
while ($d -> format('Y-m-d') != $fin) {
echo $d -> format('Y-m-d'), '
';
// On ajoute l'intervalle de 1 jour
$d -> add($di);
}
// On affiche la dernière date
echo $d -> format('Y-m-d');

?>

Dans ce bout de code, le test n'est pas suffisament rigoureux, puisque si la date de fin donnée est antérieure à la date de début, on aura une boucle sans fin. C'est juste pour illustrer le fonctionnement.

J'espère que tu fera bon usage de tout ça, il est assez rare que je prenne autant de temps pour une réponse ;)

--
Neige

Souvent la réponse à votre question se trouve dans la doc. Commencez par là ;)
Commenter la réponse de neigedhiver
Messages postés
341
Date d'inscription
mercredi 17 juillet 2002
Dernière intervention
14 juillet 2011
0
Merci
@neigedhiver
DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function.

traduction : Faire confiance aux paramètres timezone du système n'est pas fiable. Il est préférable d'utiliser le paramètre date.timezone (php.ini) ou la fonction date_default_timezone_set().

Donc, pas grand chose à voir avec un problème de code.

Conclusion : il faut TOUJOURS se préoccuper du fuseau horaire, sinon PHP ne sait pas comment calculer les heures.
Faux, puisque le paramètre "timezone" du constructeur "DateTime" est optionnel. Par défaut, celui du php.ini est pris en compte.

Au passage à l'heure d'hiver en 2010, le 30 octobre, à 3h, il sera de nouveau 2H. La journée du 30 octobre ne compte pas 24h, mais 25. Donc si tu ajoutes 24h à minuit, il est au final... 23h, toujours le 30 octobre 2010. Donc, tu as 2 fois la même date.

Faux. L'ajout dans le code n'est pas de 24 heures mais de 1 jour, c'est à dire : 31 oct. + 1 jour = 1er nov.
Petite rectification : Le changement d'heure est le 31 à 3 heures.

Pour finir, l'objet "DateInterval" et la méthode "add" n'existent que depuis PHP 5.3, ce que tout le monde n'a pas (PHP 5.1 non plus, peut-être).

Cordialement.
Commenter la réponse de cs_jeca
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
0
Merci
Hey, on va se calmer, hein.

La fuseau horaire, comme je le disais, permet à PHP de savoir quand il doit prendre en compte le changement d'heure été/hiver, s'il y a lieu : comme je l'ai expliqué, le problème vient justement de ce changement d'heure.

Le fuseau horaire est peut-être optionnel : peu de gens ont accès au php.ini sur un hébergement gratuit ou mutualisé, autant alors passer le fuseau horaire en paramètre, puisque c'est en fait à peu près le seul moyen de le définir.

Dans le code de départ :
for($aunix = $date_debunix; $aunix <= $date_finunix; $aunix = $aunix+86400)

86400 = 24 * 60 * 60, soit 24 heures. C'est pas moi qui l'invente !

Tout le monde n'a peut-être pas PHP5.1, dans ce cas commence par t'appliquer à toi-même cette restriction et ne parle pas de l'objet DateTime que tu affectionnes tant, qui était disponible comme module expérimental dans PHP 5.1.

C'est quoi le problème finalement ? Que le code que tu propose lève une erreur ? Ca te chiffonne ?

--
Neige

Souvent la réponse à votre question se trouve dans la doc. Commencez par là ;)
Commenter la réponse de neigedhiver
Messages postés
2492
Date d'inscription
jeudi 30 novembre 2006
Dernière intervention
14 janvier 2011
0
Merci
J'en remets une couche, parce que certaines choses ont besoin d'être précisées, pour le bien des développeurs qui tomberaient sur ce fil.

Donc, pas grand chose à voir avec un problème de code.

C'est là que tu te trompes énormément. PHP déclenche maintenant une erreur quand on ne précise pas le fuseau horaire. Oui, il utilise le fuseau horaire par défaut, n'empêche que si on ne fait pas attention soi-même, on peut avoir une erreur. On peut très bien partir du principe qu'en général, on héberge son site sur un serveur à la même heure que soi. C'est bien franco-française, ça, les américains se soucient davantage des fuseaux horaires que nous.
Si on utilise l'objet DateTime que tu aimes tant, PHP lève une exception. Ca veut dire que pour éviter que le script ne s'interrompe, il faut attrapper cette exception : quoi de plus illogique que d'attrapper une exception qu'on sait pouvoir éviter ?
Il s'agit d'une erreur de conception qui n'est pas acceptable : pour que le code soit portable, il faut prendre en compte ce genre de "détails".
On peut aussi utiliser mysl sans paramètre, après tout, il va utiliser les valeurs par défaut de my.cnf, mais combien de personnes font ça ?
Certains utilisent aussi les short tags, alors qu'on sait bien que c'est une pratique à ne pas encourager. Idem avec le fuseau horaire.

Faux, puisque le paramètre "timezone" du constructeur "DateTime" est optionnel. Par défaut, celui du php.ini est pris en compte.

Tu n'as pas bien lu ce que j'ai écrit (porutant tu l'as cité) ou alors, tu n'as pas compris les mots que j'ai utilisés.
Conclusion : il faut TOUJOURS se préoccuper du fuseau horaire, sinon PHP ne sait pas comment calculer les heures.

Selon toi, jeca, donc, "se préoccuper" est équivalent à "passer en paramètre" ? Se préoccuper signifie qu'il faut garder en tête que le fuseau horaire doit être correct dans son code, qu'on l'affecte par le code ou qu'on laisse la valeur par défaut de PHP. Et je viens d'expliquer pourquoi il était préférable de le passer en paramètre.

Petite rectification : Le changement d'heure est le 31 à 3 heures.

Ouch, là tu m'as eu, tu as relevé l'erreur d'inattention qui invalide tout ce que je dis. Pardon, j'ai mélangé le 31 octobre 2010 et le 30 octobre 2011.

Pardon d'avoir donné un exemple de code portable qui ne génère aucune erreur et ce avec un objet dont tu recomandes l'utilisation.

--
Neige

Souvent la réponse à votre question se trouve dans la doc. Commencez par là ;)
Commenter la réponse de neigedhiver

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.