Мне нужно создать функции в PHP, которые позволяют мне увеличивать / уменьшать количество единиц времени. В частности, мне нужно иметь возможность перейти к следующему / предыдущему месяцу с текущего.
Я думал, что могу сделать это, используя DateTime :: add / sub (P1M). Однако, когда вы пытаетесь получить предыдущий месяц, это испортится, если значение даты = 31- выглядит так, будто оно пытается подсчитать 30 дней, а не уменьшать значение месяца !:
$prevMonth = new DateTime('2010-12-31');
Попробуйте уменьшить месяц:
$prevMonth->sub(new DateInterval('P1M')); // = '2010-12-01' $prevMonth->add(DateInterval::createFromDateString('-1 month')); // = '2010-12-01' $prevMonth->sub(DateInterval::createFromDateString('+1 month')); // = '2010-12-01' $prevMonth->add(DateInterval::createFromDateString('previous month')); // = '2010-12-01'
Это, безусловно, похоже на неправильное поведение. У кого-нибудь есть понимание? Благодаря-
ПРИМЕЧАНИЕ. Версия PHP 5.3.3
(Кредит на самом деле принадлежит Алексу за указание на это в комментариях)
Проблема не в PHP, а в GNU, как описано здесь:
Относительные элементы в строках даты
Ключевым моментом здесь является дифференциация понятия «эта дата в прошлом месяце», которая, поскольку месяцы являются «нечеткими единицами» с различным количеством дат, невозможно определить для даты, например, 31 декабря (поскольку 31 ноября не существует) , и понятие «в прошлом месяце, независимо от даты».
Если нас интересует только предыдущий месяц, единственный способ дать правильный расчет DateInterval – сбросить значение даты до 1-го или другого числа, которое будет иметь каждый месяц.
Что действительно меня поражает, так это то, как недокументированная эта проблема, в PHP и в других местах, – учитывая, насколько сильно зависит от времени программное обеспечение, зависящее от даты.
Вот безопасный способ справиться с этим:
/* Handles month/year increment calculations in a safe way, avoiding the pitfall of 'fuzzy' month units. Returns a DateTime object with incremented month/year values, and a date value == 1. */ function incrementDate($startDate, $monthIncrement = 0, $yearIncrement = 0) { $startingTimeStamp = $startDate->getTimestamp(); // Get the month value of the given date: $monthString = date('Y-m', $startingTimeStamp); // Create a date string corresponding to the 1st of the give month, // making it safe for monthly/yearly calculations: $safeDateString = "first day of $monthString"; // Increment date by given month/year increments: $incrementedDateString = "$safeDateString $monthIncrement month $yearIncrement year"; $newTimeStamp = strtotime($incrementedDateString); $newDate = DateTime::createFromFormat('U', $newTimeStamp); return $newDate; }
Самый простой способ добиться этого, на мой взгляд, – использовать mktime.
Как это:
$date = mktime(0,0,0,date('m')-1,date('d'),date('Y')); echo date('dm-Y', $date);
Greetz Michael
Документацию ps mktime можно найти здесь: http://nl2.php.net/mktime
Вы можете пойти на старую школу и просто использовать функции даты и strtotime.
$ date = '2010-12-31'; $ monthOnly = date ('Y-m', strtotime ($ date)); $ previousMonth = date ('Ym-d', strtotime ($ monthOnly. '-1 month'));
(Это может быть комментарий, но это долго для одного)
Вот как это работает на Windows 7 Apache 2.2.15 с PHP 5.3.3:
<?php $dt = new DateTime('2010-12-31'); $dt->sub(new DateInterval('P1M')); print $dt->format('Ym-d').'<br>'; $dt->add(DateInterval::createFromDateString('-1 month')); print $dt->format('Ym-d').'<br>'; $dt->sub(DateInterval::createFromDateString('+1 month')); print $dt->format('Ym-d').'<br>'; $dt->add(DateInterval::createFromDateString('previous month')); print $dt->format('Ym-d').'<br>'; ?> 2010-12-01 2010-11-01 2010-10-01 2010-09-01
Так что это, похоже, подтверждает, что это связано с GNU выше.
Примечание: IMO приведенный ниже код работает должным образом.
$dt->sub(new DateInterval('P1M'));
Текущий месяц: 12
В прошлом месяце: 11
Количество дней в 12-м месяце: 31
Количество дней в 11-м месяце: 30
31 декабря – 31 день = 31 ноября
31 ноября = Ноябрь 1 + 31 Дней = 1 декабря (30 + 1)