首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP strtotime 函式深度解析:用法、原理與「-1 month」陷阱

     編輯:PHP 綜合知識

前言

strtotime() 是 PHP 核心日期時間處理函式,自 PHP 4 起內建。它能將英文文字日期描述解析為 Unix 時間戳。本文從原始碼層面剖析其實作機制,並解釋為何 strtotime("-1 month") 在特定日期會產生非預期結果。

一、常見用法與關鍵字行為

1.1 月份關鍵字

strtotime("JAN") 與 strtotime("January") 效果相同:回傳指定月份的今天;若該月無此日(如 1 月 31 日往 2 月),則順延至下月 3 號。

echo date("Y-m-d H:i:s", strtotime("feb", strtotime("2011-03-31")));
// 輸出:2011-03-03 00:00:00

原理:內部直接修改 timelib_time 結構體的 m 欄位,不重新計算日數上限。

1.2 first / last / previous / next

  • first day:將日期設為 1 號(time->d = 1)
  • last day:設為 0 號並月份 +1,相當於該月最後一天
  • previous sunday:取得指定日期前一個星期日
  • next monday:取得下一個星期一
echo date("Y-m-d H:i:s", strtotime("last day", strtotime("2000-02-01")));
// 2000-02-29 00:00:00(閏年)

1.3 back of / front of

  • back of 24:設為隔天 00:15
  • front of 0:設為前一天 23:45

參數範圍 0~24,超出會報錯。

二、核心實作原理(源碼級)

2.1 入口函式

位於 ext/date/php_date.c 的 PHP_FUNCTION(strtotime):

  1. 解析參數:支援 (string time, int now) 或單參數 (string time)
  2. 建立 timelib_time 結構體 now 作為基準時間
  3. 呼叫 timelib_strtotime() 進行詞法解析
  4. 以 timelib_fill_holes() 補齊缺失欄位
  5. timelib_update_ts() 計算最終時間戳

2.2 詞法解析器

使用 re2c 產生的解析器(parse_date.re),針對輸入字串進行模式匹配:

'yesterday'
{
    DEBUG_OUTPUT("yesterday");
    TIMELIB_INIT;
    TIMELIB_HAVE_RELATIVE();
    TIMELIB_UNHAVE_TIME();
    s->time->relative.d = -1;  // 相對天數 -1
    TIMELIB_DEINIT;
    return TIMELIB_RELATIVE;
}

關鍵結構體:

  • Scanner:掃描器狀態
  • timelib_time:儲存年月日時分秒、時區、相對時間等
  • timelib_rel_time:相對時間量(年、月、日、時、分、秒、週幾等)

三、「-1 month」求值失敗的根本原因

3.1 解析流程

輸入 "-1 month" 會匹配 relative 規則:

relnumber = ([+-]*[ \t]*[0-9]+);
reltextunit = 'month' ...;
relative = relnumber space? (reltextunit | 'week');

識別出 amount = -1、unit = TIMELIB_MONTH,執行:

case TIMELIB_MONTH:
    s->time->relative.m += amount * relunit->multiplier;  // m -= 1
    break;

僅修改相對月份,不處理日期溢位。

3.2 為何 3 月 31 日減 1 月變 3 月 3 號?

timelib_fill_holes() 補齊缺失欄位時,發現 2 月無 31 日,自動向後推算至 3 月 3 日。這是 設計行為而非 Bug——PHP 選擇保留「日」不變,由日曆系統自然溢位。

3.3 正確做法

需求「上個月同一天」應改用:

// 方法 1:先取上月 1 號,再加天數
$firstDayThisMonth = strtotime("first day of this month");
$lastDayPrevMonth = strtotime("last day of previous month", $firstDayThisMonth);

// 方法 2:DateTime 物件(PHP 5.3+)
$dt = new DateTime("2011-03-31");
$dt->modify("last day of previous month");
echo $dt->format("Y-m-d");  // 2011-02-28

總結

strtotime() 以 re2c 詞法解析器將自然語言轉為結構化相對時間,再由 timelib 計算時間戳。其「月份運算不保護日期」的特性源於底層直接操作 timelib_rel_time.m,開發者需自行處理月末邊界情況,建議改用 DateTime 類別獲得更直觀的日期運算。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved