5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP の DateTime クラスにて存在しない日付を表す文字列をパースしたらどうなるのか

Posted at

概要

PHP の DateTime クラスにて存在しない日付を表す文字列をパースしたらどうなるのかサンプルコードを書いて調べてみた。

存在しない日付の例

  • 2017年12月32日 (大晦日の次の日)
  • 2019年2月29日 (うるう年ではない年の2月29日)

動作確認環境

macOS Sierra + PHP 7.2

$ php --version
PHP 7.2.0 (cli) (built: Dec  3 2017 21:47:51) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2017 Zend Technologies

ソースコード

<?php

class MyDateTime {

  private $name;
  private $datetime;
  private $errors;

  private const FORMAT = 'Y-m-d';
  private const TIME_ZONE = 'Asia/Tokyo';

  public function __construct($dateString) {

    $timeZone = new DateTimeZone(self::TIME_ZONE);
    if (isset($dateString)) {
      $this->name = $dateString;
      $this->datetime = DateTime::createFromFormat(self::FORMAT, $dateString, $timeZone);
    } else {
      $this->name = 'now       ';
      $this->datetime = new DateTime(NULL, $timeZone);
    }
    $this->errors = DateTime::getLastErrors();
  }

  public function getErrors() {
    if ($this->errors['warning_count'] > 0 || $this->errors['error_count'] > 0) {
      return $this->errors;
    } else {
      return NULL;
    }
  }

  public function __toString() {

    $result = $this->name . ' -> ' . $this->datetime->format('Y-m-d H:i:s (U)');

    $errorMessage = $this->getErrorMessage();
    if ($errorMessage !== '') {
      $result .= ' [' . $errorMessage . ']';
    }

    $result .= "\n";
    return $result;
  }

  private function getErrorMessage() {

    $array = [];
    if ($this->errors['warning_count'] > 0) {
      $array = array_merge_recursive($array, $this->errors['warnings']);
    }
    if ($this->errors['error_count'] > 0) {
      $array = array_merge_recursive($array, $this->errors['errors']);
    }

    if (empty($array) ) {
      return '';
    } else {
      return implode('/', $array);
    }
  }

}

$dateList = [
  NULL,
  '2017-12-31',
  '2017-12-32', // 存在しない日
  '2018-01-01',
  '2019-02-29', // うるう年じゃない
  '2019-03-01', // うるう年じゃない
  '2020-02-29', // うるう年
  '2020-03-01', // うるう年
];

foreach ($dateList as $dateString) {
  $datetime = new MyDateTime($dateString);
  echo $datetime;
  if (!is_null($errors = $datetime->getErrors())) {
    print_r($errors);
  }
}

実行結果

now        -> 2017-12-13 22:41:33 (1513172493)
2017-12-31 -> 2017-12-31 22:41:33 (1514727693)
2017-12-32 -> 2018-01-01 22:41:33 (1514814093) [The parsed date was invalid]
Array
(
    [warning_count] => 1
    [warnings] => Array
        (
            [10] => The parsed date was invalid
        )

    [error_count] => 0
    [errors] => Array
        (
        )

)
2018-01-01 -> 2018-01-01 22:41:33 (1514814093)
2019-02-29 -> 2019-03-01 22:41:33 (1551447693) [The parsed date was invalid]
Array
(
    [warning_count] => 1
    [warnings] => Array
        (
            [10] => The parsed date was invalid
        )

    [error_count] => 0
    [errors] => Array
        (
        )

)
2019-03-01 -> 2019-03-01 22:41:33 (1551447693)
2020-02-29 -> 2020-02-29 22:41:33 (1582983693)
2020-03-01 -> 2020-03-01 22:41:33 (1583070093)
  • 存在しない日付の 2017-12-32 は 2018-01-01 として解釈された
  • 存在しない日付の 2019-02-29 は 2019-03-01 として解釈された

パースの際にエラーは発生しないものの DateTime::getLastErrors を利用すると「The parsed date was invalid」という警告情報を取得することができる。

他に存在しない日付かどうかチェックする方法としては checkdate 関数が用意されている。

PHP: checkdate - Manual

bool checkdate ( int $month , int $day , int $year )
引数で指定された日付の妥当性をチェックします。 各パラメータが適切に指定されている場合に、妥当であると判断されます。

存在しない日付をパースしたときの挙動は、Java の java.util.Calendar クラスの非厳密な解釈に似ている。

Calendar (Java SE 9 & JDK 9 )

public void setLenient​(boolean lenient)
日付/時間の解釈を厳密に行うかどうかを設定します。 厳密でない解釈では、「1996年2月942日」のような日付は、1996年2月1日から第941日目と同じこととみなされます。 厳密な解釈では、このような日付の場合、例外がスローされます。 デフォルトは非厳密です。

参考資料

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?