TimezoneTransformer.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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\Intl\DateFormatter\DateFormat;
  11. use Symfony\Component\Intl\Exception\NotImplementedException;
  12. /**
  13. * Parser and formatter for time zone format.
  14. *
  15. * @author Igor Wiedler <igor@wiedler.ch>
  16. *
  17. * @internal
  18. */
  19. class TimezoneTransformer extends Transformer
  20. {
  21. /**
  22. * {@inheritdoc}
  23. *
  24. * @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT)
  25. */
  26. public function format(\DateTime $dateTime, int $length): string
  27. {
  28. $timeZone = substr($dateTime->getTimezone()->getName(), 0, 3);
  29. if (!\in_array($timeZone, ['Etc', 'UTC', 'GMT'])) {
  30. throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.');
  31. }
  32. if ('Etc' === $timeZone) {
  33. // i.e. Etc/GMT+1, Etc/UTC, Etc/Zulu
  34. $timeZone = substr($dateTime->getTimezone()->getName(), 4);
  35. }
  36. // From ICU >= 59.1 GMT and UTC are no longer unified
  37. if (\in_array($timeZone, ['UTC', 'UCT', 'Universal', 'Zulu'])) {
  38. // offset is not supported with UTC
  39. return $length > 3 ? 'Coordinated Universal Time' : 'UTC';
  40. }
  41. $offset = (int) $dateTime->format('O');
  42. // From ICU >= 4.8, the zero offset is no more used, example: GMT instead of GMT+00:00
  43. if (0 === $offset) {
  44. return $length > 3 ? 'Greenwich Mean Time' : 'GMT';
  45. }
  46. if ($length > 3) {
  47. return $dateTime->format('\G\M\TP');
  48. }
  49. return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function getReverseMatchingRegExp(int $length): string
  55. {
  56. return 'GMT[+-]\d{2}:?\d{2}';
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function extractDateOptions(string $matched, int $length): array
  62. {
  63. return [
  64. 'timezone' => self::getEtcTimeZoneId($matched),
  65. ];
  66. }
  67. /**
  68. * Get an Etc/GMT timezone identifier for the specified timezone.
  69. *
  70. * The PHP documentation for timezones states to not use the 'Other' time zones because them exists
  71. * "for backwards compatibility". However all Etc/GMT time zones are in the tz database 'etcetera' file,
  72. * which indicates they are not deprecated (neither are old names).
  73. *
  74. * Only GMT, Etc/Universal, Etc/Zulu, Etc/Greenwich, Etc/GMT-0, Etc/GMT+0 and Etc/GMT0 are old names and
  75. * are linked to Etc/GMT or Etc/UTC.
  76. *
  77. * @param string $formattedTimeZone A GMT timezone string (GMT-03:00, e.g.)
  78. *
  79. * @return string A timezone identifier
  80. *
  81. * @see https://php.net/timezones.others
  82. *
  83. * @throws NotImplementedException When the GMT time zone have minutes offset different than zero
  84. * @throws \InvalidArgumentException When the value can not be matched with pattern
  85. */
  86. public static function getEtcTimeZoneId(string $formattedTimeZone): string
  87. {
  88. if (preg_match('/GMT(?P<signal>[+-])(?P<hours>\d{2}):?(?P<minutes>\d{2})/', $formattedTimeZone, $matches)) {
  89. $hours = (int) $matches['hours'];
  90. $minutes = (int) $matches['minutes'];
  91. $signal = '-' === $matches['signal'] ? '+' : '-';
  92. if (0 < $minutes) {
  93. throw new NotImplementedException(sprintf('It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: "%s".', $formattedTimeZone));
  94. }
  95. return 'Etc/GMT'.(0 !== $hours ? $signal.$hours : '');
  96. }
  97. throw new \InvalidArgumentException(sprintf('The GMT time zone "%s" does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.', $formattedTimeZone));
  98. }
  99. }