简体   繁体   English

日期/时间添加标准?

[英]Standards for Date/Time addition?

I'm looking for standards for Date/Time addition. 我正在寻找添加日期/时间的标准。 I haven't been able to find any. 我找不到任何东西。 In particular I'm hoping to find a spec that defines what should happen when you add a month to a date like January 31st. 特别是我希望找到一个规范来定义当你将一个月添加到1月31日这样的日期时会发生什么。 Is the right answer February 28th(/29th)? 2月28日(/ 29日)是正确答案吗? March 1st? 3月1日? March 2nd? 3月2日?

I've seen inconsistent implementations between different tools (PHP & MySQL in this case), and I'm trying to find some sort of standards to base my work on. 我已经看到不同工具(在这种情况下是PHP和MySQL)之间的不一致实现,我正在尝试找到一些基于我的工作的标准。

Differing Results: 不同的结果:

PHP PHP

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

MySQL 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

(sorry for the format change, my oracle foo is weak) (抱歉格式改变,我的oracle foo很弱)

Java 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

According to the POSIX.1-2001 standard, next month (as in incrementing tm_mon before calling mktime ) is done by adjusting the values until they fit. 根据POSIX.1-2001标准,下个月(如在调用mktime之前递增tm_mon )是通过调整值直到它们适合来完成的。 So, for example, next month from January 31, 2001 is March 3, 2001. This is because the tm_mday of 31 isn't valid with tm_mon of 1 (February), so it is normalized to tm_mon of 2 (March) and tm_mday of 3. 因此,例如,2001年1月31日的下个月是2001年3月3日。这是因为tm_mday 31对于tm_mon为1(二月)无效,因此它被标准化为tm_mon of 2(March)和tm_mday 3。

The next month from January 31, 2000 is March 2, 2000, because Feb. has 29 days that year. 2000年1月31日的下个月是2000年3月2日,因为2月份当年有29天。 The next month from January, 1 2038 doesn't exist, depending. 从1月开始的下个月,1 2038年不存在,具体取决于。

The great thing about standards is there are so many to chose from . 关于标准的好处是有很多选择 Check the SQL standard, I bet you can find a different meaning of next month. 检查SQL标准,我打赌你可以找到下个月的不同含义。 I suspect ISO 8601 may give you yet another choice. 我怀疑ISO 8601可能会给你另一个选择。 Point is, there are many different behaviors, the meaning of 'next month' is very domain-specific. 重点是,有许多不同的行为,“下个月”的含义是特定于域的。

edit: I think I've found how SQL-92 handles it, apparently asking for next month from January 31 is an error. 编辑:我我已经找到了SQL-92如何处理它,显然从1月31日开始要求下个月是一个错误。

Links: 链接:

I believe the defacto standard is ISO 8601. Unfortunately, there are many ambiguities, for example: 我相信事实上的标准是ISO 8601.不幸的是,有很多含糊之处,例如:

Date arithmetic is not defined 未定义日期算术

2001-03-30 + P1M = 2001-04-29 (Add 30 days)
2001-03-30 + P1M = 2001-04-30 (Add 1 mon.)

Addition is not commutative or associative 加法不是可交换的或关联的

2001-03-30 + P1D + P1M = 2001-04-30
2001-03-30 + P1M + P1D = 2001-05-01

Subtraction is not the inverse of Addition. 减法不是加法的倒数。

Precision of decimal fractions can vary. 小数部分的精度可以变化。

The full specification can be found at http://www.iso.org/iso/catalogue_detail.htm?csnumber=26780 完整规范可在http://www.iso.org/iso/catalogue_detail.htm?csnumber=26780找到

I think each product is attempting to adhere to an impossible to implement standard. 我认为每种产品都试图坚持不可能实施的标准。 The ambiguous parts are open to interpretation and so everyone interprets. 模棱两可的部分是开放的解释,所以每个人都解释。 This is the same standard that opened us up to the Y2K bug!! 这是打开我们Y2K错误的标准!

Myself, I favor an implementation that converts a date/time to a 1970 based number (UNIX timestamp), performs the calculation and converts back. 我自己,我赞成将日期/时间转换为基于1970的数字(UNIX时间戳)的实现,执行计算并转换回来。 I believe this is the approach taken by Oracle/MySQL. 我相信这是Oracle / MySQL采用的方法。 I am surprised that more attention has not been paid this issue, as it is really important, sometimes critical, in so many applications. 令我感到惊讶的是,这个问题没有得到更多的关注,因为在很多应用程序中它非常重要,有时也很重要。 Thanks for the question! 谢谢你的提问!

Edit: While doing some more reading, I found Joe Celko's thoughts on different date/time representations and standardization HERE . 编辑:在做一些更多的阅读中,我发现了乔·塞科的思想在不同的日期/时间表示,标准化这里

Query: 查询:

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';

Output: 输出:

Dec + Month  Jan + Month  Feb + Month   Mar + Month
    2011-01-31   2011-02-28   2011-03-28    2011-04-30

My conclusion: 我的结论:

  1. Calculate the number of days in the month of the input date. 计算输入日期的月份天数。
  2. Add that many days to the input date. 将这么多天添加到输入日期。
  3. Check if the day in the resulting date exceeds the maximun number of days in the resulting month. 检查生成日期中的日期是否超过结果月份中的最大天数。
  4. If yes, then change the resulting day to maximum day of the resulting month. 如果是,则将结果日期更改为结果月份的最大日期。

If you add MONTH, YEAR_MONTH, or YEAR and the resulting date has a day that is larger than the maximum day for the new month, the day is adjusted to the maximum days in the new month 如果您添加MONTH,YEAR_MONTH或YEAR,并且结果日期的日期大于新月的最大日期,则将该日期调整为新月份的最大天数

source 资源

Problem here is that it doesn't mention that the month is actually the month from the input date. 这里的问题是它没有提到月份实际上是从输入日期开始的月份。

Joda-Time in Java chooses the previous valid date when an invalid one is created. Java中的Joda-Time在创建无效日期时选择上一个有效日期。 For example, 2011-01-31 + P1M = 2011-02-28 . 例如, 2011-01-31 + P1M = 2011-02-28 I believe this is the most widely chosen default choice in date-time libraries, and thus a de facto standard. 我相信这是日期时间库中最广泛选择的默认选择,因此是事实上的标准。

ThreeTen/JSR-310 provides a strategy pattern for this, with four choices, see the code . ThreeTen / JSR-310为此提供了一种策略模式,有四种选择,请参阅代码

More amusing is the question of what the answer to 2011-01-31 + P1M-1D is. 更有趣的是2011-01-31 + P1M-1D的答案是什么。 If you add the month, then resolve the invalid date, then subtract the day, you get 2011-02-27. 如果你添加月份,然后解决无效日期,然后减去当天,你得到2011-02-27。 But I think most users expect 2011-02-28 because the period is being added in a single lump. 但我认为大多数用户都期待2011-02-28,因为这个时期是一次性加入。 See how ThreeTen handles this here . 了解ThreeTen如何处理此问题

I have considered trying to write a general purpose best practices in date/time calculations, or actual spec, but haven't really had the time! 我曾考虑过在日期/时间计算或实际规范中编写通用最佳实践,但还没有真正的时间!

First day of the month + 1 month should equal the first of the next month. 该月的第一天+ 1个月应该等于下个月的第一天。 Trying this on SQL Server 在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')

This results in 这导致了

----------------------- -----------------------
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             

Last day of this month + 1 month should equal last day of next month. 本月的最后一天+ 1个月应该等于下个月的最后一天。 This should go for next month, current month, 10 months down, etc. 这应该是下个月,当月,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')

This results in 这导致了

----------------------- -----------------------
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             

See how 31, 30, 29 all become feb 29 (2012 is a leap year). 看看31,30,29如何成为29号(2012年是闰年)。

ps I took off the time parts (all zeroes) to help make it more readable ps我取消了时间部分(全零)以帮助使其更具可读性

There is no widely accepted standard. 没有被广泛接受的标准。 The reason for the different implementations is that folks can't agree on what the standard should be. 不同实现的原因是人们无法就标准应该达成什么达成一致。 Many popular software systems give answers that no one would expect. 许多流行的软件系统给出了没有人会想到的答案。 Documentation is always necessary, therefore, to tell the user what your system will deliver. 因此,始终需要文档来告诉用户您的系统将提供什么。 You choose a methodology, however, based on what you think most folks will expect. 然而,您可以根据您认为大多数人的期望来选择一种方法。

I think most people on the street would agree that: 我想街上的大多数人会同意:

  1. You can't define a 'month' by a certain number of days. 您无法按特定天数定义“月份”。 So... 所以...
  2. When it's Jan 31 and someone says "I'll meet you a month from today", they mean the last day of February. 当它是1月31日,有人说“我会从今天起一个月见到你”,他们指的是二月的最后一天。 Basically, it's add to the month, then find the day number that is closest to today without exceeding it. 基本上,它是添加到月份,然后找到最接近今天的日期数字而不超过它。

The exception comes in accounting, where sometimes a month means 30 days. 会计中有例外,有时一个月意味着30天。

EDIT: I asked some folks around here, "If it's March 31 and someone says I'll meet you a month from today, what day are you going to meet them?" 编辑:我问过这里的一些人,“如果是3月31日,有人说我会在今天一个月见到你,你将在哪一天见到他们?” Most said, April 30, but a few said April 28 because it's four weeks away. 大多数人说,4月30日,但有一些人说4月28日,因为距离它还有四个星期。 The few were interpreting work schedules and thinking "if we met on this weekday, we'll meet again on the same weekday". 少数人正在解释工作时间表并思考“如果我们在这个工作日见面,我们将在同一个工作日再次见面”。 Basically, if they met on the last Thursday of the month, and they're due to meet in one month, it'll be on the last Thursday of that month. 基本上,如果他们在本月的最后一个星期四见面,并且他们将在一个月内见面,那么它将在那个月的最后一个星期四举行。

So, there ya go. 所以,你去吧。 :\\ :\\

Try the mysql date function : 试试mysql日期函数:

SELECT ADDDATE('2011-01-31', INTERVAL 1 MONTH) // 2011-02-28 SELECT ADDDATE('2011-01-31',INTERVAL 1 MONTH)// 2011-02-28

Input date with leap year 输入日期与闰年

SELECT ADDDATE('2012-01-31', INTERVAL 1 MONTH) // 2012-02-29 SELECT ADDDATE('2012-01-31',INTERVAL 1 MONTH)// 2012-02-29

In the .NET framework the behavior of System.DateTime.AddMonths is as follows: 在.NET框架中, System.DateTime.AddMonths的行为如下:

The AddMonths method calculates the resulting month and year, taking into account leap years and the number of days in a month, then adjusts the day part of the resulting DateTime object. AddMonths方法计算结果的月份和年份,考虑闰年和一个月中的天数,然后调整生成的DateTime对象的日期部分。 If the resulting day is not a valid day in the resulting month, the last valid day of the resulting month is used. 如果结果日期不是结果月份中的有效日期,则使用结果月份的最后一个有效日期。 For example, March 31st + 1 month = April 30th [rather than April 31st]. 例如,3月31日+ 1个月= 4月30日[而不是4月31日]。

I've tested how it works exactly: 我已经测试了它是如何工作的:

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
    */

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM