Onze webapplicatie gaat binnenkort wereldwijd gebruikt worden (
), en dus ben ik aan het kijken hoe we het beste met verschillende tijdzones kunnen werken. Belangrijk: gebruikers kunnen zelf datums en tijden in hun eigen tijdzone opgeven.
Ik heb er (voorlopig) voor gekozen om alle datetimes in UTC op te slaan in de database. Eigenlijk moet de hele applicatie uit gaan van tijden in UTC. Bij het inlogformulier wordt dmv. javascript de offset van de gebruiker vastgelegd, zodat alle tijden vertaald kunnen worden naar haar zone. Bij het opslaan van door gebruikers gespecificeerde tijden worden deze weer vertaald naar UTC.
De vraag is nu waar precies de conversie moet gebeuren.
Op dit moment hebben we bijvoorbeeld de volgende module:
Het probleem hier is dat de applicatie niet helemaal in UTC werkt: storeResult verwacht tijden in de tijdzone van de gebruiker. getResult haalt ze uit de database als UTC.
Stel een stuk maatwerkcode moet een waarde (bijv. "klaar") van een resultaat aanpassen:
Callers moeten erop letten dat getResult UTC oplevert, maar [updateResult] verwacht tijden in user timezone. Dit gaat gegarandeerd ergens mis!
Nadeel is dat er dan op erg veel plekken code gedupliceerd moet worden. Daarnaast weet de caller misschien niet waar de datums zitten!
In het voorbeeld hierboven zou handleRequest de conversie (zelf) moeten doen, maar eigenlijk moet deze functie alleen data uit het HTTP Request halen en aan de juiste functie geven (Controller). De functie moet zich niet inhoudelijk ermee bemoeien!
Vind ik bijzonder lelijk, want de DB moet nu uitzoeken welke velden datetime zijn en conversie doen. Bah bah bah, DB moet zich niet met inhoud bezig houden maar (alleen) de verbinding met de database abstraheren.
Combineert de nadelen van vorige twee alternatieven (zie hieronder).

Ik heb er (voorlopig) voor gekozen om alle datetimes in UTC op te slaan in de database. Eigenlijk moet de hele applicatie uit gaan van tijden in UTC. Bij het inlogformulier wordt dmv. javascript de offset van de gebruiker vastgelegd, zodat alle tijden vertaald kunnen worden naar haar zone. Bij het opslaan van door gebruikers gespecificeerde tijden worden deze weer vertaald naar UTC.
De vraag is nu waar precies de conversie moet gebeuren.
Op dit moment hebben we bijvoorbeeld de volgende module:
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
| class SaveResult extends Module { // deze functie is een wrapper om de rest van de API // aan te roepen vanuit een HTTP request public function handleRequest ( Request $r ) { $this->storeResult( $r->get('result') ); // alle name="result[$var]" velden van een <form> } // dit is de API die deze module beschikbaar stelt aan de rest van de applicatie. // wordt ook gebruikt door importscripts, ander maatwerk, etc public function storeResult ( array $result ) { $result = $this->sanitizeResult( $result ); return DB::insert( $result ); } public function updateResult ( $id, array $result ) { $result = $this->sanitizeResult( $result ); DB::update( $id, $result ); } protected function sanitizeResult ( array $result ) { // klopt alles? // ==> hier $result['datetime'] vertalen naar UTC <== } public function getResult ( $id ) { return DB::get( $id ); } } |
Het probleem hier is dat de applicatie niet helemaal in UTC werkt: storeResult verwacht tijden in de tijdzone van de gebruiker. getResult haalt ze uit de database als UTC.
Stel een stuk maatwerkcode moet een waarde (bijv. "klaar") van een resultaat aanpassen:
PHP:
1
2
3
4
| $sr = new SaveResult(); $result = $sr->getResult( 1337 ); $result['klaar'] = false; $sr->updateResult( 1337, $result ); // oops, hier wordt de tijd opnieuw geconverteerd! |
Callers moeten erop letten dat getResult UTC oplevert, maar [updateResult] verwacht tijden in user timezone. Dit gaat gegarandeerd ergens mis!
Alternatief 1: globaal UTC
Het ligt voor de hand ervoor te zorgen dat het systeem alle tijden als UTC moet beschouwen. SaveResult::storeResult moet dus geen conversie doen, maar dat aan callers overlaten.Nadeel is dat er dan op erg veel plekken code gedupliceerd moet worden. Daarnaast weet de caller misschien niet waar de datums zitten!
In het voorbeeld hierboven zou handleRequest de conversie (zelf) moeten doen, maar eigenlijk moet deze functie alleen data uit het HTTP Request halen en aan de juiste functie geven (Controller). De functie moet zich niet inhoudelijk ermee bemoeien!
Alternatief 2: globaal user timezone
Tijden worden als UTC opgeslagen, maar direct geconverteerd naar user timezone zodra ze uit de databsae komen.Vind ik bijzonder lelijk, want de DB moet nu uitzoeken welke velden datetime zijn en conversie doen. Bah bah bah, DB moet zich niet met inhoud bezig houden maar (alleen) de verbinding met de database abstraheren.
Alternatief 3: tijden niet als string, maar objecten rondsturen
Zo heb je in PHP bijvoorbeeld DateTime. Op die manier houd je automatisch bij wat de tijdzone van een bepaalde tijd is.Combineert de nadelen van vorige twee alternatieven (zie hieronder).
Conclusie
Is er een manier om consistent over de hele codebase om te gaan met datetimes, zonder de verantwoordelijkheid voor conversie te leggen bij stukken code die niet af horen te weten van de inhoud van de data (Module::handleRequest en DB::get)?[ Voor 0% gewijzigd door JayVee op 26-03-2012 13:58 . Reden: typo ]
ASCII stupid question, get a stupid ANSI!