Php 日期/时间添加的标准?

Php 日期/时间添加的标准?,php,mysql,oracle,datetime,standards,Php,Mysql,Oracle,Datetime,Standards,我正在寻找日期/时间添加的标准。我还没找到。我特别希望找到一个规范,定义在1月31日这样的日期上加上一个月时应该发生什么。正确答案是2月28日(/29日)?三月一日?三月二号 我看到了不同工具(本例中是PHP和MySQL)之间不一致的实现,我正试图找到某种标准来作为我工作的基础 不同的结果: PHP $end = strtotime("+1 month", 1314835200); //1317513600 Sat, 01 Oct 2011 20:00:00 -0400 MySQL SEL

我正在寻找日期/时间添加的标准。我还没找到。我特别希望找到一个规范,定义在1月31日这样的日期上加上一个月时应该发生什么。正确答案是2月28日(/29日)?三月一日?三月二号

我看到了不同工具(本例中是PHP和MySQL)之间不一致的实现,我正试图找到某种标准来作为我工作的基础

不同的结果:

PHP

$end = strtotime("+1 month", 1314835200);
//1317513600   Sat, 01 Oct 2011 20:00:00 -0400
MySQL

SELECT UNIX_TIMESTAMP(DATE_ADD(FROM_UNIXTIME(1314835200), INTERVAL 1 MONTH));
#1317427200    Fri, 30 Sep 2011 20:00:00 -0400
Oracle

SELECT ADD_MONTHS('31-Aug-11', 1) FROM dual;
#30-SEP-11
(很抱歉更改了格式,我的oracle foo很弱)

Java

Calendar c = Calendar.getInstance();
c.clear();
c.set( 2011, Calendar.AUGUST, 31 );
c.add( Calendar.MONTH, 1 );
c.getTime()
#Fri Sep 30 00:00:00 EDT 2011

每月的第一天+1个月应等于下个月的第一天。在SQL Server上尝试此操作

          SELECT CAST ('01/01/2012' AS DateTime), DATEADD (m, 1, '01/01/2012')
UNION ALL SELECT CAST ('02/01/2012' AS DateTime), DATEADD (m, 1, '02/01/2012')
UNION ALL SELECT CAST ('03/01/2012' AS DateTime), DATEADD (m, 1, '03/01/2012')
UNION ALL SELECT CAST ('04/01/2012' AS DateTime), DATEADD (m, 1, '04/01/2012')
UNION ALL SELECT CAST ('05/01/2012' AS DateTime), DATEADD (m, 1, '05/01/2012')
这导致

----------------------- -----------------------
2012-01-01              2012-02-01             
2012-02-01              2012-03-01             
2012-03-01              2012-04-01             
2012-04-01              2012-05-01             
2012-05-01              2012-06-01             
----------------------- -----------------------
2012-01-31              2012-02-29             
2012-01-30              2012-02-29             
2012-01-29              2012-02-29             
2012-01-28              2012-02-28             
2012-01-27              2012-02-27             
2012-01-26              2012-02-26             
本月最后一天+1个月应等于下个月最后一天。这应该适用于下个月、本月、10个月等等

          SELECT CAST ('01/31/2012' AS DateTime), DATEADD (m, 1, '01/31/2012')
UNION ALL SELECT CAST ('01/30/2012' AS DateTime), DATEADD (m, 1, '01/30/2012')
UNION ALL SELECT CAST ('01/29/2012' AS DateTime), DATEADD (m, 1, '01/29/2012')
UNION ALL SELECT CAST ('01/28/2012' AS DateTime), DATEADD (m, 1, '01/28/2012')
UNION ALL SELECT CAST ('01/27/2012' AS DateTime), DATEADD (m, 1, '01/27/2012')
UNION ALL SELECT CAST ('01/26/2012' AS DateTime), DATEADD (m, 1, '01/26/2012')
这导致

----------------------- -----------------------
2012-01-01              2012-02-01             
2012-02-01              2012-03-01             
2012-03-01              2012-04-01             
2012-04-01              2012-05-01             
2012-05-01              2012-06-01             
----------------------- -----------------------
2012-01-31              2012-02-29             
2012-01-30              2012-02-29             
2012-01-29              2012-02-29             
2012-01-28              2012-02-28             
2012-01-27              2012-02-27             
2012-01-26              2012-02-26             
看看31、30、29都变成了2月29日(2012年是闰年)


p、 我去掉了时间部分(全部为零)以帮助它更具可读性

没有广泛接受的标准。不同实现的原因是人们不能就标准应该是什么达成一致。许多流行的软件系统给出了没有人会想到的答案。因此,要告诉用户您的系统将提供什么,文档总是必要的。然而,你选择的方法是基于你认为大多数人会期望的

我想大多数街上的人都会同意:

  • 你不能用一定的天数来定义“月”。所以
  • 当是1月31日,有人说“从今天起一个月后见”,他们指的是2月的最后一天。基本上,它是添加到月份中,然后找到最接近今天的天数而不超过它
  • 会计方面例外,有时一个月意味着30天

    编辑:我问过这里的一些人,“如果是3月31日,有人说我从今天起一个月后见你,你打算在哪一天见他们?”大多数人说是4月30日,但也有少数人说是4月28日,因为还有四周。少数几个人在解释工作日程,并想“如果我们在这个工作日见面,我们将在同一个工作日再次见面”。基本上,如果他们在这个月的最后一个星期四会面,并且他们预定在一个月后会面,那么他们将在那个月的最后一个星期四会面


    那么,就这样吧\

    根据POSIX.1-2001标准,下个月(在调用
    mktime
    之前增加
    tm_mon
    )是通过调整值来完成的,直到它们合适为止。例如,从2001年1月31日起的下个月是2001年3月3日。这是因为31的
    tmu-mday
    对于1(2月)的
    tmu-mon
    无效,因此它被标准化为2(3月)的
    tmu-mon
    和3的
    tmu-mday

    从2000年1月31日起的下一个月是2000年3月2日,因为2月在那一年有29天。从1月开始的下一个月,2038不存在,这取决于

    。检查SQL标准,我打赌你可以找到下个月的不同含义。我怀疑ISO8601可能会给你另一个选择。重点是,有许多不同的行为,“下个月”的含义是非常特定于领域的

    编辑:我想我已经发现了SQL-92是如何处理它的,显然要求从1月31日开始的下个月是一个错误

    链接:

    • SQL-92:
    • POSIX:(虽然显然该版本现在遵从ISO C,但它似乎不那么清晰。尽管我机器上的mktime手册页很清晰)
    • ISO C:
    • 爪哇:
    查询:

    SELECT
    ADDDATE(DATE('2010-12-31'), INTERVAL 1 MONTH) 'Dec + Month',
    ADDDATE(DATE('2011-01-31'), INTERVAL 1 MONTH) 'Jan + Month',
    ADDDATE(DATE('2011-02-28'), INTERVAL 1 MONTH) 'Feb + Month',
    ADDDATE(DATE('2011-03-31'), INTERVAL 1 MONTH) 'Mar + Month';
    
    输出:

    Dec + Month Jan + Month Feb + Month Mar + Month 2011-01-31 2011-02-28 2011-03-28 2011-04-30 十二月+一月+二月+三月+月份 2011-01-31 2011-02-28 2011-03-28 2011-04-30 我的结论是:

  • 计算输入日期当月的天数
  • 将这几天添加到输入日期
  • 检查结果日期中的日期是否超过结果月份中的最大天数
  • 如果是,则将结果日期更改为结果月份的最大日期
  • 如果添加月、年、月或年,并且结果日期的天数大于新月份的最大天数,则该日期将调整为新月份的最大天数

    这里的问题是,它没有提到月份实际上是从输入日期算起的月份。

    在Java中,当创建无效日期时,选择上一个有效日期。例如,
    2011-01-31+P1M=2011-02-28
    。我相信这是日期时间库中最广泛选择的默认选择,因此是事实上的标准

    为此提供了一种策略模式,有四种选择,请参见

    更有趣的问题是
    2011-01-31+P1M-1D
    的答案是什么。如果你加上月份,然后解决无效的日期,然后减去日期,你得到2011-02-27。但我认为大多数用户都期望2011-02-28,因为这段时间是一次性增加的。看看ThreeTen是怎么处理的


    我曾考虑尝试编写一个通用的日期/时间计算最佳实践,或实际的规范,但实际上没有时间

    试试mysql日期函数:

    选择ADDDATE('2011-01-31',间隔1个月)//2011-02-28

    输入带有闰年的日期


    选择ADDDATE('2012-01-31',间隔1个月)//2012-02-29

    我相信事实上的标准是ISO 8601。不幸的是,存在许多歧义,例如:

    未定义日期算术

    2001-03-30 + P1M = 2001-04-29 (Add 30 days)
    2001-03-30 + P1M = 2001-04-30 (Add 1 mon.)
    
    加法不是交换的或结合的

    2001-03-30 + P1D + P1M = 2001-04-30
    2001-03-30 + P1M + P1D = 2001-05-01
    
    减法不是加法的倒数

    小数的精度可能会有所不同

    完整规格可在以下网址找到:

    我认为每种产品都试图遵守一个不可能实现的标准。模棱两可的部分可以解释,因此每个人都可以解释。这是同一个标准,打开了我们对千年虫

    我喜欢我自己