DatePeriod с DateInterval P1Y не генерируется в прошлом году

У меня есть такой код:

private function validatePeriodArrayYear()
{
    $this->time->setTimestamp( self::$data['to'] );
    $year1 = $this->time->format('Y');
    $this->time->setTimestamp( self::$data['from'] );
    $year2 = $this->time->format('Y');

    $overlap = $year1 - $year2;

    if( $overlap > 0 ){
        $end = clone $this->time;
        $end->setTimestamp( self::$data['to'] );
        $overlap++;

        $interval = new DateInterval('P1Y');
        $period = new DatePeriod( $this->time, $interval, $end );
        var_dump($period);
        $iteration = -1;

        foreach( $period as $onePeriod ){ 
            var_dump($onePeriod);
            $iteration++;
            if( $iteration > 0 ){
                $onePeriod->setDate( $onePeriod->format('Y'), 1, 1 );
            }
            $from = $onePeriod->getTimestamp();
            $onePeriod->setDate( $onePeriod->format('Y'), 12, 31 );
            $onePeriod->setTime( 23, 59, 59 );
            self::$data['zone'][$iteration] = array( 'from' => $from, 'to' => $onePeriod->getTimestamp()  );
        }

        self::$data['zone'][$iteration]['to'] = self::$data['to'];

    } else {
        self::$data['zone'][] = array( 'from' => self::$data['from'], 'to' => self::$data['to'] );
    }

}

Переменные

$this->time = new DateTime(),
self::$data['from'] = 1354316400,
self::$data['to'] = 1389653999

Выходная функция var_dump() выглядит следующим образом:

<pre>
    object(DatePeriod)[337]
  public start => 
    object(DateTime)[336]
      public date => string 2012-12-01 00:00:00 (length=19)
      public timezone_type => int 3
      public timezone => string Europe/Prague (length=13)
  public current => null
  public end => 
    object(DateTime)[48]
      public date => string 2014-01-13 23:59:59 (length=19)
      public timezone_type => int 3
      public timezone => string Europe/Prague (length=13)
  public interval => 
    object(DateInterval)[331]
      public y => int 1
      public m => int 0
      public d => int 0
      public h => int 0
      public i => int 0
      public s => int 0
      public weekday => int 0
      public weekday_behavior => int 0
      public first_last_day_of => int 0
      public invert => int 0
      public days => boolean false
      public special_type => int 0
      public special_amount => int 0
      public have_weekday_relative => int 0
      public have_special_relative => int 0
  public recurrences => int 1
  public include_start_date => boolean true



object(DateTime)[326]
  public date => string 2012-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)



object(DateTime)[47]
  public date => string 2013-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)


После выполнения код сохраняется в переменной self::$data['zone'] вот так:

array (size=2)
  0 => 
    array (size=2)
      from => int 1354316400
      to => int 1356994799
  1 => 
    array (size=2)
      from => int 1356994800
      to => int 1389653999


преобразовано в даты:
01.12.2012 - 31.12.2012
01.01.2013 - 13.01.2014
Теперь, пожалуйста, поправьте меня, если это не будет генерировать это, и где ошибка в данном случае:
01.12.2012 - 31.12.2012
01.01.2013 - 31.12.2013
01.01.2014 - 13.01.2014

Дополнительная информация

$ php -v
PHP 5.5.6-1+debphp.org~raring+2 (cli) (built: Nov 21 2013 14:39:09) 
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
    with Zend OPcache v7.0.3-dev, Copyright (c) 1999-2013, by Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 13.10
Release:    13.10
Codename:   saucy

$ uname -a
Linux userName-K55VJ 3.11.0-15-generic #23-Ubuntu SMP Mon Dec 9 18:17:04 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

Спасибо за ответы.

Редактировать

@Glavic: спасибо за информацию, теперь я полностью понимаю

Меня поразило, что если начальный и конечный год были одинаковыми, а я менял только месяцы или дни в этом году, то это иногда приводило к правильному результату, а иногда отсутствовало последний год.

Поэтому позвольте мне еще немного разобрать проблему.

Для пояснения я буду использовать другие входные переменные для лучшего понимания.

Переменные

self::$data['from'] = 1354316400 //1.12.2012 00:00:00
self::$data['to'] = 1417388400 //1.12.2014 00:00:00

Обратите внимание, что в self::$data['to'] не хватает одной секунды, потому что разница составила более 3 лет.

Код

$start = new DateTime()->setTimestamp( self::$data['from'] );
$end = new DateTime()->setTimestamp( self::$data['to'] );
$interval = new DateInterval('P1Y');
$period = new DatePeriod( $start, $interval, $end );
foreach( $period as $onePeriod ){
    var_dump($onePeriod);
}

Выход

object(DateTime)
  public date => string 2012-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)

object(DateTime)
  public date => string 2013-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)

В этом случае внутри класса DatePeriod создаются три даты:
2012-12-01 00:00:00
2013-12-01 00:00:00
2014-12-01 00 :00:00
Но последняя дата удалена, поэтому класс возвращает только два объекта DateTime.

Теперь добавляем секунду к переменной self::$data['to'] и снова запускаем код.

Переменная

self::$data['from'] = 1354316400 //1.12.2012 00:00:00
self::$data['to'] = 1417388401 //1.12.2014 00:00:01

Выход

object(DateTime)
  public date => string 2012-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)

object(DateTime)
  public date => string 2013-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)

object(DateTime)
  public date => string 2014-12-01 00:00:00 (length=19)
  public timezone_type => int 3
  public timezone => string Europe/Prague (length=13)

В этом случае внутри класса DatePeriod создаются четыре даты:
2012-12-01 00:00:00
2013-12-01 00:00:00
2014-12-01 00 :00:00
01.12.2014 00:00:01
Последняя дата снова удалена.

Математически выраженный

{ x | a ≤ x < b }

a = дата начала
b = дата окончания
инициализировано x равно a
тогда создание каждого периода равно
x = x + период (в моем случае P1Y принадлежит {365 366})


person Hekimen    schedule 14.01.2014    source источник


Ответы (1)


Если я правильно понял, что вы пытаетесь сделать, то это вам поможет:

$from  = new DateTime('@1354316400');
$to    = new DateTime('@1389653999');
$zones = [];

for ($Y = $from->format('Y'); $Y <= $to->format('Y'); $Y++) {
    $_from = clone $from;
    if ($Y != $from->format('Y')) {
        $_from->setDate($Y, 1, 1);
    }

    $_to = clone $to;
    if ($Y != $to->format('Y')) {
        $_to->setDate($Y, 12, 31);
    }

    $zones[] = [
        'from' => $_from->format('Y-m-d'),
        'to'   => $_to->format('Y-m-d'),
    ];
}

print_r($zones);

демонстрация

person Glavić    schedule 14.01.2014
comment
Да, методы перезаписи - это решение, но мне интересно, почему DatePeriod не создает последний объект DateTime для 2014 года. - person Hekimen; 14.01.2014
comment
@ user3193454: endDate исключен из DatePeriod. Вы можете решить эту проблему, увеличив endDate с помощью DateInterval перед отправкой в ​​DatePeriod... Пример. - person Glavić; 14.01.2014