sql >> Database teknologi >  >> RDS >> Mysql

PHP, MySQL og tidszoner

Dette svar er blevet opdateret for at imødekomme dusøren. Det originale, uredigerede svar er under stregen.

Næsten alle spørgsmålspunkterne tilføjet af bounty-ejeren er i forhold til, hvordan MySQL- og PHP-datotider skal interagere i sammenhæng med tidszoner.

MySQL har stadig patetisk tidszoneunderstøttelse , hvilket betyder, at intelligensen skal være PHP-side.

  • Indstil din MySQL forbindelsestidszone til UTC som dokumenteret i linket ovenfor. Dette vil forårsage alle datetimes håndteret af MySQL, inklusive NOW() , der skal håndteres fornuftigt.
  • Brug altid DATETIME , brug aldrig TIMESTAMP medmindre du meget udtrykkeligt kræver den særlige adfærd i en TIMESTAMP . Dette er mindre smertefuldt end det plejede at være.
    • Det er ok at gemme Unix-epoketiden som et heltal, hvis du har til, såsom til legacy-formål. Epoken er UTC.
    • MySQL's foretrukne datetime-format er oprettet ved hjælp af PHP-datoformatstrengen Y-m-d H:i:s
  • Konverter alle PHP datetimes til UTC, når de gemmer dem i MySQL, hvilket er en triviel ting som beskrevet nedenfor
  • Datotider returneret fra MySQL kan afleveres sikkert til PHP DateTime-konstruktøren. Sørg også for at passere i en UTC-tidszone!
  • Konverter PHP DateTime til brugerens lokale tidszone på ekko , ikke før. Heldigvis vil DateTime-sammenligning og matematik med andre DateTimes tage højde for den tidszone, som hver er i.
  • Du er stadig op til lunerne i DST-databasen, der leveres med PHP. Hold dine PHP- og OS-patches opdateret! Hold MySQL i den lykkelige tilstand af UTC for at fjerne én potentiel DST-gener.

Det adresserer de fleste af punkterne.

Den sidste ting er en doozy:

  • Hvad skal nogen gøre, hvis de tidligere har indsat data (f.eks. ved hjælp af NOW() ) uden at bekymre dig om tidszonen for at sikre, at alt forbliver konsekvent?

Dette er en reel irritation. Et af de andre svar pegede på MySQL's CONVERT_TZ , selvom jeg personligt ville have gjort det ved at hoppe mellem server-native og UTC-tidszoner under valg og opdateringer, for jeg er sådan en hardcore.

appen skal også være i stand til selv at indstille/vælge sommertid for hver bruger.

Du behøver ikke og bør ikke gør dette i den moderne æra.

Moderne versioner af PHP har DateTimeZone klasse, som inkluderer evnen til at liste navngivne tidszoner . Navngivne tidszoner giver brugeren mulighed for at vælge deres faktiske placering og få systemet automatisk bestemme deres sommertidsregler baseret på denne placering.

Du kan kombinere DateTimeZone med DateTime for en simpel men kraftfuld funktionalitet. Du kan simpelthen gemme og bruge alle af dine tidsstempler i UTC som standard , og konverter dem til brugerens tidszone på skærmen.

// UTC default
    date_default_timezone_set('UTC');
// Note the lack of time zone specified with this timestamp.
    $nowish = new DateTime('2011-04-23 21:44:00');
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 21:44:00
// Let's pretend we're on the US west coast.  
// This will be PDT right now, UTC-7
    $la = new DateTimeZone('America/Los_Angeles');
// Update the DateTime's timezone...
    $nowish->setTimeZone($la);
// and show the result
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 14:44:00

Ved at bruge denne teknik vil systemet automatisk vælge de korrekte sommertid-indstillinger for brugeren uden at spørge brugeren, om vedkommende er i sommertid.

Du kan bruge en lignende metode til at gengive valgmenuen. Du kan løbende omtildele tidszonen for det enkelte DateTime-objekt. For eksempel vil denne kode vise zonerne og deres aktuelle tidspunkter på dette tidspunkt:

$dt = new DateTime('now', new DateTimeZone('UTC')); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $dt->setTimeZone(new DateTimeZone($tz));
    echo $tz, ': ', $dt->format('Y-m-d H:i:s'), "\n";
}

Du kan i høj grad forenkle udvælgelsesprocessen ved at bruge noget magi på klientsiden. Javascript har en plettet, men funktionel Datoklasse, med en standardmetode til få UTC-offset på få minutter . Du kan bruge dette til at indsnævre listen over sandsynlige tidszoner under den blinde antagelse om, at brugerens ur er rigtigt.

Lad os sammenligne denne metode med at gøre det selv. Du skal faktisk udføre datomatematik hver eneste gang du manipulerer en dato og klokkeslæt, ud over at skubbe et valg af på brugeren, som de ikke rigtig bekymrer sig om. Dette er ikke bare sub-optimalt, det er bat-guano sindssygt. At tvinge brugere til at angive, når de vil have DST-support, er at bede om problemer og forvirring.

Yderligere, hvis du ønsker at bruge den moderne PHP DateTime og DateTimeZone framework til dette, skal du brug forældet Etc/GMT... tidszonestrenge i stedet for navngivne tidszoner. Disse zonenavne kan blive fjernet fra fremtidige PHP-versioner, så det ville være uklogt at gøre det. Jeg siger alt dette af erfaring.

tl;dr :Brug det moderne værktøjssæt, skån dig selv for datomatematikkens rædsler. Præsenter brugeren en liste over navngivne tidszoner. Gem dine datoer i UTC , som ikke vil blive påvirket af sommertid på nogen måde. Konverter datoklokkeslæt til brugerens valgte navngivne tidszone på displayet , ikke tidligere.

Som anmodet er her en løkke over de tilgængelige tidszoner, der viser deres GMT offset i minutter. Jeg valgte minutter her for at demonstrere en uheldig kendsgerning:ikke alle forskydninger er i hele timer! Nogle skifter faktisk en halv time frem i sommertid i stedet for en hel time. Den resulterende offset i minutter bør matche Javascripts Date.getTimezoneOffset .

$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $local = new DateTimeZone($tz);
    $dt->setTimeZone($local);
    $offset = $local->getOffset($dt); // Yeah, really.
    echo $tz, ': ', 
         $dt->format('Y-m-d H:i:s'),
         ', offset = ',
         ($offset / 60),
         " minutes\n";
}


  1. Hvordan opretter jeg en Oracle-tabel med indlejrede tabeller af objekttyper?

  2. Sådan fungerer Round() i PostgreSQL

  3. Sådan arbejder du med datofunktioner i Oracle sql

  4. Sådan gendanner du en database fra C#