Стандарты для добавления даты / времени? - PullRequest
15 голосов
/ 30 сентября 2011

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

Oracle

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

(извините за изменение формата, мой оракул фу слаб)

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

Ответы [ 8 ]

11 голосов
/ 30 сентября 2011

В соответствии со стандартом 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 может дать вам еще один выбор.Дело в том, что существует множество различных способов поведения, значение «следующий месяц» очень сильно зависит от предметной области.

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

Ссылки:

2 голосов
/ 07 октября 2011

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

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

1 голос
/ 02 октября 2011

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 голос
/ 30 сентября 2011

Запрос:

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. Если да, тоизмените итоговый день на максимальный день результирующего месяца.

Если вы добавляете MONTH, YEAR_MONTH или YEAR, и полученная дата имеет день, который больше максимального дня для новогомесяц, день корректируется на максимальное количество дней в новом месяце

source

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

0 голосов
/ 08 октября 2011

В .NET Framework поведение System.DateTime.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 голосов
/ 05 октября 2011

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

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

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

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

0 голосов
/ 30 сентября 2011

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

Я думаю, что большинство людей на улице согласятся, что:не может определить «месяц» на определенное количество дней.Так что ...

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

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

Итак,вот и ты.: \

0 голосов
/ 30 сентября 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             

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

...