質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.54%

  • PHP

    23563questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • DateTime

    64questions

    多くのプログラミング言語におけるDateTimeオブジェクトは、日付と時間に関する演算と出力を行います。

DateTime クラスの月判定に関して

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,023

te2ji

score 13900

DateTime 使った、日付の確認スクリプトを作成したのですが、意図した月判定が出来ません。

現象は

  • Ym や Y-m の判定のときのみ起こるようである。d を含むと正常(一般的な範囲の月判定)に動作する。
  • 0 や 99 といった月判定されてはおかしいようなものが月判定される。

date
m    月。数字。先頭にゼロをつける。    01 から 12

マニュアルを見る限り、範囲外に思えるのですが、よく仕様を見落とすので、勘違いしている気がします。
どなたか、解説もらえないでしょうか。
よろしくお願いします。

<?php

$dates = [ 
        '2017028',
        '201702',
        '201720',
        '20172008',
        '2017208',
        '201730',
        '201740',
        '201799',
        '201700',
        '20170',
        '2017-02-8',
        '2017-02',
        '2017-20',
        '2017-20-08',
        '2017-20-8',
        '2017-30',
        '2017-40',
        '2017-99',
        '2017-00',
        '2017-0',
];
function chk_date($date) {
    $formats = [ 
            'Ymd', 
            'Ym', 
            'Y-m-d', 
            'Y-m', 
    ];
    foreach ( $formats as $format ) {
        DateTime::createFromFormat ( $format, $date );
        $result = DateTime::getLastErrors ();

        if (! $result ['warning_count'] && ! $result ['error_count']) {
        echo "<pre>";
        print_r(date_parse_from_format ( $format ,$date ));
        echo "</pre>";
            return TRUE;
        }
    }
    return FALSE;
}

foreach ( $dates as $date ) {
    echo $date . ':';
    echo chk_date ( $date ) ? 'OK' : 'NG';
    echo '<br>';
}
2017028:
Array
(
    [year] => 2017
    [month] => 2
    [day] => 8
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
201702:
Array
(
    [year] => 2017
    [month] => 2
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
201720:
Array
(
    [year] => 2017
    [month] => 20
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
20172008:NG
2017208:NG
201730:
Array
(
    [year] => 2017
    [month] => 30
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
201740:
Array
(
    [year] => 2017
    [month] => 40
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
201799:
Array
(
    [year] => 2017
    [month] => 99
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
201700:
Array
(
    [year] => 2017
    [month] => 0
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
20170:
Array
(
    [year] => 2017
    [month] => 0
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-02-8:
Array
(
    [year] => 2017
    [month] => 2
    [day] => 8
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-02:
Array
(
    [year] => 2017
    [month] => 2
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-20:
Array
(
    [year] => 2017
    [month] => 20
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-20-08:NG
2017-20-8:NG
2017-30:
Array
(
    [year] => 2017
    [month] => 30
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-40:
Array
(
    [year] => 2017
    [month] => 40
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-99:
Array
(
    [year] => 2017
    [month] => 99
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-00:
Array
(
    [year] => 2017
    [month] => 0
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK
2017-0:
Array
(
    [year] => 2017
    [month] => 0
    [day] => 
    [hour] => 
    [minute] => 
    [second] => 
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

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

    [is_localtime] => 
)
OK

DateTime::createFromFormat を活かしたスクリプト

<?php

$dates = [
'2017028',
'201702',
'201720',
'20172008',
'2017208',
'201730',
'201740',
'201799',
'201700',
'20170',
'2017-02-8',
'2017-02',
'2017-20',
'2017-20-08',
'2017-20-8',
'2017-30',
'2017-40',
'2017-99',
'2017-00',
'2017-0',
];
function chk_date($date) {
    $formats = [
    'Ymd',
    'Ym',
    'Y-m-d',
    'Y-m',
    ];
    foreach ( $formats as $format ) {
        $datetime = DateTime::createFromFormat ( $format, $date );
        $result = DateTime::getLastErrors ();
        if (! $result ['warning_count'] && ! $result ['error_count']) {
            echo ", format : $format <br>";
            echo "DateTime : {$datetime->format('Y-m-d H:i:s')} <br>";
            return TRUE;
        }
    }
    return FALSE;
}

foreach ( $dates as $date ) {
    echo "date : $date";
    echo chk_date ( $date ) ? 'OK' : 'NG';
    echo '<br><br>';
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

DateTime 使った、日付の確認スクリプト

でありながら、なぜ DateTime::createFromFormat の結果を捨て、
代わりに date_parse_from_format の結果を表示しているのですか?

スクリプトを以下のように修正すれば、DateTime::createFromFormat がどのように動作するか理解できるかと思います。

<?php

$dates = [ 
        '2017028',
        '201702',
        '201720',
        '20172008',
        '2017208',
        '201730',
        '201740',
        '201799',
        '201700',
        '20170',
        '2017-02-8',
        '2017-02',
        '2017-20',
        '2017-20-08',
        '2017-20-8',
        '2017-30',
        '2017-40',
        '2017-99',
        '2017-00',
        '2017-0',
];
function chk_date($date) {
    $formats = [ 
            'Ymd', 
            'Ym', 
            'Y-m-d', 
            'Y-m', 
    ];
    foreach ( $formats as $format ) {
        // DateTime::createFromFormat ( $format, $date );
        $datetime = DateTime::createFromFormat ( $format, $date );
        $result = DateTime::getLastErrors ();

        echo "date : $date, format : $format" . PHP_EOL;

        if (! $result ['warning_count'] && ! $result ['error_count']) {
        // echo "<pre>";
        // print_r(date_parse_from_format ( $format ,$date ));
        // echo "</pre>";
            print_r($datetime);
            return TRUE;
        } else {
            print_r($result);
        }
    }
    return FALSE;
}

foreach ( $dates as $date ) {
    echo $date . ':';
    echo chk_date ( $date ) ? 'OK' : 'NG';
    echo '<br>';
}


動作結果

2017028:date : 2017028, format : Ymd
DateTime Object
(
    [date] => 2017-02-08 12:52:27.000000
    [timezone_type] => 3
    [timezone] => Asia/Tokyo
)
OK<br>201702:date : 201702, format : Ymd
Array
(
    [warning_count] => 0
    [warnings] => Array
        (
        )

    [error_count] => 1
    [errors] => Array
        (
            [6] => Data missing
        )

)
date : 201702, format : Ym
DateTime Object
(
    [date] => 2017-02-17 12:52:27.000000
    [timezone_type] => 3
    [timezone] => Asia/Tokyo
)
OK<br>201720:date : 201720, format : Ymd
Array
(
    [warning_count] => 0
    [warnings] => Array
        (
        )

    [error_count] => 1
    [errors] => Array
        (
            [6] => Data missing
        )

)
date : 201720, format : Ym
DateTime Object
(
    [date] => 2018-08-17 12:52:27.000000
    [timezone_type] => 3
    [timezone] => Asia/Tokyo
)
OK<br>20172008:date : 20172008, format : Ymd
Array
(
    [warning_count] => 1
    [warnings] => Array
        (
            [8] => The parsed date was invalid
        )

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

)
date : 20172008, format : Ym
Array
(
    [warning_count] => 0
    [warnings] => Array
        (
        )

    [error_count] => 1
    [errors] => Array
        (
            [6] => Trailing data
        )

)
date : 20172008, format : Y-m-d
Array
(
    [warning_count] => 1
    [warnings] => Array
        (
            [8] => The parsed date was invalid
        )

    [error_count] => 2
    [errors] => Array
        (
            [4] => The separation symbol could not be found
            [6] => The separation symbol could not be found
        )

)

※ 「10000文字以内で入力してください」と怒られたので、以下省略

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/17 16:50

    > date_parse_from_format の結果を表示しているのですか?
    継ぎはぎスクリプトなんで、というのが理由ですが、結果から見るとそのほうが分かりやすかったかと。

    KiyoshiMotoki さんの記述していただいたスクリプトで結果を見ても、やはり[month] は正しく評価されていないように見えます。20月とか99月とか。
    d がつくと正常に戻るので、モヤッとします。

    何でですかね?不思議です。。。

    キャンセル

  • 2017/02/17 17:41

    その「正しく」は、あなたの思い込みに過ぎないのではありませんか?

    公式のドキュメントには、
    > 例2 DateTime::createFromFormat() の複雑な例

    の他に DateTime::createFromFormat の動作(※)に関する詳細な情報は記載されていません。
    http://php.net/manual/ja/datetime.createfromformat.php

    そして、そこには範囲外の月を指定した場合の動作は記載されていません。

    記載がない以上、現状の動作を
    「そういうもの」
    と受け入れるしかないと、私は考えます。

    ※ どのような入力値のときにどのような DateTimeオブジェクトを返却し、
     どのような入力値のときにパースが失敗するのか、という。

    キャンセル

  • 2017/02/17 18:06 編集

    http://php.net/manual/ja/datetime.createfromformat.php
    > このオブジェクトは、time で指定した文字列を format で指定した書式に沿って解釈した時刻を表します。
    > m および n 月を表す数値。先頭のゼロを含むものと含まないもの 01 から 12 あるいは 1 から 12
    > 書式文字列の中に解釈不能な文字が含まれていると処理は失敗し、 返り値にはエラーメッセージが付加されます。エラーメッセージを調べるには DateTime::getLastErrors() を使います。

    とあるので、書式文字に合致しない 'm 20' とかは「正しくない」と判断しましたが、 範囲外の月を指定した場合の動作は確かに記載がありませんね^^;

    記載がないからと言って、「書式文字に合致しない場合も合致した場合と同じ動作をする」というのにはちょっと違和感がありますが、「そういうもの」として受け入れざるをえなさそうなことも理解しました。

    回答ありがとうございました。

    キャンセル

0

数字の羅列を日付として解釈させるのは
8桁であればYmdであると判断できますが、それ以外は所詮無理やりです
7桁の場合、4桁の年と、残りの3桁は1月1日を1としたn日目と
解釈されるようです。(2017001→2017-01-01)
ただし366日めまでしかカウントしてくれません(うるう年対策?)
例えば123が1/23か12/3か解釈できないからでしょうね

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/17 10:26

    少し誤解を与える質問になっていたので修正しました。
    Y-m のように切れ目が分かるフォーマットでも再現します。

    キャンセル

  • 2017/02/17 12:33

    Datetimeクラスは昔のstrtotimeでの解釈と異なるようですね
    結局想定外のフォーマットのバグ取りをしているだけであれば
    とりあえず分割されたらcheckedate()することで
    チェックしたほうが現実的かもしれません。

    キャンセル

  • 2017/02/17 17:45

    d 入れると正常なんですよね。d あるときは結構使えるヤツですw
    checkedate() ならきっと正規表現で分離しますよね。年月の場合は、Datetimeクラスは出番なしとしたほうが良さ気ですね。

    キャンセル

同じタグがついた質問を見る

  • PHP

    23563questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • DateTime

    64questions

    多くのプログラミング言語におけるDateTimeオブジェクトは、日付と時間に関する演算と出力を行います。