Newer
Older
José Manuel Rodríguez Vélez
committed
<?php
namespace Drupal\facets\Utility;
use Drupal\Core\Datetime\DateFormatterInterface;
José Manuel Rodríguez Vélez
committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* Dates Handler service.
*/
class FacetsDateHandler {
/**
* String that represents a time gap of a day between two dates.
*/
const FACETS_DATE_DAY = 'DAY';
/**
* String that represents a time gap of a year between two dates.
*/
const FACETS_DATE_YEAR = 'YEAR';
/**
* String that represents a time gap of a month between two dates.
*/
const FACETS_DATE_MONTH = 'MONTH';
/**
* String that represents a time gap of an hour between two dates.
*/
const FACETS_DATE_HOUR = 'HOUR';
/**
* String that represents a time gap of a minute between two dates.
*/
const FACETS_DATE_MINUTE = 'MINUTE';
/**
* String that represents a time gap of a second between two dates.
*/
const FACETS_DATE_SECOND = 'SECOND';
/**
* Date string for ISO 8601 date formats.
*/
const FACETS_DATE_ISO8601 = 'Y-m-d\TH:i:s\Z';
/**
* Regex pattern for range queries.
*/
const FACETS_REGEX_RANGE = '/^[\[\{](\S+) TO (\S+)[\]\}]$/';
/**
* Regex pattern for date queries.
*/
const FACETS_REGEX_DATE = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/';
/**
* Regex pattern for date ranges.
*/
const FACETS_REGEX_DATE_RANGE = '/^\[((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z) TO ((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z)\]$/';
/**
* The date formatting service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
José Manuel Rodríguez Vélez
committed
*/
protected $dateFormatter;
/**
* FacetsDateHandler constructor.
*
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
José Manuel Rodríguez Vélez
committed
* The date formatting service.
*/
public function __construct(DateFormatterInterface $date_formatter) {
José Manuel Rodríguez Vélez
committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
$this->dateFormatter = $date_formatter;
}
/**
* Converts dates from Unix timestamps into ISO 8601 format.
*
* @param int $timestamp
* An integer containing the Unix timestamp being converted.
* @param string $gap
* A string containing the gap, see FACETS_DATE_* constants for valid
* values. Defaults to FACETS_DATE_SECOND.
*
* @return string
* A string containing the date in ISO 8601 format.
*/
public function isoDate($timestamp, $gap = 'SECOND') {
switch ($gap) {
case static::FACETS_DATE_SECOND:
$format = static::FACETS_DATE_ISO8601;
break;
case static::FACETS_DATE_MINUTE:
$format = 'Y-m-d\TH:i:00\Z';
break;
case static::FACETS_DATE_HOUR:
$format = 'Y-m-d\TH:00:00\Z';
break;
case static::FACETS_DATE_DAY:
$format = 'Y-m-d\T00:00:00\Z';
break;
case static::FACETS_DATE_MONTH:
$format = 'Y-m-01\T00:00:00\Z';
break;
case static::FACETS_DATE_YEAR:
$format = 'Y-01-01\T00:00:00\Z';
break;
default:
$format = static::FACETS_DATE_ISO8601;
break;
}
return gmdate($format, $timestamp);
}
/**
* Return a date gap one increment smaller than the one passed.
*
* @param string $gap
* A string containing the gap, see FACETS_DATE_* constants for valid
* values.
* @param string $min_gap
* A string containing the the minimum gap that can be returned, defaults to
* FACETS_DATE_SECOND. This is useful for defining the smallest increment
* that can be used in a date drilldown.
*
* @return string
* A string containing the smaller date gap, NULL if there is no smaller
* gap. See FACETS_DATE_* constants for valid values.
*/
public function getNextDateGap($gap, $min_gap = self::FACETS_DATE_SECOND) {
// Array of numbers used to determine whether the next gap is smaller than
// the minimum gap allowed in the drilldown.
MUNAVIJAYALAKSHMI P
committed
$gap_numbers = [
José Manuel Rodríguez Vélez
committed
static::FACETS_DATE_YEAR => 6,
static::FACETS_DATE_MONTH => 5,
static::FACETS_DATE_DAY => 4,
static::FACETS_DATE_HOUR => 3,
static::FACETS_DATE_MINUTE => 2,
static::FACETS_DATE_SECOND => 1,
MUNAVIJAYALAKSHMI P
committed
];
José Manuel Rodríguez Vélez
committed
// Gets gap numbers for both the gap and minimum gap, checks if the next gap
// is within the limit set by the $min_gap parameter.
$gap_num = isset($gap_numbers[$gap]) ? $gap_numbers[$gap] : 6;
$min_num = isset($gap_numbers[$min_gap]) ? $gap_numbers[$min_gap] : 1;
return ($gap_num > $min_num) ? array_search($gap_num - 1, $gap_numbers) : $min_gap;
}
/**
* Determines the best search gap to use for an arbitrary date range.
*
* Generally, we use the maximum gap that fits between the start and end date.
* If they are more than a year apart, 1 year; if they are more than a month
* apart, 1 month; etc.
*
* This function uses Unix timestamps for its computation and so is not useful
* for dates outside that range.
*
* @param int $start_time
* A string containing the start date as an ISO date string.
* @param int $end_time
* A string containing the end date as an ISO date string.
* @param string|null $min_gap
José Manuel Rodríguez Vélez
committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
* (Optional) The minimum gap that should be returned.
*
* @return string
* A string containing the gap, see FACETS_DATE_* constants for valid
* values. Returns FALSE of either of the dates cannot be converted to a
* timestamp.
*/
public function getTimestampGap($start_time, $end_time, $min_gap = NULL) {
$time_diff = $end_time - $start_time;
switch (TRUE) {
case ($time_diff >= 31536000):
$gap = static::FACETS_DATE_YEAR;
break;
case ($time_diff >= 86400 * gmdate('t', $start_time)):
$gap = static::FACETS_DATE_MONTH;
break;
case ($time_diff >= 86400):
$gap = static::FACETS_DATE_DAY;
break;
case ($time_diff >= 3600):
$gap = static::FACETS_DATE_HOUR;
break;
case ($time_diff >= 60):
$gap = static::FACETS_DATE_MINUTE;
break;
default:
$gap = static::FACETS_DATE_SECOND;
break;
}
// Return the calculated gap if a minimum gap was not passed of the
// calculated gap is a larger interval than the minimum gap.
if (is_null($min_gap) || $this->gapCompare($gap, $min_gap) >= 0) {
return $gap;
}
else {
return $min_gap;
}
}
/**
* Converts ISO date strings to Unix timestamps.
*
* Passes values to the FACETS_get_timestamp_gap() function to calculate the
* gap.
*
* @param string $start_date
* A string containing the start date as an ISO date string.
* @param string $end_date
* A string containing the end date as an ISO date string.
* @param string|null $min_gap
José Manuel Rodríguez Vélez
committed
* (Optional) The minimum gap that should be returned.
*
* @return string
* A string containing the gap, see FACETS_DATE_* constants for valid
* values. Returns FALSE of either of the dates cannot be converted to a
* timestamp.
*
* @see FACETS_get_timestamp_gap()
*/
public function getDateGap($start_date, $end_date, $min_gap = NULL) {
MUNAVIJAYALAKSHMI P
committed
$range = [strtotime($start_date), strtotime($end_date)];
José Manuel Rodríguez Vélez
committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
if (!in_array(FALSE, $range, TRUE)) {
return $this->getTimestampGap($range[0], $range[1], $min_gap);
}
return FALSE;
}
/**
* Returns a formatted date based on the passed timestamp and gap.
*
* This function assumes that gaps less than one day will be displayed in a
* search context in which a larger containing gap including a day is already
* displayed. So, HOUR, MINUTE, and SECOND gaps only display time information,
* without date.
*
* @param int $timestamp
* An integer containing the Unix timestamp.
* @param string $gap
* A string containing the gap, see FACETS_DATE_* constants for valid
* values, defaults to YEAR.
*
* @return string
* A gap-appropriate display date used in the facet link.
*/
public function formatTimestamp($timestamp, $gap = self::FACETS_DATE_YEAR) {
switch ($gap) {
case static::FACETS_DATE_MONTH:
return $this->dateFormatter->format($timestamp, 'custom', 'F Y', 'UTC');
case static::FACETS_DATE_DAY:
return $this->dateFormatter->format($timestamp, 'custom', 'F j, Y', 'UTC');
case static::FACETS_DATE_HOUR:
return $this->dateFormatter->format($timestamp, 'custom', 'g A', 'UTC');
case static::FACETS_DATE_MINUTE:
return $this->dateFormatter->format($timestamp, 'custom', 'g:i A', 'UTC');
case static::FACETS_DATE_SECOND:
return $this->dateFormatter->format($timestamp, 'custom', 'g:i:s A', 'UTC');
default:
return $this->dateFormatter->format($timestamp, 'custom', 'Y', 'UTC');
}
}
/**
* Returns a formatted date based on the passed ISO date string and gap.
*
* @param string $date
* A string containing the date as an ISO date string.
* @param int $gap
* An integer containing the gap, see FACETS_DATE_* constants for valid
* values, defaults to YEAR.
* @param string $callback
* The formatting callback, defaults to "FACETS_format_timestamp". This is
* a string that can be called as a valid callback.
*
* @return string
* A gap-appropriate display date used in the facet link.
*
* @see FACETS_format_timestamp()
*/
public function formatDate($date, $gap = self::FACETS_DATE_YEAR, $callback = 'facets_format_timestamp') {
$timestamp = strtotime($date);
return $callback($timestamp, $gap);
}
/**
* Returns the next increment from the given ISO date and gap.
*
* This function is useful for getting the upper limit of a date range from
* the given start date.
*
* @param string $date
* A string containing the date as an ISO date string.
* @param string $gap
* A string containing the gap, see FACETS_DATE_* constants for valid
* values, defaults to YEAR.
*
* @return string
* A string containing the date, FALSE if the passed date could not be
* parsed.
*/
public function getNextDateIncrement($date, $gap) {
if (preg_match(static::FACETS_REGEX_DATE, $date, $match)) {
// Increments the timestamp.
switch ($gap) {
case static::FACETS_DATE_MONTH:
$match[2] += 1;
break;
case static::FACETS_DATE_DAY:
$match[3] += 1;
break;
case static::FACETS_DATE_HOUR:
$match[4] += 1;
break;
case static::FACETS_DATE_MINUTE:
$match[5] += 1;
break;
case static::FACETS_DATE_SECOND:
$match[6] += 1;
break;
default:
$match[1] += 1;
break;
}
// Gets the next increment.
return $this->isoDate(
gmmktime($match[4], $match[5], $match[6], $match[2], $match[3], $match[1])
);
}
return FALSE;
}
/**
* Compares two timestamp gaps.
*
* @param int $gap1
* An integer containing the gap, see FACETS_DATE_* constants for valid
* values.
* @param int $gap2
* An integer containing the gap, see FACETS_DATE_* constants for valid
* values.
*
* @return int
* Returns -1 if gap1 is less than gap2, 1 if gap1 is greater than gap2, and
* 0 if they are equal.
*/
public function gapCompare($gap1, $gap2) {
José Manuel Rodríguez Vélez
committed
static::FACETS_DATE_YEAR => 6,
static::FACETS_DATE_MONTH => 5,
static::FACETS_DATE_DAY => 4,
static::FACETS_DATE_HOUR => 3,
static::FACETS_DATE_MINUTE => 2,
static::FACETS_DATE_SECOND => 1,
José Manuel Rodríguez Vélez
committed
$gap1_num = isset($gap_numbers[$gap1]) ? $gap_numbers[$gap1] : 6;
$gap2_num = isset($gap_numbers[$gap2]) ? $gap_numbers[$gap2] : 6;
if ($gap1_num == $gap2_num) {
return 0;
}
else {
return ($gap1_num < $gap2_num) ? -1 : 1;
}
}
/**
* Extracts "start" and "end" dates from an active item.
José Manuel Rodríguez Vélez
committed
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
*
* @param string $item
* The active item to extract the dates.
*
* @return mixed
* Returns FALSE if no item found and an array with the dates if the dates
* were extracted as expected.
*/
public function extractActiveItems($item) {
$active_item = [];
if (preg_match(static::FACETS_REGEX_DATE_RANGE, $item, $matches)) {
$active_item['start'] = [
'timestamp' => strtotime($matches[1]),
'iso' => $matches[1],
];
$active_item['end'] = [
'timestamp' => strtotime($matches[8]),
'iso' => $matches[8],
];
return $active_item;
}
return FALSE;
}
}