Calcul sur les dates : afficher les semaines d'un mois donné

Eléphant du PHP | 136 Messages

26 oct. 2008, 11:53

Je travaille sur une fonction pour calculer les semaines d'un mois.

Voici la fonction :
function semainesDuMois($datedebut, $datefin) {
 
	$explDateD = explode('/', $dateDebut);
	$explDateF = explode('/', $dateFin);
	$dateD = $explDateD[2].'-'.$explDateD[1].'-'.$explDateD[0];
	$dateF = $explDateF[2].'-'.$explDateF[1].'-'.$explDateF[0];
	
	$toTimeStart = strtotime($dateD);
	$toTimeEnd = strtotime($dateF);
	$jourDeb = date('w', $toTimeStart);
// début de construction du tableau
echo '<table width="55%" border="0" cellspacing="0" cellpadding="0"><tr>
    <td>NOM DU MOIS</td> ';
// boucle sur les semaines
	while ($toTimeStart < $toTimeEnd) {
		
		$joursRestant = 7 - $jourDeb;
		echo date('d\.m\.y', $toTimeStart).' au ';
		$toTimeStart = strtotime('+'.$joursRestant.' day', $toTimeStart);
		echo date('d\.m\.y', $toTimeStart).'<br />';
		$toTimeStart = strtotime('+ 1 day', $toTimeStart);
		$jourDeb = date('w', $toTimeStart);
		
	}
// Suite et fin du tableau
echo '</tr></table>';
}

Le but étant d'afficher un tableau de ce type :
OCTOBRE 2008 | 01.10.08 au 05.10.08 | 06.10.08 au 12.10.08 | 13.10.08 au 19.10.08 | 20.10.08 au 26.10.08 | 27.10.08 au 31.10.08

Or je n'arrive pas à trouver le bug qui fait que ma fonction affiche jusqu'au 2 novembre (soit la semaine complète) et non jusqu'au 31.10...

Une petite idée ?

Bisvan
Modifié en dernier par Bisvan le 04 nov. 2008, 13:34, modifié 2 fois.
Bisvan :)

ViPHP
ViPHP | 4674 Messages

26 oct. 2008, 12:19

Hey :),

Est-ce qu'on pourrait avoir les entrées de ta fonction, ce serait plus simple pour nous (car je soupçonne naïvement une date anglaise ou française qui met la bazar).
Et juste en remarque, j'aurais donné une date en Timestamp dès le début, ça se manipule nettement mieux …
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

26 oct. 2008, 13:57

Hello,

Je mets :

Code : Tout sélectionner

$datedebut="01/10/2008"; $datefin="31/10/2008";
Bisvan :)

ViPHP
ViPHP | 4674 Messages

26 oct. 2008, 23:50

Et si tu essayes d'afficher toutes les valeurs une par une pour bien comprendre, ça te donne quoi ? Essaye de débugger par toi-même (si ce n'est pas déjà fait). Mais j'avoue ne pas trop comprendre ton code. De plus, ton exemple est faux : 01 au 05 octobre, ça ne fait pas une semaine. D'autant plus que le mois a commencé un mercredi et pas un lundi.

Essaye de travailler directement en timestamp pour commencer, ajoute 604 800 secondes (3600 * 24 * 7), et observe le résultat à travers date().
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

27 oct. 2008, 10:20

En fait j'essaye d'avoir dans un tableau juste le mois.
Donc du 1er au 5 octobre (la semaine commence un mercredi)
Puis etc... par semaine jusqu'au 31 octobre.

Bon j'ai revu mon code depuis le début pour l'adapter en fonction du premier jour du mois (pas forcément le lundi !)
$dateDebut='2008-10-01';
$dateFin='2008-10-31';
$TimeStart = strtotime($dateD);
$TimeEnd = strtotime($dateF);
$jourDebut = date('w', $TimeStart);

while ($TimeStart < $toTimeEnd) 
{
	if($jourDebut!= 0)
	{
		$joursRestant = 7 - $jourDebut +1;
		echo date('d\.m\.y', $TimeStart);
		echo "<BR>";
		$TimeStart = strtotime('+'.$joursRestant.' day', $TimeStart);
		$jourDebut = 1;
	}
	
	if($jourDebut== 0)
	{
		$joursRestant = 1;
		echo date('d\.m\.y', $TimeStart);
		echo "<BR>";
		$TimeStart = strtotime('+'.$joursRestant.' day', $TimeStart);
		$jourDebut = 1;
	}
}
Là j'ai à l'affichage tous les débuts de semaine du mois :

01.10.08
06.10.08
13.10.08
20.10.08
27.10.08

Mais maintenant je suis bloqué pour l'affichage de la date de fin de semaine.
Pour octobre la première "semaine" va du Mercredi 1er au Dimanche 5 octobre
La dernière du lundi 27 au vendredi 31 octobre...
Je ne vois pas comment afficher à chaque semaine ...la fin de semaine...

En clair comment afficher :
01.10.08 > 05.01.08
06.10.08 > 12.10.08
13.10.08 > 19.10.08
20.10.08 > 26.10.08
27.10.08 > 31.10.08

Une petite idée ?
Bisvan :)

ViPHP
ViPHP | 4674 Messages

27 oct. 2008, 13:57

Hey :),

Bon, j'ai pris 20 minutes pour faire ton script, mais sans utiliser strtotime. Déjà parce que ça m'intéressait et pour te montrer une autre façon de procéder.

On travaille sur le nombre de semaine. En effet, chaque semaine a un numéro attribué dans l'année. Par exemple, cette semaine a le numéro 44.

L'idée est de prendre le numéro de semaine du début du mois et de fin de mois. On itère un jour qui démarre de 0 et qui va jusqu'à la fin du mois. Pour chaque itération, on regarde le numéro de semaine à changer ou pas. S'il change, c'est qu'on démarre une nouvelle semaine, donc on temporise le jour, sinon on continue l'itération.
Enfin, une fois nos débuts de semaine temporisé, on affiche les semaines complètes (c'est l'affichage du résultat, il est tout simple).

Je n'ai pas testé les effets de bord, i.e. si le mois commence un lundi par exemple. L'affichage pourrait peut-être avoir des soucis (je n'y ai pas réfléchit). À toi de tester. Ce n'est qu'une base.

Voici l'algorithme :
<?php

ini_set('date.timezone', 'Europe/Paris');

$now = time();
$df  = 1;
$dl  = (int) date('t', $now);
$m   = (int) date('m', $now);
$y   = (int) date('Y', $now);
$wm  = (int) date('W', mktime(0, 0, 0, $m, $df, $y));
$wM  = (int) date('W', mktime(0, 0, 0, $m, $dl, $y));
$id  = 0;
$l   = $wm;
$ws  = array();

while(   $wM   >= ($t = (int) date('W', mktime(0, 0, 0, $m, $id, $y)))
      && $id++ <  $dl) {

    if($t == $l)
        continue;

    $ws[] = $id - 1;
    $l++;
}   

$prv = $df;
for($i = 0, $max = count($ws); $i < $max; $i++) {

    var_dump($prv . ' -> ' . ($ws[$i] - 1));
    $prv = $ws[$i]; 
}   

if($prv != $dl)
    var_dump($prv . ' -> ' . $dl);
et ça m'affiche

Code : Tout sélectionner

string(6) "1 -> 5" string(7) "6 -> 12" string(8) "13 -> 19" string(8) "20 -> 26" string(8) "27 -> 31"
QED.

Alors oui, j'ai oublié de préciser pour les variables, j'ai pas mis des noms complets (pour mes tests, j'ai mes règles de nommage courtes :P). Donc :
  • df : day first soit le premier jour du mois ;
  • dl : day last soit le dernier jour du mois ;
  • m : month soit le mois ;
  • y : year soit l'année ;
  • wm : week min soit le numéro de la première semaine du mois ;
  • wM : week Max soit le numéro de la dernière semaine du mois ;
  • id : i day soit la variable incrémentée sur les jours (pour la boucle) ;
  • l : last soit la dernière semaine itérée (pour la boucle) ;
  • ws : week start soit le début de chaque semaine (notre résultat de boucle).
Et on dit ceci : tant que la semaine courante (calculée avec notre variable id) est inférieure à la semaine maximum, et que notre variable id, soit le jour du mois itéré est inférieur au jour maximum du mois (en gros : on lit toutes les semaines du mois), alors si la semaine calculée t est égale à la semaine courante étudiée l, on ne fait rien. Sinon, on a changé de semaine, donc il faut temporiser le jours qui représente le début de semaine.

Et l'affichage est tout simple. On prend notre résultat, et on affiche les intervalles.

Ça peut paraître compliqué comme ça, mais en fait le raisonnement est tout simple. On travaille sur les numéros de semaine et c'est tout. Notre boucle fonctionne même si on est en fin d'année où le numéro de semaine maximum risque de passer à 1 car on fait aussi un test sur les jours (à travers id). Donc normalement, pas de soucis.

Je te laisse méditer là-dessus :).
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

31 oct. 2008, 20:30

Merci beaucoup pour ton aide ! Ca m'aide beaucoup dans la compréhension du script et du problème.

J'ai fais des petits tests en mettant :
$now = mktime (0,0,0,11,01,2008);
Là pas de problème il indique bien

Code : Tout sélectionner

string(6) "1 -> 2" string(6) "3 -> 9" string(8) "10 -> 16" string(8) "17 -> 23" string(8) "24 -> 30"
Par contre comme tu t'en doutais déjà il y a effectivement un bug sur les mois commençant un lundi.
Par exemple pour décembre 2008 :
$now = mktime (0,0,0,12,01,2008);
Le script donne le résultat suivant :

Code : Tout sélectionner

string(7) "1 -> 31"
Je vais essayer de trouver où cela coince, mais il me faut un peu de temps histoire de bien comprendre ton script. Si tu as une idée n'hésites pas.

Merci encore,
Bisvan :)

ViPHP
ViPHP | 4674 Messages

02 nov. 2008, 22:08

Hey :),

J'ai passé peu de temps sur le script connaissant pertinemment les problèmes de bords. Hélas je n'ai pas des masses de temps pour me pencher dessus, mais tu devrais y arriver je pense :). Si tu n'y arrives vraiment pas, insiste ici et je m'y pencherais plus sérieusement.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

03 nov. 2008, 14:00

Bon j'ai décortiqué ton script.
Je commence à y voir plus clair.

Déjà : l'erreur que j'ai mis ci-dessus concerne une erreur avec le mois de décembre et non le problème du lundi.

En effet la première boucle calcul si $wM et >= à t (le numéro de début de semaine avec le jour itéré).
Or pour les mois de décembre dont la dernière semaine se trouve à cheval en janvier : wM = 1.
La boucle ne peut se faire vu que $wM n'est pas >= à t

Voici le résultat pour décembre 2007 (car en 2008 il y a aussi le problème du lundi) :

Code : Tout sélectionner

string(7) "1 -> 31"
Le deuxième problème vient des mois dont la semaine commence un lundi, voici le résultat pour JUIN 2009 (commençant un LUNDI) :

Code : Tout sélectionner

string(7) "1 -> -1" string(6) "0 -> 0" string(6) "1 -> 1" string(6) "2 -> 2" string(6) "3 -> 3" string(6) "4 -> 4" string(6) "5 -> 5" string(6) "6 -> 6" string(6) "7 -> 7" string(6) "8 -> 8" etc...jusqu'à string(8) "29 -> 30"
Si d'autres personnes veulent participer à la recherche des bug perdus...ils sont les bienvenues :)
Bisvan :)

ViPHP
ViPHP | 4674 Messages

03 nov. 2008, 16:28

Tu peux aussi reprendre le script depuis zéro mais utiliser la même méthodologie, i.e. travailler sur les semaines :).
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

03 nov. 2008, 20:10

Oui mais je suis perdu :shock:
En fait j'aimerais mettre la condition de la semaine démarrant le lundi ici :
while($weekmax >= ($t = (int) date('W', mktime(0, 0, 0, $month, $iday, $year))) && $iday++ <  $datelast) {

if($t == $last)
{
continue;
$weekstart[] = $iday;
$last++;
}
// peut être mettre la condition ICI > si le début de semaine est 1 (lundi) on prend le premier chiffre de la semaine donc on laisse $iday comme il est
elseif($debutsemaine == 1)
{ $weekstart[] = $iday; $last++; }

}

Mais je connais pas assez l'utilisation du continue; pour savoir comment le faire :cry:
Bisvan :)

ViPHP
ViPHP | 4674 Messages

03 nov. 2008, 20:24

Le mot-clé continue saute une itération de la boucle grosso modo. Va voir le manuel, il a plus le temps que moi pour t'expliquer ;-). C'est complémentaire de break si tu connais.
« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

Hoa : http://hoa-project.net (sur @hoaproject).

Eléphant du PHP | 136 Messages

04 nov. 2008, 13:37

Alors j'ai fini par refaire un script (surement bcp plus brouillon que le tiens...:oops: )
J'ai repris quelques idées sur le fonctionnement de ton script.

Je rappel que le but était d'afficher les semaines d'un mois donnée.
Les problèmes de mois commençant un lundi sont réglés.
<?php
/*
* AFFICHAGE DES SEMAINES D'UN MOIS DONNEE
*
*/

/*
* 1. PARAMETRES
*
*/
$jour	= "01";		// 1er jour du mois
$mois	= "11";		// mois
$annee	= "2008";	// année

/*
* 2. CALCULS EN FONCTION DES PARAMETRES
*
*/
$mkdebut	= mktime (0,0,0,$mois,$jour,$annee);		// Timestamp UNIX du début premier jour du mois
$nbrejour	= (int) date('t', $mkdebut);				// Nombre de jour dans le mois
$mkfin		= mktime (0,0,0,$mois,$nbrejour,$annee);	// Timestamp UNIX du dernier jour du mois

$jourdebut	= (int) date('w', $mkdebut);				// Numéro dans la semaine du premier jour du mois
														// (0=dim, 1=lundi, 2=mardi, 3=mer, 4=jeudi, 5=vend, 6=sam)
														
$jourfin	= (int) date('w', $mkfin);					// Numéro dans la semaine du dernier jour du mois


/*
* 3. FONCTION CALCUL DEBUT/FIN DE SEMAINE
*
* Cette fonction donne la date du début et de fin d'une semaine en fonction d'une date
* $x = 0 (pour la semaine en cours) | $x = 1 (pour la semaine précédente) | $x = 2 etc...
* $mois = le mois en cours
* $jour = le 1er jour du mois
* $annee = l'année en cours
* $jourdebut = Numéro dans la semaine du premier jour du mois
*
*/
function calculSem($x, $mois, $jour, $annee, $jourdebut)
{
$premier_jour = mktime(0,0,0,$mois,$jour-$jourdebut+1-$x*7,$annee);
$datedeb = date("d-m-Y", $premier_jour);

$dernier_jour= explode ("-",$datedeb);
$dernier_jour = strtotime("$dernier_jour[2]-$dernier_jour[1]-$dernier_jour[0]");
$dernier_jour = strtotime('+6 day', $dernier_jour);
$datefin = date("d-m-Y", $dernier_jour);
return array($datedeb, $datefin);
}



/*
* 4. CALCUL FINAL (Semaines du mois)
*
*
*/

// 4.1 Si le premier jour du mois est un lundi
if($jourdebut == 1) 		
{ 

	// Boucle sur chaque jour du mois
	for($i=1; $i <= $nbrejour; $i++) 
	{
		// Calcul le numéro du jour de $i 
		$mk_i = mktime (0,0,0,$mois,$i,$annee); 
		$jour_i = (int) date('w', $mk_i);
		
		// Si $i est un lundi	
		if($jour_i == 1) 
		{
		// Récupération par la fonction du début et de la fin de semaine en cours
		$retour_semaine= calculSem(0, $mois, $i, $annee, 1);
		$retour_semaine_expl=explode ("-",$retour_semaine[1]);
		
		// Affichage de la date du début de la semaine en cours
		echo $retour_semaine[0].' au ';
			
			// si la fin de semaine n'est pas dans le même mois on affiche le dernier jour du mois
			if($retour_semaine_expl[1] != $mois) 
			{echo $nbrejour.'-'.$mois.'-'.$annee;}
			// si la fin de semaine est dans le même mois on affiche le dernier jour de la semaine
			elseif($retour_semaine_expl[1] == $mois) 
			{echo $retour_semaine[1].'<BR>';}
					
		}
	}
}

// 4.2 Si le premier jour du mois n'est pas un lundi
else
{
// Récupération par la fonction du début et de la fin de la premiere semaine du mois
$retour_semaine= calculSem(0, $mois, $jour, $annee, $jourdebut); 
$retour_semaine_expl=explode ("-",$retour_semaine[1]);
	
// Affichage premier jour du mois
echo $jour.'-'.$mois.'-'.$annee.' au '; 
// Affichage dernier jour de la première semaine du mois
echo $retour_semaine_expl[0].'-'.$retour_semaine_expl[1].'-'.$retour_semaine_expl[2].'<BR>';
	
// On ajoute 1 pour revenir au premier jour de la seconde semaine du mois
$ideb=$retour_semaine_expl[0]+1;
	
	// Boucle sur chaque jour du mois à partir de la seconde semaine (commençant donc un lundi)
	for($i=$ideb; $i <= $nbrejour; $i++)
	{
	
	// Calcul le numéro du jour de $i
	$mk_i = mktime (0,0,0,$mois,$i,$annee);
	$jour_i = (int) date('w', $mk_i);
	
		// Si $i est un lundi
		if($jour_i == 1)
		{
		// Récupération du premier et du dernier jour de la semaine en cours
		$retour_semaine= calculSem(0, $mois, $i, $annee, 1);
		$retour_semaine_expl=explode ("-",$retour_semaine[1]);
		
		// Affichage de la date du début de la semaine en cours
		echo $retour_semaine[0].' au ';
			// Si la fin de semaine n'est pas dans le même mois on affiche le dernier jour du mois
			if($retour_semaine_expl[1] != $mois) 
			{echo $nbrejour.'-'.$mois.'-'.$annee;}
			// Si la fin de semaine est dans le même mois on affiche le dernier jour de la semaine
			elseif($retour_semaine_expl[1] == $mois) 
			{echo $retour_semaine[1].'<BR>';}
		
		}
	}

}


	?>
En espérant que cela aidera des personnes qui ont ce besoin (ou alors c'est simplement moi qui suis complètement barré avec mes idées :lol:

Encore merci HyWaN pour ta patience et ton aide :)
Bisvan :)

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 13231 Messages

04 nov. 2008, 20:01

Modération :
Bisvan, si ta question est résolue, pense à ajouter le tag [Résolu]
pour indiquer aux personnes qui voudront consulter ce sujet qu'il contient une solution.
Tu peux réaliser cette opération en cliquant sur le bouton Image en haut à gauche de ce sujet.
Connaître son ignorance est la meilleure part de la connaissance
Pour un code lisible : n'hésitez pas à sauter des lignes et indenter

twitter - site perso - Github - Zend Certified Engineer