LoginSignup
9
9

More than 5 years have passed since last update.

日付文字列の検証(バリデーション)

Last updated at Posted at 2014-12-17

たとえば、内部的にDateTimeオブジェクトなどを使って日時を扱っているとき、日時や日付を表す値を文字列として受け取ったとき、どのように検証(バリデーション)していますか?

フレームワークによっては、フォームからの入力値を自動的に検証して、DateTimeオブジェクトに変換してくれるものもありますが、たとえば、GETパラメータで受け取ったときや、CSVを使って一括で処理する場合などには、より柔軟なフォーマット定義が必要な場合があります。

$dateTime = new \DateTime('hoge');
// Exception: DateTime::__construct(): Failed to parse time string (hoge)

私の場合は、次のような日付文字列を検証するためのメソッドを作成しました。

DateTimeUtil.php
class DateTimeUtil
{
    /**
     * 日時文字列を DateTime オブジェクトに変換
     *
     * @param string $format 日付フォーマット
     * @param string $string 日付文字列
     * @return \DateTime|null
     */
    public static function convertStringToDateTime($format, $string)
    {
        $dateTime = \DateTime::createFromFormat($format, $string);

        if (!$dateTime instanceof \DateTime || $dateTime->format($format) !== $string || $dateTime->getTimestamp() < 0) {
            return null;
        }

        return $dateTime;
    }

    /**
     * 日付文字列を検証します
     *
     * @param string $string
     * @param array $formats
     * @return bool
     */
    public static function validateDateString($string, $formats = array())
    {
        if (empty($formats)) {
            $formats = array(
                'Y-m-d',    // 2012-01-01
                'Y/m/d',    // 2012/01/01
                'Y/n/j',    // 2012/1/1
                'Y年n月j日', // 2012年1月1日
            );
        }

        foreach ($formats as $format) {
            $datetime = self::convertStringToDateTime($format, $string);

            if ($datetime instanceof \DateTime) {
                return true;
            }
        }

        return false;
    }

    /**
     * 日時文字列を検証します

     * @param string $string
     * @param array $formats
     * @return bool
     */
    public static function validateDateTimeString($string, $formats = array())
    {
        if (empty($formats)) {
            $formats = array(
                'Y-m-d H:i:s',   // 2012-01-01 01:01:01
                'Y/m/d H:i:s',   // 2012/01/01 01:01:01
                'Y/n/j G:i:s',   // 2012/1/1 1:01:01
                'Y-m-d\TH:i:sP', // 2012-01-01T01:01:01+00:00
            );
        }

        foreach ($formats as $format) {
            $datetime = self::convertStringToDateTime($format, $string);

            if ($datetime instanceof \DateTime) {
                return true;
            }
        }

        return false;
    }
}
DateTimeUtilTest.php
class StringUtilTest extends \PHPUnit_Framework_TestCase
{
    public function testValidateDateString()
    {
        // 正常系
        $this->assertEquals(StringUtil::validateDateString('2014-01-01'), true);
        $this->assertEquals(StringUtil::validateDateString('2014/01/01'), true);
        $this->assertEquals(StringUtil::validateDateString('2014/1/1'), true);
        $this->assertEquals(StringUtil::validateDateString('2014年1月1日'), true);

        // 不正なフォーマット
        $this->assertEquals(StringUtil::validateDateTimeString('2014/01/01', array('Y/n/j')), false);

        // 不正な年
        $this->assertEquals(StringUtil::validateDateString('/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('0/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('1/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('10/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('100/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('20a1/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('0100/1/1'), false);
        $this->assertEquals(StringUtil::validateDateString('20121/1/1'), false);

        // 不正な月
        $this->assertEquals(StringUtil::validateDateString('2012//1'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/0/1'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/13/1'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/100/1'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1a/1'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/01/1'), false);

        // 不正な日
        $this->assertEquals(StringUtil::validateDateString('2012/1/'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1/0'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1/01'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1/32'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1/1a'), false);
        $this->assertEquals(StringUtil::validateDateString('2012/1/100'), false);

        // うるう年の判定
        $this->assertEquals(StringUtil::validateDateString('2012/2/29'), true);
        $this->assertEquals(StringUtil::validateDateString('2014/2/29'), false);
    }

    public function testValidateDateTimeString()
    {
        // 正常系
        $this->assertEquals(StringUtil::validateDateTimeString('2014-01-01 01:01:01'), true);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/01/01 01:01:01'), true);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:01:01'), true);
        $this->assertEquals(StringUtil::validateDateTimeString('2014-01-01T01:01:01+00:00'), true);

        // 不正なフォーマット
        $this->assertEquals(StringUtil::validateDateTimeString('2014-01-01T01:01:01+00:00', array('Y/n/j G:i:s')), false);

        // 不正な時
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 25:00:00'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 100:00:00'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 :00:00'), false);

        // 不正な分
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:60:00'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:100:00'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1::00'), false);

        // 不正な秒
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:01'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:01:60'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:01:100'), false);
        $this->assertEquals(StringUtil::validateDateTimeString('2014/1/1 1:01:'), false);
    }
}

ただ、もっといい方法があるような気が…

9
9
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
9
9