41c4726f0eb738ce01cf2896cdb8cd4014f0b07e
[yaffs-website] / web / core / modules / datetime / src / Plugin / views / filter / Date.php
1 <?php
2
3 namespace Drupal\datetime\Plugin\views\filter;
4
5 use Drupal\Component\Datetime\DateTimePlus;
6 use Drupal\Core\Datetime\DateFormatterInterface;
7 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
8 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
9 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
10 use Drupal\views\FieldAPIHandlerTrait;
11 use Drupal\views\Plugin\views\filter\Date as NumericDate;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Symfony\Component\HttpFoundation\RequestStack;
14
15 /**
16  * Date/time views filter.
17  *
18  * Even thought dates are stored as strings, the numeric filter is extended
19  * because it provides more sensible operators.
20  *
21  * @ingroup views_filter_handlers
22  *
23  * @ViewsFilter("datetime")
24  */
25 class Date extends NumericDate implements ContainerFactoryPluginInterface {
26
27   use FieldAPIHandlerTrait;
28
29   /**
30    * The date formatter service.
31    *
32    * @var \Drupal\Core\Datetime\DateFormatterInterface
33    */
34   protected $dateFormatter;
35
36   /**
37    * Date format for SQL conversion.
38    *
39    * @var string
40    *
41    * @see \Drupal\views\Plugin\views\query\Sql::getDateFormat()
42    */
43   protected $dateFormat = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
44
45   /**
46    * Determines if the timezone offset is calculated.
47    *
48    * @var bool
49    */
50   protected $calculateOffset = TRUE;
51
52   /**
53    * The request stack used to determine current time.
54    *
55    * @var \Symfony\Component\HttpFoundation\RequestStack
56    */
57   protected $requestStack;
58
59   /**
60    * Constructs a new Date handler.
61    *
62    * @param array $configuration
63    *   A configuration array containing information about the plugin instance.
64    * @param string $plugin_id
65    *   The plugin ID for the plugin instance.
66    * @param mixed $plugin_definition
67    *   The plugin implementation definition.
68    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
69    *   The date formatter service.
70    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
71    *   The request stack used to determine the current time.
72    */
73   public function __construct(array $configuration, $plugin_id, $plugin_definition, DateFormatterInterface $date_formatter, RequestStack $request_stack) {
74     parent::__construct($configuration, $plugin_id, $plugin_definition);
75     $this->dateFormatter = $date_formatter;
76     $this->requestStack = $request_stack;
77
78     $definition = $this->getFieldStorageDefinition();
79     if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
80       // Date format depends on field storage format.
81       $this->dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
82       // Timezone offset calculation is not applicable to dates that are stored
83       // as date-only.
84       $this->calculateOffset = FALSE;
85     }
86   }
87
88   /**
89    * {@inheritdoc}
90    */
91   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
92     return new static(
93       $configuration,
94       $plugin_id,
95       $plugin_definition,
96       $container->get('date.formatter'),
97       $container->get('request_stack')
98     );
99   }
100
101   /**
102    * Override parent method, which deals with dates as integers.
103    */
104   protected function opBetween($field) {
105     $timezone = $this->getTimezone();
106     $origin_offset = $this->getOffset($this->value['min'], $timezone);
107
108     // Although both 'min' and 'max' values are required, default empty 'min'
109     // value as UNIX timestamp 0.
110     $min = (!empty($this->value['min'])) ? $this->value['min'] : '@0';
111
112     // Convert to ISO format and format for query. UTC timezone is used since
113     // dates are stored in UTC.
114     $a = new DateTimePlus($min, new \DateTimeZone($timezone));
115     $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);
116     $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone));
117     $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);
118
119     // This is safe because we are manually scrubbing the values.
120     $operator = strtoupper($this->operator);
121     $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
122     $this->query->addWhereExpression($this->options['group'], "$field $operator $a AND $b");
123   }
124
125   /**
126    * Override parent method, which deals with dates as integers.
127    */
128   protected function opSimple($field) {
129     $timezone = $this->getTimezone();
130     $origin_offset = $this->getOffset($this->value['value'], $timezone);
131
132     // Convert to ISO. UTC timezone is used since dates are stored in UTC.
133     $value = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone));
134     $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);
135
136     // This is safe because we are manually scrubbing the value.
137     $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
138     $this->query->addWhereExpression($this->options['group'], "$field $this->operator $value");
139   }
140
141   /**
142    * Get the proper time zone to use in computations.
143    *
144    * Date-only fields do not have a time zone associated with them, so the
145    * filter input needs to use UTC for reference. Otherwise, use the time zone
146    * for the current user.
147    *
148    * @return string
149    *   The time zone name.
150    */
151   protected function getTimezone() {
152     return $this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT
153       ? DateTimeItemInterface::STORAGE_TIMEZONE
154       : drupal_get_user_timezone();
155   }
156
157   /**
158    * Get the proper offset from UTC to use in computations.
159    *
160    * @param string $time
161    *   A date/time string compatible with \DateTime. It is used as the
162    *   reference for computing the offset, which can vary based on the time
163    *   zone rules.
164    * @param string $timezone
165    *   The time zone that $time is in.
166    *
167    * @return int
168    *   The computed offset in seconds.
169    */
170   protected function getOffset($time, $timezone) {
171     // Date-only fields do not have a time zone or offset from UTC associated
172     // with them. For relative (i.e. 'offset') comparisons, we need to compute
173     // the user's offset from UTC for use in the query.
174     $origin_offset = 0;
175     if ($this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT && $this->value['type'] === 'offset') {
176       $origin_offset = $origin_offset + timezone_offset_get(new \DateTimeZone(drupal_get_user_timezone()), new \DateTime($time, new \DateTimeZone($timezone)));
177     }
178
179     return $origin_offset;
180   }
181
182 }