PHPで日付表示の方法を列挙しまくってみる

NO IMAGE

改めて改めてPHPでの日付計算の真となる方法を探してみたいと思います

今現在から1ヶ月後を表示する方法

昔はこんな感じで書いてた気がする

 2010-12-17

5.1系での書き方

 2010-12-17

5.2系での書き方

format('Y-m-d');
// => 2010-12-17

こう書いても大丈夫((monthがmonthsでも同じ挙動みたい))

format('Y-m-d');
// => 2010-12-17

5.3からはこんな書き方も

add( new DateInterval( 'P1M' ) )->format( 'Y-m-d' );
// => 2010-12-17

***こっちの方がわかりやすいかな?

add( DateInterval::createFromDateString( 'next month' ) )->format( 'Y-m-d' );
// => 2010-12-17

ややこしいけど手続き型でも書けるよ!

 2010-12-17

ところが月末の場合はこうなっちゃう!

mktime

 October 2010-10-01

あら、9/31は10/1になってしまいました。

strtotime

 October 2010-10-01

こちらも同じ挙動ですね

DateTime

format('F Y-m-d');
// => October 2010-10-01

DateInterval

sub( DateInterval::createFromDateString( 'next month' ) )->format( 'F Y-m-d' );
// => October 2010-10-01

と言う感じでどの書き方をしたとしても正しい日付は返ってきません。
結局はmktimeの処理を他の書き方をしているだけで、日付はそのままで月の数字だけを操作してるのでしょう。
その結果timestampは2010-9-31 = 2010-10-01 になってるんじゃないかと思います。

と、ここまで書いて思ったのですが10/31の1ヶ月前の日付って言う場合に何月何日が返ってくるのが正しいんでしょうかね・・・
30日前、31日前って形でやるほうが正しいんじゃないかと思ってみたり。。。

先月は何月?って場合は↑のが問題になりますね

そんな時は強制的に一日にしちゃえばOKΨ(`∇´)Ψ
DateTime

modify( 'first day of previous month' );
echo $d->format( 'F' );
// September

というわけで
100年前とか100日前とかなら問題になりませんが100ヶ月前とかやると色々めんどくさいよ!っという例なのでした(*゜ー゜)

参考記事
PHP 5.3.0 の日付処理クラスと関数の追加・変更について – t_komuraの日記
PHP 5.3 の DateTime オブジェクト関連の便利な新機能 – 肉とご飯と甘いもの @ sotarok
PHPで、先月、翌月などを扱うときの注意 – bushimichiの日記

おまけ

MySQLの場合

SELECT date(date_add('2010-10-31', interval 1 month))
#2010-11-30

postgreの場合

SELECT CAST('2010-10-31' AS DATE) + CAST('1 months' AS INTERVAL);
#2010-11-30

Pythonの場合
dateutilというモジュールをインストールしないと出来ないっぽい・・・

from datetimeimport date
from dateutil.relativedelta import relativedelta
date(2010, 10, 31) + relativedelta(months=1) # datetime.date(2010, 11, 30)

Perlの場合
DateTimeモジュールを使えば出来る・・・らしいがCPANから入れるのが大変そうなので省略

じゃあPHPでSQLと同じ結果にしたい場合はどうすればいいのか
+がんばって前月の日付を取って計算する
+Zend_Date API を使う

ある日付に対して月を足したり引いたりすると、 もとの日付が月の最後のほうだった場合に予期せぬ結果となることがあります。 たとえば「1月31日」に一ヶ月足すことを考えてみましょう。 SQL に慣れている人なら、この結果は「2月28日」になるものと思われるでしょう。 一方、Excel や OpenOffice を使っている人は、この結果が「3月3日」 になるものと考えるのではないでしょうか。 この問題は、計算結果の月の日数が元の日付の月の日数より少ない場合に起こりえます。 Zend Framework の開発者向けには、どちらの方式かを選択できるようにしました。 extend_month オプションを FALSE にすると SQL 風の挙動になり、TRUE にすると表計算ソフト風の挙動になります。 extend_month のデフォルトは FALSE なので、 Zend_Date は SQL 互換の計算をします。 デフォルトでは、Zend_Date は月の計算をする際に (必要に応じて) 日を切り詰めます。計算した結果に該当する日がその月に存在しなかった場合に、 翌月にまたがることはありません。 Zend_Date::setOptions(array('extend_month' => true)) とすると、表計算ソフトと同様の方式で計算することになります。

Zend Framework: Documentation: Zend_Date API の概要 - Zend Framework Manual