"Same" DateInterval instances lead to different results, depending on the origin (in combination with DST-switches)
オープン
まだ誰も着手していません。
Bug
Extension: date
Status: Needs Triage
- 主要言語
- C
- スター
- 40.4k
- フォーク
- 8.2k
- 平均マージ
- 2日 13時間
- マージ済み PR(30日)
- 96
説明
Description
The following code:
<?php
function getIntervalSpecificationFromDateInterval(DateInterval $interval): string
{
$specification = '';
if ($interval->invert) {
$specification .= '-';
}
$specification .= 'P';
$specification .= $interval->y ? $interval->y . 'Y' : '';
$specification .= $interval->m ? $interval->m . 'M' : '';
$specification .= $interval->d ? $interval->d . 'D' : '';
if ($interval->h + $interval->i + $interval->s) {
$specification .= 'T';
$specification .= $interval->h ? $interval->h . 'H' : '';
$specification .= $interval->i ? $interval->i . 'M' : '';
$specification .= $interval->s ? $interval->s . 'S' : '';
}
if ($specification === 'P' || $specification === '-P') {
$specification = 'PT0S';
}
return $specification;
}
$timezone = new DateTimeZone('Europe/Berlin');
$dstSwitchA = new DateTimeImmutable('2025-03-30 01:00:00', $timezone);
$dstSwitchB = new DateTimeImmutable('2025-10-26 01:00:00', $timezone);
$durationA = new DateInterval('PT3H');
$durationB = (new DateTimeImmutable('2025-03-28 01:00:00', $timezone))->diff(
new DateTimeImmutable('2025-03-28 04:00:00', $timezone),
true
);
$durationC = (new DateTimeImmutable('2025-03-28 01:00:00', $timezone))->diff(
new DateTimeImmutable('2025-03-28 04:00:00', $timezone),
);
$durationD = (new DateTimeImmutable('2025-03-28 01:00:00', $timezone))->diff(
new DateTimeImmutable('2025-03-28 04:00:00', $timezone),
);
$durationD = new DateInterval(getIntervalSpecificationFromDateInterval($durationD));
// Test 1st DST-switch (+1 hour)
echo 'TEST 1 (DST-switch +1 hour)' . PHP_EOL;
$datetimeA = $dstSwitchA->setTimezone($timezone)->add($durationA);
echo 'Constructed: ' . $datetimeA->format('Y-m-d H:i:s') . PHP_EOL; // expected: 2025-03-30 05:00:00
$datetimeB = $dstSwitchA->setTimezone($timezone)->add($durationB);
echo 'Diff (absolute): ' . $datetimeB->format('Y-m-d H:i:s') . PHP_EOL; // expected: 2025-03-30 05:00:00
$datetimeC = $dstSwitchA->setTimezone($timezone)->add($durationC);
echo 'Diff (relative): ' . $datetimeC->format('Y-m-d H:i:s') . PHP_EOL; // expected: 2025-03-30 05:00:00
$datetimeD = $dstSwitchA->setTimezone($timezone)->add($durationD);
echo 'Workaround: ' . $datetimeD->format('Y-m-d H:i:s') . PHP_EOL; // expected: 2025-03-30 05:00:00
echo PHP_EOL;
echo 'TEST 2 (DST-switch -1 hour)' . PHP_EOL;
// Test 2nd DST-switch (-1 hour)
// Expected value: 2025-10-26 03:00:00
$datetimeA = $dstSwitchB->setTimezone($timezone)->add($durationA);
echo 'Constructed: ' . $datetimeA->format('Y-m-d H:i:s') . PHP_EOL;
$datetimeB = $dstSwitchB->setTimezone($timezone)->add($durationB);
echo 'Diff (absolute): ' . $datetimeB->format('Y-m-d H:i:s') . PHP_EOL;
$datetimeC = $dstSwitchB->setTimezone($timezone)->add($durationC);
echo 'Diff (relative): ' . $datetimeC->format('Y-m-d H:i:s') . PHP_EOL;
$datetimeD = $dstSwitchB->setTimezone($timezone)->add($durationD);
echo 'Workaround: ' . $datetimeD->format('Y-m-d H:i:s') . PHP_EOL;
Resulted in this output:
TEST 1 (DST-switch +1 hour)
Constructed: 2025-03-30 05:00:00
Diff (absolute): 2025-03-30 04:00:00
Diff (relative): 2025-03-30 04:00:00
Workaround: 2025-03-30 05:00:00
TEST 2 (DST-switch -1 hour)
Constructed: 2025-10-26 03:00:00
Diff (absolute): 2025-10-26 04:00:00
Diff (relative): 2025-10-26 04:00:00
Workaround: 2025-10-26 03:00:00
But I expected this output instead:
TEST 1 (DST-switch +1 hour)
Constructed: 2025-03-30 05:00:00
Diff (absolute): 2025-03-30 05:00:00
Diff (relative): 2025-03-30 05:00:00
Workaround: 2025-03-30 05:00:00
TEST 2 (DST-switch -1 hour)
Constructed: 2025-10-26 03:00:00
Diff (absolute): 2025-10-26 03:00:00
Diff (relative): 2025-10-26 03:00:00
Workaround: 2025-10-26 03:00:00
PHP Version
PHP 8.3.16 (cli) (built: Jan 14 2025 20:07:09) (NTS Visual C++ 2019 x64)
Copyright (c) The PHP Group
Zend Engine v4.3.16, Copyright (c) Zend Technologies
with Zend OPcache v8.3.16, Copyright (c), by Zend Technologies
with Xdebug v3.4.1, Copyright (c) 2002-2025, by Derick Rethans
Tested with PHP 8.1.0 to 8.4.8
Operating System
No response
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
まず、issue で説明されている DateTimeImmutable::add() と DateInterval の動作に対して、提供された PHP の再現コードを実行し、コンストラクターと DateTimeImmutable::diff() によって作成された間隔を比較します。DST の遷移を担当する PHP の日付/時刻実装を追跡します。両方の DST 切り替えケースで、同等の 3 時間の間隔が期待されるタイムスタンプを生成し、実証されたシナリオに対するリグレッションカバレッジがあれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- php
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 35/100