3

2 つの Datetime の違いに問題があります。以下は、DateInterval オブジェクトを表示するコマンド ラインです。

php -r "\$a = new Datetime('first day of 4 months ago midnight'); \$b = new Datetime('first day of 1 month ago midnight'); var_dump(\$a->diff(\$b));"

そして、ここでDateInterval出力:

class DateInterval#3 (15) {
  public $y =>      int(0)
  public $m =>      int(3)
  public $d =>      int(3)
  public $h =>      int(0)
  public $i =>      int(0)
  public $s =>      int(0)
  public $weekday =>               int(0)
  public $weekday_behavior =>      int(0)
  public $first_last_day_of =>     int(0)
  public $invert =>                int(0)
  public $days =>                  int(92)
  public $special_type =>               int(0)
  public $special_amount =>             int(0)
  public $have_weekday_relative =>      int(0)
  public $have_special_relative =>      int(0)
}

編集:最初と2番目の日時:

class DateTime#1 (3) {
  public $date =>
  string(19) "2014-03-01 00:00:00"
  public $timezone_type =>
  int(3)
  public $timezone =>
  string(13) "Europe/Zurich"
}

class DateTime#2 (3) {
  public $date =>
  string(19) "2014-06-01 00:00:00"
  public $timezone_type =>
  int(3)
  public $timezone =>
  string(13) "Europe/Zurich"
}

3日間に注意!私は PHP 5.5.8 を使用していますが、この DateInterval が数日前に 0 か月だったことは確かです。PHP 5.4.28 および 5.5.14 で、DateInterval が 0 日を出力します。PHPのバージョンが影響するかどうかはわかりません。

どちらの場合も、days プロパティは 92 です。

4

2 に答える 2

4

Paul T. Rawkeen's answerへの洞察を提供すると、問題DateTime::diffは、計算の前に最初にタイムゾーンを UTC に変換することです。

<?php

$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');

$a = new DateTime('first day of 4 months ago midnight',$zurich);
$b = new DateTime('first day of 1 month ago midnight',$zurich);

var_dump($a,$b);

$a->setTimezone($utc);
$b->setTimezone($utc);

var_dump($a,$b);

?>

以下を与える:

object(DateTime)[3]
  public 'date' => string '2014-03-01 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
  public 'date' => string '2014-06-01 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[3]
  public 'date' => string '2014-02-28 23:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
  public 'date' => string '2014-05-31 22:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)

タイムゾーンが現在とそれぞれの日付に変換さEurope/Zurichれた後、3 日間の不一致は非常に明確になりました。UTC2014-02-28 23:00:002014-05-31 22:00:00$a$b


解決策は、完全に UTC で動作し、DateTime を表示する前に変換することです。

<?php

$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');

$a = new DateTime('first day of 4 months ago midnight',$utc);
$b = new DateTime('first day of 1 month ago midnight',$utc);

var_dump($a,$b);

$a->setTimezone($zurich);
$b->setTimezone($zurich);

var_dump($a,$b);

?>

01時間は少し異なりますが、すべての日が現在になっていることに注意してください(この回答の最後にあるメモを参照してください):

object(DateTime)[3]
  public 'date' => string '2014-03-01 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
  public 'date' => string '2014-06-01 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)
object(DateTime)[3]
  public 'date' => string '2014-03-01 01:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
  public 'date' => string '2014-06-01 02:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Zurich' (length=13)

この現象を少し理解するには、次の点に注意してください。

  • 2月は28日(2014年)
  • 5月は31日あります
  • DST は 3 月 30 日に始まります: +01:00
  • ヨーロッパ/チューリッヒは UTC+02:00

ヨーロッパ/チューリッヒから UTC に変換する場合、年、月、日だけでなく、時間、分、秒も考慮する必要があります。これが月の最初以外の日であれば、この問題は発生しませんが、時間は依然として 23:00 と 22:00 になります (上記の例の前)。

于 2014-07-24T10:55:08.690 に答える
2

これはあなたが提供するものに依存しDateTimeZoneます。

Europe/Zurichまたはいつでも設定EESTすると、説明した結果が得られます。

GMT/の場合UTC、たとえば、 が得られ$d = 0ます。


このような問題を回避するために、プロジェクトに沿ってグローバル タイム ゾーン定義を使用できます (都合がよければ) 。

date_default_timezone_set( "ヨーロッパ/チューリッヒ" );

または、オブジェクトに必要なタイム ゾーンを定義しDateTimeます。


UPD: @mudasobwaによるコメントで以下に記載されているように、この問題は約 3 年前にここで言及されています。

于 2014-07-24T10:34:55.533 に答える