Tijd tussen twee afspraken
Nu wil de tijd berekenen tussen de eindtijd van de ene row en de begintijd van de volgende row. Hierdoor wil ik bepalen hoeveel tijd er tussen de twee afspraken zit. Dit om te bepalen of er nog een afspraak tussen kan.
Moet ik dit in PHP regelen of in sql? Tot nu toe is het mij niet gelukt om dit te berekenen.
Hieronder wat ik nu heb, de begin en eindtijd komen er per row wel uit, maar op deze manier trek ik de verkeerde getallen van elkaar. Appointment_begin en end zijn datumnotaties, dus in seconden.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$sql_appointments = $conn->query('
SELECT appointment_begin, appointment_end
FROM appointment
WHERE employeeID = ' . $conn->quote(2) . 'ORDER BY appointment_begin');
while ($row_salon_appointments = $sql_salon_appointments->fetch(PDO::FETCH_ASSOC))
{
$aa = $row_salon_appointments['appointment_begin']);
$bb date('Y-m-d H:i', $row_salon_appointments['appointment_end']);
$cc = $bb - $aa;
}
SELECT appointment_begin, appointment_end
FROM appointment
WHERE employeeID = ' . $conn->quote(2) . 'ORDER BY appointment_begin');
while ($row_salon_appointments = $sql_salon_appointments->fetch(PDO::FETCH_ASSOC))
{
$aa = $row_salon_appointments['appointment_begin']);
$bb date('Y-m-d H:i', $row_salon_appointments['appointment_end']);
$cc = $bb - $aa;
}
MySQL en MariaDB kunnen niet omgaan met periodes. Ze kennen geen INTERVAL als gegevenstype.
Dat betekent dat je met deze databases, voor zoiets triviaals het wiel al wéér zelf mag gaan uitvinden.
Je kunt dat omslachtig doen in SQL, maar je kunt het ook omslachtig doen in PHP, dat maakt niet veel uit.
De meesten rommelen dan wat aan met seconden per uur et cetera, maar uiteindelijk is het heel ingewikkeld om het helemaal goed te krijgen. Vanwege veranderende tijdzones en schrikkeljaren en schrikkelseconden.
Als je Postgres gebruikt, krijg je na het berekenen van het verschil tussen twee datums gewoon een INTERVAL gegevenstype, waarmee al het rekenwerk al voor je is gedaan, zoals het hoort in een database.
Je kunt je dan bezighouden met de vraag wat je wilt maken, in plaats van hoe je dat voor elkaar kunt krijgen.
https://mariadb.com/kb/en/date-and-time-units/
https://mariadb.com/kb/en/timediff/
Triviaal voorbeeld :
Code (php)
1
SELECT aanvang, einde, TIME_FORMAT( TIMEDIFF( einde,aanvang ), "%k uur %i minuten" ) AS duur FROM afspraken;
Maar de vraag was hoe je het verschil berekent tussen eindtijd van een regel en de begintijd van de volgende regel.
Bijvoorbeeld in een lus in PHP :
Code (php)
1
2
3
2
3
for ($i = 0; $i < count; $i++) {
echo $row[$i+1]['aanvang'] - $row[$i]['einde'] . '<br />';
}
echo $row[$i+1]['aanvang'] - $row[$i]['einde'] . '<br />';
}
Toevoeging op 24/05/2021 17:46:51:
Om de vragensteller op weg te helpen.
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
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
<table>
<thead>
<tr>
<th>Einde (huidige row)</th>
<th>Aanvang (volgende row)</th>
<th>Duur</th>
</tr>
</thead>
<?php
// aangenomen aanvang en einde zelfde dag
$afspraken = [];
while ( $row = $result->fetch_object() )
{
$afspraken[] = $row;
//echo '<pre>' . print_r( $afspraken, TRUE ) . '</pre>';
}
for ( $i = 0; $i < count($afspraken); $i++)
{
// verder uitwerken aanvang volgende row vroeger/later einde huidige row
//echo '<pre>' . print_r($row, TRUE ) . '</pre>';
$einde = new DateTime( $afspraken[$i]->einde );
if ($i < count($afspraken) )
{
$aanvang = new DateTime( $afspraken[$i+1]->aanvang );
$verschil = $einde->diff($aanvang);
}
echo '<tr><td>' .
$einde->format('H:i') .
'</td><td>' .
$aanvang->format('H:i') .
'</td><td>' .
$verschil->format('%H:') .
str_pad($verschil->format('%i'),2,"0",STR_PAD_LEFT) .
'</td></tr>';
}
?>
</table>
<thead>
<tr>
<th>Einde (huidige row)</th>
<th>Aanvang (volgende row)</th>
<th>Duur</th>
</tr>
</thead>
<?php
// aangenomen aanvang en einde zelfde dag
$afspraken = [];
while ( $row = $result->fetch_object() )
{
$afspraken[] = $row;
//echo '<pre>' . print_r( $afspraken, TRUE ) . '</pre>';
}
for ( $i = 0; $i < count($afspraken); $i++)
{
// verder uitwerken aanvang volgende row vroeger/later einde huidige row
//echo '<pre>' . print_r($row, TRUE ) . '</pre>';
$einde = new DateTime( $afspraken[$i]->einde );
if ($i < count($afspraken) )
{
$aanvang = new DateTime( $afspraken[$i+1]->aanvang );
$verschil = $einde->diff($aanvang);
}
echo '<tr><td>' .
$einde->format('H:i') .
'</td><td>' .
$aanvang->format('H:i') .
'</td><td>' .
$verschil->format('%H:') .
str_pad($verschil->format('%i'),2,"0",STR_PAD_LEFT) .
'</td></tr>';
}
?>
</table>
Gewijzigd op 24/05/2021 17:47:53 door Adoptive Solution
Toch ligt het anders (helaas).
TIMEDIFF() is gemaakt om verkeerde uitkomsten te geven als de data niet klopt of als de twee datums te ver uit elkaar liggen:
Quote:
"The result returned by TIMEDIFF() is limited to the range allowed for TIME values."
En over TIME values wordt gezegd:
Quote:
"TIME values may range from '-838:59:59' to '838:59:59'."
Het meest slecht is dat de verwachte uitkomst stilzwijgend wordt veranderd naar iets dat MySQL past:
Quote:
"By default, values that lie outside the TIME range but are otherwise valid are clipped to the closest endpoint of the range. [...] Note that because '00:00:00' is itself a valid TIME value, there is no way to tell, from a value of '00:00:00' stored in a table, whether the original value was specified as '00:00:00' or whether it was invalid."
Het precieze gedrag van TIMEDIFF() wordt ook nog eens beïnvloed door het niet goed ondersteunen van tijdzones en globale serverinstellingen als ALLOW_INVALID_DATES.
Rekenen met tijd in MySQL is dus weinig consequent en weinig betrouwbaar.
Helaas biedt MySQL geen andere opties om met een INTERVAL te werken. Een INTERVAL onthoudt het aantal kalendermaanden, weken, dagen, etc. De enige manier is om te rekenen met seconden, die geen rekening houden met schrikkeljaren, schrikkelseconden, kalendermaanden of tijdzones:
Quote:
"Alternatively, you can use either of the functions TIMESTAMPDIFF() and UNIX_TIMESTAMP(), both of which return integers."
Nu was er ooit, 15 jaar geleden, een slim iemand die vroeg om het INTERVAL datatype te ondersteunen.
Maar daar is nooit wat mee gedaan.
Wat moet je dan nog met MySQL?
Dan het alternatief in PHP, is dat zoveel beter?
Mijn ervaring is dat DateTime in PHP 4 niet eens werkte, en alleen maar foute resultaten gaf.
Uit de lange maar ook recente lijst van bugs valt op te maken dat het nog steeds niet altijd betrouwbaar is:
https://bugs.php.net/search.php?cmd=display&project=PHP&order_by=id&direction=DESC&limit=30&package_name[]=Date%2Ftime+related
Ik zou het niet in een productie-omgeving durven te gebruiken.
Maar waarom zou je ook blijven aanmodderen en steeds het wiel weer opnieuw uitvinden, als je in plaats daarvan IntlCalendar kunt gebruiken, of een fatsoenlijke database als Postgres (eigenlijk alles behalve MySQL/MariaDB) ?
select * from tbl t1 join tbl t2 where ???
Hier moeten we de exacte structuur hebben
maar iets zoals zelfde medewerker en later maar vroegste uur dan deze afspraak
Net zelf zoiets gedaan voor vervolg van gesprek.
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
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
<?php
$sql_appointments = $conn->query("
SELECT appointment_begin
FROM appointments
WHERE appointment_begin >= $appointment_beginID AND employeeID = $employeeID
ORDER BY appointment_begin
LIMIT 1");
$row_appointments = $sql_appointments->fetch(PDO::FETCH_ASSOC);
$start_free_time = $row_appointments['appointment_begin'];
$end_free_time = $appointment_beginID;
$result_free_time = $start_free_time - $end_free_time ;
$result_free_time_minutes = ($result_free_time/60) - $duration_total;
if($start_free_time <> "") {
if( $result_free_time_minutes < 0) {
?>
<div class="alert">
<span class="closebtn" onclick="location.href='index.php';" >×</span>
<strong>Let op!</strong> Deze afspraak is te lang voor de beschikbare tijd.
Druk p het kruisje om terug te gaan.
</div>
<?php ; } ?>
$sql_appointments = $conn->query("
SELECT appointment_begin
FROM appointments
WHERE appointment_begin >= $appointment_beginID AND employeeID = $employeeID
ORDER BY appointment_begin
LIMIT 1");
$row_appointments = $sql_appointments->fetch(PDO::FETCH_ASSOC);
$start_free_time = $row_appointments['appointment_begin'];
$end_free_time = $appointment_beginID;
$result_free_time = $start_free_time - $end_free_time ;
$result_free_time_minutes = ($result_free_time/60) - $duration_total;
if($start_free_time <> "") {
if( $result_free_time_minutes < 0) {
?>
<div class="alert">
<span class="closebtn" onclick="location.href='index.php';" >×</span>
<strong>Let op!</strong> Deze afspraak is te lang voor de beschikbare tijd.
Druk p het kruisje om terug te gaan.
</div>
<?php ; } ?>
Edit:
Code-tag gefixxed. Bracket miste in de tag.
Gewijzigd op 28/05/2021 00:38:12 door - Ariën -
Wat nu als er geen volgende appointment gevonden wordt door die query?
Dank krijg je null en weet je ook wat te doen :)