کلاس DateInterval

Introduction

نمایش بازه زمانی. بازه تاریخی در زمان ثابتی (بصورت سال، ماه، روز، ساعت،...) یا رشته زمان نسبی در قالب پشتیبانی شده توسط سازنده DateTime را نمایش می‌دهد.

Class synopsis

DateInterval {
/* Methods */
public __construct ( string $interval_spec )
public static DateInterval createFromDateString ( string $time )
public string format ( string $format )
}

Table of Contents

add a note add a note

User Contributed Notes 9 notes

up
15
p dot scheit at ps-webforge dot com
3 years ago
If you want to convert a Timespan given in Seconds into an DateInterval Object you could dot the following:

<?php

$dv
= new DateInterval('PT'.$timespan.'S');
?>

but wenn you look at the object, only the $dv->s property is set.

As stated in the documentation to DateInterval::format

The DateInterval::format() method does not recalculate carry over points in time strings nor in date segments. This is expected because it is not possible to overflow values like "32 days" which could be interpreted as anything from "1 month and 4 days" to "1 month and 1 day".

If you still want to calculate the seconds into hours / days / years, etc do the following:

<?php

$d1
= new DateTime();
$d2 = new DateTime();
$d2->add(new DateInterval('PT'.$timespan.'S'));
     
$iv = $d2->diff($d1);

?>

$iv is an DateInterval set with days, years, hours, seconds, etc ...
up
11
artur at qrupa dot com
1 year ago
When using DateInterval('P3M') on 30th of November you get March instead of Ferbuary.
up
4
Joan
11 months ago
I had the doubt after reading this page on how to create negative intervals. So far the only solution is to create the interval and negativize it.

<?php
$date1
= new DateTime();
$eightynine_days_ago = new DateInterval( "P89D" );
$eightynine_days_ago->invert = 1; //Make it negative.
$date1->add( $eightynine_days_ago );
?>

and then $date1 is now 89 days in the past.

This information is extracted from another php comment http://www.php.net/manual/en/dateinterval.construct.php#102976 but this page seems to be the first place where people will look for it.
up
4
theDustin
1 year ago
If you want to format a \DateInterval to something like you input (new \DateInterval("P3W2D")) you can use one of this:

<?php

class MyDateInterval extends \DateInterval
{

   
/**
     * formating string like ISO 8601 (PnYnMnDTnHnMnS)
     */
   
const INTERVAL_ISO8601 = 'P%yY%mM%dDT%hH%iM%sS';

   
/**
     * formating the interval like ISO 8601 (PnYnMnDTnHnMnS)
     *
     * @return string
     */
   
function __toString()
    {
       
$sReturn = 'P';

        if(
$this->y){
           
$sReturn .= $this->y . 'Y';
        }

        if(
$this->m){
           
$sReturn .= $this->m . 'M';
        }

        if(
$this->d){
           
$sReturn .= $this->d . 'D';
        }

        if(
$this->h || $this->i || $this->s){
           
$sReturn .= 'T';

            if(
$this->h){
               
$sReturn .= $this->h . 'H';
            }

            if(
$this->i){
               
$sReturn .= $this->i . 'M';
            }

            if(
$this->s){
               
$sReturn .= $this->s . 'S';
            }
        }

        return
$sReturn;
    }
}

?>

example use:

<?php

$oDateIntervalValue
= new MyDateInterval('P3M');

$sFormatResult = $oDateIntervalValue->format(MyDateInterval::INTERVAL_ICALENDAR); // "P0Y3M0DT0H0M0S"
$sToStringResult = (string) $oDateIntervalValue; // "P3M"

var_dump(new MyDateInterval($sFormatResult)); // object like $oDateIntervalValue
var_dump(new MyDateInterval($sToStringResult)); // object like $oDateIntervalValue

?>
up
4
computrius
1 year ago
It appears that they "days" property that is populated by \DateTime::diff does not contain a float for the differences in time.
It is rounded down to the nearest whole day.

    $d1 = new \DateTime("2013-07-31 10:29:00");
    $d2 = new \DateTime("2013-08-02 5:32:12");
    echo $d1->diff($d2)->days;

Output: 1
up
2
0bccbf3a at opayq dot com
7 months ago
invert flag is unreliable.
If you've created interval with \DateInterval::createFromDateString with value like '1 day ago' than actually days counter will be negative, and invert flag will be 0. Also, setting invert to 1 with negative units is not working.
Reliable solution to check if interval is negative is to actually apply it and compare:
<?php
   
private function isNegative(\DateInterval $interval)
    {
       
$now = new \DateTimeImmutable();
       
$newTime = $now->add($interval);
        return
$newTime < $now;
    }
?>
Also, if you want to compare some units of two intervals you should take abs() of them. Or make whole interval absolute:
<?php
   
private function absInterval(\DateInterval $interval)
    {
       
$now = new \DateTimeImmutable();
       
$new = $now->add($interval);
       
$newInt = $now->diff($new);
        if (
1 === $newInt->invert) {
           
$newInt->invert = 0;
        }
        return
$newInt;
    }
?>
P.S.: tested on 5.5.12-dev and 5.5.9
up
0
Miller
1 year ago
This DateInterval extension allows you to write a formatted timestamp but omit the "zero values" and handle things like listing, plurals, etc.
Example input: '%y year(s)', '%m month(s)', '%d day(s)', '%h hour(s)', '%i minute(s)', '%s second(s)'
Example output: 1 year, 2 months, 16 days, 1 minute, and 15 seconds
Example input: '%y año(s)', '%m mes(es)', '%d día(s)', '%h hora(s)', '%i minuto(s)', '%s segundo(s)'
Example output: 1 año, 2 meses, 16 días, 1 minuto, y 15 segundos

<meta charset="UTF-8">
<?php
error_reporting
(E_ALL);

class
MyDateInterval extends DateInterval {
    public
       
$pluralCheck = '()',
           
// Must be exactly 2 characters long
            // The first character is the opening brace, the second the closing brace
            // Text between these braces will be used if > 1, or replaced with $this->singularReplacement if = 1
       
$singularReplacement = '',
           
// Replaces $this->pluralCheck if = 1
            // hour(s) -> hour
       
$separator = ', ',
           
// Delimiter between units
            // 3 hours, 2 minutes
       
$finalSeparator = ', and ',
           
// Delimeter between next-to-last unit and last unit
            // 3 hours, 2 minutes, and 1 second
       
$finalSeparator2 = ' and ';
           
// Delimeter between units if there are only 2 units
            // 3 hours and 2 minutes

   
public static function createFromDateInterval (DateInterval $interval) {
       
$obj = new self('PT0S');
        foreach (
$interval as $property => $value) {
           
$obj->$property = $value;
        }
        return
$obj;
    }

    public function
formatWithoutZeroes () {
       
// Each argument may have only one % parameter
        // Result does not handle %R or %r -- but you can retrieve that information using $this->format('%R') and using your own logic
       
$parts = array ();
        foreach (
func_get_args() as $arg) {
           
$pre = mb_substr($arg, 0, mb_strpos($arg, '%'));
           
$param = mb_substr($arg, mb_strpos($arg, '%'), 2);
           
$post = mb_substr($arg, mb_strpos($arg, $param)+mb_strlen($param));
           
$num = intval(parent::format($param));

           
$open = preg_quote($this->pluralCheck[0], '/');
           
$close = preg_quote($this->pluralCheck[1], '/');
           
$pattern = "/$open(.*)$close/";
            list (
$pre, $post) = preg_replace($pattern, $num == 1 ? $this->singularReplacement : '$1', array ($pre, $post));

            if (
$num != 0) {
               
$parts[] = $pre.$num.$post;
            }
        }

       
$output = '';
       
$l = count($parts);
        foreach (
$parts as $i => $part) {
           
$output .= $part.($i < $l-2 ? $this->separator : ($l == 2 ? $this->finalSeparator2 : ($i == $l-2 ? $this->finalSeparator : '')));
        }
        return
$output;
    }
}

date_default_timezone_set('America/Phoenix');

$today = new DateTime('today');
echo
'Today is ', $today->format('F d, Y h:ia'), '.<br>', PHP_EOL;
   
// Today is August 28, 2013 12:00am.<br>
$expiration = new DateTime('today +1 year +2 months +16 days +1 minute +15 seconds');
echo
'Expires ', $expiration->format('F d, Y h:ia'), '.<br>', PHP_EOL;
   
// Expires November 13, 2014 12:01am.<br>

$interval = MyDateInterval::createFromDateInterval($today->diff($expiration));

echo
'That is ', $interval->formatWithoutZeroes('%y year(s)', '%m month(s)', '%d day(s)', '%h hour(s)', '%i minute(s)', '%s second(s)'), ' from now.<br>', PHP_EOL;
   
// That is 1 year, 2 months, 16 days, 1 minute, and 15 seconds from now.

$interval->finalSeparator = ', y ';
$interval->finalSeparator2 = ' y ';
echo
'Que es de ', $interval->formatWithoutZeroes('%y año(s)', '%m mes(es)', '%d día(s)', '%h hora(s)', '%i minuto(s)', '%s segundo(s)'), ' a partir de ahora.';
   
// Que es de 1 año, 2 meses, 16 días, 1 minuto, y 15 segundos a partir de ahora.
    // Is that correct? Spanish isn't my strength....
?>
up
-1
till at php dot net
1 year ago
It should be noted that the following code will not throw an exception or return false, or anything:

<?php
$interval
= new \DateInterval::createFromDateString("this is not a date interval");
?>

Your best way to check if what you created is a "valid" interval, by doing something like the following:

<?php
$interval
= new \DateInterval::createFromDateString("this is not a date interval");
if (
0 == $interval->format('s')) {
     throw new \
LogicException("Wrong interval");
}
?>
up
-1
till at php dot net
1 year ago
It should be noted that the following code will not throw an exception or return false, or anything:

<?php
$interval
= new \DateInterval::createFromDateString("this is not a date interval");
?>

Your best way to check if what you created is a "valid" interval, by doing something like the following:

<?php
$interval
= new \DateInterval::createFromDateString("this is not a date interval");
if (
0 == $interval->format('s')) {
     throw new \
LogicException("Wrong interval");
}
?>
To Top