I am currently working on a php project and need to format a DateInterval as ISO8601 (something like this):
P5D
This format can be used to create DateTime and DateInterval objects, but I can't figure out a way to format a DateInterval into this format. Is there any? If not, what might be a lightweight solution to that?
To expand on the solution provided by Dave and the solution provided by Guss.
A workaround to add the T
portion of the duration of the ISO 8601 standard and not need to default as P0M
, is to insert 2 additional checks after the P0Y
replaced by Y
.
Y0M
replaced by Y
P0M
replaced by P
D0H
to TD0H
replaced by DT
Y0M
replaced by Y0M
This works because the interval format will always generate the same structure, without leading 0's and str_replace
walks through each replacement in the arrays from left to right.
As a result there is a possibility that we could receive P1YT
or PT
as a return value. Simply remove any trailing PT
characters by using rtrim
. Then substitute an empty value with a desired default value, as in the answer by Guss.
Compatible with PHP 5.3+
The use of the static keyword is to improve performance of repeated calls to the function, as the static variables do not lose their values after leaving the function scope
function date_interval_iso(DateInterval $interval, $default = 'PT0S') {
static $f = array('M0S', 'H0M', 'DT0H', 'M0D', 'P0Y', 'Y0M', 'P0M');
static $r = array('M', 'H', 'DT', 'M', 'P', 'Y', 'P');
return rtrim(str_replace($f, $r, $interval->format('P%yY%mM%dDT%hH%iM%sS')), 'PT') ?: $default;
}
Test
To compare I created an array containing every possible duration combination (excluding microseconds) with a value of 1. Which can be viewed in the example link above.
$durations ['P1Y', /*...*/ 'P1Y1M1DT1H1M1S'];
$isos = array();
foreach ($durations as $duration) {
$isos[] = date_interval_iso(new DateInterval($duration));
}
$diff = array_diff($durations, $isos);
if (!empty($diff)) {
//output any differences
var_dump($diff);
}
//test 0 duration DateInterval
$date1 = new DateTime();
echo date_interval_iso($date1->diff($date1));
Result
PT0S
You can add microseconds easily, by including S0F
replaced by S
of the first array values and adding %fF
to your DateInterval::format()
.
However F
(microseconds) is not currently a supported interval specification for DateInterval::__construct()
or the ISO 8601 standard.
NOTE: there was a bug in PHP <= 7.2.13 with
DateTime::diff
that prevents the properDateInterval
retrieval when the difference was less than one second.
function date_interval_iso(DateInterval $interval, string $default = 'PT0F') {
static $f = ['S0F', 'M0S', 'H0M', 'DT0H', 'M0D', 'P0Y', 'Y0M', 'P0M'];
static $r = ['S', 'M', 'H', 'DT', 'M', 'P', 'Y', 'P'];
return rtrim(str_replace($f, $r, $interval->format('P%yY%mM%dDT%hH%iM%sS%fF')), 'PT') ?: $default;
}
Test
$date1 = new DateTimeImmutable();
$date2 = new DateTimeImmutable();
//test 0 duration DateInterval
echo date_interval_iso($date1->diff($date1));
//test microseconds
echo date_interval_iso($date2->diff($date1));
Result
PT0F
PT21F
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With