strtotime的一些坑

本文解析了PHP中使用strtotime和date函数处理日期时遇到的一个常见误区,即当涉及到跨月边界时,如何正确处理月末日期的问题,并提供了修正短语如'firstdayof'和'lastdayof'的使用示例。

一、抛出问题

  • 当今天是7-31号的时候,下面这行代码会输出什么?
     
echo date("Y-m-d",strtotime("-1 month"));
  • 正常来说应该是 2019-06-30号才对,但实际上却是 2019-07-01

 

二、原来如此

  • 一开始很困惑,但是看了鸟哥的讲解后就释然了
  • 先做-1 month, 那么当前是07-31, 减去一以后就是06-31 (所以是先对月份减1)
  • 再做日期规范化, 因为6月没有31号, 所以就好像2点60等于3点一样, 6月31就等于了7月1

 

三、这个问题怎么解?

  • 从PHP5.3开始, date新增了一系列修正短语, 来明确这个问题, 那就是”first day of” 和 “last day of”, 也就是你可以限定好不要让date自动”规范化”
  •  

    var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2019-03-31"))));
    //输出2019-02-28
    var_dump(date("Y-m-d", strtotime("first day of +1 month", strtotime("2019-08-31"))));
    //输出2019-09-01
    var_dump(date("Y-m-d", strtotime("first day of next month", strtotime("2019-01-31"))));
    //输出2019-02-01
    var_dump(date("Y-m-d", strtotime("last day of last month", strtotime("2019-03-31"))));
    //输出2019-02-28
    
    

     

     

    附上鸟哥详解:http://www.laruence.com/2018/07/31/3207.html

 

<think> 我们之前讨论的是`strpos()`函数因参数类型不匹配引发的`TypeError`(期望字符串但收到整数)。现在用户希望结合这个错误,了解`strtotime`函数的使用、可能出现的问题及解决方案。 首先,需要明确:`strtotime()`是PHP中用于将任何英文文本的日期时间描述解析为Unix时间戳的函数。它的函数签名是: ```php strtotime(string $datetime, ?int $baseTimestamp = null): int|false ``` 第一个参数`$datetime`必须是字符串类型(string),第二个参数是可选的基准时间戳(整数,默认为当前时间)。 因此,与`strpos()`类似,`strtotime()`的第一个参数也要求是字符串。如果传入整数,同样会触发类型错误(TypeError)。 ### 一、`strtotime()`函数的基本用法 `strtotime()`接受一个表示日期/时间的字符串,并尝试将其解析为Unix时间戳(自1970-01-01 00:00:00 UTC以来的秒数)。例如: ```php $timestamp = strtotime("2023-01-01"); // 返回1672531200(具体值取决于时区) ``` 它还支持相对时间格式: ```php $nextWeek = strtotime("+1 week"); // 一周后的时间戳 $yesterday = strtotime("yesterday"); ``` ### 二、常见错误及解决方案 #### 1. 类型错误(TypeError):期望字符串,实际收到整数 **错误示例**: ```php $input = 20230101; // 整数,表示日期 $timestamp = strtotime($input); // 错误:TypeError: strtotime() expects parameter 1 to be string, int given ``` **原因**:`$input`是整数类型,而`strtotime()`要求第一个参数是字符串。 **解决方案**: - 显式转换为字符串: ```php $input = 20230101; $timestamp = strtotime((string)$input); // 转换为字符串 ``` 但注意:直接转换后的字符串格式(`"20230101"`)可能不是`strtotime()`能识别的标准格式,可能导致解析失败(返回`false`)。因此,通常需要先格式化为标准日期字符串: ```php $input = 20230101; $dateStr = date('Y-m-d', strtotime((string)$input)); // 先转字符串,再尝试转换格式(可能不可靠) // 更可靠的方式:使用DateTime $date = DateTime::createFromFormat('Ymd', (string)$input); $timestamp = $date->getTimestamp(); ``` #### 2. 解析失败(返回`false`) **错误示例**: ```php $timestamp = strtotime("Invalid Date String"); if ($timestamp === false) { echo "解析失败"; } ``` **原因**:传入的字符串不符合日期格式。 **解决方案**: - 使用前验证输入格式: - 使用正则表达式验证是否为有效日期字符串。 - 使用`DateTime::createFromFormat()`进行更严格的解析(推荐): ```php $date = DateTime::createFromFormat('Y-m-d', $inputString); if ($date) { $timestamp = $date->getTimestamp(); } else { // 处理错误 } ``` #### 3. 时区问题 **问题描述**:`strtotime()`解析时会使用PHP设置的默认时区(可通过`date_default_timezone_set()`设置),若未设置可能产生非预期结果。 **解决方案**: - 明确设置时区: ```php date_default_timezone_set('Asia/Shanghai'); // 设置时区 $timestamp = strtotime("2023-01-01 12:00:00"); ``` #### 4. 数值型字符串被误识别为时间戳 **问题描述**:如果传入的字符串是全数字(如`"1234567890"`),`strtotime()`会将其视为时间戳而不是日期字符串,导致解析结果错误。 **解决方案**: - 避免使用纯数字字符串表示日期(如用`"2023-01-01"`而非`"20230101"`)。 - 对于纯数字字符串,先判断是否是时间戳(通常为10位或13位数字): ```php if (is_numeric($input) && strlen($input) === 10) { // 可能是时间戳,直接使用intval() $timestamp = (int)$input; } else { $timestamp = strtotime($input); } ``` ### 三、最佳实践总结 1. **确保传入字符串**:使用`(string)`或`strval()`强制转换非字符串参数。 2. **验证日期格式**:优先使用`DateTime`对象代替`strtotime()`,因其提供更严格的解析和错误处理[^1]。 3. **处理解析失败**:始终检查`strtotime()`的返回值是否为`false`。 4. **时区设置**:在脚本开始时使用`date_default_timezone_set()`明确时区。 5. **避免歧义格式**:尽量使用ISO-8601格式(如`"2023-01-01"`)。 ### 代码示例:安全的日期解析 ```php function safeStrtotime($input) { // 确保输入为字符串 $inputStr = (string)$input; // 尝试解析 $timestamp = strtotime($inputStr); // 若解析失败,尝试使用DateTime if ($timestamp === false) { $date = DateTime::createFromFormat('Y-m-d', $inputStr); if ($date) { return $date->getTimestamp(); } return false; // 完全无法解析 } return $timestamp; } // 使用示例 $input = 20230101; // 整数 $timestamp = safeStrtotime($input); // 内部会转换并尝试解析 if ($timestamp === false) { echo "日期解析失败"; } else { echo "时间戳: " . $timestamp; } ``` ### 相关问题 1. 如何将Unix时间戳转换为格式化的日期字符串? 2. PHP中除了`strtotime()`和`DateTime`,还有哪些处理日期时间的函数? 3. 如何处理不同时区的日期时间转换? 4. 为什么`strtotime("10000")`返回一个时间戳而不是日期? </think> ### `strtotime()` 函数使用指南及常见错误解决方案 `strtotime()` 是将英文文本日期时间描述解析为 Unix 时间戳的核心函数,但使用不当会引发类似 `strpos()` 的类型错误。以下是关键知识点: --- ### 一、`strtotime()` 的正确用法 **函数定义**: ```php strtotime(string $datetime, ?int $baseTimestamp = null): int|false ``` - **`$datetime`**:必须为字符串(如 `"2023-01-01"` 或 `"next Monday"`) - **`$baseTimestamp`**:可选基准时间戳(整数) - **返回值**:成功返回时间戳(整数),失败返回 `false` **示例**: ```php // 正确:字符串参数 $timestamp = strtotime("2023-12-25"); // 返回 1703462400 (时间戳) $nextWeek = strtotime("+1 week"); // 一周后的时间戳 // 错误:传入整数参数 $invalid = strtotime(20231225); // TypeError! ``` --- ### 二、常见错误及解决方案 #### 1. **类型错误(TypeError)** **错误原因**: > 类似 `strpos()`,当 `$datetime` 是非字符串类型(如整数、数组)时触发: > ```php > // 错误示例 > $date = 20231225; // 整数类型 > strtotime($date); // TypeError: 期望字符串,收到整数 > ``` **解决方案**: - **显式类型转换**: ```php $date = 20231225; $timestamp = strtotime((string)$date); // 强制转为字符串 ``` - **输入验证**: ```php if (is_int($input)) { $input = (string)$input; // 转换整数 } elseif (!is_string($input)) { throw new InvalidArgumentException("必须为字符串"); } $timestamp = strtotime($input); ``` #### 2. **解析失败(返回 `false`)** **错误原因**: - 日期格式无法识别(如 `"25/12/2023"` 非标准格式) - 无效日期(如 `"2023-02-30"`) **解决方案**: - **使用标准格式**(ISO 8601): ```php $valid = strtotime("2023-12-25"); // 推荐格式 ``` - **验证返回值**: ```php $timestamp = strtotime($input); if ($timestamp === false) { throw new Exception("日期解析失败: " . $input); } ``` - **改用 `DateTime` 对象**(更健壮): ```php $date = DateTime::createFromFormat('Y/m/d', "2023/12/25"); if ($date) { $timestamp = $date->getTimestamp(); // 安全获取时间戳 } ``` #### 3. **时区问题** **错误现象**: 解析结果与预期相差数小时。 **解决方案**: - **显式设置时区**: ```php date_default_timezone_set('Asia/Shanghai'); // 设置默认时区 $timestamp = strtotime("2023-12-25 08:00:00"); ``` - **使用时区参数**(`DateTime`): ```php $date = new DateTime("2023-12-25", new DateTimeZone('Asia/Shanghai')); $timestamp = $date->getTimestamp(); ``` --- ### 三、最佳实践总结 1. **强制字符串输入**: ```php $safeInput = is_string($input) ? $input : strval($input); ``` 2. **优先使用 `DateTime`**: - 支持更严格的格式控制和时区管理[^1] 3. **错误处理全覆盖**: ```php try { $timestamp = strtotime((string)$input); if ($timestamp === false) { throw new Exception("解析失败"); } } catch (TypeError $e) { // 处理类型错误 } ``` 4. **避免纯数字日期**: - 如 `20231225` 易被误认为整数 → 改用 `"2023-12-25"` --- ### 四、扩展场景示例 **场景**:从 API 接收混合类型日期数据 ```php $apiData = ['date' => 20231225]; // 整数格式日期 // 安全解析 $dateStr = strval($apiData['date']); // 转为字符串 $dateObj = DateTime::createFromFormat('Ymd', $dateStr); // 指定格式 if ($dateObj) { echo $dateObj->format('Y-m-d'); // 输出:2023-12-25 } else { echo "日期无效"; } ``` --- ### 相关问题 1. 如何将 Unix 时间戳转换为带时区的日期字符串? 2. `DateTime` 和 `strtotime()` 在性能上有何差异? 3. 如何验证用户提交的日期字符串是否合法? 4. 为什么 `strtotime("10000")` 返回时间戳而非错误?[^1] [^1]: PHP 会尝试将纯数字字符串解释为时间戳(如 `"10000"` 视为 1970-01-01 02:46:40 UTC),而非日期格式。建议使用 `DateTime` 避免歧义。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值