Die Klasse DatePeriod

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

Einführung

Stellt einen Datumsbereich dar.

Ein Datumsbereich ermöglicht die Iteration über eine Reihe von Datumsangaben und Uhrzeiten, die sich im angegebenen Zeitraum in regelmäßigen Intervallen wiederholen.

Klassenbeschreibung

class DatePeriod implements IteratorAggregate {
/* Konstanten */
public const int EXCLUDE_START_DATE;
public const int INCLUDE_END_DATE;
/* Eigenschaften */
public readonly ?DateTimeInterface $start;
public readonly ?DateTimeInterface $current;
public readonly ?DateTimeInterface $end;
public readonly ?DateInterval $interval;
public readonly int $recurrences;
public readonly bool $include_start_date;
public readonly bool $include_end_date;
/* Methoden */
public __construct(
    DateTimeInterface $start,
    DateInterval $interval,
    int $recurrences,
    int $options = 0
)
public __construct(
    DateTimeInterface $start,
    DateInterval $interval,
    DateTimeInterface $end,
    int $options = 0
)
public __construct(string $isostr, int $options = 0)
public static createFromISO8601String(string $specification, int $options = 0): static
}

Vordefinierte Konstanten

DatePeriod::EXCLUDE_START_DATE

Anfangsdatum ausschließen; verwendet in DatePeriod::__construct().

DatePeriod::INCLUDE_END_DATE

Enddatum einschließen; verwendet in DatePeriod::__construct().

Eigenschaften

recurrences

Die minimale Anzahl von Instanzen, die vom Iterator zurückgegeben wird.

Wenn im Konstruktor der DatePeriod-Instanz die Anzahl der Wiederholungen explizit über den Parameter recurrences übergeben wurde, dann enthält diese Eigenschaft diesen Wert, plus eins, wenn das Startdatum nicht mittels DatePeriod::EXCLUDE_START_DATE deaktiviert wurde, plus eins, wenn das Enddatum mittels DatePeriod::INCLUDE_END_DATE aktiviert wurde.

Wenn die Anzahl der Wiederholungen nicht explizit übergeben wurde, dann enthält diese Eigenschaft die Mindestanzahl der zurückgegebenen Instanzen. Dies wäre 0, plus eins, wenn das Startdatum nicht mittels DatePeriod::EXCLUDE_START_DATE deaktiviert wurde, plus eins, wenn das Enddatum mittels DatePeriod::INCLUDE_END_DATE aktiviert wurde.

<?php
$start
= new DateTime('2018-12-31 00:00:00');
$end = new DateTime('2021-12-31 00:00:00');
$interval = new DateInterval('P1M');
$recurrences = 5;

// Wiederholungen explizit im Konstruktor gesetzt
$period = new DatePeriod($start, $interval, $recurrences, DatePeriod::EXCLUDE_START_DATE);
echo
$period->recurrences, "\n";

$period = new DatePeriod($start, $interval, $recurrences);
echo
$period->recurrences, "\n";

$period = new DatePeriod($start, $interval, $recurrences, DatePeriod::INCLUDE_END_DATE);
echo
$period->recurrences, "\n";

// Wiederholungen nicht im Konstruktor gesetzt
$period = new DatePeriod($start, $interval, $end);
echo
$period->recurrences, "\n";

$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);
echo
$period->recurrences, "\n";
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:


5
6
7
1
0

Siehe auch DatePeriod::getRecurrences().

include_end_date

Legt fest, ob das Enddatum in der Menge der wiederkehrenden Termine enthalten sein soll oder nicht.

include_start_date

Legt fest, ob das Anfangsdatum in der Menge der wiederkehrenden Termine enthalten sein soll, oder nicht.

start

Das Anfangsdatum des Zeitraums.

current

Während einer Iteration enthält diese Eigenschaft das aktuelle Datum innerhalb des Zeitraums.

end

Das Enddatum des Zeitraums.

interval

Eine ISO-8601-konforme Angabe eines sich wiederholenden Intervalls.

Changelog

Version Beschreibung
8.2.0 Die Konstante DatePeriod::INCLUDE_END_DATE und die Eigenschaft include_end_date wurden hinzugefügt.
8.0.0 DatePeriod implementiert nun IteratorAggregate. Zuvor wurde stattdessen Traversable implementiert.

Inhaltsverzeichnis

add a note add a note

User Contributed Notes 11 notes

up
144
josh dot love at verizon dot net
11 years ago
Just an example to include the end date using the DateTime method 'modify'

<?php

$begin
= new DateTime( '2012-08-01' );
$end = new DateTime( '2012-08-31' );
$end = $end->modify( '+1 day' );

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

foreach(
$daterange as $date){
    echo
$date->format("Ymd") . "<br>";
}
?>
up
58
logos-php at kith dot org
11 years ago
Thanks much to those of you who supplied sample code; that helps a lot.

I wanted to mention another thing that helped me: when you do that foreach ( $period as $dt ), the $dt values are DateTime objects.

That may be obvious to those of you with more experience, but I wasn't sure until I looked it up on Stack Overflow. So I figured it was worth posting here to help others like me who might've been confused or uncertain.
up
6
mike at saymikeo dot com
6 years ago
Calculating business days can be cumbersome. Here is an iterator for handling business days. Usage examples below for adding # of business days and calculating how many business days between two dates.

Here is the iterator class
https://gist.github.com/styks1987/29dd0f6a68e3b07ba70fec18f732eb86

Usage

Counting # of Business Days between two dates

<?php
function countBusinessDays($start, $stop)
    {
        if(
$start > $stop){
           
$tmpStart = clone $start;
           
$start = clone $stop;
           
$stop = clone $tmpStart;
        }

       
// Adding the time to the end date will include it
       
$period = new \DatePeriod($start->setTime(0,0,0), new \DateInterval('P1D'), $stop->setTime(23,59,59), \DatePeriod::EXCLUDE_START_DATE);
       
$periodIterator = new BusinessDayPeriodIterator($period);
       
$businessDays = 0;
        while(
$periodIterator->valid()){
           
// If we run into a weekend, don't count it
           
if(!$periodIterator->isWeekend()){
               
$businessDays++;
            }
           
$periodIterator->next();
        }

        return
$businessDays;
    }
?>

Add # of business days

<?php
function addBusinessDays(\DateTime $startDateTime, $daysToAdd)
    {
       
$endDateTime = clone $startDateTime;
       
$endDateTime->add(new \DateInterval('P' . $daysToAdd . 'D'))->setTime(23,59,59);
       
$period = new \DatePeriod($startDateTime, new \DateInterval('P1D'), $endDateTime);

       
$periodIterator = new BusinessDayPeriodIterator($period);
       
$adjustedEndingDate = clone $startDateTime;
        while(
$periodIterator->valid()){
           
$adjustedEndingDate = $periodIterator->current();
           
// If we run into a weekend, extend our days
           
if($periodIterator->isWeekend()){
               
$periodIterator->extend();
            }
           
$periodIterator->next();
        }

        return
$adjustedEndingDate;
    }
?>
up
10
mail at pascalhofmann dot de
7 years ago
When looping over a DatePeriod object, the returned objects always implement DateTimeInterface. The exact type returned depends on how the DatePeriod was created. If $start was a DateTimeImmutable, the objects returned will be of type DateTimeImmutable. If a DateTime object was used, the objects returned will be of type DateTime.
up
6
php at karlsruler dot de
8 years ago
The iterator seems to check the time as well, it excludes the end element if its time is 00:00:00. So the slightly safer version (to compare it against joshs suggestion) is to use $date->setTime(23, 59, 59) instead of $date->modify("+1 day").
up
15
jkaatz at gmx dot de
14 years ago
Nice example from PHP Spring Conference (thanks to Johannes Schlüter and David Zülke)

<?php
$begin
= new DateTime( '2007-12-31' );
$end = new DateTime( '2009-12-31 23:59:59' );

$interval = DateInterval::createFromDateString('last thursday of next month');
$period = new DatePeriod($begin, $interval, $end, DatePeriod::EXCLUDE_START_DATE);

foreach (
$period as $dt )
  echo
$dt->format( "l Y-m-d H:i:s\n" );
?>

DateInterval specs could be found at http://en.wikipedia.org/wiki/ISO_8601#Time_intervals
up
6
patrick at adrichem dot nu
10 years ago
DatePeriod is not compatible with negative intervals.

To do so you can simply use DateInterval and loop through it yourself like this: (not start should be ahead of end if you use a negative interval

    class DateRange extends ArrayIterator
    {

        protected $oDate = null;
        protected $oStartDate = null;
        protected $oEndDate = null;
        protected $oInterval = null;

        public function __construct( DateTime $oStartDate, DateTime $oEndDate, DateInterval $oInterval = null )
        {
            $this->oStartDate = $oStartDate;
            $this->oDate = clone $oStartDate;
            $this->oEndDate = $oEndDate;
            $this->oInterval = $oInterval;
        }

        public function next()
        {
            $this->oDate->add($this->oInterval);
            return $this->oDate;
        }

        public function current()
        {
            return $this->oDate;
        }

        public function valid()
        {
            if ($this->oStartDate > $this->oEndDate)
            {
                return $this->oDate >= $this->oEndDate;
            }
            return $this->oDate <= $this->oEndDate;
        }

    }

$oRange = new DateRange(new DateTime("2013-10-01"), new DateTime("2013-01-01"), DateInterval::createFromDateString("-1 month") );
    foreach ($oRange as $oDate)
    {
        echo $oDate->format("Y-m-d") . "<br />";
    }
up
7
Memori
13 years ago
If you want to include the end-date, add one day to it:

<?php
$startDate
= new DateTime();
$endDate = new DateTime();

$startDateInt = new DateInterval( "P1Y" );
$endDateInt = new DateInterval( "P1D" );

$startDate->sub( $startDateInt );
$endDate->add( $endDateInt );

$periodInt = new DateInterval( "P1M" );
$period = new DatePeriod( $startDate, $periodInt, $endDate );

// At februari 2011:
// $period = (8,9,10,11,12,1,2)
?>
up
2
Joey
4 years ago
This class isn't always intuitive. End is exclusive. All you need to do to include it is to add a second (the smallest amount).

Microseconds are ignored.

php -r '
/***/$p1d = new DateInterval("P1D");
/***/$b = (new DateTime())->setTime(0, 0, 0, 0);
/***/$a = (clone $b)->sub($p1d);

/***/$f = function(DateTime $b)use($a, $p1d):void {
/***//***/$p = new DatePeriod($a, $p1d, $b);
/***//***/$i = iterator_to_array($p);
/***//***/print_r([$i[0], end($i), $a, $b]);
/***/};

/***/$f($b);
/***/$f($b->setTime(0, 0, 0, 1));
/***/$f($b->setTime(0, 0, 1, 0));
'

It is a sign of the end times.
up
1
johnzenith71(at)gmail.com
5 years ago
An extra piece of information on how to use the (modify method) to easily get date ranges:

<?php
$subscription_start_date
= new DateTime( 'now' );

// Modify the $subscription_start_date date value to required date range,
// this could be in days or months depending on how your application is designed

// Days
$subscription_expiration = $subscription_start_date->modify( '+60 days' );
echo
$subscription_expiration->format( 'Y-m-d' ) . "\n";

// Months
$subscription_expiration = $subscription_start_date->modify( '+2 Months' );
echo
$subscription_expiration->format( 'Y-m-d' ) . "\n";

// Or even in years
$subscription_expiration = $subscription_start_date->modify( '+2 years' );
echo
$subscription_expiration->format( 'Y-m-d' ) . "\n";
?>
up
-1
joseph dot cardwell at jbcwebservices dot com
5 years ago
As someone noted, at least in 7.2, dates with time 0 are excluded from the start and end.

To get a regular span of dates I ended up with:

$dates = new DatePeriod(
    ( new DateTime($date_start) )->setTime(0,0,1),
    new DateInterval('P1D'),
    ( new DateTime($date_end) )->setTime(0,0,1)
);
To Top