You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.7 KiB

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