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

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

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

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

Q&A

解決済

2回答

4878閲覧

PHP: DateIntervalクラスを使って「1日前」

退会済みユーザー

退会済みユーザー

総合スコア0

PHP

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

0グッド

0クリップ

投稿2017/03/01 10:50

編集2017/03/01 11:25

こんなコードを書いてみました。
(質問ようにコンパクトにしてしまったために再現しなくなっていたので、元のコードに差し替えました。)

php

1// 1月前の日付を得る 2$now = new DateTime(); 3$begin_date = $now; 4$begin_date->sub(new DateInterval('P1M')); 5echo $begin_date->format('Ymd'); 6// 1日前の日付を得る 7$end_date = $now; 8$end_date->sub(new DateInterval('P1D')); 9echo $end_date->format('Ymd');

今日は2017年3月1日なので、end_dateは「20170228」という出力が得られるものと期待しましたが、「20170131」が得られました。

DateTimeクラスやDateIntervalクラスって本当に大丈夫なんでしょうか。
もしかして、$nowも$begin_dateも$end_dateも繋がっちゃってますかね。

結果的に、下記のブログ記事を見つけて対応したので問題なしですが、少し気味が悪いです。
(テスト環境は、XAMPP-Windowsに含まれているPHP 5.4.16)

前日日付などを取得する方法 - Chocotte de Choo

php

1date("Ymd",strtotime("-1 day")); //1日前 2date("Ymd",strtotime("-1 week")); //1週間前 3date("Ymd",strtotime("-1 month")); //1月前 4date("Ymd",strtotime("-1 year")); //1年前 5date("Ymd",strtotime("1 day")); //1日後 6date("Ymd",strtotime("1 week")); //1週間後 7date("Ymd",strtotime("1 month")); //1月後 8date("Ymd",strtotime("1 year")); //1年後

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

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

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

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

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

guest

回答2

0

ベストアンサー

少なくとも私の手元の環境と paiza.io では、意図通り20170228と表示されました。
https://paiza.io/projects/1XKLk2acPZ5Hm9naQUCF-A

$end_date->sub()の前後で$end_dateを var_dump したり、
以下の設定などを確認してみてください。

  • ini_get('date.timezone') の戻り値
  • 実行環境の時刻が狂っていないか?

以下、コメント欄のやり取りを転載

php

1// 1月前の日付を得る 2$now = new DateTime(); 3$begin_date = $now; 4$begin_date->sub(new DateInterval('P1M')); 5echo $begin_date->format('Ymd'); 6// 1日前の日付を得る 7$end_date = $now; 8$end_date->sub(new DateInterval('P1D')); 9echo $end_date->format('Ymd');

上のコードだと、変数now, $begin_date, $end_dateが全て、同じ参照を指していることになります。
そのため、1つを変更すると、他の 2つの変数の値も変更される(ように見える)ことになります。

php

1$now = new DateTime(); 2$begin_date = $now; 3$begin_date->sub(new DateInterval('P1M')); 4echo $begin_date->format('Ymd') . PHP_EOL; 5 6echo PHP_EOL; 7 8$end_date = $now; 9$end_date->sub(new DateInterval('P1D')); 10echo $end_date->format('Ymd') . PHP_EOL; 11 12echo PHP_EOL; 13 14echo $now->format('Ymd') . PHP_EOL; 15echo $begin_date->format('Ymd') . PHP_EOL;

実行結果

20170201 20170131 20170131 20170131

$begin_dateと$end_dateに最初に直接 new DateTime()を突っ込んどけば混乱しなかったんですね。

php

1$begin_date = new DateTime(); 2$end_date = new DateTime();

ということでしょうか?

これだと、1行目を実行してから 2行目が実行されるまでの間に日付が変わると、結果が 1日ズレてしまいます。

以下のように、clone キーワードを使用するのが良いと思います。

php

1$begin_date = new DateTime(); 2$end_date = clone $begin_date;

http://php.net/manual/ja/language.oop5.cloning.php

投稿2017/03/01 11:08

編集2017/03/01 11:50
KiyoshiMotoki

総合スコア4791

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

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

退会済みユーザー

退会済みユーザー

2017/03/01 11:14

回答有り難うございます。 「string 'Asia/Tokyo' (length=10)」が得られました。 XAMPPは自分のパソコンで動かしていてNTPサーバー同期なども仕掛けていて正しく2017年3月1日です。 20時を過ぎたせいなのか、報告した現象が再現しなくなってしまい、今は「20170228」が得られています。
KiyoshiMotoki

2017/03/01 11:20

レスありがとうございます。 > 20時を過ぎたせいなのか、報告した現象が再現しなくなってしまい、今は「20170228」が得られています。 特に何も変更していないにも関わらず、ということでしょうか? であれば、謎現象ですね… さすがに「20時を過ぎたせい」ということはないと思いますがw
退会済みユーザー

退会済みユーザー

2017/03/01 11:24

例示したコードは、端折りすぎました。改めてチェックお願いします。
KiyoshiMotoki

2017/03/01 11:25

$now, $begin_date, $end_date が、同じ参照を指していますよ。
退会済みユーザー

退会済みユーザー

2017/03/01 11:28

やはりそうでしたか、大変お恥ずかしいコードでした。
KiyoshiMotoki

2017/03/01 11:28

コード <?php $now = new DateTime(); $begin_date = $now; $begin_date->sub(new DateInterval('P1M')); echo $begin_date->format('Ymd') . PHP_EOL; echo PHP_EOL; $end_date = $now; $end_date->sub(new DateInterval('P1D')); echo $end_date->format('Ymd') . PHP_EOL; echo PHP_EOL; echo $now->format('Ymd') . PHP_EOL; echo $begin_date->format('Ymd') . PHP_EOL; 実行結果 20170201 20170131 20170131 20170131
KiyoshiMotoki

2017/03/01 11:29

遅かったw > 大変お恥ずかしいコードでした。 いえいえ、うっかりハマりやすいポイントだと思います。
退会済みユーザー

退会済みユーザー

2017/03/01 11:31

$begin_dateと$end_dateに最初に直接 new DateTime()を突っ込んどけば混乱しなかったんですね。
KiyoshiMotoki

2017/03/01 11:39

> $begin_dateと$end_dateに最初に直接 new DateTime()を突っ込んどけば  $begin_date = new DateTime();  $end_date = new DateTime(); ということでしょうか? これだと、1行目を実行してから 2行目が実行されるまでの間に日付が変わると、結果が 1日ズレてしまいます。 以下のように、clone キーワードを使用するのが良いと思います。  $begin_date = new DateTime();  $end_date = clone $begin_date; http://php.net/manual/ja/language.oop5.cloning.php
KiyoshiMotoki

2017/03/01 11:50

コメント欄の内容を、回答欄に転載させていただきました。
退会済みユーザー

退会済みユーザー

2017/03/01 11:58

clone、勉強になりました。重ね重ねありがとうございます。
guest

0

3v4lに投げてみましたが、問題なく「20170228」となっているようです(PHP 5.3~7.1)。環境要因…なのでしょうか。

投稿2017/03/01 11:12

maisumakun

総合スコア145183

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

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

退会済みユーザー

退会済みユーザー

2017/03/01 11:24

例示したコードは、端折りすぎました。改めてチェックお願いします。
maisumakun

2017/03/01 11:27

DateTimeはミュータブルなので、「$end_date = $now」としても1ヶ月戻ったままのオブジェクトです。
退会済みユーザー

退会済みユーザー

2017/03/01 11:32

ひとつき引いて、いちにち引けば、そうなりますもんね。失礼しました。
maisumakun

2017/03/01 11:33

DateTimeImmutableもあるので、状況によってはそっちが便利かもしれません。
退会済みユーザー

退会済みユーザー

2017/03/01 11:35

リリース先がPHP 5.4なもので、とても悔しいです。(サーバー引っ越し稟議中)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問