Correct werken met datums/tijden
Mijn vraag gaat over het correct werken met datums en tijden. Ik denk dat ik nu namelijk (waarschijnlijk) een probleem heb met tijdzones. Dit is een verwarrende vraag! Dus ik hoop dat ik het goed uitleg. Anders hoor ik het graag.
Ik ben een applicatie aan het ontwikkelen waarbij o.a. afspraken kunnen worden opgeslagen in een agenda van de gebruiker. Deze mogelijkheid lijkt een beetje op de agenda die veel mensen kennen van bijvoorbeeld Outlook.
De interactie met de server is in de vorm van een API.
Het clientside gedeelte van de app is een desktop programma (dus geen omgeving in webbrowser). Alle output van de server is in JSON format. Alle input van de client is een simpele formdata POST request, GET request, of POST request met de input in JSON format.
Hier is een relevant stukje van de JSON die de client naar de server stuurt als de gebruiker een afspraak in zijn agenda zet:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
{
"agenda_id":"2",
"user_id":"1",
"title":"Vergadering",
"description":"Vergadering over laatste kwartaal 2021...",
"start_date":"2022-02-06T09:00:00+01:00",
"end_date":"2022-02-06T11:30:00+01:00",
"location":"Kantoor, ruimte ...",
}
"agenda_id":"2",
"user_id":"1",
"title":"Vergadering",
"description":"Vergadering over laatste kwartaal 2021...",
"start_date":"2022-02-06T09:00:00+01:00",
"end_date":"2022-02-06T11:30:00+01:00",
"location":"Kantoor, ruimte ...",
}
Let op de start_date en end_date in de JSON. Dit is uiteraard de start en eind datum en tijd van de afspraak. (Het is overigens dus ook mogelijk dat een afspraak meerdere dagen duurt).
In de client code (desktop app geschreven in C#) gebruik ik altijd voor elke datum een DateTime object (vrijwel volledig vergelijkbaar met PHP's ingebouwde DateTime class).
Als ik een datum en tijd heb: 2022-02-06 09:00, en hiervan maak ik een DateTime object op de client, dan wordt dit: 2022-02-06T09:00:00+01:00. Je zier hier dat er vanzelf een offset van +01:00 aan de tijd is toegevoegd door de client. Dat komt omdat de tijdzone in Nederland UTC+01:00 is.
Ik heb dit zo gelaten, want het lijkt me correct, en wordt door het systeem vanzelf zo toegevoegd. (Logica van DateTime class).
Dus de server ontvangt datums zoals: 2022-02-06T09:00:00+01:00 waaruit ook de tijdzone is af te leiden.
De datums sla ik op in een MySQL database, in een DATETIME kolom.
Als ik dan in PhpMyAdmin kijk, bevat de kolom de volgende waarde: 2022-02-06 09:00:00. Hierbij is de tijdzone (die +01:00) weer weg.
Als ik vervolgens een API request doe naar de server om de afspraak te laden, dan ontvang ik van de server de volgende datum: 2022-02-06T09:00:00+00:00. Hierin zit wél weer een tijdzone component, (echter nu +00:00). Dat komt doordat:
- In de database kolom, zoals bleek, de datum zonder tijdzone wordt opgeslagen.
- PHP dus de string uit de database kolom ontvangt in de vorm: 2022-02-06 09:00:00
- Ik hiervan een PHP DateTime object maak, en dat deze DateTime automatisch +00:00 krijgt als ik deze in JSON output. De PHP DateTime plakt er een tijdzone aan, en deze is hier +00:00 omdat de tijdzone van de server UTC is.
De client ontvangt de JSON dan met de datum string 2022-02-06T09:00:00+00:00.
De client verwerkt de datum string in een C# DateTime. Hij ziet "+00:00" en denkt: dit is UTC-0 tijd. De client weet dat hij in Nederland is, en dat NL een UTC +01:00 offset heeft, dus maakt de client er doodleuk 2022-02-06T10:00:00+01:00 van.
Ik begrijp het bijna zelf niet meer, dus wil ik weten hoe je correct omgaat met datums en tijd op een consistente wijze.
Ik wil de datums graag opslaan als UTC tijd, wat betekent dat ze soort van 'tijdzone onafhankelijk zijn', maar ik begrijp dit niet goed.
Ook ben ik erachter gekomen dat het kolomtype DATETIME in MySQL geen tijdzone informatie in de waarde opslaat, wat ik ook weer raar vind.
Als je daarentegen bijvoorbeeld naar kolomtype TIMESTAMP kijkt, blijkt dat hierbij de datums impliciet gewoon als een unix timestamp worden opgeslagen (het aantal seconden sinds 01-01-1970), en dat dit UTC-0 tijd is.
Ik ben bereid om dingen volledig om te gooien, maar begrijp gewoon niet hoe ik moet werken met datum-tijden, hoe ik ze correct opsla, en hoe ik verwarring met tijdzones voorkom.
Wat ik ook niet zou weten, is of het nou wel/niet goed is dat de client automatisch de datums omzet op basis van tijdzone.
Iemand een idee of hetzelfde probleem gehad? Laat me weten als ik ergens niet duidelijk ben.
Gewijzigd op 06/02/2022 18:45:39 door Mark Hogeveen
TIMESTAMP:
aangeboden datum en tijd worden automatisch omgerekend naar UTC tijd en ook weer terug gerekend naar de tijd in de "vooraf ingestelde" tijdzone.
Die tijdzone kun je op drie manieren instellen:
a) in my.cnf
b) in de @@global.time_zone variable
c) in de @@session.time_zone variable
In mijn ogen heb je aan a en b niets als je een centrale database (in UTC) gebruikt en je clients hebt over alle hoeken van de wereld. Dan blijft c over. Bij c moet je gewoon telkens als je client software verbinding maakt met de mysql server direct de @@session.time_zone variable instellen met de volgende query.
Nuttige info: https://www.techbeamers.com/mysql-timestamp/
DATETIME:
Heeft helemaal geen besef van bovenstaande en slaat gewoon de waarden op zoals je ze geeft.
Dit betekent simpelweg dat jouw client software (C#) de tijd zal moeten omrekenen van lokaal naar utc en andersom.
Toevoeging op 06/02/2022 23:55:48:
>> Ook ben ik erachter gekomen dat het kolomtype DATETIME in MySQL geen tijdzone informatie in de waarde opslaat, wat ik ook weer raar vind.
Vaak heb je ook helemaal geen tijdzone nodig. Als jij een app maakt voor jouw plaatselijke kapper die al zijn klanten binnen een straal van 5km heeft dan is en denkt een ieder in dezelfde tijdzone. en waarom zou je data opslaan in een database die je niet gebruikt? (tenzij je aandelen hebt in harde schijven :p) Bovendien zou een app een globale tijdzone kunnen hebben welke dus eenmalig ingesteld wordt.
Gewijzigd op 06/02/2022 23:06:26 door Frank Nietbelangrijk
Welke tijd je data uiteindelijk heeft, HANGT AF VAN DE SERVERCONFIGURATIE.
Hoe de tijd uit MySQL komt naar PHP wordt gestuurd, hangt niet alleen af van de serverconfiguratie, maar OOK VAN DE CLIENTCONFIGURATIE.
Ik zet het er maar even in koeienletters neer om het in te laten dalen wat een effect deze 'design'keuze heeft op je code.
Het betekent dat wanneer je de serverconfiguratie aanpast, dat je DATA WORDT AANGEPAST.
Eigenlijk is er maar 1 remedie: gebruik in de serverconfiguratie en in de clientconfiguratie dezelfde tijdzone, anders is de kans groot dat je compleet gek wordt (is mij al een keer gebeurd met MySQL, vooral toen MySQL 'ineens' een probleem had met zomer- en wintertijd, want DAT IS OOK EEN TIJDZONE).
Zelfs phpulp.nl kan dankzij MySQL niet overweg met tijdzones. Als je hier een bericht plaatst geeft-ie aan '59 minuten vanaf nu' of met nog een uur extra tijdsverschil.
Ofwel als je tijd wilt opslaan met MySQL (en dus ook DATUMS, want 2u tijdsverschil kan maken dat een datum EEN DAG VERSCHUIFT), dan kan je beter een echte database gebruiken, bij voorkeur PostgreSQL.
Toevoeging op 07/02/2022 11:27:54:
Kleine aanvulling: het probleem ligt dus niet bij jou! MySQL is gewoon !@$%.