Eerste woensdag van de huidige maand en de 5 volgende maanden
In het maken van PHP-scripts ben ik een leek en probeer aldoende bij te leren. Voor onze sportvereniging onderhoud ik een website met daarop onderandere een agenda. Het eerste item op deze agenda is een maandelijkse bijeenkomst, telkens op de eerste woensdag van de maand, ongeacht of dit een feestdag is.
De originele Agenda is een html-pagina waar ik dan eenmaal per maand de data verander.
Daar er aan de website een database gekoppeld is, heb ik van deze pagina een PHP-paginaPHP-pagina gemaakt waar ik succesvol een script heb ingebracht (via een include()) om de namen te tonen van de leden die in de huidige maand verjaren.
Nu wou ik een tweede script toevoegen waarin de eerste woensdag van de huidige maand en de eerste woensdag van de volgende 5 maanden wordt weergegeven. Dat script is klaar en werkt naar behoren!
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
//lus om opeenvolgende maanden te bepalen
for ($i==0;$i<6;$i++){
// Voor de huidige maand, splits in 3 factoren
if($i==0)
{
$haaldatum = date('d-m-Y');
$splitsdatum[$i] = explode('-',$haaldatum);
//$num bepaald op welke dag de eerste van de maand valt
$num = date("w",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]));
// als de eerste van de maan een woensdag is, echo datum
if($num==3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]))."<br/>";
// is de eerste van de maand geen woensdag maar groter, bereken de eerste woensdag vanaf 3de van de maand
}elseif($num>3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(8-$num)))."<br/>";
// is de eerste van de maand geen woensdag maar kleiner, bereken de eerste woensdag voor de 3de van de maand
}else{
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(1-$num)))."<br/>";
}
// haal datum op voor de *i-de volgende maannd
}else{
$haaldatum = date('d-m-Y',strtotime('+'.$i.' months'));
$splitsdatum[$i] = explode('-',$haaldatum);
$num = date("w",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]));
// als de eerste van de maan een woensdag is, echo datum
if($num==3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]))."<br/>";
// is de eerste van de maand geen woensdag maar groter, bereken de eerste woensdag vanaf 3de van de maand
}elseif($num>3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(8-$num)))."<br/>";
// is de eerste van de maand geen woensdag maar kleiner, bereken de eerste woensdag voor de 3de van de maand
}else{
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(1-$num)))."<br/>";
}
}
}
?>
//lus om opeenvolgende maanden te bepalen
for ($i==0;$i<6;$i++){
// Voor de huidige maand, splits in 3 factoren
if($i==0)
{
$haaldatum = date('d-m-Y');
$splitsdatum[$i] = explode('-',$haaldatum);
//$num bepaald op welke dag de eerste van de maand valt
$num = date("w",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]));
// als de eerste van de maan een woensdag is, echo datum
if($num==3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]))."<br/>";
// is de eerste van de maand geen woensdag maar groter, bereken de eerste woensdag vanaf 3de van de maand
}elseif($num>3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(8-$num)))."<br/>";
// is de eerste van de maand geen woensdag maar kleiner, bereken de eerste woensdag voor de 3de van de maand
}else{
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(1-$num)))."<br/>";
}
// haal datum op voor de *i-de volgende maannd
}else{
$haaldatum = date('d-m-Y',strtotime('+'.$i.' months'));
$splitsdatum[$i] = explode('-',$haaldatum);
$num = date("w",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]));
// als de eerste van de maan een woensdag is, echo datum
if($num==3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],1,$splitsdatum[$i][2]))."<br/>";
// is de eerste van de maand geen woensdag maar groter, bereken de eerste woensdag vanaf 3de van de maand
}elseif($num>3){
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(8-$num)))."<br/>";
// is de eerste van de maand geen woensdag maar kleiner, bereken de eerste woensdag voor de 3de van de maand
}else{
echo date("j F Y",mktime(0,0,0,$splitsdatum[$i][1],3,$splitsdatum[$i][2])+(86400*(1-$num)))."<br/>";
}
}
}
?>
Als resultaat krijg ik dus telkens de datum van de eerste woensdag voor de huidige en 5 volgende maanden.
Als ik dit script nu kopieer en plak op de pagina agenda2.php krijg ik de echo's niet te zien.
Agenda bestaat uit een tabel en de code heb al op verschillende manieren ingevoerd (Copy/paste, include ()...
Ik krijg ook geen foutmelding en de pagina wordt gewoon opgebouwd.
Iemand enig idee waar dit fout gaat?
functies
Toevoeging op 20/10/2012 18:18:51:
Dus zo zou het ook kunnen,
In ieder geval is dit een hele mooie spaghetti-brei. Veel if/else lussen maken de code compleet onleesbaar en dus ook onbeheersbaar Kijk eens naar Toevoeging op 20/10/2012 18:18:51:
Dus zo zou het ook kunnen,
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function getNextWeekday($timestamp, $wday, $weeks) {
$date = getdate($timestamp); // vraag info op van deze timestamp
$days = $wday - $date['wday']; // gevraagde weekdag - dag vd week van timestamp
if($days < 0)
$days += 7;
$timestamp += 60 * 60 * 24 * $days; // sec * min * uur * dagen
$timestamp += $weeks * 60 * 60 * 24 * 7; // $weeks vooruit
return $timestamp;
}
$timestamp = mktime();
for($weeks = 0 ; $weeks < 5 ; $weeks++) {
$wednesday = getNextWeekday($timestamp, 3, $weeks);
echo ($weeks + 1).'e woensdag: '.date('d-m-Y', $wednesday).'<br>';
}
?>
function getNextWeekday($timestamp, $wday, $weeks) {
$date = getdate($timestamp); // vraag info op van deze timestamp
$days = $wday - $date['wday']; // gevraagde weekdag - dag vd week van timestamp
if($days < 0)
$days += 7;
$timestamp += 60 * 60 * 24 * $days; // sec * min * uur * dagen
$timestamp += $weeks * 60 * 60 * 24 * 7; // $weeks vooruit
return $timestamp;
}
$timestamp = mktime();
for($weeks = 0 ; $weeks < 5 ; $weeks++) {
$wednesday = getNextWeekday($timestamp, 3, $weeks);
echo ($weeks + 1).'e woensdag: '.date('d-m-Y', $wednesday).'<br>';
}
?>
Gewijzigd op 20/10/2012 18:31:55 door Frank Nietbelangrijk
enne welkom hoor ook van mij
De pagina waar het script wordt toegevoegd met include(), bevat reeds een include(). als ik nu die ene wegdoen, dan verschijnt de andere wel. Kan een pagina maar 1 include () bevatten?
Ik ben persoonlijk geen voorstander van includes op de gekste plekken midden in de code. Ik zou deze zoveel mogelijk bovenin de pagina die door de server opgevraagd wordt houden. in combinatie met de eerder besproken functies werkt dat prima.
Toevoeging op 20/10/2012 19:21:06:
datum.php:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
function getNextWeekday($timestamp, $wday, $weeks) {
$date = getdate($timestamp); // vraag info op van deze timestamp
$days = $wday - $date['wday']; // gevraagde weekdag - dag vd week van timestamp
if($days < 0)
$days += 7;
$timestamp += 60 * 60 * 24 * $days; // sec * min * uur * dagen
$timestamp += $weeks * 60 * 60 * 24 * 7; // $weeks vooruit
return $timestamp;
}
?>
function getNextWeekday($timestamp, $wday, $weeks) {
$date = getdate($timestamp); // vraag info op van deze timestamp
$days = $wday - $date['wday']; // gevraagde weekdag - dag vd week van timestamp
if($days < 0)
$days += 7;
$timestamp += 60 * 60 * 24 * $days; // sec * min * uur * dagen
$timestamp += $weeks * 60 * 60 * 24 * 7; // $weeks vooruit
return $timestamp;
}
?>
index.php:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
include_once('datum.php');
$timestamp = mktime();
for($weeks = 0 ; $weeks < 5 ; $weeks++) {
$wednesday = getNextWeekday($timestamp, 3, $weeks);
echo ($weeks + 1).'e woensdag: '.date('d-m-Y', $wednesday).'<br>';
}
?>
include_once('datum.php');
$timestamp = mktime();
for($weeks = 0 ; $weeks < 5 ; $weeks++) {
$wednesday = getNextWeekday($timestamp, 3, $weeks);
echo ($weeks + 1).'e woensdag: '.date('d-m-Y', $wednesday).'<br>';
}
?>
Gewijzigd op 20/10/2012 19:21:39 door Frank Nietbelangrijk
hoera voor mezelf! dit draadje mag voor mij part gesloten worden, probleem opgelost.
Volgende stap is de script uiteen trekken in functies!
Toevoeging op 20/10/2012 19:32:58:
hey frank, heb jouw scripts eens bekeken. Als ik het goed begrijp, bereken jij hier alle data van de vijf opeenvolgende woensdagen? Als ik het niet goed begrijp... dan heb ik nog veel te leren!!! ;-)
Ik wil enkel de eerste woensdag van de maand.
groeten, bart
Als je functies had gebruikt had je geen last gehad van een dubbele $i of $j.
groet,
Frank
http://www.webmastercity.nl/forum/viewtopic.php?f=35&t=38059
En dan query:
Dit geeft (even via mijn database gedaan):
Let op dat het YYYY-MM-DD-formaat is ;).
En geen gezeur met halve weken, GROUP BY etc.
Qua snelheid....: Weergave van records 0 - 4 ( 5 totaal, query duurde 0.0008 sec) [datum: 2012-11-07 - 2013-03-06]
Tevens werkt de mijne ook bij schrikkeldagen/jaren als de dag geen 24*60*60 seconden telt.
Enig nadeel (wat heel simpel te verhelpen is): hij gaat maar tot 2100.
En dan query:
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
SELECT datum, DAYNAME( datum )
FROM alle_data
WHERE datum > NOW( )
AND DAYOFWEEK( datum ) =4
AND DAYOFMONTH( datum ) BETWEEN 1 AND 8
ORDER BY datum ASC
LIMIT 0, 5
FROM alle_data
WHERE datum > NOW( )
AND DAYOFWEEK( datum ) =4
AND DAYOFMONTH( datum ) BETWEEN 1 AND 8
ORDER BY datum ASC
LIMIT 0, 5
Dit geeft (even via mijn database gedaan):
Code (php)
1
2
3
4
5
6
2
3
4
5
6
datum DAYNAME( datum )
2012-11-07 Wednesday
2012-12-05 Wednesday
2013-01-02 Wednesday
2013-02-06 Wednesday
2013-03-06 Wednesday
2012-11-07 Wednesday
2012-12-05 Wednesday
2013-01-02 Wednesday
2013-02-06 Wednesday
2013-03-06 Wednesday
Let op dat het YYYY-MM-DD-formaat is ;).
En geen gezeur met halve weken, GROUP BY etc.
Qua snelheid....: Weergave van records 0 - 4 ( 5 totaal, query duurde 0.0008 sec) [datum: 2012-11-07 - 2013-03-06]
Tevens werkt de mijne ook bij schrikkeldagen/jaren als de dag geen 24*60*60 seconden telt.
Enig nadeel (wat heel simpel te verhelpen is): hij gaat maar tot 2100.
Gewijzigd op 20/10/2012 20:04:02 door Eddy E
Ik haal 3 fouten uit jouw query, nl:
Quote:
WHERE datum > NOW()
Wat als NOW de eerste woensdag (euh donderdag) van de maand is?
Quote:
AND DAYOFWEEK( datum ) =4
is een donderdag
Quote:
AND DAYOFMONTH( datum ) BETWEEN 1 AND 8
als jouw donderdag op de eerste valt heb je 2 donderdagen
Maar dat je dit op een forum durft te zetten:
Quote:
Alle werkdagen van een jaar/maand
In Nederland werken we van maandag t/m vrijdag
Maar dan veel belangrijker:
Een database is bedoeld om om dynamische gegevens op te slaan, een kalender 1971 tot 2037 (het bereik van da normale datetime funcies in PHP) is niet dynamisch maar ligt vast
Code (php)
1
2
3
4
5
6
2
3
4
5
6
SELECT datum, DAYNAME(datum)
FROM alle_data
WHERE YEAR( datum ) = '2009'
AND MONTH( datum ) = '3'
AND DAYOFWEEK( datum ) BETWEEN 2 AND 6
ORDER BY datum ASC
FROM alle_data
WHERE YEAR( datum ) = '2009'
AND MONTH( datum ) = '3'
AND DAYOFWEEK( datum ) BETWEEN 2 AND 6
ORDER BY datum ASC
In Nederland werken we van maandag t/m vrijdag
Maar dan veel belangrijker:
Een database is bedoeld om om dynamische gegevens op te slaan, een kalender 1971 tot 2037 (het bereik van da normale datetime funcies in PHP) is niet dynamisch maar ligt vast
Gewijzigd op 20/10/2012 22:51:19 door Ger van Steenderen
SQL begint op zondag en dus is 4 woensdag. Zondag is volgens diverse systemen ook de eerste dag. Zeker in de Gregoriaanse kalender. Daarnaast zie je de resultaten... 4 = Wednesday...
Tenzij je dit script op woensdag 1 xxx om 23:69:59,999999 aanroept is er niets aan de hand.
Ger en Eddy database masters zijn jullie :)
Echter, ben ik van mening dat in een database niet alleen dynamische gegevens staan, maar gewoon 'gegevens'.
En een datum is ook een gegeven iets.
Als ik de query iets optimaliseer krijg ik dit:
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
SELECT datum, DAYNAME(datum)
FROM alle_data
WHERE datum >= NOW()
AND DAYOFWEEK(datum) = 4
AND DAYOFMONTH(datum) BETWEEN 1 AND 7
ORDER BY datum ASC
LIMIT 5
FROM alle_data
WHERE datum >= NOW()
AND DAYOFWEEK(datum) = 4
AND DAYOFMONTH(datum) BETWEEN 1 AND 7
ORDER BY datum ASC
LIMIT 5
Mocht op jouw systeem de donderdag verschijnen, verander dan de 4 naar een 3.
Ik zou er nooit aan gedacht hebben en los het zo op in php, maar dit kan natuurlijk ook en ik vind t knap :)
De wortel van 666 is ook een gegegeven, maar je gaat toch geen tabel maken voor de uitkomsten van de wortel van 1 tot en met weet ik veel wat. Dit is logica die in de applicatie laag thuis hoort, zo ook voor een datum.
Overigens heb je gelijk met de dag van de week, ik was even in de war met PHP.
datum >= NOW() gaat nog steeds niet goed, al datum vandaag is wordt dit
2012-10-21 00:00:00 >= 2012-10-21 12:56:04
omdat je een datum met een datum tijd vergelijkt.
Toevoeging op 21/10/2012 14:21:11:
De PHP oplossing:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
//$dayofweek 1 voor ma 7 voor zondag
function getAFirstDayOfMonths($dayofweek = 3, $range = 5, $dateformat = '%d-%m-%Y') {
$date = time();
$wd = date('N', $date);
//bepaal de eerste ?dag van de week
$someday = $date + (($dayofweek - $wd) * 86400);
//corrigeer als er een maand terug gegaan wordt
$someday += (date('m', $date) != date('m', $someday)) ? 7 * 86400: 0;
$dom = date('j', $someday);
//als de dag van de maand groter is dan 7 moeten we er een x aantal weken aftrekken
//en een coorectie uitvoeren op veelvouden van 7
$firstday = ($dom > 7) ? $someday - ((($dom % 7 == 0) ? -1 : 0) + floor($dom / 7)) * 7 * 86400 : $someday;
$maand = date('m', $firstday);
$firstdayS = array(strftime($dateformat, $firstday));
for ($i=1; $i<=$range;$i++) {
$dom = date('j', $firstday);
$year = date('Y');
//bepaal de laatse dag van de maand
if ($maand <= 7) {
//t/m juli hebben de oneven maanden 31 dagen
if($maand % 2 > 0) {
$ldom = 31;
}
else {
if($maand != 2) { //behalve feb
$ldom = 30;
}
else { //feb 28 behalve schrikkeljaren
$ldom = ($year % 4 != 0 || ($year % 100 == 0 && $year % 400 != 0)) ? 28 : 29;
}
}
}
else { //na juli de even maamden 31
$ldom = ($maand % 2 == 0) ? 31 : 30;
}
$weeks2add = ($ldom - $dom - 28 < 0) ? 4 : 5;
$firstday += $weeks2add * 7 * 86400;
$firstdayS[] = strftime($dateformat, $firstday);
$maand = date('m', $firstday);
}
return $firstdayS;
}
?>
//$dayofweek 1 voor ma 7 voor zondag
function getAFirstDayOfMonths($dayofweek = 3, $range = 5, $dateformat = '%d-%m-%Y') {
$date = time();
$wd = date('N', $date);
//bepaal de eerste ?dag van de week
$someday = $date + (($dayofweek - $wd) * 86400);
//corrigeer als er een maand terug gegaan wordt
$someday += (date('m', $date) != date('m', $someday)) ? 7 * 86400: 0;
$dom = date('j', $someday);
//als de dag van de maand groter is dan 7 moeten we er een x aantal weken aftrekken
//en een coorectie uitvoeren op veelvouden van 7
$firstday = ($dom > 7) ? $someday - ((($dom % 7 == 0) ? -1 : 0) + floor($dom / 7)) * 7 * 86400 : $someday;
$maand = date('m', $firstday);
$firstdayS = array(strftime($dateformat, $firstday));
for ($i=1; $i<=$range;$i++) {
$dom = date('j', $firstday);
$year = date('Y');
//bepaal de laatse dag van de maand
if ($maand <= 7) {
//t/m juli hebben de oneven maanden 31 dagen
if($maand % 2 > 0) {
$ldom = 31;
}
else {
if($maand != 2) { //behalve feb
$ldom = 30;
}
else { //feb 28 behalve schrikkeljaren
$ldom = ($year % 4 != 0 || ($year % 100 == 0 && $year % 400 != 0)) ? 28 : 29;
}
}
}
else { //na juli de even maamden 31
$ldom = ($maand % 2 == 0) ? 31 : 30;
}
$weeks2add = ($ldom - $dom - 28 < 0) ? 4 : 5;
$firstday += $weeks2add * 7 * 86400;
$firstdayS[] = strftime($dateformat, $firstday);
$maand = date('m', $firstday);
}
return $firstdayS;
}
?>
Gewijzigd op 21/10/2012 14:27:05 door Ger van Steenderen
Of het sneller is kan niet zo 1-2-3 zien, wel dat het een stukje geavanceerder is.
En wat een gedoe met het aantal dagen van de maand. Dat is toch ook vaststaand en niet zo ingewikkeld als regel 20 tm 37?
Wellicht een array met 30, 28, 30, 31, 30, 31, 30, 31, 31, 30, 31 beter?
Als er geen schrikkeljaren bestaan had ik het misschien wel in een array gezet, maar ik ben sowieso niet van de hardcoding.
Ik heb even een test gedaan:
de functie: 0.00044798851013184
connect (lokale!)database en select database (nog geen query uitgevoerd!):0.013045072555542
een paar keer getest maar de functie blijft altijd sneller.
En een beetje zelfkritiek kan ook geen kwaad:
Waarom in hemelsnaam rekening houdem met eeuwen terwijl over 25 jaar deze functie niet meer zal werken
Toevoeging op 21/10/2012 15:10:28:
Speciaal voor jou:
Ger van Steenderen op 21/10/2012 15:00:19:
Hi Eddy,
Als er geen schrikkeljaren bestaan had ik het misschien wel in een array gezet, maar ik ben sowieso niet van de hardcoding.
Als er geen schrikkeljaren bestaan had ik het misschien wel in een array gezet, maar ik ben sowieso niet van de hardcoding.
Is ook niet zo moeilijk:
Code (php)
Groet, Frank
Toevoeging op 21/10/2012 17:26:16:
Wel grappig om te zien dat we allemaal een andere oplossing hebben voor hetzelfde probleem
Maar goed, zoals het nu voorstaat in PHP, haalt de functie jaar 2038 niet eens dus laat staan jaar 2100 (wat ik ik ook niet zal halen, dus boeien). En ik gebruik bewust geen datetime objecten omdat die geen locales ondersteunen.
En helemaal met je eens: in 2025 zullen deze scripts ook niet meer nodig zijn, want dan is er wel iets beters/makkelijkers/handigers voor gevonden.