问题
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?
回答1:
Well, if you look at the spec for the format when you construct one:
Y years
M months
D days
W weeks. These get converted into days, so can not be combined with D.
H hours
M minutes
S seconds
Then look at what you have to work with (http://php.net/manual/en/dateinterval.format.php), it seems like what you would do is:
$dateInterval = new DateInterval( /* whatever */ );
$format = $dateInterval->format("P%yY%mM%dD%hH%iM%sS");
//P0Y0M5D0H0M0S
//now, we need to remove anything that is a zero, but make sure to not remove
//something like 10D or 20D
$format = str_replace(["M0S", "H0M", "D0H", "M0D", "Y0M", "P0Y"], ["M", "H", "D", "M", "Y0M", "P"], $format);
echo $format;
//P0M5D
Now, the one thing I did differently is I always include the months, even if it is 0. The reason for this is that minutes
and months
are both represented by M
- if we always include the month, then if there is a minute we know it is minutes. Otherwise we have to do a bunch of logic to see if we need to change the P
to PT
so it knows that the a M
in this instance stands for Minute
.
For example:
// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("PT3M"));
But instead we do:
// For 3 Months
new DateInterval("P3M");
// For 3 Minutes
new DateInterval("P0M3M"));
回答2:
With regards to @dave, I re-implemented his solution in a way that solves some of the problems, specifically the requirement to always leave a month field. From the Wikipedia article on ISO-8601, it doesn't seem like the T
designation is optional, even through its completely missing from the above mentioned implementation. By introducing it we can solve most of our problems and make for some cleaner code:
function date_interval_iso_format(DateInterval $interval) {
list($date,$time) = explode("T",$interval->format("P%yY%mM%dDT%hH%iM%sS"));
// now, we need to remove anything that is a zero, but make sure to not remove
// something like 10D or 20D
$res =
str_replace([ 'M0D', 'Y0M', 'P0Y' ], [ 'M', 'Y', 'P' ], $date) .
rtrim(str_replace([ 'M0S', 'H0M', 'T0H'], [ 'M', 'H', 'T' ], "T$time"),"T");
if ($res == 'P') // edge case - if we remove everything, DateInterval will hate us later
return 'PT0S';
return $res;
}
Note that we remove the T if it is not needed, so now both uses of M
work fine:
"P5M" == date_interval_iso_format(new DateInterval("P5M")); // => true
"PT5M" == date_interval_iso_format(new DateInterval("PT5M")); // => true
来源:https://stackoverflow.com/questions/33787039/format-dateinterval-as-iso8601