Я много работал с, DateTime class
и недавно столкнулся с ошибкой при добавлении месяцев. После небольшого исследования выяснилось, что это не ошибка, а работает как задумано. Согласно документации, найденной здесь :
Пример # 2 Будьте осторожны при добавлении или вычитании месяцев
<?php
$date = new DateTime('2000-12-31');
$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
?>
The above example will output: 2001-01-31 2001-03-03
Может ли кто-нибудь объяснить, почему это не считается ошибкой?
Кроме того, есть ли у кого-нибудь изящные решения, чтобы исправить проблему и сделать так, чтобы +1 месяц работал так, как ожидалось, а не как предполагалось?
strtotime()
stackoverflow.com/questions/7119777/…Ответы:
Почему это не ошибка:
Текущее поведение правильное. Внутри происходит следующее:
+1 month
увеличивает номер месяца (первоначально 1) на единицу. Это делает дату2010-02-31
.Во втором месяце (февраль) в 2010 году всего 28 дней, поэтому PHP автоматически исправляет это, просто продолжая считать дни с 1 февраля. Затем вы заканчиваете 3 марта.
Как получить желаемое:
Чтобы получить то, что вы хотите: вручную проверьте следующий месяц. Затем добавьте количество дней в следующем месяце.
Надеюсь, вы сами сможете это написать. Я просто говорю, что делать.
Способ PHP 5.3:
Чтобы добиться правильного поведения, вы можете использовать одну из новых функций PHP 5.3, которая вводит раздел относительного времени
first day of
. Эта строфа может быть использована в сочетании сnext month
,fifth month
или+8 months
пойти в первый день указанного месяца. Вместо того,+1 month
что вы делаете, вы можете использовать этот код, чтобы получить первый день следующего месяца следующим образом:Этот скрипт будет правильно выводить
February
. Когда PHP обрабатывает этуfirst day of next month
строфу, происходят следующие вещи :next month
увеличивает номер месяца (первоначально 1) на единицу. Это делает дату 31.02.2010.first day of
устанавливает номер дня равным1
, в результате чего получается дата 01.02.2010.источник
Вот еще одно компактное решение, полностью использующее методы DateTime, изменяющее объект на месте без создания клонов.
Он выводит:
источник
$dt->modify()->modify()
. Так же хорошо работает.Это может быть полезно:
источник
$dateTime->modify('first day of next month')->modify('-1day')
Мое решение проблемы:
источник
Я согласен с мнением OP о том, что это противоречит интуиции и разочаровывает, но также определяет, что
+1 month
означает в сценариях, где это происходит. Рассмотрим эти примеры:Вы начинаете с 2015-01-31 и хотите добавить месяц 6 раз, чтобы получить цикл планирования для отправки информационного бюллетеня по электронной почте. Учитывая первоначальные ожидания OP, это вернется:
Сразу же обратите внимание, что мы ожидаем
+1 month
иметь в видуlast day of month
или, альтернативно, добавить 1 месяц за итерацию, но всегда со ссылкой на начальную точку. Вместо того, чтобы интерпретировать это как «последний день месяца», мы могли бы прочитать его как «31-й день следующего месяца или последний доступный в этом месяце». Это означает, что мы переходим с 30 апреля на 31 мая, а не на 30 мая. Обратите внимание, что это не потому, что это «последний день месяца», а потому, что мы хотим «ближайший доступный к дате начала месяца».Итак, предположим, что один из наших пользователей подписывается на другой информационный бюллетень, который начнется 30 января 2015 года. Для чего нужна интуитивная дата
+1 month
? Одна интерпретация будет «30-е число следующего месяца или ближайший доступный», который вернет:Это было бы хорошо, кроме случаев, когда наш пользователь получает обе рассылки в один и тот же день. Предположим, что это проблема со стороны предложения, а не со стороны спроса. Мы не беспокоимся о том, что пользователя раздражает получение 2 информационных бюллетеней в один и тот же день, а вместо этого наши почтовые серверы не могут позволить себе пропускную способность для отправки в два раза больше. много информационных бюллетеней. Имея это в виду, мы возвращаемся к другой интерпретации «+1 месяц» как «отправлять со второго по последний день каждого месяца», которая вернется:
Теперь мы избежали какого-либо совпадения с первым набором, но мы также закончили апрель и 29 июня, что, безусловно, соответствует нашей первоначальной интуиции, которая
+1 month
просто должна вернутьсяm/$d/Y
или привлекательной и простойm/30/Y
для всех возможных месяцев. Итак, теперь давайте рассмотрим третью интерпретацию+1 month
использования обеих дат:31 января
30 января
Вышесказанное имеет некоторые проблемы. Февраль пропускается, что может быть проблемой как при окончании предложения (скажем, если есть ежемесячное распределение пропускной способности и февраль тратится впустую, а март удваивается), так и при окончании спроса (пользователи чувствуют себя обманутыми после февраля и воспринимают дополнительный мартовский период). как попытка исправить ошибку). С другой стороны, обратите внимание, что установлены две даты:
Учитывая последние два набора, было бы несложно просто откатить одну из дат, если она выпадет за пределы фактического следующего месяца (поэтому откатитесь к 28 февраля и 30 апреля в первом наборе) и не потерять сон в течение случайное перекрытие и отклонение от модели «последний день месяца» и «второй и последний день месяца». Но ожидание, что библиотека будет выбирать между «самым красивым / естественным», «математической интерпретацией переполнения 02/31 и других месяцев» и «относительно первого или последнего месяца», всегда заканчивается тем, что чьи-то ожидания не оправдываются и какой-то график, требующий корректировки «неправильной» даты, чтобы избежать реальной проблемы, связанной с «неправильной» интерпретацией.
Итак, опять же, хотя я также ожидал,
+1 month
что верну дату, которая на самом деле будет в следующем месяце, это не так просто, как интуиция, и, учитывая выбор, использование математики вместо ожиданий веб-разработчиков, вероятно, является безопасным выбором.Вот альтернативное решение, которое по-прежнему неуклюже, но, на мой взгляд, дает хорошие результаты:
Это не оптимально, но основная логика такова: если добавление 1 месяца приводит к дате, отличной от ожидаемой в следующем месяце, отбросьте эту дату и вместо этого добавьте 4 недели. Вот результаты с двумя датами тестирования:
31 января
30 января
(Мой код беспорядочный и не будет работать в многолетнем сценарии. Я приветствую всех, кто переписывает решение с более элегантным кодом, пока основная предпосылка остается неизменной, т.е. если +1 месяц возвращает фанковую дату, используйте +4 недели вместо этого.)
источник
Я создал функцию, которая возвращает DateInterval, чтобы убедиться, что добавление месяца показывает следующий месяц и удаляет дни после него.
источник
В сочетании с ответом Шамиттомара это могло быть для добавления месяцев «безопасно»:
источник
Я нашел более короткий способ обойти это, используя следующий код:
источник
Вот реализация улучшенной версии ответа Джуханы на связанный вопрос:
Эта версия исходит
$createdDate
из предположения, что вы имеете дело с повторяющимся ежемесячным периодом, например подпиской, который начался в определенную дату, например 31-го числа. Всегда требуется$createdDate
так поздно, что даты повторения не будут сдвигаться к более низким значениям, поскольку они продвигаются вперед на менее важные месяцы (например, так, что все 29-е, 30-е или 31-е повторяющиеся даты в конечном итоге не застрянут 28-го числа после прохождения через февраль невисокосного года).Вот код драйвера для проверки алгоритма:
Какие выходы:
источник
Это улучшенная версия ответа Касихаси на связанный вопрос. Это позволит правильно добавить или вычесть произвольное количество месяцев к дате.
Например:
выведет:
источник
Если вы просто хотите избежать пропуска месяца, вы можете выполнить что-то вроде этого, чтобы получить дату и запустить цикл в следующем месяце, уменьшив дату на единицу и перепроверив ее до правильной даты, где $ start_calculated - допустимая строка для strtotime (т.е. mysql datetime или «сейчас»). Таким образом, самый конец месяца находится от 1 минуты до полуночи, вместо того, чтобы пропускать месяц.
источник
Расширение для класса DateTime, решающее задачу добавления или вычитания месяцев.
https://gist.github.com/66Ton99/60571ee49bf1906aaa1c
источник
При использовании
strtotime()
просто используйте$date = strtotime('first day of +1 month');
источник
Мне нужно было получить дату «в этом месяце в прошлом году», и довольно быстро становится неприятно, когда в этом месяце февраль в високосном году. Тем не менее, я считаю, что это работает ...: - / Уловка, похоже, состоит в том, чтобы вносить изменения в 1-й день месяца.
источник
источник
выйдет
2
(февраль). будет работать и в другие месяцы.источник
Несколько дней:
Важный:
Метод
add()
класса DateTime изменяет значение объекта, поэтому после вызоваadd()
объекта DateTime он возвращает новый объект даты, а также сам изменяет объект.источник
вы можете сделать это только с помощью date () и strtotime (). Например, чтобы добавить 1 месяц к сегодняшней дате:
если вы хотите использовать класс datetime, это тоже хорошо, но это так же просто. подробнее здесь
источник
Принятый ответ уже объясняет, почему это не но, а некоторые другие ответы представляют собой изящное решение с выражениями php, такими как
first day of the +2 months
. Проблема с этими выражениями в том, что они не заполняются автоматически.Однако решение довольно простое. Во-первых, вы должны найти полезные абстракции, отражающие вашу проблемную область. В данном случае это файл
ISO8601DateTime
. Во-вторых, должно быть несколько реализаций, которые могут дать желаемое текстовое представление. Например,Today
,Tomorrow
,The first day of this month
,Future
- все они представляют собой реализацию специфичнойISO8601DateTime
концепции.Итак, в вашем случае вам нужна реализация
TheFirstDayOfNMonthsLater
. Его легко найти, просто взглянув на список подклассов в любой среде IDE. Вот код:То же самое и с последними числами месяца. Для получения дополнительных примеров этого подхода прочтите это .
источник
источник