2008/04/01

strtotime() + month 要小心!

因故需要找出當月的最後一天,一開始是用以下語法:
$last_day_of_this_month = date("Y-m-d", (mktime(0, 0, 0, date("m", 
strtotime("next month")), 1, date("Y", strtotime("next month")))) - 86400);


由於每個月的天數不一樣,最後一天可能是 28、29、30、31,因此作法是先找出下個月第一天的 unix timestamp,然後減去 86400 秒(一天),再利用 date() 去調整成想要的日期格式。

但是這寫法在今天 2008-03-31 卻出問題,跑出來的結果是 2008-04-30,不是預期的 2008-03-31。
仔細追查,發現問題是出在 strtotime("next month") 這段程式碼。

測試程式碼:

echo date("Y-m-d", strtotime("today")); // 2008-03-31
echo date("Y-m-d", strtotime("+1 month")); // 2008-05-01
echo date("Y-m-d", strtotime("next month")); // 2008-05-01

從這個結果來看,感覺 strtotime() 在處理 +1 month 時,是直接把月份 +1,像是 3 月 31 日就直接變成 4 月 31 日,由於 4 月只有 30 天,就變成 5 月 1 日。

既然 strtotime() 不能用,就換個寫法:
$last_day_of_this_month = date("Y-m-d", (mktime(0,0,0,
(((date("n")+1)>12) ? 1 : date("n")+1),1,date("Y", strtotime("next month"))))-24*60*60);

還是自己算月份比較保險...

附帶一提,在官方手冊上有提到,在 PHP 4.4.0 以前的版本,next 會誤判成 +2,建議使用 +1 取代 next。但我在 4.4.8 和 5.2.5 下測試,結果都是一樣錯。

3 則留言:

  1. $lastday = mktime(0, 0, 0, 3, 0, 2000); //時 分 秒 月 日 年
    echo strftime("Last day in Feb 2000 is: %d", $lastday);

    Output:
    The last day in Feb 2000 is: 29

    date()也可以.
    *t - 當月的天數,例如:" 28" 到 " 31"

    給你參考

    回覆刪除
  2. To Chair,
    說來慚愧,date() 語法都看了不下十回,從沒注意到 date() 還有 t 這個參數。
    用這個參數就簡單多了,謝謝囉!

    回覆刪除
  3. 其實 strtotime() 並沒錯,因為他是固定 +30 天 不分大小月的

    3 月 30 變成 4 月 30
    3 月 31 變成 5 月 1

    例如你買一個月的會員 固定就是給 30 天,

    如果硬要說只能說需求不同 ...

    回覆刪除