#php #mysql #oracle #datetime #standards
#php #mysql #Oracle #дата и время #стандарты
Вопрос:
Я ищу стандарты для добавления даты / времени. Я не смог найти ни одного. В частности, я надеюсь найти спецификацию, которая определяет, что должно произойти, когда вы добавляете месяц к дате, например, 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
(извините за изменение формата, мой 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. В основном существует два типа адов: ад кодирования и ад даты и времени.
2. Я думаю, что один квазистандарт находится где-то в любом древнем (Unix?) библиотека определяет
strtotime()
синтаксический анализ1 week
и тому подобное. К сожалению, эта важная функция очень плохо документирована. Интересно, стоит ли открывать этот вопрос для большего количества тегов, которые просто PHP? Джон Скит мог бы добавить кое-что, он имел дело с библиотеками даты / времени в прошлом . На самом деле, я думаю, что пропингую его.3. @Pekka IIRC PHP однажды использовал gnutime() для этого, но с тех пор Дерик переписал код.
4. @Pekka В данном конкретном случае я сравниваю » 1 месяц» и DATE_ADD MySQL (временная метка, ИНТЕРВАЛ 1 МЕСЯЦ), они отличаются.
Ответ №1:
Согласно стандарту 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 января является ошибкой.
Ссылки:
- SQL-92: http://www.contrib.andrew.cmu.edu /~shadow/sql/sql1992.txt
- POSIX: http://pubs.opengroup.org/onlinepubs/9699919799 / (хотя, по-видимому, эта версия теперь относится к ISO C, что кажется не таким ясным. Хотя справочная страница mktime на моем компьютере понятна)
- ISO C: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
- Java: http://download.oracle.com/javase/6/docs/api/java/util/Calendar.html
Комментарии:
1. 1 Интересно. Я думаю, что суть этого вопроса не столько в том, чтобы найти один стандарт, сколько в том, чтобы выяснить, какие из них, вероятно, используются, и какая библиотека придерживается какой. Знаете ли вы какую-либо документацию, которая определяет поведение
POSIX.1-2001
?2. @derobert, хороший ответ. У вас есть ссылка? Стандартов примерно столько же, сколько ответов от людей на улице. Конечно, нет общепринятого стандарта, и ответы из этого стандарта POSIX такие, каких не ожидал бы ни один человек.
3. @JonathanM: добавил несколько ссылок.
4. @derobert, я надеюсь, вы не возражаете против ссылки, которую я добавил. Не стесняйтесь откатить его, если вы это сделаете.
5. Спасибо, может показаться (даже после щедрости), что проблема формально не определена вне определенных пространств, в которых задокументированы их собственные решения. Спасибо за сбор нескольких ответов и всем, кто представил дополнения к списку ссылок.
Ответ №2:
Я считаю, что стандартом defacto является 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. Другой взгляд (Маркуса Куна) на стандарты ISO 8601 здесь (). RFC 3339 и RFC 2822, оба ссылаются на twinsun.com/tz/tz-link.htm это также может иметь отношение к делу. Я думаю, что основная проблема заключается в том, что вас интересуют месяцы, а не дни или годы. Дни и годы определены астрономически, в то время как месяцы — это искусственная конструкция, зависящая от того, чьему календарю вы собираетесь следовать.
2. Первый URL-адрес, связанный в этом комментарии, отмечает: «И день, и год являются полезными единицами структурирования времени, потому что они описывают положение солнца на небе, которое влияет на нашу жизнь. Однако 12 месяцев в году имеют какое-то неясное мистическое происхождение и сегодня не имеют реальной цели, за исключением того, что люди привыкли к ним (они даже не описывают текущее положение Луны). »
Ответ №3:
Запрос:
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';
Вывод:
Декабрь месяц Январь Месяц Февраль Месяц Март Месяц 2011-01-31 2011-02-28 2011-03-28 2011-04-30
Мой вывод:
- Вычислите количество дней в месяце входной даты.
- Добавьте столько дней к вводимой дате.
- Проверьте, превышает ли день в результирующей дате максимальное количество дней в результирующем месяце.
- Если да, то измените результирующий день на максимальный день результирующего месяца.
Если вы добавляете MONTH, YEAR_MONTH или YEAR и результирующая дата имеет день, который больше максимального дня для нового месяца, день корректируется на максимальное количество дней в новом месяце
Проблема здесь в том, что в нем не упоминается, что месяц на самом деле является месяцем с даты ввода.
Комментарии:
1. 1 круто, я не знал, что руководство MySQL объясняет свою логику даты так подробно.
Ответ №4:
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. Обновление: JSR 310 был реализован как классы java.time , встроенные в Java 8 и более поздние версии. Смотрите учебник .
Ответ №5:
Первый день месяца 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. Я убрал временные части (все нули), чтобы сделать его более читаемым
Комментарии:
1. Я действительно ищу основанный на стандартах ответ на этот вопрос, а не пул мнений.
2. @preinheimer, это должен быть пул мнений, потому что, если вы зададите вопрос людям на улице, вы получите разные ответы. Они не связаны каким-либо стандартом. И поскольку программное обеспечение date должно моделировать реальные интерпретации единиц времени, программное обеспечение должно либо уточнить ожидания, либо четко определить, как оно будет интерпретировать такие фразы.
3. Как насчет со 2-го по последний день этого месяца? Что будет в следующем месяце с 29 января 2001 года? Является ли «в следующем месяце» симметричным с «в прошлом месяце»?
4. Предположительно, с несколькими органами по стандартизации, существующими для кодификации преобладающих и логически правильных мнений в стандарты. Один из них сделал это.
5. Я проголосовал против, потому что считаю, что это очень интересный вопрос, и, хотя я согласен с этим мнением, это всего лишь мнение, а не определенный набор правил, на которых можно основывать свою работу, о чем и просит OP.
Ответ №6:
Не существует общепринятого стандарта. Причина различных реализаций в том, что люди не могут договориться о том, каким должен быть стандарт. Многие популярные программные системы дают ответы, которые никто не ожидал. Поэтому всегда необходима документация, чтобы сообщить пользователю, что будет предоставлять ваша система. Однако вы выбираете методологию, основанную на том, что, по вашему мнению, ожидает большинство людей.
Я думаю, что большинство людей на улице согласятся с этим:
- Вы не можете определить «месяц» по определенному количеству дней. Итак…
- Когда 31 января и кто-то говорит: «Встретимся через месяц с сегодняшнего дня», они имеют в виду последний день февраля. По сути, это добавление к месяцу, затем найдите номер дня, который ближе всего к сегодняшнему дню, не превышая его.
Исключение составляет бухгалтерский учет, где иногда месяц означает 30 дней.
РЕДАКТИРОВАТЬ: я спросил некоторых людей здесь: «Если это 31 марта, и кто-то говорит, что я встречусь с вами через месяц с сегодняшнего дня, в какой день вы собираетесь встретиться с ними?» Большинство сказали, 30 апреля, но некоторые сказали 28 апреля, потому что до этого осталось четыре недели. Немногие интерпретировали рабочие графики и думали: «Если мы встретились в этот будний день, мы встретимся снова в тот же будний день». В принципе, если они встретились в последний четверг месяца, и они должны встретиться через месяц, это будет в последний четверг этого месяца.
Итак, поехали. :
Комментарии:
1. Downvoter, хотите прокомментировать? Помогите мне улучшить мой ответ, чтобы удалить ваш downvote.
2. Ну, на самом деле это не ответ на вопрос, не так ли? OP запрашивает определенные стандарты, на которых будет основываться его работа. Насколько я его понимаю, это не означает, что стандарт должен быть общепринятым, просто существует некоторый набор определенных правил для работы. Вы утверждаете, что таких наборов правил не существует? Я думаю, что это маловероятно.
3. @Pekka, поскольку нет способа доказать, что стандарт не существует (вы не можете доказать отрицательный результат), вам действительно следует удалить downvote, пока вы не докажете, что существует полезный, широко принятый стандарт. На этом этапе я буду рад проголосовать за себя. 🙂
4. @Jonathan M, вопрос был не о «полезном, общепринятом стандарте», это был вопрос о том, существует ли вообще какой-либо стандарт. Таким образом, ваш ответ … ну … не один.
5. @Jonathan M, я не собираюсь ничего предполагать о заинтересованности OP в удобстве использования. Если есть интерес к удобству использования, ответственность лежит на операторе, чтобы выразить свое недовольство предоставленными стандартами и явно запросить стандарт, который является более доступным. Ответьте на заданный вопрос.
Ответ №7:
Попробуйте функцию даты mysql :
ВЫБЕРИТЕ ADDDATE(‘2011-01-31’, ИНТЕРВАЛ 1 МЕСЯЦ) // 2011-02-28
Введите дату с високосным годом
ВЫБЕРИТЕ ADDDATE(‘2012-01-31’, ИНТЕРВАЛ 1 МЕСЯЦ) // 2012-02-29
Ответ №8:
В .NET framework поведение системы.Дата-время.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
*/