DrupalDateTime.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <?php
  2. namespace Drupal\Core\Datetime;
  3. use Drupal\Component\Datetime\DateTimePlus;
  4. use Drupal\Core\StringTranslation\StringTranslationTrait;
  5. /**
  6. * Extends DateTimePlus().
  7. *
  8. * This class extends the basic component and adds in Drupal-specific
  9. * handling, like translation of the format() method.
  10. *
  11. * Static methods in base class can also be used to create DrupalDateTime objects.
  12. * For example:
  13. *
  14. * DrupalDateTime::createFromArray( array('year' => 2010, 'month' => 9, 'day' => 28) )
  15. *
  16. * @see \Drupal\Component\Datetime\DateTimePlus
  17. */
  18. class DrupalDateTime extends DateTimePlus {
  19. use StringTranslationTrait;
  20. /**
  21. * Formatted strings translation cache.
  22. *
  23. * Translation cache represents an instance storage for formatted date
  24. * strings. It contains a multidimensional array where:
  25. * - first level keys - are drupal language codes;
  26. * - second level keys - are each symbols of given format string (like 'F');
  27. * - third level keys - are original matched strings related to the symbol;
  28. * - values - are translated or not-translated original strings (depends on
  29. * if a particular symbol represents translatable value according to PHP's
  30. * date() format character).
  31. *
  32. * For example:
  33. * @code
  34. * [
  35. * 'en' => [
  36. * 'F' => [
  37. * 'November' => t('November'),
  38. * 'December' => t('December'),
  39. * ],
  40. * 'd' => [
  41. * '10' => '10',
  42. * '31' => '31',
  43. * ],
  44. * ],
  45. * ]
  46. * @endcode
  47. *
  48. * @var array
  49. */
  50. protected $formatTranslationCache;
  51. /**
  52. * Constructs a date object.
  53. *
  54. * @param string $time
  55. * A date/input_time_adjusted string. Defaults to 'now'.
  56. * @param mixed $timezone
  57. * PHP DateTimeZone object, string or NULL allowed.
  58. * Defaults to NULL. Note that the $timezone parameter and the current
  59. * timezone are ignored when the $time parameter either is a UNIX timestamp
  60. * (e.g. @946684800) or specifies a timezone
  61. * (e.g. 2010-01-28T15:00:00+02:00).
  62. * @see http://php.net/manual/datetime.construct.php
  63. * @param array $settings
  64. * - validate_format: (optional) Boolean choice to validate the
  65. * created date using the input format. The format used in
  66. * createFromFormat() allows slightly different values than format().
  67. * Using an input format that works in both functions makes it
  68. * possible to a validation step to confirm that the date created
  69. * from a format string exactly matches the input. This option
  70. * indicates the format can be used for validation. Defaults to TRUE.
  71. * - langcode: (optional) Used to control the result of the format() method.
  72. * Defaults to NULL.
  73. * - debug: (optional) Boolean choice to leave debug values in the
  74. * date object for debugging purposes. Defaults to FALSE.
  75. */
  76. public function __construct($time = 'now', $timezone = NULL, $settings = []) {
  77. if (!isset($settings['langcode'])) {
  78. $settings['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
  79. }
  80. // Instantiate the parent class.
  81. parent::__construct($time, $timezone, $settings);
  82. }
  83. /**
  84. * Overrides prepareTimezone().
  85. *
  86. * Override basic component timezone handling to use Drupal's
  87. * knowledge of the preferred user timezone.
  88. */
  89. protected function prepareTimezone($timezone) {
  90. if (empty($timezone)) {
  91. // Fallback to user or system default timezone.
  92. $timezone = date_default_timezone_get();
  93. }
  94. return parent::prepareTimezone($timezone);
  95. }
  96. /**
  97. * Overrides format().
  98. *
  99. * @param string $format
  100. * A format string using either PHP's date().
  101. * @param array $settings
  102. * - timezone: (optional) String timezone name. Defaults to the timezone
  103. * of the date object.
  104. * - langcode: (optional) String two letter language code used to control
  105. * the result of the format() method. Defaults to NULL.
  106. *
  107. * @return string
  108. * The formatted value of the date. Since the format may contain user input,
  109. * this value should be escaped when output.
  110. */
  111. public function format($format, $settings = []) {
  112. $langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
  113. $value = '';
  114. // Format the date and catch errors.
  115. try {
  116. // Encode markers that should be translated. 'A' becomes
  117. // '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
  118. // and we assume they are not in the input string.
  119. // Paired backslashes are isolated to prevent errors in
  120. // read-ahead evaluation. The read-ahead expression ensures that
  121. // A matches, but not \A.
  122. $format = preg_replace(['/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'], ["\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"], $format);
  123. // Call date_format().
  124. $format = parent::format($format, $settings);
  125. // Translates a formatted date string.
  126. $translation_callback = function ($matches) use ($langcode) {
  127. $code = $matches[1];
  128. $string = $matches[2];
  129. if (!isset($this->formatTranslationCache[$langcode][$code][$string])) {
  130. $options = ['langcode' => $langcode];
  131. if ($code == 'F') {
  132. $options['context'] = 'Long month name';
  133. }
  134. if ($code == '') {
  135. $this->formatTranslationCache[$langcode][$code][$string] = $string;
  136. }
  137. else {
  138. $this->formatTranslationCache[$langcode][$code][$string] = $this->t($string, [], $options);
  139. }
  140. }
  141. return $this->formatTranslationCache[$langcode][$code][$string];
  142. };
  143. // Translate the marked sequences.
  144. $value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', $translation_callback, $format);
  145. }
  146. catch (\Exception $e) {
  147. $this->errors[] = $e->getMessage();
  148. }
  149. return $value;
  150. }
  151. }