Отрицательный DateInterval

Я хочу создать объект DatePeriod с отрицательным DateInterval.

Это создает DatePeriod с годом, увеличивающимся с сегодняшнего дня до 2016 года.

$this->Set('dates', new DatePeriod(new DateTime(), new DateInterval('P1Y'), new DateTime('2016-06-06'))); 

Я хочу начать с 2016 года и использовать отрицательный переход DateInterval в сторону сегодняшнего года

Что-то вроде этого может проиллюстрировать мое желание

 $this->Set('dates', new DatePeriod(new DateTime('2016-06-06'), new DateInterval('-P1Y'), new DateTime())); 

Я просто не могу найти подробную информацию о DatePeriod или DateInterval о том, как это сделать. Все, что я нахожу, это то, что DateInterval можно инвертировать.

Solutions Collecting From Web of "Отрицательный DateInterval"

Согласно комментарию kevinpeno на 17 марта 2011 года 07:47 на странице php.net о DateInterval :: __ construct () , вы не можете напрямую создавать отрицательные DateIntervals через конструктор:

 new DateInterval('-P1Y'); // Exception "Unknown or bad format (-P1Y)" 

Вместо этого вам необходимо создать положительный интервал и явно установить его свойство invert в 1 :

 $di = new DateInterval('P1Y'); $di->invert = 1; // Proper negative date interval 

Просто проверил вышеприведенный код сам, он работает именно так.

Это заняло немного копания. Единственным способом, которым я смог получить отрицательный DateInterval было следующее:

 $interval = DateInterval::createFromDateString('-1 day'); 

Однако есть улов. DatePeriod похоже, не работает для отрицательных интервалов. Если вы устанавливаете дату начала до даты окончания, то она не содержит никаких дат вообще, и если вы переверните так, чтобы дата начала была после даты окончания, то она будет выглядеть бесконечно бесконечно.

Вероятно, вам придется реструктурировать свой код, чтобы DateInterval даты с помощью DateTime::sub с положительным DateInterval или DateTime::add с отрицательным.

Я пробовал это сам, и это невозможно с DatePeriod покое, но я думаю, что это имеет смысл: он просто отражает периоды, которые обычно не имеют какого-либо определенного порядка и поэтому не могут быть переупорядочены (его можно рассматривать как набор) ,

Единственный способ получить даты и отсортировать их в обратном порядке, насколько я вижу, – это что-то вроде этого

 $result = array(); forech ($dateperiod as $date) { array_push ($result, $data); } 

Обновить

 $date = new DateTime('2016-06-06'); $i = new DateInterval('P1Y'); $now = new DateTime; while ($date >= $now) { echo $date->format('c') . PHP_EOL; $date = $date->sub($i); } 

Этот отрывок работал для меня:

  $iDate = $endDate; while($iDate >= $startDate) { $dates[] = new DateTime($iDate->format('Ym-d')); $iDate->sub(new DateInterval("P1D")); } 

У меня была та же проблема (и некоторые другие), и я создал класс, чтобы иметь возможность добавлять и подставлять DateInterval. Он также поддерживает отрицательный интервал даты ISO8601 (например, «P-2M1DT3H-56M21S»).

Здесь код (предложения приветствуются, я очень новичок в PHP):

 class MyDateInterval extends DateInterval { public function __construct($interval_spec) { $interval_spec = str_replace('+', '', $interval_spec); $pos = strpos($interval_spec, '-'); if ($pos !== false) { // if at least 1 negative part $pattern = '/P(?P<ymd>(?P<years>-?\d+Y)?(?P<months>-?\d+M)?(?P<days>-?\d+D)?)?(?P<hms>T(?P<hours>-?\d+H)?(?P<minutes>-?\d+M)?(?P<seconds>-?\d+S)?)?/'; $match = preg_match($pattern, $interval_spec, $matches); $group_names = array('years', 'months', 'days', 'hours', 'minutes', 'seconds'); $negative_parts = array(); $positive_parts = array(); $all_negative = true; foreach ($matches as $k => $v) { if (in_array($k, $group_names, true)) { if (substr($v, 0, 1) == '-' and $v != '') $negative_parts[$k] = $v; if (substr($v, 0, 1) != '-' and $v != '') $positive_parts[$k] = $v; } } if (count($positive_parts) == 0) { // only negative parts $interval_spec = str_replace('-', '', $interval_spec); parent::__construct($interval_spec); $this->invert = 1; } else { // the negative and positive parts are to be sliced $negative_interval_spec = 'P'; $positive_interval_spec = 'P'; if ($matches['ymd'] != '') { foreach ($matches as $k => $v) { if (in_array($k, array_slice($group_names, 0, 3))) { $negative_interval_spec .= $negative_parts[$k]; $positive_interval_spec .= $positive_parts[$k]; } } } if ($matches['hms'] != '') { $negative_ok = false; $positive_ok = false; foreach ($matches as $k => $v) { if (in_array($k, array_slice($group_names, 3, 3))) { if ($negative_parts[$k] != '' and ! $negative_ok) { $negative_interval_spec .= 'T'; $negative_ok = true; } $negative_interval_spec .= $negative_parts[$k]; if ($positive_parts[$k] != '' and ! $positive_ok) { $positive_interval_spec .= 'T'; $positive_ok = true; } $positive_interval_spec .= $positive_parts[$k]; } } } $negative_interval_spec = str_replace('-', '', $negative_interval_spec); $from = new DateTime('2013-01-01'); $to = new DateTime('2013-01-01'); $to = $to->add(new DateInterval($positive_interval_spec)); $to = $to->sub(new DateInterval($negative_interval_spec)); $diff = $from->diff($to); parent::__construct($diff->format('P%yY%mM%dDT%hH%iM%sS')); $this->invert = $diff->invert; } } else { // only positive parts parent::__construct($interval_spec); } } public static function fromDateInterval(DateInterval $from) { return new MyDateInterval($from->format('P%yY%mM%dDT%hH%iM%sS')); } public static function fromSeconds($from) { $invert = false; if ($from < 0) $invert = true; $from = abs($from); $years = floor($from / (365 * 30 * 24 * 60 * 60)); $from = $from % (365 * 30 * 24 * 60 * 60); $months = floor($from / (30 * 24 * 60 * 60)); $from = $from % (30 * 24 * 60 * 60); $days = floor($from / (24 * 60 * 60)); $from = $from % (24 * 60 * 60); $hours = floor($from / (60 * 60)); $from = $from % (60 * 60); $minutes = floor($from / 60); $seconds = floor($from % 60); if ($invert) return new MyDateInterval(sprintf("P-%dY-%dM-%dDT-%dH-%dM-%dS", $years, $months, $days, $hours, $minutes, $seconds)); return new MyDateInterval(sprintf("P%dY%dM%dDT%dH%dM%dS", $years, $months, $days, $hours, $minutes, $seconds)); } public function to_seconds() { $seconds = ($this->y * 365 * 24 * 60 * 60) + ($this->m * 30 * 24 * 60 * 60) + ($this->d * 24 * 60 * 60) + ($this->h * 60 * 60) + ($this->i * 60) + $this->s; if ($this->invert == 1) return $seconds * -1; return $seconds; } public function to_hours() { $hours = round($this->to_seconds() / (60 * 60), 2); return $hours; } public function add($interval) { $sum = $this->to_seconds() + $interval->to_seconds(); $new = MyDateInterval::fromSeconds($sum); foreach ($new as $k => $v) $this->$k = $v; return $this; } public function sub($interval) { $diff = $this->to_seconds() - $interval->to_seconds(); $new = MyDateInterval::fromSeconds($diff); foreach ($new as $k => $v) $this->$k = $v; return $this; } public function recalculate() { $seconds = $this->to_seconds(); $new = MyDateInterval::fromSeconds($seconds); foreach ($new as $k => $v) $this->$k = $v; return $this; } } 

EDIT: обратите внимание, что это было вдохновлено кодом ханы выше.

Вот полный рабочий сценарий для моего прецедента, который должен отображать строки года + месяца с текущего месяца, возвращая N количество месяцев. Он должен работать с днями или годами как интервалы и проверяется с помощью PHP версии 5.3.3.

 <?php date_default_timezone_set('America/Los_Angeles'); $monthsBack=16; $monthDateList = array(); $previousMonthDate = new DateTime(); for($monthInterval = 0; $monthInterval < $monthsBack; $monthInterval++) { array_push($monthDateList, $previousMonthDate->format('Ym')); $previousMonthDate->sub(new DateInterval("P1M")); } print_r($monthDateList) . "\n"; ?> 

Выход:

 Array ( [0] => 201705 [1] => 201704 [2] => 201703 [3] => 201702 [4] => 201701 [5] => 201612 [6] => 201611 [7] => 201610 [8] => 201609 [9] => 201608 [10] => 201607 [11] => 201606 [12] => 201605 [13] => 201604 [14] => 201603 [15] => 201602 )