Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 1 | <?php |
| 2 | |
| 3 | /* |
| 4 | * This file is part of the Symfony package. |
| 5 | * |
| 6 | * (c) Fabien Potencier <fabien@symfony.com> |
| 7 | * |
| 8 | * For the full copyright and license information, please view the LICENSE |
| 9 | * file that was distributed with this source code. |
| 10 | */ |
| 11 | |
| 12 | namespace Symfony\Component\VarDumper\Caster; |
| 13 | |
| 14 | use Symfony\Component\VarDumper\Cloner\Stub; |
| 15 | |
| 16 | /** |
| 17 | * Casts DateTimeInterface related classes to array representation. |
| 18 | * |
| 19 | * @author Dany Maillard <danymaillard93b@gmail.com> |
| 20 | * |
| 21 | * @final |
| 22 | */ |
| 23 | class DateCaster |
| 24 | { |
| 25 | private const PERIOD_LIMIT = 3; |
| 26 | |
| 27 | public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter) |
| 28 | { |
| 29 | $prefix = Caster::PREFIX_VIRTUAL; |
| 30 | $location = $d->getTimezone()->getLocation(); |
| 31 | $fromNow = (new \DateTime())->diff($d); |
| 32 | |
| 33 | $title = $d->format('l, F j, Y') |
| 34 | ."\n".self::formatInterval($fromNow).' from now' |
| 35 | .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') |
| 36 | ; |
| 37 | |
| 38 | unset( |
| 39 | $a[Caster::PREFIX_DYNAMIC.'date'], |
| 40 | $a[Caster::PREFIX_DYNAMIC.'timezone'], |
| 41 | $a[Caster::PREFIX_DYNAMIC.'timezone_type'] |
| 42 | ); |
| 43 | $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); |
| 44 | |
| 45 | $stub->class .= $d->format(' @U'); |
| 46 | |
| 47 | return $a; |
| 48 | } |
| 49 | |
| 50 | public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter) |
| 51 | { |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 52 | $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 53 | $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); |
| 54 | $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; |
| 55 | |
| 56 | $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; |
| 57 | |
| 58 | return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; |
| 59 | } |
| 60 | |
| 61 | private static function formatInterval(\DateInterval $i): string |
| 62 | { |
| 63 | $format = '%R '; |
| 64 | |
| 65 | if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 66 | $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); |
| 67 | $i = $d->diff($d->add($i)); // recalculate carry over points |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 68 | $format .= 0 < $i->days ? '%ad ' : ''; |
| 69 | } else { |
| 70 | $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); |
| 71 | } |
| 72 | |
| 73 | $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; |
| 74 | $format = '%R ' === $format ? '0s' : $format; |
| 75 | |
| 76 | return $i->format(rtrim($format)); |
| 77 | } |
| 78 | |
| 79 | public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter) |
| 80 | { |
| 81 | $location = $timeZone->getLocation(); |
| 82 | $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); |
| 83 | $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; |
| 84 | |
| 85 | $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; |
| 86 | |
| 87 | return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; |
| 88 | } |
| 89 | |
| 90 | public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter) |
| 91 | { |
| 92 | $dates = []; |
| 93 | foreach (clone $p as $i => $d) { |
| 94 | if (self::PERIOD_LIMIT === $i) { |
| 95 | $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); |
| 96 | $dates[] = sprintf('%s more', ($end = $p->getEndDate()) |
| 97 | ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) |
| 98 | : $p->recurrences - $i |
| 99 | ); |
| 100 | break; |
| 101 | } |
| 102 | $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); |
| 103 | } |
| 104 | |
| 105 | $period = sprintf( |
| 106 | 'every %s, from %s (%s) %s', |
| 107 | self::formatInterval($p->getDateInterval()), |
| 108 | self::formatDateTime($p->getStartDate()), |
| 109 | $p->include_start_date ? 'included' : 'excluded', |
| 110 | ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s' |
| 111 | ); |
| 112 | |
| 113 | $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; |
| 114 | |
| 115 | return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; |
| 116 | } |
| 117 | |
| 118 | private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string |
| 119 | { |
| 120 | return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); |
| 121 | } |
| 122 | |
| 123 | private static function formatSeconds(string $s, string $us): string |
| 124 | { |
| 125 | return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); |
| 126 | } |
| 127 | } |