Я хочу создать объект 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 можно инвертировать.
Согласно комментарию 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 )