Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / modules / datetime / src / Plugin / views / filter / Date.php
index 0358054587671b445b23534f65554ab0d30e8f3e..14215206c58ea2739a77ede2ee3869ae643cec87 100644 (file)
@@ -2,9 +2,11 @@
 
 namespace Drupal\datetime\Plugin\views\filter;
 
+use Drupal\Component\Datetime\DateTimePlus;
 use Drupal\Core\Datetime\DateFormatterInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
 use Drupal\views\FieldAPIHandlerTrait;
 use Drupal\views\Plugin\views\filter\Date as NumericDate;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -38,7 +40,14 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
    *
    * @see \Drupal\views\Plugin\views\query\Sql::getDateFormat()
    */
-  protected $dateFormat = DATETIME_DATETIME_STORAGE_FORMAT;
+  protected $dateFormat = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
+
+  /**
+   * Determines if the timezone offset is calculated.
+   *
+   * @var bool
+   */
+  protected $calculateOffset = TRUE;
 
   /**
    * The request stack used to determin current time.
@@ -66,10 +75,13 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
     $this->dateFormatter = $date_formatter;
     $this->requestStack = $request_stack;
 
-    // Date format depends on field storage format.
     $definition = $this->getFieldStorageDefinition();
     if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
-      $this->dateFormat = DATETIME_DATE_STORAGE_FORMAT;
+      // Date format depends on field storage format.
+      $this->dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+      // Timezone offset calculation is not applicable to dates that are stored
+      // as date-only.
+      $this->calculateOffset = FALSE;
     }
   }
 
@@ -90,20 +102,23 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
    * Override parent method, which deals with dates as integers.
    */
   protected function opBetween($field) {
-    $origin = ($this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
-    $a = intval(strtotime($this->value['min'], $origin));
-    $b = intval(strtotime($this->value['max'], $origin));
+    $timezone = $this->getTimezone();
+    $origin_offset = $this->getOffset($this->value['min'], $timezone);
 
-    // Formatting will vary on date storage.
+    // Although both 'min' and 'max' values are required, default empty 'min'
+    // value as UNIX timestamp 0.
+    $min = (!empty($this->value['min'])) ? $this->value['min'] : '@0';
 
     // Convert to ISO format and format for query. UTC timezone is used since
     // dates are stored in UTC.
-    $a = $this->query->getDateFormat("'" . $this->dateFormatter->format($a, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
-    $b = $this->query->getDateFormat("'" . $this->dateFormatter->format($b, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+    $a = new DateTimePlus($min, new \DateTimeZone($timezone));
+    $a = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($a->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
+    $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone));
+    $b = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($b->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
 
     // This is safe because we are manually scrubbing the values.
     $operator = strtoupper($this->operator);
-    $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+    $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
     $this->query->addWhereExpression($this->options['group'], "$field $operator $a AND $b");
   }
 
@@ -111,15 +126,57 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
    * Override parent method, which deals with dates as integers.
    */
   protected function opSimple($field) {
-    $origin = (!empty($this->value['type']) && $this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
-    $value = intval(strtotime($this->value['value'], $origin));
+    $timezone = $this->getTimezone();
+    $origin_offset = $this->getOffset($this->value['value'], $timezone);
 
-    // Convert to ISO. UTC is used since dates are stored in UTC.
-    $value = $this->query->getDateFormat("'" . $this->dateFormatter->format($value, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+    // Convert to ISO. UTC timezone is used since dates are stored in UTC.
+    $value = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone));
+    $value = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($value->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
 
     // This is safe because we are manually scrubbing the value.
-    $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+    $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
     $this->query->addWhereExpression($this->options['group'], "$field $this->operator $value");
   }
 
+  /**
+   * Get the proper time zone to use in computations.
+   *
+   * Date-only fields do not have a time zone associated with them, so the
+   * filter input needs to use UTC for reference. Otherwise, use the time zone
+   * for the current user.
+   *
+   * @return string
+   *   The time zone name.
+   */
+  protected function getTimezone() {
+    return $this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT
+      ? DateTimeItemInterface::STORAGE_TIMEZONE
+      : drupal_get_user_timezone();
+  }
+
+  /**
+   * Get the proper offset from UTC to use in computations.
+   *
+   * @param string $time
+   *   A date/time string compatible with \DateTime. It is used as the
+   *   reference for computing the offset, which can vary based on the time
+   *   zone rules.
+   * @param string $timezone
+   *   The time zone that $time is in.
+   *
+   * @return int
+   *   The computed offset in seconds.
+   */
+  protected function getOffset($time, $timezone) {
+    // Date-only fields do not have a time zone or offset from UTC associated
+    // with them. For relative (i.e. 'offset') comparisons, we need to compute
+    // the user's offset from UTC for use in the query.
+    $origin_offset = 0;
+    if ($this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT && $this->value['type'] === 'offset') {
+      $origin_offset = $origin_offset + timezone_offset_get(new \DateTimeZone(drupal_get_user_timezone()), new \DateTime($time, new \DateTimeZone($timezone)));
+    }
+
+    return $origin_offset;
+  }
+
 }