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

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

新規登録して質問してみよう
ただいま回答率
85.48%
DateTime

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

PHP

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

Q&A

2回答

3252閲覧

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

退会済みユーザー

退会済みユーザー

総合スコア0

DateTime

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

PHP

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

0グッド

0クリップ

投稿2017/02/17 00:13

編集2017/02/17 07:52

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

現象は

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

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

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

php

1<?php 2 3$dates = [ 4 '2017028', 5 '201702', 6 '201720', 7 '20172008', 8 '2017208', 9 '201730', 10 '201740', 11 '201799', 12 '201700', 13 '20170', 14 '2017-02-8', 15 '2017-02', 16 '2017-20', 17 '2017-20-08', 18 '2017-20-8', 19 '2017-30', 20 '2017-40', 21 '2017-99', 22 '2017-00', 23 '2017-0', 24]; 25function chk_date($date) { 26 $formats = [ 27 'Ymd', 28 'Ym', 29 'Y-m-d', 30 'Y-m', 31 ]; 32 foreach ( $formats as $format ) { 33 DateTime::createFromFormat ( $format, $date ); 34 $result = DateTime::getLastErrors (); 35 36 if (! $result ['warning_count'] && ! $result ['error_count']) { 37 echo "<pre>"; 38 print_r(date_parse_from_format ( $format ,$date )); 39 echo "</pre>"; 40 return TRUE; 41 } 42 } 43 return FALSE; 44} 45 46foreach ( $dates as $date ) { 47 echo $date . ':'; 48 echo chk_date ( $date ) ? 'OK' : 'NG'; 49 echo '<br>'; 50} 51
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>'; }

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

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

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

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

php

1<?php 2 3$dates = [ 4 '2017028', 5 '201702', 6 '201720', 7 '20172008', 8 '2017208', 9 '201730', 10 '201740', 11 '201799', 12 '201700', 13 '20170', 14 '2017-02-8', 15 '2017-02', 16 '2017-20', 17 '2017-20-08', 18 '2017-20-8', 19 '2017-30', 20 '2017-40', 21 '2017-99', 22 '2017-00', 23 '2017-0', 24]; 25function chk_date($date) { 26 $formats = [ 27 'Ymd', 28 'Ym', 29 'Y-m-d', 30 'Y-m', 31 ]; 32 foreach ( $formats as $format ) { 33 // DateTime::createFromFormat ( $format, $date ); 34 $datetime = DateTime::createFromFormat ( $format, $date ); 35 $result = DateTime::getLastErrors (); 36 37 echo "date : $date, format : $format" . PHP_EOL; 38 39 if (! $result ['warning_count'] && ! $result ['error_count']) { 40 // echo "<pre>"; 41 // print_r(date_parse_from_format ( $format ,$date )); 42 // echo "</pre>"; 43 print_r($datetime); 44 return TRUE; 45 } else { 46 print_r($result); 47 } 48 } 49 return FALSE; 50} 51 52foreach ( $dates as $date ) { 53 echo $date . ':'; 54 echo chk_date ( $date ) ? 'OK' : 'NG'; 55 echo '<br>'; 56}

動作結果

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 03:57

KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/02/17 07:50

> date_parse_from_format の結果を表示しているのですか? 継ぎはぎスクリプトなんで、というのが理由ですが、結果から見るとそのほうが分かりやすかったかと。 KiyoshiMotoki さんの記述していただいたスクリプトで結果を見ても、やはり[month] は正しく評価されていないように見えます。20月とか99月とか。 d がつくと正常に戻るので、モヤッとします。 何でですかね?不思議です。。。
KiyoshiMotoki

2017/02/17 08:41

その「正しく」は、あなたの思い込みに過ぎないのではありませんか? 公式のドキュメントには、 > 例2 DateTime::createFromFormat() の複雑な例 の他に DateTime::createFromFormat の動作(※)に関する詳細な情報は記載されていません。 http://php.net/manual/ja/datetime.createfromformat.php そして、そこには範囲外の月を指定した場合の動作は記載されていません。 記載がない以上、現状の動作を 「そういうもの」 と受け入れるしかないと、私は考えます。 ※ どのような入力値のときにどのような DateTimeオブジェクトを返却し、  どのような入力値のときにパースが失敗するのか、という。
退会済みユーザー

退会済みユーザー

2017/02/17 09:48 編集

http://php.net/manual/ja/datetime.createfromformat.php > このオブジェクトは、time で指定した文字列を format で指定した書式に沿って解釈した時刻を表します。 > m および n 月を表す数値。先頭のゼロを含むものと含まないもの 01 から 12 あるいは 1 から 12 > 書式文字列の中に解釈不能な文字が含まれていると処理は失敗し、 返り値にはエラーメッセージが付加されます。エラーメッセージを調べるには DateTime::getLastErrors() を使います。 とあるので、書式文字に合致しない 'm 20' とかは「正しくない」と判断しましたが、 範囲外の月を指定した場合の動作は確かに記載がありませんね^^; 記載がないからと言って、「書式文字に合致しない場合も合致した場合と同じ動作をする」というのにはちょっと違和感がありますが、「そういうもの」として受け入れざるをえなさそうなことも理解しました。 回答ありがとうございました。
guest

0

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

投稿2017/02/17 00:55

yambejp

総合スコア114784

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/02/17 01:26

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

2017/02/17 03:33

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

退会済みユーザー

2017/02/17 08:45

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問