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;
*
* @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.
$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;
}
}
* 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");
}
* 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;
+ }
+
}