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来的更加优雅.

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

94 Comments

  1. chenweijie
    chenweijie 2018-11-26

    稳!

  2. 安满
    安满 2018-11-24

    鸟哥分析的很清晰易懂

  3. fatming
    fatming 2018-11-21

    还是很慌怎么办….
    既然正常人都觉得 10-31 的 *上一个月*不应该还是10月份,
    那为什么不把默认值就设置为9月30号呢. 就算有短语可以修正,真的一点都不优雅, 而且很拗口=.=

  4. noone
    noone 2018-11-20

    漂亮

  5. AlicFeng
    AlicFeng 2018-11-17

    nice👍👍

  6. avrilko
    avrilko 2018-11-16

    学习了

  7. 三顿饭
    三顿饭 2018-11-14

    鸟哥。正文的字体太细了,能否改粗点

  8. showbox
    showbox 2018-11-06

    经常会有人被strtotime结合

  9. van23qf
    van23qf 2018-11-05

    还有这种操作,平时都没注意到。。。

  10. yun
    yun 2018-10-29

    学习了

  11. qwqw
    qwqw 2018-10-22

    这个问题不应该用鸟哥最后讲的方法解决,还是应该在函数内部解决

  12. straiway
    straiway 2018-10-20

    之前遇到过这样的情况,也觉得strtotime不靠谱,但是没深究。感谢鸟哥,看来遇到问题最彻底的办法就是看源码😆

  13. DusNoob
    DusNoob 2018-10-18

    涨姿势了,做项目一直都没注意到

  14. annon
    annon 2018-10-15

    鸟哥好棒

    • yang
      yang 2018-11-15

      nice

      • yang
        yang 2018-11-15

        1

  15. Venus Zhu
    Venus Zhu 2018-09-30

    只有我觉得右侧新浪添加关注的插件会挡住昵称而别扭嘛?!逼死强迫症。谷歌浏览器 版本 69.0.3497.100(正式版本) (64 位)

  16. 2s
    2s 2018-09-28

    不慌不慌

  17. jian.liu
    jian.liu 2018-09-27

    简单的问题复杂化,复杂的问题简单化!

  18. zhebie
    zhebie 2018-09-26

    通俗易懂,厉害厉害,我是转行过来做的php,现在会点python和golang,一入编程深似海,感觉学不完,随便哪个语言。。。

  19. Liu zhi peng
    Liu zhi peng 2018-09-26

    我刚刚百度完鸟哥的个人信息,好厉害,以后我们就叫你做大哥,你就叫我们练功夫

  20. bunsen zhou
    bunsen zhou 2018-09-25

    还是挺好理解的,官网文档看仔细点就不会乱了

  21. Donne
    Donne 2018-09-14

    一般获得「上个月的今天,当这天不存在就取上月的最后一天」,是更「亲人」的描述方式吧,这种逻辑就没办法很简单的一个 strtotime 去处理了。

  22. JackMa
    JackMa 2018-09-12

    涨姿势了

  23. Lanffy
    Lanffy 2018-09-05

    向大佬学习,这以前还真不知道。哈哈

  24. king
    king 2018-08-31

    这是大佬时间长没写过业务了么? 为什么我搞清楚了反倒是感到这个函数好不靠谱? strtotime一点都不php风格,我现在有需求要求查询用户上个月的今天到今天一共做了某某个事件,请问怎么用一句strtotime解决???

    • za
      za 2018-08-31

      取数据的时候,取这俩个时间的unix时间戳之间的对应的数据,count一下,不行嘛?

      • pp
        pp 2018-10-02

        取数据的时候,取这俩个时间的unix时间戳之间的对应的数据,count一下,不行嘛?

    • ********
      ******** 2018-10-17

      echo date(“Y-m-d H:i:s”, strtotime(date(‘Y-m’)));

      看下这个日期是不是你要的结果

  25. charlie
    charlie 2018-08-31

    慌而不乱

Leave a Reply

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