Press "Enter" to skip to content

令人困惑的strtotime

经常会有人被strtotime结合-1 month, +1 month, next month的时候搞得很困惑, 然后就会觉得这个函数有点不那么靠谱, 动不动就出问题. 用的时候就会很慌…

这不, 刚刚就有人在微博上又问我:

鸟哥,今天是2018-07-31 执行代码:

date("Y-m-d",strtotime("-1 month"))

怎么输出是2018-07-01?

好的吧, 虽然这个问题看起来很迷惑, 但从内部逻辑上来说呢, 其实是”对”的, 你先别着急哈, 让我慢慢讲:

我们来模拟下date内部的对于这种事情的处理逻辑:

  • 1. 先做-1 month, 那么当前是07-31, 减去一以后就是06-31.
  • 2. 再做日期规范化, 因为6月没有31号, 所以就好像2点60等于3点一样, 6月31就等于了7月1

是不是逻辑很”清晰”呢? 我们也可以手动验证第二个步骤, 比如:

var_dump(date("Y-m-d", strtotime("2017-06-31")));
//输出2017-07-01

也就是说, 只要涉及到大小月的最后一天, 都可能会有这个迷惑, 我们也可以很轻松的验证类似的其他月份, 印证这个结论:

var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2017-03-31"))));
//输出2017-03-03
var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2017-08-31"))));
//输出2017-10-01
var_dump(date("Y-m-d", strtotime("next month", strtotime("2017-01-31"))));
//输出2017-03-03
var_dump(date("Y-m-d", strtotime("last month", strtotime("2017-03-31"))));
//输出2017-03-03

那怎么办呢?

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

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

那如果是5.3之前的版本(还有人用么?), 你可以使用mktime之类的, 把所有的日子忽略掉, 比如都限定为每月1号就可以了, 只不过就不如直接用first day来的更加优雅.

现在, 搞清楚了内部原理, 是不是就不慌了? 🙂

45 Comments

  1. 轮子
    轮子 2018-08-20

    我司还在用5.2 #fml

  2. Bill Gates
    Bill Gates 2018-08-19

    作为PHP开发组为数不多的华人成员,私觉得鸟哥适合稍微提高一下发文频次,让我们这些“二把刀”能够对PHP有更深的了解。比如,PHP未来的发展方向、最新主版本(比如现在的PHP 7.3 Beta)在性能上的表现以及为什么。

  3. breeze
    breeze 2018-08-18

    模板比以前好看

  4. 笨小孩
    笨小孩 2018-08-15

    第一个参数为时间戳时,在windows环境下,返回值一直正常。但是在linux环境下,有些时间戳返回正常,有些时间戳返回与预期不一样。(正常应该返回false),例如strtotime(1500516291)

  5. 奋斗
    奋斗 2018-08-15

    感觉这个博客模板很low啊

  6. cloedy
    cloedy 2018-08-13

    鸟儿哥, 你终于终于换了个blog模板了

  7. aaa
    aaa 2018-08-09

    鸟哥

  8. masker
    masker 2018-08-09

    仰望鸟哥,我这种PHP小菜鸟快完蛋了

  9. 裸奔の蜗牛
    裸奔の蜗牛 2018-08-09

    更加慌了

  10. mengkang
    mengkang 2018-08-07

    恭喜鸟哥博客前端改版,看微博得知还是鸟哥亲自操刀。

  11. 落舞者
    落舞者 2018-08-07

    哈哈哈,还是还以为谁博客呢。换主题了。差点没认出来。鸟哥加油永远支持php。

  12. 推广网
    推广网 2018-08-07

    为什么不直接在php服务器里做判断 ,而是要用first day of 这个判断 ,每个月多少天是固定的

  13. Fast Light
    Fast Light 2018-08-06

    我在写js的时候也遇到过类似的问题,都是因为各个月份天数不同造成的。

  14. lcp0578
    lcp0578 2018-08-06

    我也有点慌了,鸟哥

  15. 谢谢鸟哥
    谢谢鸟哥 2018-08-06

    哇,更新了,谢谢鸟哥

  16. AIinjection
    AIinjection 2018-08-05

    PHP 的时间函数和时区结合起来,感觉有点乱。特别是当语言自身,框架,ini 都可以配置时区的时候,就更加措手不及了。

  17. Willove
    Willove 2018-08-05

    还是很慌。。。为啥 first day of next month 取的不是0点的时间戳,而还是对应了当前时间的时分秒,大多数能够用到这个参数的需求是想取0点的时间戳。

  18. dilex
    dilex 2018-08-04

    谢谢

  19. 龙笑天
    龙笑天 2018-08-04

    time()、date()、strtotime() 这三个函数的时区问题很烦~~

  20. 沦陷今生
    沦陷今生 2018-08-02

    我也遇到过,幸好可以用mysql填这个坑

    SELECT DATE_ADD( ‘2017-01-31’, INTERVAL 1 MONTH ),DATE_ADD( ‘2017-01-31’, INTERVAL 2 MONTH ),DATE_ADD( ‘2017-01-31’, INTERVAL 3 MONTH );

    就可以得到和预期一样的结果

    2017-02-28
    2017-03-31
    2017-04-30

    http://lxjsmdc.com/php/get-next-month.html

  21. jack
    jack 2018-08-01

    额 其实还是很方啊,我一直以为是这样的,2.28 +1 month = 3.31 结果是3.28
    统计的时候,要判断是大小月 还要判断上个月是大小月,然后还得今天是月末,还是月初。。。。
    之前在php手册上看到 这个
    [kumar AT swatantra.info Swatantra Kumar ]
    这位同学在5 years ago的说的
    var_dump(date(“Y-m-d”, strtotime(“-1 month”, strtotime(“2017-03-31”))));
    //输出2017-03-03
    只是感觉不对劲
    这能归结为一个bug么。鸟大大 。

  22. 余志斌
    余志斌 2018-07-31

    写错日期了,修改写:实际我是想获取到前者(2018-09-12)

  23. Tonight
    Tonight 2018-07-31

    鸟哥,请问下first day of/last day of后面的关键词,在底层是如何解析的?

  24. 余志斌
    余志斌 2018-07-31

    使用first day of +1 month或者last day of +1 month,是可以获取到当前日期下个月的第一天或最后一天。

    当如果我当前日期是2018-08-12,使用+1 month,会获取到2018-09-12,使用first day of +1 month,会得到2018-09-01。实际我是想获取到前者(2018-08-12),所以这个first day of还是有些情况不能兼容到

  25. dream-fei
    dream-fei 2018-07-31

    不慌了,原来如此

  26. 白菜
    白菜 2018-07-31

    感觉还是mktime好用,这种情况还是比较特殊的。不够灵活。

  27. 侉帮子
    侉帮子 2018-07-31

    鸟哥终于更新博客了。

  28. loadinger
    loadinger 2018-07-31

    鸟哥竟然更新了…

    最近是玩农药玩得少了呀,这可不行.再说dota2,马上ti了,训练不能少~!

  29. Charles
    Charles 2018-07-31

    谢谢鸟哥回复

  30. mengkang
    mengkang 2018-07-31

    匹配这么多种情况,还有这么长的字符串,我还是用mktime吧,啥时候给博客加上微博表情包就好了。

Leave a Reply

Your email address will not be published. Required fields are marked *