Стандарты для добавления даты/времени?


Я ищу стандарты для добавления даты/времени. Я не смог найти ни одного. В частности, я надеюсь найти спецификацию, которая определяет, что должно произойти, когда вы добавляете месяц к такой дате, как 31 января. Правильный ли ответ 28 февраля (/29-го)? 1 марта? 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

Оракул

SELECT ADD_MONTHS('31-Aug-11', 1) FROM dual;
#30-SEP-11

(извините за изменение формата, мой оракул 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
Author: preinheimer, 2011-09-30

8 answers

В соответствии со стандартом POSIX.1-2001, в следующем месяце (как при увеличении tm_mon перед вызовом mktime) выполняется корректировка значений до тех пор, пока они не будут соответствовать. Так, например, следующий месяц с 31 января 2001 года - 3 марта 2001 года. Это связано с тем, что tm_mday из 31 недопустимо с tm_mon 1 (февраль), поэтому оно нормализуется до tm_mon 2 (март) и tm_mday 3.

Следующий месяц с 31 января 2000 года - 2 марта 2000 года, потому что февраль. в этом году у него 29 дней. В следующем месяце с 1 января 2038 года не существует, в зависимости от обстоятельств.

Самое замечательное в стандартах то, что их так много, из чего можно выбрать. Проверьте стандарт SQL, держу пари, вы сможете найти другое значение следующего месяца. Я подозреваю, что ISO 8601 может дать вам еще один выбор. Дело в том, что существует много различных вариантов поведения, значение "в следующем месяце" очень специфично для конкретной области.

Редактировать: Я думаю, что Я нашел, как SQL-92 справляется с этим, очевидно, запрос на следующий месяц с 31 января является ошибка.

Ссылки:

 11
Author: derobert, 2011-09-30 20:45:45

Я полагаю, что стандартом де-факто является 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. Я удивлен, что этому вопросу не уделялось больше внимания, так как это действительно важно, иногда критически важно во многих приложениях. Спасибо за вопрос!

Редактировать: Читая еще немного, я нашел мысли Джо Селко о различных представлениях даты/времени и стандартизации ЗДЕСЬ.

 2
Author: Sinthia V, 2011-10-06 20:40:39

Запрос:

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

Мой вывод:

  1. Вычислите количество дней в месяце входной даты.
  2. Добавьте это количество дней к дате ввода.
  3. Проверьте, превышает ли день в результирующей дате максимальное количество дней в результирующем месяце.
  4. Если да, то измените итоговый день на максимальный день итогового месяца.

Если вы добавляете МЕСЯЦ, ГОД_МЕСЯЦ или ГОД, и полученная дата имеет день, который больше максимального дня для нового месяца, настраивается на максимальное количество дней в новом месяце

Источник

Проблема здесь в том, что в нем не упоминается, что месяц на самом деле является месяцем с даты ввода.

 1
Author: Bhesh Gurung, 2011-09-30 19:20:57

Joda-Time в Java выбирает предыдущую действительную дату при создании недопустимой. Например, 2011-01-31 + P1M = 2011-02-28. Я считаю, что это наиболее широко используемый вариант по умолчанию в библиотеках даты и времени и, следовательно, стандарт де-факто.

ThreeTen/JSR-310 предоставляет шаблон стратегии для этого с четырьмя вариантами, см. Код .

Более забавным является вопрос о том, каков ответ на 2011-01-31 + P1M-1D. Если вы добавляете месяц, затем устраняете недопустимую дату, а затем вычитаете день, вы получаете 2011-02-27. Но я думаю, что большинство пользователей ожидают 2011-02-28, потому что период добавляется одним куском. Посмотрите, как ThreeTen справляется с этим здесь.

Я подумывал о том, чтобы попытаться написать рекомендации общего назначения по вычислению даты/времени или фактической спецификации, но на самом деле у меня не было времени!

 1
Author: JodaStephen, 2011-10-02 00:11:37

Первый день месяца + 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. Я снял части времени (все нули), чтобы помочь сделать его более читаемый

 0
Author: Raj More, 2011-09-30 19:55:59

Общепринятого стандарта не существует. Причина различных реализаций заключается в том, что люди не могут договориться о том, каким должен быть стандарт. Многие популярные программные системы дают ответы, которых никто не ожидал. Поэтому документация всегда необходима, чтобы сообщить пользователю, что будет предоставлять ваша система. Однако вы выбираете методологию, основанную на том, что, по вашему мнению, ожидает большинство людей.

Я думаю, что большинство людей на улице согласятся с тем, что:

  1. Ты не можешь определите "месяц" по определенному количеству дней. Итак...
  2. Когда сегодня 31 января и кто-то говорит: "Встретимся через месяц", они имеют в виду последний день февраля. По сути, это добавление к месяцу, а затем поиск числа дня, которое ближе всего к сегодняшнему дню, не превышая его.

Исключение составляет бухгалтерский учет, где иногда месяц означает 30 дней.

РЕДАКТИРОВАТЬ: Я спросил некоторых людей здесь: "Если сегодня 31 марта, и кто-то скажет, что я встречусь с вами через месяц, в какой день ты собираешься встретиться с ними?" Большинство сказали, 30 апреля, но некоторые сказали 28 апреля, потому что до этого еще четыре недели. Некоторые из них интерпретировали рабочие графики и думали: "Если мы встретились в этот будний день, мы встретимся снова в тот же будний день". В принципе, если они встретились в последний четверг месяца, и они должны встретиться через месяц, это будет в последний четверг этого месяца.

Итак, вот и все.:\

 0
Author: Jonathan M, 2011-09-30 20:06:30

Попробуйте функцию даты mysql:

ВЫБЕРИТЕ ДАТУ ДОБАВЛЕНИЯ ('2011-01-31', ИНТЕРВАЛ 1 МЕСЯЦ)//2011-02-28

Дата ввода с високосным годом

ВЫБЕРИТЕ ДАТУ ДОБАВЛЕНИЯ ('2012-01-31', ИНТЕРВАЛ 1 МЕСЯЦ)//2012-02-29

 0
Author: nilesh1006, 2011-10-05 10:03:23

В.СЕТЕВАЯ структура поведения системы .Дата и время.AddMonths выглядит следующим образом:

Метод AddMonths вычисляет результирующий месяц и год с учетом високосных лет и количества дней в месяце, затем корректирует дневную часть результирующего объекта DateTime. Если результирующий день не является допустимым днем в результирующем месяце, используется последний допустимый день результирующего месяца. Например, 31 марта + 1 месяц = 30 апреля [вместо апреля 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
    */
 0
Author: Peter O., 2011-10-07 22:54:36