[PHP]DateTimeのtimezone_typeって何だ？

:::note info
興味本位のエントリのため、実用性は低いと思われます。
:::
## なんかみつけた
[DateTime](https://www.php.net/manual/ja/class.datetime.php)オブジェクトを[var_dump](https://www.php.net/manual/ja/function.var-dump.php)してみます。
```sh
PS> php -r "var_dump(new DateTime());"         
Command line code:1:
class DateTime#1 (3) {
  public $date =>
  string(26) "2025-06-03 14:15:47.149510"
  public $timezone_type =>
  int(3)
  public $timezone =>
  string(3) "UTC"
}
```
マニュアルを見てもDateTimeにpublicプロパティはないはずですが、3つのプロパティが表示されました。

```sh
PS> php -r "var_dump((new DateTime())->date);"
PHP Warning:  Undefined property: DateTime::$date in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0

Warning: Undefined property: DateTime::$date in Command line code on line 1

Call Stack:
    0.0001     485008   1. {main}() Command line code:0

Command line code:1:
NULL
```
もちろん、存在しないプロパティなので、アクセスするとエラーになります。

それはそうとして、timezone_typeプロパティとやらが気になります。何ものでしょうか？

dateはわかります。日時の文字列表現ですね。[microtime](https://www.php.net/manual/ja/function.microtime.php)と同様に、マイクロ秒まで保持しています。
timezoneもわかります。タイムゾーンですね。今回たまたまUTCでしたが、日本なら"Asia/Tokyo"あたりが出ることでしょう。
timezone_typeは、タイムゾーンのタイプだということは見りゃわかりますが、謎の数字です。1や2はあるのか？4以上は？

## 3以外を探してみる
色々試して3以外のtimezone_typeを見つけてみましょう。
```sh
PS> php -r "var_dump(new DateTime('2025-06-03 14:15:47.149510+0900'));"
Command line code:1:
class DateTime#1 (3) {
  public $date =>
  string(26) "2025-06-03 14:15:47.149510"
  public $timezone_type =>
  int(1)
  public $timezone =>
  string(6) "+09:00"
}
```
オフセットを指定する形でタイムゾーンを添えてやるとtimezone_typeが1になりました。

```sh
PS> php -r "var_dump(new DateTime('2025-06-03 14:15:47.149510Z'));"
Command line code:1:
class DateTime#1 (3) {
  public $date =>
  string(26) "2025-06-03 14:15:47.149510"
  public $timezone_type =>
  int(2)
  public $timezone =>
  string(1) "Z"
}
```
UTCを表す"Z"を末尾につけると2になりました。

```sh
PS> php -r "var_dump(new DateTime('2025-06-03 14:15:47.149510 JST'));"
Command line code:1:
class DateTime#1 (3) {
  public $date =>
  string(26) "2025-06-03 14:15:47.149510"
  public $timezone_type =>
  int(2)
  public $timezone =>
  string(3) "JST"
}
```
日本標準時を表す"JST"でも2になりました。ちなみにグリニッジ標準時"GMT"も2。

```sh
PS> php -r "var_dump(new DateTime('2025-06-03 14:15:47.149510 Asia/Tokyo'));"
Command line code:1:
class DateTime#1 (3) {
  public $date =>
  string(26) "2025-06-03 14:15:47.149510"
  public $timezone_type =>
  int(3)
  public $timezone =>
  string(10) "Asia/Tokyo"
}
```
同じく日本標準時を表す"Asia/Tokyo"では3のようです。

```sh
PS> php -r "var_dump(new DateTime('2025-06-03 14:15:47.149510 Landroll/Tokione'));"
PHP Fatal error:  Uncaught DateMalformedStringException: Failed to parse time string (2025-06-03 14:15:47.149510 Landroll/Tokione) at position 27 (L): The timezone could not be found in the database in Command line code:1
Stack trace:
#0 Command line code(1): DateTime->__construct('2025-06-03 14:1...')
#1 {main}
  thrown in Command line code on line 1

Fatal error: Uncaught DateMalformedStringException: Failed to parse time string (2025-06-03 14:15:47.149510 Landroll/Tokione) at position 27 (L): The timezone could not be found in the database in Command line code on line 1

DateMalformedStringException: Failed to parse time string (2025-06-03 14:15:47.149510 Landroll/Tokione) at position 27 (L): The timezone could not be found in the database in Command line code on line 1

Call Stack:
    0.0001     484584   1. {main}() Command line code:0
    0.0001     484664   2. DateTime->__construct($datetime = '2025-06-03 14:15:47.149510 Landroll/Tokione') Command line code:1
```
存在しないタイムゾーンを指定すると例外が発生するようです。

まあこのくらいでしょうか。

## たぶんこういうこと

|timezone_type|意味|例|
|:-:|:-|:-|
|1|オフセットを直接指定|"+0900"、"+00:00"|
|2|タイムゾーンの通称|"JST"、"Z"、"GMT"|
|3|タイムゾーンの正式名|"Asia/Tokyo"、"UTC"、指定なし("UTC"扱い)|

## PHPのソースコードでは？
リポジトリ内の検索結果がこちら。
[https://github.com/search?q=repo%3Aphp%2Fphp-src%20timezone_type&type=code](https://github.com/search?q=repo%3Aphp%2Fphp-src%20timezone_type&type=code)

実装があるのはphp_date.c内。
[https://github.com/php/php-src/blob/36891a677593c6b2cfcac29adae7cd26f21b754e/ext/date/php_date.c](https://github.com/php/php-src/blob/36891a677593c6b2cfcac29adae7cd26f21b754e/ext/date/php_date.c)

どの意味で使っているのかわかりやすいのは[date_object_to_hash](https://github.com/php/php-src/blob/36891a677593c6b2cfcac29adae7cd26f21b754e/ext/date/php_date.c#L1896)関数内。
```c:php_date.c
		switch (dateobj->time->zone_type) {
			case TIMELIB_ZONETYPE_ID:
				ZVAL_STRING(&zv, dateobj->time->tz_info->name);
				break;
			case TIMELIB_ZONETYPE_OFFSET: {
				zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
				int utc_offset = dateobj->time->z;

				ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
					utc_offset < 0 ? '-' : '+',
					abs(utc_offset / 3600),
					abs(((utc_offset % 3600) / 60)));

				ZVAL_NEW_STR(&zv, tmpstr);
				}
				break;
			case TIMELIB_ZONETYPE_ABBR:
				ZVAL_STRING(&zv, dateobj->time->tz_abbr);
				break;
		}
```
うんうん、だいたいさっき実行して感じた通りみたいですね。正式名はID扱いのようです。
そして、1、2、3以外が想定されていないことも見て取れます。

## まとめ
先ほどとほぼ重複になりますが、改めてまとめるとこうなります。
|timezone_type|意味|例|
|:-:|:-|:-|
|1|オフセットを直接指定|"+0900"、"+00:00"|
|2|タイムゾーンの略称|"JST"、"Z"、"GMT"|
|3|タイムゾーンID|"Asia/Tokyo"、"UTC"、指定なし("UTC"扱い)|

まあ、わかったところでプロパティに直接アクセスできないんで実用性はなさそうですけどね。

## (おまけ)AIに聞いてみた
https://g.co/gemini/share/ec3a5f3f692d

> DateTimeオブジェクトで夏時間を正しく処理するためには、このタイプ3のタイムゾーン識別子を使用することが推奨されます。

おお、まさかの実用例が出てきた。