我正在寻找添加日期/时间的标准。我找不到任何东西。特别是我希望找到一个规范来定义当你将一个月添加到1月31日这样的日期时会发生什么。 2月28日(/ 29日)是正确答案吗? 3月1日? 3月2日?
我看到不同工具之间的实现不一致(在这种情况下是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很弱)
爪哇
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
答案 0 :(得分:11)
根据POSIX.1-2001标准,下个月(如在调用tm_mon
之前递增mktime
)是通过调整值直到它们适合来完成的。因此,例如,2001年1月31日的下个月是2001年3月3日。这是因为31的tm_mday
对于1 {2月的tm_mon
无效,所以它被标准化为tm_mon
2(3月)和tm_mday
3。
2000年1月31日的下一个月是2000年3月2日,因为2月份当年有29天。从1月开始的下个月,1 2038年不存在,具体取决于。
The great thing about standards is there are so many to chose from。检查SQL标准,我打赌你可以找到下个月的不同含义。我怀疑ISO 8601可能会给你另一个选择。重点是,有许多不同的行为,“下个月”的含义非常特定于域名。
编辑:我想我发现SQL-92如何处理它,显然是从1月31日开始要求下个月出错。
链接:
答案 1 :(得分:2)
我相信事实上的标准是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
减法不是加法的倒数。
小数部分的精度可能会有所不同。
完整规范可在http://www.iso.org/iso/catalogue_detail.htm?csnumber=26780
找到我认为每种产品都试图坚持不可能实施的标准。模棱两可的部分是开放的解释,所以每个人都解释。这是打开我们Y2K错误的标准!
我自己,我赞成将日期/时间转换为基于1970的数字(UNIX时间戳)的实现,执行计算并转换回来。我相信这是Oracle / MySQL采用的方法。令我感到惊讶的是,这个问题没有得到更多的关注,因为在很多应用程序中它非常重要,有时也很重要。谢谢你的问题!
编辑:在进行更多阅读时,我发现了Joe Celko对不同日期/时间表示和标准化HERE的想法。
答案 2 :(得分:1)
查询:
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
我的结论:
如果您添加MONTH,YEAR_MONTH或YEAR,并且结果日期的日期大于新月的最大日期,则将该日期调整为新月的最大天数
问题在于它没有提到月份实际上是输入日期的月份。
答案 3 :(得分:1)
Joda-Time选择创建无效日期的先前有效日期。例如,2011-01-31 + P1M = 2011-02-28
。我相信这是日期时间库中最广泛选择的默认选择,因此是事实上的标准。
ThreeTen/JSR-310为此提供了一种策略模式,有四种选择,请参阅code。
更有趣的是2011-01-31 + P1M-1D
的答案是什么的问题。如果你添加月份,然后解决无效日期,然后减去当天,你得到2011-02-27。但我认为大多数用户都期待2011-02-28,因为这个时期是一次性加入。了解ThreeTen如何处理此here。
我曾考虑过在日期/时间计算或实际规范中编写通用最佳实践,但还没有真正的时间!
答案 4 :(得分:0)
该月的第一天+ 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
本月最后一天+ 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-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如何成为29号(2012年是闰年)。
P.S。我把时间部分(全部为零)取下来以使其更具可读性
答案 5 :(得分:0)
没有被广泛接受的标准。不同实现的原因是人们无法就标准应该达成什么达成一致。许多流行的软件系统给出了没有人会想到的答案。因此,始终需要文档来告诉用户您的系统将提供什么。但是,您可以根据您认为大多数人们期望的内容来选择一种方法。
我认为大街上的大多数人会同意:
例外是会计,有时一个月意味着30天。
编辑:我问过这里的一些人,“如果是3月31日,有人说我会在今天一个月见到你,你将在哪一天见到他们?”大多数人说,4月30日,但有一些人说4月28日,因为距离它还有四个星期。少数人正在解释工作时间表并思考“如果我们在这个工作日见面,我们将在同一个工作日再次见面”。基本上,如果他们在本月的最后一个星期四见面,并且他们将在一个月内见面,那么它将在那个月的最后一个星期四。所以,你去吧。 :\
答案 6 :(得分:0)
尝试使用mysql日期函数:
SELECT ADDDATE('2011-01-31',INTERVAL 1 MONTH)// 2011-02-28
闰年输入日期
SELECT ADDDATE('2012-01-31',INTERVAL 1 MONTH)// 2012-02-29
答案 7 :(得分:0)
在.NET框架中,System.DateTime.AddMonths的行为如下:
AddMonths方法计算得到的月份和年份 考虑到闰年和一个月的天数,那么 调整生成的DateTime对象的日期部分。如果 结果日期不是最后一个月的有效日期 使用结果月份的有效日期。例如,3月31日+ 1 月= 4月30日[而不是4月31日]。
我已经测试了它是如何工作的:
Console.WriteLine(new DateTime(2008,2,27).AddMonths(1));
Console.WriteLine(new DateTime(2008,2,28).AddMonths(1));
Console.WriteLine(new DateTime(2008,2,29).AddMonths(1));
Console.WriteLine(new DateTime(2011,2,27).AddMonths(1));
Console.WriteLine(new DateTime(2011,2,28).AddMonths(1));
Console.WriteLine(new DateTime(2008,1,30).AddMonths(1));
Console.WriteLine(new DateTime(2008,1,31).AddMonths(1));
Console.WriteLine(new DateTime(2011,1,30).AddMonths(1));
Console.WriteLine(new DateTime(2011,1,31).AddMonths(1));
/* output
3/27/2008 12:00:00 AM
3/28/2008 12:00:00 AM
3/29/2008 12:00:00 AM
3/27/2011 12:00:00 AM
3/28/2011 12:00:00 AM
2/29/2008 12:00:00 AM
2/29/2008 12:00:00 AM
2/28/2011 12:00:00 AM
2/28/2011 12:00:00 AM
*/