0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP の DateTime とタイムゾーンの話

Last updated at Posted at 2025-04-28

PHP の DateTime における、コンストラクタとタイムゾーンの関係ついて解説します。

実は「指定したはずのタイムゾーンが無視される」挙動をすることもあり、意外と油断なりません。その点についてもしっかりと解説してきます。

前提条件

この記事における PHP のデフォルトタイムゾーンは Asia/Tokyo とします。これは DateTime でタイムゾーンが未指定だったときのデフォルト値として使われます。

Case 1. now にタイムゾーンを指定する

まず最初に、現在日時(now)で DateTime を作ったときの実例を見てみましょう。

現在日時は「日本時間で 2025年2月17日 午前9時」であると仮定します。

// Case 1-1. タイムゾーンの指定なし
$nowJst = new DateTime('now');
var_dump($nowJst->format('Y-m-d H:i:s, e, U'));
// => string(43) "2025-02-17 09:00:00, Asia/Tokyo, 1739750400"

// Case 1-2. 第2引数でタイムゾーンを指定する
$nowUtc1 = new DateTime('now', new DateTimeZone('UTC'));
var_dump($nowUtc1->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

// Case 1-3. タイムゾーンを後から変換する
$nowUtc2 = new DateTime('now');
$nowUtc2->setTimezone(new DateTimeZone('UTC'));
var_dump($nowUtc2->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

ポイント

  • 第2引数が省略された場合、デフォルトのタイムゾーンである Asia/Tokyo が使われる。
  • 第2引数が指定された場合、そのタイムゾーンが使われる。
  • UNIX時間 (1970年1月1日 0時0分0秒 からの経過秒数)はどのケースでも同じ値になる。
    • now はタイムゾーンの指定に関わらず同じUNIX時間が生成される。

コンストラクタでタイムゾーンを指定したかどうかに関わらず、 UNIX時間は同じ値になります。また setTimeZone() で後からタイムゾーンを変換してもUNIX時間は変化しません。

なお format() で出力される日時は、現在設定されているタイムゾーンを反映したものになります。日本時間では午前9時に、UTCでは午前0時になっているのは、 format() 時点のタイムゾーンの違いによるものです。

Case 2. 年月日時分秒のみを指定する

次に、年月日時分秒を指定して DateTime を作ったときの実例を見てみましょう。

今回のケースでは、コンストラクタで「2025年2月17日 午前9時」を指定します。

// Case 2-1. タイムゾーンの指定なし
$nowJst = new DateTime('2025-02-17 09:00:00');
var_dump($nowJst->format('Y-m-d H:i:s, e, U'));
// => string(43) "2025-02-17 09:00:00, Asia/Tokyo, 1739750400"

// Case 2-2. 第2引数でタイムゾーンを指定する
$nowUtc1 = new DateTime('2025-02-17 09:00:00', new DateTimeZone('UTC'));
var_dump($nowUtc1->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 09:00:00, UTC, 1739782800"

// Case 2-3. タイムゾーンを後から変換する
$nowUtc2 = new DateTime('2025-02-17 09:00:00');
$nowUtc2->setTimezone(new DateTimeZone('UTC'));
var_dump($nowUtc2->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

var_dump($nowUtc1 <=> $nowUtc2);
// => int(1)

var_dump($nowJst <=> $nowUtc2);
// => int(0)

ポイント

  • UNIX時間は「第1引数の日時を、タイムゾーンで解釈した値」から算出される。
    • 明示的にタイムゾーンが指定された場合はその値が使われ、そうでない場合はデフォルトのタイムゾーンが使われる。
  • setTimeZone() を呼び出しても UNIX時間は変化しない。
  • UNIX時間が同じ場合、タイムゾーンを問わず DateTime は等価とみなされる。

Case 1 で述べた通り now はタイムゾーンによる解釈が変わらない値でした。

一方で、今回のような「2025年2月17日 午前9時」という値はタイムゾーンによって解釈が変化する値です。算出されるUNIX時間もまた変わります。

この違いにより、 Case 2 では以下のようなUNIX時間の差異が発生しています。

  • 「タイムゾーンを省略した場合(Case 2-1)」と「タイムゾーンをコンストラクタで指定した場合(Case 2-2)」でUNIX時間が異なる。
  • 「タイムゾーンをコンストラクタで指定した場合(Case 2-2)」と「タイムゾーンを後から変更した場合(Case 2-3)」でUNIX時間が異なる。

Case 3. today を指定する

now と似た値で today という値を使うこともできます。しかし、両者の挙動は大きく異なります。

// 現在日時は「日本時間で 2025年2月17日 午前9時」であると仮定します。

// Case 3-1. タイムゾーンの指定なし
$nowJst = new DateTime('today');
var_dump($nowJst->format('Y-m-d H:i:s, e, U'));
// => string(43) "2025-02-17 00:00:00, Asia/Tokyo, 1739718000"

// Case 3-2. 第2引数でタイムゾーンを指定する
$nowUtc = new DateTime('today', new DateTimeZone('UTC'));
var_dump($nowUtc->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

ポイント

today はタイムゾーンによって解釈が変わる値です。その結果、概ね Case 2 と同様の結果になります。

なお yesterday, tomorrow, noon といった値も同様に、タイムゾーンによって変わる値です。

Case 4. タイムゾーンありの日時を指定する

今までは DateTime の第2引数でタイムゾーンを指定していましたが、実は第1引数でタイムゾーンを指定する方法もあります。

第1引数に以下のような値を渡すことで、タイムゾーンありの日時を指定することができます。

  • now UTC
  • 2025-02-17T09:00:00+00:00

これらの値を使ったとき、生成される DateTime は以下のようになります。

// 現在日時は「日本時間で 2025年2月17日 午前9時」であると仮定します。

// Case 3-1. 第1引数のみタイムゾーンを指定し、現在時刻を生成する
$nowCase31 = new DateTime('now UTC');
var_dump($nowCase31->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

// Case 3-2. 第1引数と第2引数の両方でタイムゾーンを指定し、現在時刻を生成する
$nowCase32 = new DateTime('now UTC', new DateTimeZone('America/Los_Angeles'));
var_dump($nowCase32->format('Y-m-d H:i:s, e, U'));
// => string(36) "2025-02-17 00:00:00, UTC, 1739750400"

// Case 3-3. 第1引数のみタイムゾーンを指定し、指定された年月日を生成する
$nowCase33 = new DateTime('2025-02-17T09:00:00+00:00');
var_dump($nowCase33->format('Y-m-d H:i:s, e, U'));
// => string(39) "2025-02-17 09:00:00, +00:00, 1739782800"

// Case 3-4. 第1引数と第2引数の両方でタイムゾーンを指定し、指定された年月日を生成する
$nowCase34 = new DateTime('2025-02-17T09:00:00+00:00', new DateTimeZone('America/Los_Angeles'));
var_dump($nowCase34->format('Y-m-d H:i:s, e, U'));
// => string(39) "2025-02-17 09:00:00, +00:00, 1739782800"

ポイント

  • 第1引数でタイムゾーンが指定された場合、第2引数やデフォルトのタイムゾーンは無視される。

結果はいたってシンプルで、単に第1引数のタイムゾーンのみが使用されます。この挙動は PHP 公式のマニュアルにも記載されています。

https://www.php.net/manual/ja/datetime.construct.php より

注意:

\$datetime パラメータが UNIX タイムスタンプ (@946684800 など) であったりタイムゾーンつきで指定した場合 (2010-01-28T15:00:00+02:00 など) は、$timezone パラメータや現在のタイムゾーンは無視されます。

まとめ

この記事ではPHP の DateTime クラスの仕様について、主に DateTime コンストラクタ を中心に説明しました。

DateTime コンストラクタで使用可能の書式には、タイムゾーンによって解釈が変わる値と、そうでない値があります。now はタイムゾーンによらず一定のUNIX時間が生成されます。一方で、 today や日時を指定した場合はタイムゾーンによってUNIX時間が変わります。

DateTime コンストラクタの第1引数にタイムゾーンが含まれる場合、第2引数は無視されます。

setTimeZone() で後からタイムゾーンを変更してもUNIX時間に変化はありません。

format() で出力される年月日時分秒はタイムゾーンの影響を受けます。

DateTime の大小比較は、UNIX時間を比較した結果と同じになります。設定されているタイムゾーンは関係ありません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?