DrupalDateTime.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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.php
  17. */
  18. class DrupalDateTime extends DateTimePlus {
  19. use StringTranslationTrait;
  20. /**
  21. * Format string translation cache.
  22. */
  23. protected $formatTranslationCache;
  24. /**
  25. * Constructs a date object.
  26. *
  27. * @param string $time
  28. * A date/input_time_adjusted string. Defaults to 'now'.
  29. * @param mixed $timezone
  30. * PHP DateTimeZone object, string or NULL allowed.
  31. * Defaults to NULL. Note that the $timezone parameter and the current
  32. * timezone are ignored when the $time parameter either is a UNIX timestamp
  33. * (e.g. @946684800) or specifies a timezone
  34. * (e.g. 2010-01-28T15:00:00+02:00).
  35. * @see http://php.net/manual/datetime.construct.php
  36. * @param array $settings
  37. * - validate_format: (optional) Boolean choice to validate the
  38. * created date using the input format. The format used in
  39. * createFromFormat() allows slightly different values than format().
  40. * Using an input format that works in both functions makes it
  41. * possible to a validation step to confirm that the date created
  42. * from a format string exactly matches the input. This option
  43. * indicates the format can be used for validation. Defaults to TRUE.
  44. * - langcode: (optional) Used to control the result of the format() method.
  45. * Defaults to NULL.
  46. * - debug: (optional) Boolean choice to leave debug values in the
  47. * date object for debugging purposes. Defaults to FALSE.
  48. */
  49. public function __construct($time = 'now', $timezone = NULL, $settings = []) {
  50. if (!isset($settings['langcode'])) {
  51. $settings['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
  52. }
  53. // Instantiate the parent class.
  54. parent::__construct($time, $timezone, $settings);
  55. }
  56. /**
  57. * Overrides prepareTimezone().
  58. *
  59. * Override basic component timezone handling to use Drupal's
  60. * knowledge of the preferred user timezone.
  61. */
  62. protected function prepareTimezone($timezone) {
  63. if (empty($timezone)) {
  64. // Fallback to user or system default timezone.
  65. $timezone = drupal_get_user_timezone();
  66. }
  67. return parent::prepareTimezone($timezone);
  68. }
  69. /**
  70. * Overrides format().
  71. *
  72. * @param string $format
  73. * A format string using either PHP's date().
  74. * @param array $settings
  75. * - timezone: (optional) String timezone name. Defaults to the timezone
  76. * of the date object.
  77. * - langcode: (optional) String two letter language code used to control
  78. * the result of the format() method. Defaults to NULL.
  79. *
  80. * @return string
  81. * The formatted value of the date. Since the format may contain user input,
  82. * this value should be escaped when output.
  83. */
  84. public function format($format, $settings = []) {
  85. $langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
  86. $value = '';
  87. // Format the date and catch errors.
  88. try {
  89. // Encode markers that should be translated. 'A' becomes
  90. // '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
  91. // and we assume they are not in the input string.
  92. // Paired backslashes are isolated to prevent errors in
  93. // read-ahead evaluation. The read-ahead expression ensures that
  94. // A matches, but not \A.
  95. $format = preg_replace(['/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'], ["\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"], $format);
  96. // Call date_format().
  97. $format = parent::format($format, $settings);
  98. // Translates a formatted date string.
  99. $translation_callback = function ($matches) use ($langcode) {
  100. $code = $matches[1];
  101. $string = $matches[2];
  102. if (!isset($this->formatTranslationCache[$langcode][$code][$string])) {
  103. $options = ['langcode' => $langcode];
  104. if ($code == 'F') {
  105. $options['context'] = 'Long month name';
  106. }
  107. if ($code == '') {
  108. $this->formatTranslationCache[$langcode][$code][$string] = $string;
  109. }
  110. else {
  111. $this->formatTranslationCache[$langcode][$code][$string] = $this->t($string, [], $options);
  112. }
  113. }
  114. return $this->formatTranslationCache[$langcode][$code][$string];
  115. };
  116. // Translate the marked sequences.
  117. $value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', $translation_callback, $format);
  118. }
  119. catch (\Exception $e) {
  120. $this->errors[] = $e->getMessage();
  121. }
  122. return $value;
  123. }
  124. }