| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- <?php
- declare(strict_types=1);
- /**
- * Author: Alexandre Alapetite https://alexandre.alapetite.fr
- * 2014-06-01
- * License: GNU AGPLv3 http://www.gnu.org/licenses/agpl-3.0.html
- *
- * Parser of ISO 8601 time intervals http://en.wikipedia.org/wiki/ISO_8601#Time_intervals
- * Examples: "2014-02/2014-04", "2014-02/04", "2014-06", "P1M"
- */
- /*
- example('2014-03');
- example('201403');
- example('2014-03-30');
- example('2014-05-30T13');
- example('2014-05-30T13:30');
- example('2014-02/2014-04');
- example('2014-02--2014-04');
- example('2014-02/04');
- example('2014-02-03/05');
- example('2014-02-03T22:00/22:15');
- example('2014-02-03T22:00/15');
- example('2014-03/');
- example('/2014-03');
- example('2014-03/P1W');
- example('P1W/2014-05-25T23:59:59');
- example('P1Y/');
- example('P1Y');
- example('P2M/');
- example('P3W/');
- example('P4D/');
- example('PT5H/');
- example('PT6M/');
- example('PT7S/');
- example('P1DT1H/');
- function example(string $dateInterval): void {
- $dateIntervalArray = parseDateInterval($dateInterval);
- echo $dateInterval, "\t=>\t",
- $dateIntervalArray[0] == null ? 'null' : @date('c', $dateIntervalArray[0]), '/',
- $dateIntervalArray[1] == null ? 'null' : @date('c', $dateIntervalArray[1]), "\n";
- }
- */
- function _dateFloor(string $isoDate): string {
- $x = explode('T', $isoDate, 2);
- $t = isset($x[1]) ? str_pad($x[1], 6, '0') : '000000';
- return str_pad($x[0], 8, '01') . 'T' . $t;
- }
- function _dateCeiling(string $isoDate): string {
- $x = explode('T', $isoDate, 2);
- $t = isset($x[1]) && strlen($x[1]) > 1 ? str_pad($x[1], 6, '59') : '235959';
- switch (strlen($x[0])) {
- case 4:
- return $x[0] . '1231T' . $t;
- case 6:
- $d = @strtotime($x[0] . '01');
- if ($d != false) {
- return $x[0] . date('t', $d) . 'T' . $t;
- }
- }
- return $x[0] . 'T' . $t;
- }
- /** @phpstan-return ($isoDate is null ? null : ($isoDate is '' ? null : string)) */
- function _noDelimit(?string $isoDate): ?string {
- return $isoDate === null || $isoDate === '' ? null : str_replace(['-', ':'], '', $isoDate); //FIXME: Bug with negative time zone
- }
- function _dateRelative(?string $d1, ?string $d2): ?string {
- if ($d2 === null) {
- return $d1 !== null && $d1[0] !== 'P' ? $d1 : null;
- }
- if ($d2 !== '' && $d2[0] != 'P' && $d1 !== null && $d1[0] !== 'P') {
- $y2 = substr($d2, 0, 4);
- if (strlen($y2) < 4 || !ctype_digit($y2)) { //Does not start by a year
- $d2 = _noDelimit($d2);
- return substr($d1, 0, -strlen($d2)) . $d2; //Add prefix from $d1
- }
- }
- return _noDelimit($d2);
- }
- /**
- * Parameter $dateInterval is a string containing an ISO 8601 time interval.
- * @return array{int|null|false,int|null|false} an array with the minimum and maximum Unix timestamp of this interval,
- * or null if open interval, or false if error.
- */
- function parseDateInterval(string $dateInterval): array {
- $dateInterval = trim($dateInterval);
- $dateInterval = str_replace('--', '/', $dateInterval);
- $dateInterval = strtoupper($dateInterval);
- $min = null;
- $max = null;
- $x = explode('/', $dateInterval, 2);
- $d1 = _noDelimit($x[0]);
- $d2 = _dateRelative($d1, count($x) > 1 ? $x[1] : null);
- if ($d1 !== null && $d1[0] !== 'P') {
- $min = @strtotime(_dateFloor($d1));
- }
- if ($d2 !== null) {
- if ($d2[0] === 'P') {
- try {
- $di2 = new DateInterval($d2);
- $dt1 = @date_create(); //new DateTime() would create an Exception if the default time zone is not defined
- if ($dt1 === false) {
- $max = false;
- } else {
- if ($min !== null && $min !== false) {
- $dt1->setTimestamp($min);
- }
- $max = $dt1->add($di2)->getTimestamp() - 1;
- }
- } catch (Exception $e) {
- $max = false;
- }
- } elseif ($d1 === null || $d1[0] !== 'P') {
- $max = @strtotime(_dateCeiling($d2));
- } else {
- $max = @strtotime($d2);
- }
- }
- if ($d1 !== null && $d1[0] === 'P') {
- try {
- $di1 = new DateInterval($d1);
- $dt2 = @date_create();
- if ($dt2 === false) {
- $min = false;
- } else {
- if ($max !== null && $max !== false) {
- $dt2->setTimestamp($max);
- }
- $min = $dt2->sub($di1)->getTimestamp() + 1;
- }
- } catch (Exception $e) {
- $min = false;
- }
- }
- return [$min, $max];
- }
|