Calcul de dates avec PHP

Résolu
Renald689 Messages postés 6 Date d'inscription vendredi 27 août 2010 Statut Membre Dernière intervention 4 octobre 2010 - 3 oct. 2010 à 16:47
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre 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
A voir également:

11 réponses

Renald689 Messages postés 6 Date d'inscription vendredi 27 août 2010 Statut Membre Dernière intervention 4 octobre 2010
3 oct. 2010 à 18:07
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
3
Renald689 Messages postés 6 Date d'inscription vendredi 27 août 2010 Statut Membre Dernière intervention 4 octobre 2010
3 oct. 2010 à 17:39
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
0
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
3 oct. 2010 à 19:47
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à ;)
0
cs_jeca Messages postés 341 Date d'inscription mercredi 17 juillet 2002 Statut Membre Dernière intervention 14 juillet 2011 14
4 oct. 2010 à 06:04
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
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
syndrael Messages postés 2378 Date d'inscription lundi 4 février 2002 Statut Membre Dernière intervention 29 décembre 2012 20
4 oct. 2010 à 09:20
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.
0
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
4 oct. 2010 à 09:22
@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à ;)
0
Renald689 Messages postés 6 Date d'inscription vendredi 27 août 2010 Statut Membre Dernière intervention 4 octobre 2010
4 oct. 2010 à 20:04
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
0
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
4 oct. 2010 à 21:35
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à ;)
0
cs_jeca Messages postés 341 Date d'inscription mercredi 17 juillet 2002 Statut Membre Dernière intervention 14 juillet 2011 14
5 oct. 2010 à 03:33
@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.
0
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
5 oct. 2010 à 08:41
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à ;)
0
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
5 oct. 2010 à 09:32
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à ;)
0
Rejoignez-nous