2017-05-03 17 views
0

私は、タイムスタンプと、タイムスタンプに基づいて計算された値(計算は私の質問では関係ありません)を持つテーブルをPHPで実装しています。PHPのDateTimeの追加がDSTで間違っています

夏時間から冬時間(夏時間)のトランジション時にaddメソッドを使用すると、PHPのDateTimeオブジェクトで非常に異常な動作が発生しました。私の例では

、私はタイムスタンプに15分を追加し、それを印刷(オフセット秒ローカルフォーマットは、UNIXのUTCタイムスタンプとタイムスタンプを使用して)います:

<?php 

date_default_timezone_set('Europe/Vienna'); 

$offset = new DateInterval("PT15M"); 

foreach([new DateTime("2016-03-27 01:00:00"), 
     new DateTime("2016-10-30 01:00:00")] as $dt) { 
    $lastTs = NULL; 

    for($j = 0; $j < 12; $j++) { 
     echo $dt->format('d.M.Y H:i:s P (U)'); 

     if(!is_null($lastTs)) 
      echo ' (+' . ($dt->format('U') - $lastTs) . ')'; 

     $lastTs = $dt->format('U'); 

     echo "\n"; 

     $dt->add($offset); 
    } 

    echo "\n"; 
} 

この小さなスクリプトは私にこのようなテーブルを提供します(01.Oct.2016 02:15の巨大ジャンプに気づく):

27.Mar.2016 01:00:00 +01:00 (1459036800) 
27.Mar.2016 01:15:00 +01:00 (1459037700) (+900) 
27.Mar.2016 01:30:00 +01:00 (1459038600) (+900) 
27.Mar.2016 01:45:00 +01:00 (1459039500) (+900) 
27.Mar.2016 03:00:00 +02:00 (1459040400) (+900) 
27.Mar.2016 03:15:00 +02:00 (1459041300) (+900) 
27.Mar.2016 03:30:00 +02:00 (1459042200) (+900) 
27.Mar.2016 03:45:00 +02:00 (1459043100) (+900) 
27.Mar.2016 04:00:00 +02:00 (1459044000) (+900) 
27.Mar.2016 04:15:00 +02:00 (1459044900) (+900) 
27.Mar.2016 04:30:00 +02:00 (1459045800) (+900) 
27.Mar.2016 04:45:00 +02:00 (1459046700) (+900) 

30.Oct.2016 01:00:00 +02:00 (1477782000) 
30.Oct.2016 01:15:00 +02:00 (1477782900) (+900) 
30.Oct.2016 01:30:00 +02:00 (1477783800) (+900) 
30.Oct.2016 01:45:00 +02:00 (1477784700) (+900) 
30.Oct.2016 02:00:00 +02:00 (1477785600) (+900) 
30.Oct.2016 02:15:00 +01:00 (1477790100) (+4500) 
30.Oct.2016 02:30:00 +01:00 (1477791000) (+900) 
30.Oct.2016 02:45:00 +01:00 (1477791900) (+900) 
30.Oct.2016 03:00:00 +01:00 (1477792800) (+900) 
30.Oct.2016 03:15:00 +01:00 (1477793700) (+900) 
30.Oct.2016 03:30:00 +01:00 (1477794600) (+900) 
30.Oct.2016 03:45:00 +01:00 (1477795500) (+900) 

On 27. Marすべてが正しいように見える。しかし、冬の時間に戻ったとき、巨大なジャンプがあります。私はこれがDSTの仕組みだとは思わない。 02:00 2回発生したが異なるオフセット(およびcorret UNIXタイムスタンプ)を今すぐ

30.Oct.2016 01:45:00 +02:00 (1477784700) (+900) 
30.Oct.2016 02:00:00 +02:00 (1477785600) (+900) 
30.Oct.2016 02:15:00 +02:00 (1477786500) (+900) 
30.Oct.2016 02:30:00 +02:00 (1477787400) (+900) 
30.Oct.2016 02:45:00 +02:00 (1477788300) (+900) 
30.Oct.2016 02:00:00 +01:00 (1477789200) (+900) 
30.Oct.2016 02:15:00 +01:00 (1477789200) (+900) 
30.Oct.2016 02:30:00 +01:00 (1477791000) (+900) 
30.Oct.2016 02:45:00 +01:00 (1477791900) (+900) 
30.Oct.2016 03:00:00 +01:00 (1477792800) (+900) 
30.Oct.2016 03:15:00 +01:00 (1477793700) (+900) 

は代わりに、私は本当に(メモ帳で編集)この出力に含まを見てみたかったです。

上記の正しい結果を得るには、私のコードにどのような変更が必要ですか?

答えて

0

タイムゾーンでDateTimeオブジェクトを使用する方法はありません。

PHPはタイムスタンプを保存しませんが、ローカルタイム+タイムゾーンCheck this answer for detailsを保存します。 $dt->format('U')を実行すると、現地時間をタイムスタンプに変換します。 30.Oct.2016 02:15:00 (Europe/Vienna)は、1477786500(Sun、2016年10月30日00:15:00 UTC)と1477790100(Sun、2016年10月30日01:15:00 UTC)の2つのタイムスタンプに解決できます。あいまいさから、PHPは後の方を選択します。これにより、計算が中断されます。

この問題を回避するには、任意の日付・時刻操作のためUTCタイムゾーンを使用し、出力のみのローカルタイムゾーンに変換することです:

$utc = new DateTimeZone('utc'); 
$viena = new DateTimeZone('Europe/Vienna'); 

$offset = new DateInterval("PT15M"); 

foreach([new DateTime("2016-03-26 23:00:00", $utc), 
     new DateTime("2016-10-29 23:00:00", $utc)] as $dt) { 
    $lastTs = NULL; 

    for($j = 0; $j < 12; $j++) { 
     $local = clone $dt; 
     $local->setTimeZone($viena); 
     echo $local->format('d.M.Y H:i:s P'); // <== the only place where you need local timezone 
     echo $dt->format(' (U)'); 

     if(!is_null($lastTs)) 
      echo ' (+' . ($dt->format('U') - $lastTs) . ')'; 

     $lastTs = $dt->format('U'); 

     echo "\n"; 

     $dt->add($offset); 
    } 

    echo "\n"; 
} 
+0

私はMySQLはDATETIMEのを使用して同じことをしていることに気づきました。 DATETIMEのような抽象化は正しく計算されないため、UNIXタイムスタンプを使用してすべてのタイムスタンプを保存する必要がありますか? –

+0

タイムスタンプ、またはUTCタイムゾーンを使用するDateTimeのいずれかがジョブを実行します。 –

+0

MySQL NOW()がデフォルトでUTCを出力しない理由を知りませんか? mysql-devsは現地時間をDATETIMEに保存しますか? –

関連する問題