時刻の文字列を指定されたフォーマットに従ってパースする

# DateTimeImmutable::createFromFormat

# date_create_immutable_from_format

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

DateTimeImmutable::createFromFormat -- date_create_immutable_from_format — 時刻の文字列を指定されたフォーマットに従ってパースする

### 説明

オブジェクト指向型

public static function DateTimeImmutable::createFromFormat(string `$format`, string `$datetime`, ?DateTimeZone `$timezone` = `null`): DateTimeImmutable|false

手続き型

function date_create_immutable_from_format(string `$format`, string `$datetime`, ?DateTimeZone `$timezone` = `null`): DateTimeImmutable|false

新しい DateTimeImmutable オブジェクトを返します。 このオブジェクトは、`datetime` で指定した文字列を `format` で指定した書式に沿って解釈した時刻を表します。

### パラメータ

`format`

  書式を文字列で渡します。以下の書式オプションを参照ください。 大半は、date() で使える文字と同じです。

  全てのフィールドは現在の日付/時刻で初期化されます。 ほとんどの場合、これらの値を "ゼロ" (Unix タイムのエポック `1970-01-01 00:00:00 UTC`) でリセットしたいはずです。 これは、`format` の最初の文字に `!` を入れるか、 最後の文字に `|` を入れることで実現できます。 詳細な情報は、以下で示すそれぞれの文字に関するドキュメントを参照ください。

  フォーマットの解釈は左から右に行われます。 これは、フォーマット文字が現れる順番が結果に影響する場合があるということです。 たとえば `z`(1年における何番目の日か)　の場合、 `Y` や `y` を使って年が既にパースされた後に使わなければなりません。

  数値の解釈に使われる文字については、広範な値が許されています。 これには、論理的に許される範囲以上のものも含みます。 たとえば、`d` (ある月における何番目の日か) の場合、 `00` から `99` までの値が許されています。これに対する唯一の制約は、桁数のみです。 範囲外の値が指定された場合、 日付/時刻のパーサーのオーバーフローの仕組みが使われます。 オーバーフローのふるまいについては、 後にいくつかのサンプルを示します。

  データは、フォーマット文字に従って貪欲にパースされます。 つまり、そのフォーマットが許している最大の長さまで読みこまれます。 これによって、次のフォーマット文字に十分な文字数が `datetime` にもう残っていないことがありえます。 このことが引き起こす問題については、このページのサンプルでも触れます。

  `format` パラメータに渡せる文字
  `format` 文字 | 説明 | 取りうる値の例
  --- | --- | ---
  日 | --- | ---
  `d` および `j` | 2桁の日付。先頭のゼロを含むものと含まないもの | `01` から `31` あるいは `1` から `31` (その月の日数分以上の2桁の数値も指定可能ですが、 その月はオーバーフローします。 たとえば January において 33 を指定した場合、 February 2nd という意味になります)
  `D` および `l` | 曜日を表す文字列 | `Mon` から `Sun` あるいは `Sunday` から `Saturday`。 指定された曜日が、パースされた(またはデフォルトの)日付と異なっていた場合、 指定された曜日の 次の 日にオーバーフローします。詳細は後に示すサンプルを参照ください。
  `S` | 日付の後につける英語の接尾辞。二文字。処理中には無視されます。 | `st`、`nd`、`rd` あるいは `th`
  `z` | 年始からの通算日数 (最初は 0)。 `Y` または `y` の後に続けて置かなければいけません。 | `0` から `365` (1年の日数分以上の3桁の数値も指定可能ですが、 その年はオーバーフローします。 たとえば 2022年において `366` を指定した場合は、 January 2nd, 2023 という意味になります)
  月 | --- | ---
  `F` および `M` | 月を表す文字列。January あるいは Sept など | `January` から `December` あるいは `Jan` から `Dec`
  `m` および `n` | 月を表す数値。先頭のゼロを含むものと含まないもの | `01` から `12` あるいは `1` から `12` (12 以上の2桁の数値も指定可能ですが、 その年についてはオーバーフローします。 たとえば `13` の場合は、 翌年の1月という意味になります)
  年 | --- | ---
  `X` および `x` | 年の数値表現。19桁まで指定可能。オプションで `+` や `-` を先頭に指定可能です。 | 例: `0055`, `787`, `1999`, `-2003`, `+10191`
  `Y` | 4 桁までの数値で表した年 | 例: `25` (`0025` と同じ), `787`, `1999`, `2003`
  `y` | 2 桁の数値で表した年 (1970年から2069年の間だとみなされます) | 例: `99` あるいは `03` (それぞれ、 `1999` および `2003` と見なされます)
  時刻 | --- | ---
  `a` および `A` | 午前および午後 | `am` あるいは `pm`
  `g` および `h` | 12 時間制での時間。先頭のゼロを含むものと含まないもの | `1` から `12` あるいは `01` から `12` (12 以上の2桁の数値も指定可能ですが、 日の指定がオーバーフローします。 たとえば `14` の場合は、 次の午前/午後の `02` 時という意味になります)
  `G` および `H` | 24 時間制での時間。先頭のゼロを含むものと含まないもの | `0` から `23`、あるいは `00` から `23` (24 より大きな、2桁の値も指定可能ですが、 この場合、日の指定がオーバーフローします。 たとえば、`26` は翌日の `02:00` という意味になります)
  `i` | 分。先頭のゼロを含む | `00` から `59` (59 以上の2桁の数値も指定可能ですが、 次の1時間にオーバーフローします。 たとえば `66` の場合は、 次の1時間の `:06` という意味になります)
  `s` | 秒。先頭のゼロを含む | `00` から `59` (59 以上の2桁の数値も指定可能ですが、 次の分にオーバーフローします。 たとえば `90` の場合は、 次の分の `:30` という意味になります)
  `v` | ミリ秒 (最大 3桁) | 例: `12` (`0.12` 秒という意味), `345` (`0.345` 秒という意味)
  `u` | マイクロ秒 (最大 6 桁) | 例: `45` (`0.45` 秒という意味), `654321` (`0.654321` 秒という意味)
  タイムゾーン | --- | ---
  `e`, `O`, `p`, `P` および `T` | タイムゾーン識別子、UTC からの時差 (時間単位)、 UTC からの時差 (コロン区切りでの時間と分)、そしてタイムゾーンの短縮形 | 例: `UTC`、`GMT`、 `Atlantic/Azores`、 `+0200`、`+02:00`、 `EST`、`MDT`
  完全な日付/時刻 | --- | ---
  `U` | Unix エポック (January 1 1970 00:00:00 GMT) からの経過秒数 | 例: `1292177455`
  空白および区切り | --- | ---
  `` (空白) | ゼロ文字以上のスペース、タブ、NBSP(U+A0)、NNBSP(U+202F) | 例: `"\t"`, `" "`
  `#` | 次の区切り文字のいずれか: `;`, `:`, `/`, `.`, `,`, `-`, `(`, `)` | 例: `/`
  `;`, `:`, `/`, `.`, `,`, `-`, `(`, `)` | 指定した文字 | 例: `-`
  `?` | ランダムなバイト | 例: `^` (UTF-8 文字の場合は複数の `?` が必要になるでしょう。この場合、おそらく `*` を使うと要望が満たせるはずです)
  `*` | 次の区切り文字あるいは数字までのランダムなバイト列 | 例: `Y-*-d` の中の `*` は、文字列 `2009-aWord-08` の中の `aWord` にマッチします
  `!` | すべてのフィールド (年、月、日、時、分、秒、マイクロ秒およびタイムゾーン情報) を ゼロ相当の値 (時、分、秒、マイクロ秒については `0`。 月、日については `1`。 年については `1970`。 タイムゾーンについてはデフォルトのタイムゾーン) にリセットします。 | `!` がなければ、すべてのフィールドは現在の日時に設定されます。
  `|` | まだパースされていないすべてのフィールド (年、月、日、時、分、秒、マイクロ秒およびタイムゾーン情報) を ゼロ相当の値にリセットします。 | `Y-m-d|` は、文字列をパースした結果から年月日を設定し 時分秒には 0 を設定します。
  `+` | この文字があると、文字列のそれ以降のデータではエラーが発生せず、 かわりに警告を発生させる | それ以降のデータが存在したかどうかを調べるには DateTime::getLastErrors() を使います。

  書式文字列の中に解釈不能な文字が含まれていると処理は失敗し、 戻り値にはエラーメッセージが付加されます。エラーメッセージを調べるには DateTime::getLastErrors() を使います。

  `format` にリテラル文字を含めるには、 バックスラッシュ (`\`) でエスケープする必要があります。

  `format` に文字 `!` が含まれない場合は、作成した時刻値のうち `format` で指定されていない部分を 現在のシステム時刻で初期化します。

  `format` に文字 `!` が含まれる場合は、作成した時刻値のうち `format` で指定されていない部分と `!` の左側の部分を Unix エポックの対応する箇所の値で初期化します。

  Unix エポックは 1970-01-01 00:00:00 です。

`datetime`

  時刻を表す文字列。

`timezone`

  指定したいタイムゾーンを表す DateTimeZone オブジェクト。

  `timezone` を省略されるか、または `null` の場合、 かつ `datetime` にタイムゾーンが含まれない場合は、 現在のタイムゾーンを使います。

  > 注意:

  > `datetime` パラメータが UNIX タイムスタンプ (例: `946684800`) だったり、タイムゾーンを含んでいたり (例: `2010-01-28T15:00:00+02:00`) する場合は、 `timezone` パラメータや現在のタイムゾーンは無視します。

### 戻り値

新しい DateTimeImmutable のインスタンスを返します。 失敗した場合に `false` を返します.

### エラー / 例外

`datetime` に NULLバイトが含まれている場合は、 ValueError がスローされます。

### 変更履歴

バージョン | 説明
--- | ---
8.2.9 | `` (空白) 文字は、 新たに NBSP(U+A0) と NNBSP(U+202F) をサポートするようになりました。
8.2.0 | `format` に指定できる文字に、 `X` と `x` が追加されました。
8.0.21, 8.1.8, 8.2.0 | `datetime` に NULLバイトが含まれている場合は、 ValueError がスローされるようになりました。 これより前のバージョンでは、こうした値は静かに無視されていました。
7.3.0 | `format` に指定できる文字に、 `v` が追加されました。

### 例

例1 DateTimeImmutable::createFromFormat() の例

オブジェクト指向型

    <?php
    $date = DateTimeImmutable::createFromFormat('j-M-Y', '15-Feb-2009');
    echo $date->format('Y-m-d');

例2 定義済みのフォーマットの定数を DateTimeImmutable::createFromFormat() で使う

オブジェクト指向型

    <?php
    $date = DateTimeImmutable::createFromFormat(
        DateTimeInterface::ISO8601,
        '2004-02-12T15:19:21+00:00'
    );
    echo $date->format('c e') . "\n";

    $date = DateTimeImmutable::createFromFormat(
        DateTimeInterface::RFC3339_EXTENDED,
        '2013-10-14T09:00:00.000+02:00'
    );
    echo $date->format('c e') . "\n";

この例で使われている 定義済みの定数 は、DateTimeImmutable の フォーマット 文字からなる文字列です。 ほとんどの場合、それらのフォーマット文字は 上の パラメータ で定義されている 日付/時刻 の要素とマッチしますが、 カバーしている範囲は狭いです。

例3 DateTimeImmutable::createFromFormat() の複雑な例

    <?php
    echo 'Current time: ' . date('Y-m-d H:i:s') . "\n";

    $format = 'Y-m-d';
    $date = DateTimeImmutable::createFromFormat($format, '2009-02-15');
    echo "Format: $format; " . $date->format('Y-m-d H:i:s') . "\n";

    $format = 'Y-m-d H:i:s';
    $date = DateTimeImmutable::createFromFormat($format, '2009-02-15 15:16:17');
    echo "Format: $format; " . $date->format('Y-m-d H:i:s') . "\n";

    $format = 'Y-m-!d H:i:s';
    $date = DateTimeImmutable::createFromFormat($format, '2009-02-15 15:16:17');
    echo "Format: $format; " . $date->format('Y-m-d H:i:s') . "\n";

    $format = '!d';
    $date = DateTimeImmutable::createFromFormat($format, '15');
    echo "Format: $format; " . $date->format('Y-m-d H:i:s') . "\n";

    $format = 'i';
    $date = DateTimeImmutable::createFromFormat($format, '15');
    echo "Format: $format; " . $date->format('Y-m-d H:i:s') . "\n";

上の例の出力は、 たとえば以下のようになります。

    Current time: 2022-06-02 15:50:46
    Format: Y-m-d; 2009-02-15 15:50:46
    Format: Y-m-d H:i:s; 2009-02-15 15:16:17
    Format: Y-m-!d H:i:s; 1970-01-15 15:16:17
    Format: !d; 1970-01-15 00:00:00
    Format: i; 2022-06-02 00:15:00

例4 リテラル文字を含む書式文字列

    <?php
    echo DateTimeImmutable::createFromFormat('H\h i\m s\s','23h 15m 03s')->format('H:i:s');

上の例の出力は、 たとえば以下のようになります。

    23:15:03

例5 オーバーフローした際のふるまい

    <?php
    echo DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2021-17-35 16:60:97')->format(DateTimeImmutable::RFC2822);

上の例の出力は、 たとえば以下のようになります。

    Sat, 04 Jun 2022 17:01:37 +0000

上の結果は奇妙に見えますが、正しい動作です。 なぜなら、以下のようなオーバーフローが発生しているからです:

1. `97` 秒は `1` 分オーバーフローしており、`37` 秒が残ります。
2. `61` 分は `1` 時間オーバーフローしており、`1` 分が残ります。
3. `35` 日は、`1` ヶ月分オーバーフローしており、`4` 日が残ります。 どれだけの日が残るかは月に依存します。 なぜなら、すべての月が同じ日数を持つとは限らないからです。
4. `18` ヶ月は、`1` 年分オーバーフローしており、`6` ヶ月が残ります。

例6 曜日がオーバーフローした場合のふるまい

    <?php
    $d = DateTime::createFromFormat(DateTimeInterface::RFC1123, 'Mon, 3 Aug 2020 25:00:00 +0000');
    echo $d->format(DateTime::RFC1123), "\n";

上の例の出力は、 たとえば以下のようになります。

    Mon, 10 Aug 2020 01:00:00 +0000

上の結果は奇妙に見えますが、正しい動作です。 なぜなら、以下のようなオーバーフローが発生しているからです:

1. `3 Aug 2020 25:00:00` はオーバーフローしており、`(Tue) 4 Aug 2020 01:00` となります。
2. `Mon` が適用されます。 これは、日付が `Mon, 10 Aug 2020 01:00:00` まで進むからです。 `Mon` のような相対的なキーワード指定については、 相対的な書式 で説明されています。

日付のオーバーフローを検出するために、 DateTimeImmutable::getLastErrors() が使えます。 このメソッドの結果には、オーバーフローが発生した場合に警告が含まれます。

例7 オーバーフローした日付を検出する

    <?php
    $d = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2021-17-35 16:60:97');
    echo $d->format(DateTimeImmutable::RFC2822), "\n\n";

    var_dump(DateTimeImmutable::getLastErrors());

上の例の出力は、 たとえば以下のようになります。

    Sat, 04 Jun 2022 17:01:37 +0000

    array(4) {
      'warning_count' =>
      int(2)
      'warnings' =>
      array(1) {
        [19] =>
        string(27) "The parsed date was invalid"
      }
      'error_count' =>
      int(0)
      'errors' =>
      array(0) {
      }
    }

例8 貪欲なパースの振る舞い

    <?php
    print_r(date_parse_from_format('Gis', '60101'));

上の例の出力は、 たとえば以下のようになります。

    Array
    (
        [year] =>
        [month] =>
        [day] =>
        [hour] => 60
        [minute] => 10
        [second] => 0
        [fraction] => 0
        [warning_count] => 1
        [warnings] => Array
            (
                [5] => The parsed time was invalid
            )

        [error_count] => 1
        [errors] => Array
            (
                [4] => A two digit second could not be found
            )

        [is_localtime] =>
    )

フォーマット文字 `G` は、 24時間制での時間をパースします。 先頭のゼロを含んでいても、含まなくてもパースを行います。 このフォーマットは、1桁か2桁の数値が必要ですが、 2桁の数値が存在するので、ここでは貪欲に `60` を読み込みます。

その後に続く `i` と `s` フォーマット文字は、両方2桁の数値が必要です。 つまり、`10` が (フォーマット文字 `i` の) 分として渡されるので、 (フォーマット文字 `s` の) 秒としてパースする十分な桁数が残っていません。

配列の `errors` キーの内容が、 この問題を説明しています。

さらに、24時間制の時は、 `0`-`24` の範囲であるため、 `60` は範囲外の値です。 よって、`warnings` キーに、 指定された時間が正しくない旨の警告が含まれています。

### 参考

- DateTimeImmutable::__construct() - 新しい DateTimeImmutable オブジェクトを返す
- DateTimeImmutable::getLastErrors() - 警告およびエラーを返す
- checkdate() - グレゴリオ暦の日付/時刻の妥当性を確認します
- strptime() - strftime が生成した日付/時刻をパースする

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open