1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* @file
|
5 |
|
|
* This module will make the date API available to other modules.
|
6 |
|
|
* Designed to provide a light but flexible assortment of functions
|
7 |
|
|
* and constants, with more functionality in additional files that
|
8 |
|
|
* are not loaded unless other modules specifically include them.
|
9 |
|
|
*/
|
10 |
|
|
|
11 |
|
|
/**
|
12 |
|
|
* Set up some constants.
|
13 |
|
|
*
|
14 |
|
|
* Includes standard date types, format strings, strict regex strings for ISO
|
15 |
|
|
* and DATETIME formats (seconds are optional).
|
16 |
|
|
*
|
17 |
|
|
* The loose regex will find any variety of ISO date and time, with or
|
18 |
|
|
* without time, with or without dashes and colons separating the elements,
|
19 |
|
|
* and with either a 'T' or a space separating date and time.
|
20 |
|
|
*/
|
21 |
|
|
define('DATE_ISO', 'date');
|
22 |
|
|
define('DATE_UNIX', 'datestamp');
|
23 |
|
|
define('DATE_DATETIME', 'datetime');
|
24 |
|
|
define('DATE_ARRAY', 'array');
|
25 |
|
|
define('DATE_OBJECT', 'object');
|
26 |
|
|
define('DATE_ICAL', 'ical');
|
27 |
|
|
|
28 |
|
|
define('DATE_FORMAT_ISO', "Y-m-d\TH:i:s");
|
29 |
|
|
define('DATE_FORMAT_UNIX', "U");
|
30 |
|
|
define('DATE_FORMAT_DATETIME', "Y-m-d H:i:s");
|
31 |
|
|
define('DATE_FORMAT_ICAL', "Ymd\THis");
|
32 |
|
|
define('DATE_FORMAT_ICAL_DATE', "Ymd");
|
33 |
|
|
define('DATE_FORMAT_DATE', 'Y-m-d');
|
34 |
|
|
|
35 |
|
|
define('DATE_REGEX_ISO', '/(\d{4})?(-(\d{2}))?(-(\d{2}))?([T\s](\d{2}))?(:(\d{2}))?(:(\d{2}))?/');
|
36 |
|
|
define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/');
|
37 |
|
|
define('DATE_REGEX_LOOSE', '/(\d{4})-?(\d{1,2})-?(\d{1,2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)?/');
|
38 |
|
|
define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/');
|
39 |
|
|
define('DATE_REGEX_ICAL_DATETIME', '/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?/');
|
40 |
|
|
|
41 |
|
|
/**
|
42 |
|
|
* Core DateTime extension module used for as many date operations as possible.
|
43 |
|
|
*/
|
44 |
|
|
|
45 |
|
|
/**
|
46 |
|
|
* Implements hook_help().
|
47 |
|
|
*/
|
48 |
|
|
function date_help($path, $arg) {
|
49 |
|
|
switch ($path) {
|
50 |
|
|
case 'admin/help#date':
|
51 |
|
|
$output = '';
|
52 |
|
|
$messages = date_api_status();
|
53 |
|
|
$output = '<h2>Date API Status</h2>';
|
54 |
|
|
if (!empty($messages['success'])) {
|
55 |
|
|
$output .= '<ul><li>' . implode('</li><li>', $messages['success']) . '</li></ul>';
|
56 |
|
|
}
|
57 |
|
|
if (!empty($messages['errors'])) {
|
58 |
|
|
$output .= '<h3>Errors</h3><ul class="error"><li>' . implode('</li><li>', $messages['errors']) . '</li></ul>';
|
59 |
|
|
}
|
60 |
|
|
|
61 |
|
|
if (module_exists('date_tools')) {
|
62 |
b720ea3e
|
Assos Assos
|
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. The !date_wizard makes it easy to create a simple date content type and with a date field.', array('!date_wizard' => l(t('Date wizard'), 'admin/config/date/tools/date_wizard')));
|
63 |
85ad3d82
|
Assos Assos
|
}
|
64 |
|
|
else {
|
65 |
b720ea3e
|
Assos Assos
|
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. If you enable the Date Tools module, it provides a Date Wizard that makes it easy to create a simple date content type with a date field.');
|
66 |
85ad3d82
|
Assos Assos
|
}
|
67 |
|
|
|
68 |
|
|
$output .= '<h2>More Information</h2><p>' . t('Complete documentation for the Date and Date API modules is available at <a href="@link">http://drupal.org/node/92460</a>.', array('@link' => 'http://drupal.org/node/262062')) . '</p>';
|
69 |
|
|
|
70 |
|
|
return $output;
|
71 |
b720ea3e
|
Assos Assos
|
|
72 |
85ad3d82
|
Assos Assos
|
}
|
73 |
|
|
}
|
74 |
|
|
|
75 |
|
|
/**
|
76 |
|
|
* Helper function to retun the status of required date variables.
|
77 |
|
|
*/
|
78 |
|
|
function date_api_status() {
|
79 |
|
|
$t = get_t();
|
80 |
|
|
|
81 |
|
|
$error_messages = array();
|
82 |
|
|
$success_messages = array();
|
83 |
|
|
|
84 |
|
|
$value = variable_get('date_default_timezone');
|
85 |
|
|
if (isset($value)) {
|
86 |
|
|
$success_messages[] = $t('The timezone has been set to <a href="@regional_settings">@timezone</a>.', array('@regional_settings' => url('admin/config/regional/settings'), '@timezone' => $value));
|
87 |
|
|
}
|
88 |
|
|
else {
|
89 |
|
|
$error_messages[] = $t('The Date API requires that you set up the <a href="@regional_settings">site timezone</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings')));
|
90 |
|
|
}
|
91 |
|
|
|
92 |
|
|
$value = variable_get('date_first_day');
|
93 |
|
|
if (isset($value)) {
|
94 |
|
|
$days = date_week_days();
|
95 |
|
|
$success_messages[] = $t('The first day of the week has been set to <a href="@regional_settings">@day</a>.', array('@regional_settings' => url('admin/config/regional/settings'), '@day' => $days[$value]));
|
96 |
|
|
}
|
97 |
|
|
else {
|
98 |
|
|
$error_messages[] = $t('The Date API requires that you set up the <a href="@regional_settings">site first day of week settings</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings')));
|
99 |
|
|
}
|
100 |
|
|
|
101 |
|
|
$value = variable_get('date_format_medium');
|
102 |
|
|
if (isset($value)) {
|
103 |
|
|
$now = date_now();
|
104 |
b720ea3e
|
Assos Assos
|
$success_messages[] = $t('The medium date format type has been set to @value. You may find it helpful to add new format types like Date, Time, Month, or Year, with appropriate formats, at <a href="@regional_date_time">Date and time</a> settings.', array('@value' => $now->format($value), '@regional_date_time' => url('admin/config/regional/date-time')));
|
105 |
85ad3d82
|
Assos Assos
|
}
|
106 |
|
|
else {
|
107 |
|
|
$error_messages[] = $t('The Date API requires that you set up the <a href="@regional_date_time">system date formats</a> to function correctly.', array('@regional_date_time' => url('admin/config/regional/date-time')));
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
return array('errors', $error_messages, 'success' => $success_messages);
|
111 |
|
|
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
/**
|
115 |
|
|
* Implements hook_menu().
|
116 |
|
|
*
|
117 |
|
|
* Creates a 'Date API' section on the administration page for Date
|
118 |
|
|
* modules to use for their configuration and settings.
|
119 |
|
|
*/
|
120 |
|
|
function date_api_menu() {
|
121 |
|
|
$items['admin/config/date'] = array(
|
122 |
|
|
'title' => 'Date API',
|
123 |
|
|
'description' => 'Settings for modules the use the Date API.',
|
124 |
|
|
'position' => 'left',
|
125 |
|
|
'weight' => -10,
|
126 |
|
|
'page callback' => 'system_admin_menu_block_page',
|
127 |
|
|
'access arguments' => array('administer site configuration'),
|
128 |
|
|
'file' => 'system.admin.inc',
|
129 |
|
|
'file path' => drupal_get_path('module', 'system'),
|
130 |
|
|
);
|
131 |
|
|
return $items;
|
132 |
|
|
}
|
133 |
|
|
|
134 |
|
|
/**
|
135 |
|
|
* Extend PHP DateTime class with granularity handling, merge functionality and
|
136 |
|
|
* slightly more flexible initialization parameters.
|
137 |
|
|
*
|
138 |
|
|
* This class is a Drupal independent extension of the >= PHP 5.2 DateTime
|
139 |
|
|
* class.
|
140 |
|
|
*
|
141 |
|
|
* @see FeedsDateTimeElement class
|
142 |
|
|
*/
|
143 |
|
|
class DateObject extends DateTime {
|
144 |
|
|
public $granularity = array();
|
145 |
|
|
public $errors = array();
|
146 |
b720ea3e
|
Assos Assos
|
protected static $allgranularity = array(
|
147 |
|
|
'year',
|
148 |
|
|
'month',
|
149 |
|
|
'day',
|
150 |
|
|
'hour',
|
151 |
|
|
'minute',
|
152 |
|
|
'second',
|
153 |
|
|
'timezone'
|
154 |
|
|
);
|
155 |
85ad3d82
|
Assos Assos
|
private $serializedTime;
|
156 |
|
|
private $serializedTimezone;
|
157 |
|
|
|
158 |
|
|
/**
|
159 |
|
|
* Prepares the object during serialization.
|
160 |
|
|
*
|
161 |
|
|
* We are extending a core class and core classes cannot be serialized.
|
162 |
|
|
*
|
163 |
|
|
* @return array
|
164 |
|
|
* Returns an array with the names of the variables that were serialized.
|
165 |
|
|
*
|
166 |
|
|
* @see http://bugs.php.net/41334
|
167 |
|
|
* @see http://bugs.php.net/39821
|
168 |
|
|
*/
|
169 |
|
|
public function __sleep() {
|
170 |
|
|
$this->serializedTime = $this->format('c');
|
171 |
|
|
$this->serializedTimezone = $this->getTimezone()->getName();
|
172 |
|
|
return array('serializedTime', 'serializedTimezone');
|
173 |
|
|
}
|
174 |
|
|
|
175 |
|
|
/**
|
176 |
|
|
* Re-builds the object using local variables.
|
177 |
|
|
*/
|
178 |
|
|
public function __wakeup() {
|
179 |
|
|
$this->__construct($this->serializedTime, new DateTimeZone($this->serializedTimezone));
|
180 |
|
|
}
|
181 |
|
|
|
182 |
|
|
/**
|
183 |
|
|
* Returns the date object as a string.
|
184 |
|
|
*
|
185 |
|
|
* @return string
|
186 |
|
|
* The date object formatted as a string.
|
187 |
|
|
*/
|
188 |
|
|
public function __toString() {
|
189 |
|
|
return $this->format(DATE_FORMAT_DATETIME) . ' ' . $this->getTimeZone()->getName();
|
190 |
|
|
}
|
191 |
|
|
|
192 |
|
|
/**
|
193 |
|
|
* Constructs a date object.
|
194 |
|
|
*
|
195 |
|
|
* @param string $time
|
196 |
|
|
* A date/time string or array. Defaults to 'now'.
|
197 |
|
|
* @param object|string|null $tz
|
198 |
|
|
* PHP DateTimeZone object, string or NULL allowed. Defaults to NULL.
|
199 |
|
|
* @param string $format
|
200 |
|
|
* PHP date() type format for parsing. Doesn't support timezones; if you
|
201 |
|
|
* have a timezone, send NULL and the default constructor method will
|
202 |
|
|
* hopefully parse it. $format is recommended in order to use negative or
|
203 |
|
|
* large years, which php's parser fails on.
|
204 |
|
|
*/
|
205 |
|
|
public function __construct($time = 'now', $tz = NULL, $format = NULL) {
|
206 |
|
|
$this->timeOnly = FALSE;
|
207 |
|
|
$this->dateOnly = FALSE;
|
208 |
|
|
|
209 |
|
|
// Store the raw time input so it is available for validation.
|
210 |
|
|
$this->originalTime = $time;
|
211 |
|
|
|
212 |
|
|
// Allow string timezones.
|
213 |
|
|
if (!empty($tz) && !is_object($tz)) {
|
214 |
|
|
$tz = new DateTimeZone($tz);
|
215 |
|
|
}
|
216 |
|
|
|
217 |
|
|
// Default to the site timezone when not explicitly provided.
|
218 |
|
|
elseif (empty($tz)) {
|
219 |
|
|
$tz = date_default_timezone_object();
|
220 |
|
|
}
|
221 |
|
|
// Special handling for Unix timestamps expressed in the local timezone.
|
222 |
|
|
// Create a date object in UTC and convert it to the local timezone. Don't
|
223 |
|
|
// try to turn things like '2010' with a format of 'Y' into a timestamp.
|
224 |
|
|
if (is_numeric($time) && (empty($format) || $format == 'U')) {
|
225 |
|
|
// Assume timestamp.
|
226 |
|
|
$time = "@" . $time;
|
227 |
|
|
$date = new DateObject($time, 'UTC');
|
228 |
|
|
if ($tz->getName() != 'UTC') {
|
229 |
|
|
$date->setTimezone($tz);
|
230 |
|
|
}
|
231 |
|
|
$time = $date->format(DATE_FORMAT_DATETIME);
|
232 |
|
|
$format = DATE_FORMAT_DATETIME;
|
233 |
|
|
$this->addGranularity('timezone');
|
234 |
|
|
}
|
235 |
|
|
elseif (is_array($time)) {
|
236 |
|
|
// Assume we were passed an indexed array.
|
237 |
|
|
if (empty($time['year']) && empty($time['month']) && empty($time['day'])) {
|
238 |
|
|
$this->timeOnly = TRUE;
|
239 |
|
|
}
|
240 |
|
|
if (empty($time['hour']) && empty($time['minute']) && empty($time['second'])) {
|
241 |
|
|
$this->dateOnly = TRUE;
|
242 |
|
|
}
|
243 |
|
|
$this->errors = $this->arrayErrors($time);
|
244 |
|
|
// Make this into an ISO date, forcing a full ISO date even if some values
|
245 |
|
|
// are missing.
|
246 |
|
|
$time = $this->toISO($time, TRUE);
|
247 |
|
|
// We checked for errors already, skip parsing the input values.
|
248 |
|
|
$format = NULL;
|
249 |
|
|
}
|
250 |
|
|
else {
|
251 |
|
|
// Make sure dates like 2010-00-00T00:00:00 get converted to
|
252 |
|
|
// 2010-01-01T00:00:00 before creating a date object
|
253 |
|
|
// to avoid unintended changes in the month or day.
|
254 |
|
|
$time = date_make_iso_valid($time);
|
255 |
|
|
}
|
256 |
|
|
|
257 |
|
|
// The parse function will also set errors on the date parts.
|
258 |
|
|
if (!empty($format)) {
|
259 |
|
|
$arg = self::$allgranularity;
|
260 |
|
|
$element = array_pop($arg);
|
261 |
|
|
while (!$this->parse($time, $tz, $format) && $element != 'year') {
|
262 |
|
|
$element = array_pop($arg);
|
263 |
|
|
$format = date_limit_format($format, $arg);
|
264 |
|
|
}
|
265 |
|
|
if ($element == 'year') {
|
266 |
|
|
return FALSE;
|
267 |
|
|
}
|
268 |
|
|
}
|
269 |
|
|
elseif (is_string($time)) {
|
270 |
|
|
// PHP < 5.3 doesn't like the GMT- notation for parsing timezones.
|
271 |
|
|
$time = str_replace("GMT-", "-", $time);
|
272 |
|
|
$time = str_replace("GMT+", "+", $time);
|
273 |
|
|
// We are going to let the parent dateObject do a best effort attempt to
|
274 |
|
|
// turn this string into a valid date. It might fail and we want to
|
275 |
|
|
// control the error messages.
|
276 |
|
|
try {
|
277 |
|
|
@parent::__construct($time, $tz);
|
278 |
|
|
}
|
279 |
|
|
catch (Exception $e) {
|
280 |
|
|
$this->errors['date'] = $e;
|
281 |
|
|
return;
|
282 |
|
|
}
|
283 |
|
|
if (empty($this->granularity)) {
|
284 |
|
|
$this->setGranularityFromTime($time, $tz);
|
285 |
|
|
}
|
286 |
|
|
}
|
287 |
|
|
|
288 |
|
|
// If we haven't got a valid timezone name yet, we need to set one or
|
289 |
|
|
// we will get undefined index errors.
|
290 |
|
|
// This can happen if $time had an offset or no timezone.
|
291 |
|
|
if (!$this->getTimezone() || !preg_match('/[a-zA-Z]/', $this->getTimezone()->getName())) {
|
292 |
|
|
|
293 |
|
|
// If the original $tz has a name, use it.
|
294 |
|
|
if (preg_match('/[a-zA-Z]/', $tz->getName())) {
|
295 |
|
|
$this->setTimezone($tz);
|
296 |
|
|
}
|
297 |
|
|
// We have no information about the timezone so must fallback to a default.
|
298 |
|
|
else {
|
299 |
|
|
$this->setTimezone(new DateTimeZone("UTC"));
|
300 |
|
|
$this->errors['timezone'] = t('No valid timezone name was provided.');
|
301 |
|
|
}
|
302 |
|
|
}
|
303 |
|
|
}
|
304 |
|
|
|
305 |
|
|
/**
|
306 |
|
|
* Merges two date objects together using the current date values as defaults.
|
307 |
|
|
*
|
308 |
|
|
* @param object $other
|
309 |
|
|
* Another date object to merge with.
|
310 |
|
|
*
|
311 |
|
|
* @return object
|
312 |
|
|
* A merged date object.
|
313 |
|
|
*/
|
314 |
|
|
public function merge(FeedsDateTime $other) {
|
315 |
|
|
$other_tz = $other->getTimezone();
|
316 |
|
|
$this_tz = $this->getTimezone();
|
317 |
|
|
// Figure out which timezone to use for combination.
|
318 |
|
|
$use_tz = ($this->hasGranularity('timezone') || !$other->hasGranularity('timezone')) ? $this_tz : $other_tz;
|
319 |
|
|
|
320 |
|
|
$this2 = clone $this;
|
321 |
|
|
$this2->setTimezone($use_tz);
|
322 |
|
|
$other->setTimezone($use_tz);
|
323 |
|
|
$val = $this2->toArray(TRUE);
|
324 |
|
|
$otherval = $other->toArray();
|
325 |
|
|
foreach (self::$allgranularity as $g) {
|
326 |
|
|
if ($other->hasGranularity($g) && !$this2->hasGranularity($g)) {
|
327 |
|
|
// The other class has a property we don't; steal it.
|
328 |
|
|
$this2->addGranularity($g);
|
329 |
|
|
$val[$g] = $otherval[$g];
|
330 |
|
|
}
|
331 |
|
|
}
|
332 |
|
|
$other->setTimezone($other_tz);
|
333 |
|
|
|
334 |
|
|
$this2->setDate($val['year'], $val['month'], $val['day']);
|
335 |
|
|
$this2->setTime($val['hour'], $val['minute'], $val['second']);
|
336 |
|
|
return $this2;
|
337 |
|
|
}
|
338 |
|
|
|
339 |
|
|
/**
|
340 |
|
|
* Sets the time zone for the current date.
|
341 |
|
|
*
|
342 |
|
|
* Overrides default DateTime function. Only changes output values if
|
343 |
|
|
* actually had time granularity. This should be used as a "converter" for
|
344 |
|
|
* output, to switch tzs.
|
345 |
|
|
*
|
346 |
|
|
* In order to set a timezone for a datetime that doesn't have such
|
347 |
|
|
* granularity, merge() it with one that does.
|
348 |
|
|
*
|
349 |
|
|
* @param object $tz
|
350 |
|
|
* A timezone object.
|
351 |
|
|
* @param bool $force
|
352 |
|
|
* Whether or not to skip a date with no time. Defaults to FALSE.
|
353 |
|
|
*/
|
354 |
|
|
public function setTimezone($tz, $force = FALSE) {
|
355 |
|
|
// PHP 5.2.6 has a fatal error when setting a date's timezone to itself.
|
356 |
|
|
// http://bugs.php.net/bug.php?id=45038
|
357 |
|
|
if (version_compare(PHP_VERSION, '5.2.7', '<') && $tz == $this->getTimezone()) {
|
358 |
|
|
$tz = new DateTimeZone($tz->getName());
|
359 |
|
|
}
|
360 |
|
|
|
361 |
|
|
if (!$this->hasTime() || !$this->hasGranularity('timezone') || $force) {
|
362 |
|
|
// This has no time or timezone granularity, so timezone doesn't mean
|
363 |
|
|
// much. We set the timezone using the method, which will change the
|
364 |
|
|
// day/hour, but then we switch back.
|
365 |
|
|
$arr = $this->toArray(TRUE);
|
366 |
|
|
parent::setTimezone($tz);
|
367 |
|
|
$this->setDate($arr['year'], $arr['month'], $arr['day']);
|
368 |
|
|
$this->setTime($arr['hour'], $arr['minute'], $arr['second']);
|
369 |
|
|
$this->addGranularity('timezone');
|
370 |
|
|
return;
|
371 |
|
|
}
|
372 |
|
|
return parent::setTimezone($tz);
|
373 |
|
|
}
|
374 |
|
|
|
375 |
|
|
/**
|
376 |
|
|
* Returns date formatted according to given format.
|
377 |
|
|
*
|
378 |
|
|
* Overrides base format function, formats this date according to its
|
379 |
|
|
* available granularity, unless $force'ed not to limit to granularity.
|
380 |
|
|
*
|
381 |
|
|
* @TODO Add translation into this so translated names will be provided.
|
382 |
|
|
*
|
383 |
|
|
* @param string $format
|
384 |
|
|
* A date format string.
|
385 |
|
|
* @param bool $force
|
386 |
|
|
* Whether or not to limit the granularity. Defaults to FALSE.
|
387 |
|
|
*
|
388 |
|
|
* @return string|false
|
389 |
|
|
* Returns the formatted date string on success or FALSE on failure.
|
390 |
|
|
*/
|
391 |
|
|
public function format($format, $force = FALSE) {
|
392 |
|
|
return parent::format($force ? $format : date_limit_format($format, $this->granularity));
|
393 |
|
|
}
|
394 |
|
|
|
395 |
|
|
/**
|
396 |
|
|
* Adds a granularity entry to the array.
|
397 |
|
|
*
|
398 |
|
|
* @param string $g
|
399 |
|
|
* A single date part.
|
400 |
|
|
*/
|
401 |
|
|
public function addGranularity($g) {
|
402 |
|
|
$this->granularity[] = $g;
|
403 |
|
|
$this->granularity = array_unique($this->granularity);
|
404 |
|
|
}
|
405 |
|
|
|
406 |
|
|
/**
|
407 |
|
|
* Removes a granularity entry from the array.
|
408 |
|
|
*
|
409 |
|
|
* @param string $g
|
410 |
|
|
* A single date part.
|
411 |
|
|
*/
|
412 |
|
|
public function removeGranularity($g) {
|
413 |
b720ea3e
|
Assos Assos
|
if (($key = array_search($g, $this->granularity)) !== FALSE) {
|
414 |
85ad3d82
|
Assos Assos
|
unset($this->granularity[$key]);
|
415 |
|
|
}
|
416 |
|
|
}
|
417 |
|
|
|
418 |
|
|
/**
|
419 |
|
|
* Checks granularity array for a given entry.
|
420 |
|
|
*
|
421 |
|
|
* @param array|null $g
|
422 |
|
|
* An array of date parts. Defaults to NULL.
|
423 |
|
|
*
|
424 |
|
|
* @returns bool
|
425 |
|
|
* TRUE if the date part is present in the date's granularity.
|
426 |
|
|
*/
|
427 |
|
|
public function hasGranularity($g = NULL) {
|
428 |
|
|
if ($g === NULL) {
|
429 |
|
|
// Just want to know if it has something valid means no lower
|
430 |
|
|
// granularities without higher ones.
|
431 |
|
|
$last = TRUE;
|
432 |
|
|
foreach (self::$allgranularity as $arg) {
|
433 |
|
|
if ($arg == 'timezone') {
|
434 |
|
|
continue;
|
435 |
|
|
}
|
436 |
|
|
if (in_array($arg, $this->granularity) && !$last) {
|
437 |
|
|
return FALSE;
|
438 |
|
|
}
|
439 |
|
|
$last = in_array($arg, $this->granularity);
|
440 |
|
|
}
|
441 |
|
|
return in_array('year', $this->granularity);
|
442 |
|
|
}
|
443 |
|
|
if (is_array($g)) {
|
444 |
|
|
foreach ($g as $gran) {
|
445 |
|
|
if (!in_array($gran, $this->granularity)) {
|
446 |
|
|
return FALSE;
|
447 |
|
|
}
|
448 |
|
|
}
|
449 |
|
|
return TRUE;
|
450 |
|
|
}
|
451 |
|
|
return in_array($g, $this->granularity);
|
452 |
|
|
}
|
453 |
|
|
|
454 |
|
|
/**
|
455 |
|
|
* Determines if a a date is valid for a given granularity.
|
456 |
|
|
*
|
457 |
|
|
* @param array|null $granularity
|
458 |
|
|
* An array of date parts. Defaults to NULL.
|
459 |
|
|
* @param bool $flexible
|
460 |
|
|
* TRUE if the granuliarty is flexible, FALSE otherwise. Defaults to FALSE.
|
461 |
|
|
*
|
462 |
|
|
* @return bool
|
463 |
|
|
* Whether a date is valid for a given granularity.
|
464 |
|
|
*/
|
465 |
|
|
public function validGranularity($granularity = NULL, $flexible = FALSE) {
|
466 |
|
|
$true = $this->hasGranularity() && (!$granularity || $flexible || $this->hasGranularity($granularity));
|
467 |
|
|
if (!$true && $granularity) {
|
468 |
|
|
foreach ((array) $granularity as $part) {
|
469 |
b720ea3e
|
Assos Assos
|
if (!$this->hasGranularity($part) && in_array($part, array(
|
470 |
|
|
'second',
|
471 |
|
|
'minute',
|
472 |
|
|
'hour',
|
473 |
|
|
'day',
|
474 |
|
|
'month',
|
475 |
|
|
'year')
|
476 |
|
|
)) {
|
477 |
db9ffd17
|
Assos Assos
|
switch ($part) {
|
478 |
|
|
case 'second':
|
479 |
|
|
$this->errors[$part] = t('The second is missing.');
|
480 |
|
|
break;
|
481 |
b720ea3e
|
Assos Assos
|
|
482 |
db9ffd17
|
Assos Assos
|
case 'minute':
|
483 |
|
|
$this->errors[$part] = t('The minute is missing.');
|
484 |
|
|
break;
|
485 |
b720ea3e
|
Assos Assos
|
|
486 |
db9ffd17
|
Assos Assos
|
case 'hour':
|
487 |
|
|
$this->errors[$part] = t('The hour is missing.');
|
488 |
|
|
break;
|
489 |
b720ea3e
|
Assos Assos
|
|
490 |
db9ffd17
|
Assos Assos
|
case 'day':
|
491 |
|
|
$this->errors[$part] = t('The day is missing.');
|
492 |
|
|
break;
|
493 |
b720ea3e
|
Assos Assos
|
|
494 |
db9ffd17
|
Assos Assos
|
case 'month':
|
495 |
|
|
$this->errors[$part] = t('The month is missing.');
|
496 |
|
|
break;
|
497 |
b720ea3e
|
Assos Assos
|
|
498 |
db9ffd17
|
Assos Assos
|
case 'year':
|
499 |
|
|
$this->errors[$part] = t('The year is missing.');
|
500 |
|
|
break;
|
501 |
|
|
}
|
502 |
85ad3d82
|
Assos Assos
|
}
|
503 |
|
|
}
|
504 |
|
|
}
|
505 |
|
|
return $true;
|
506 |
|
|
}
|
507 |
|
|
|
508 |
|
|
/**
|
509 |
|
|
* Returns whether this object has time set.
|
510 |
|
|
*
|
511 |
|
|
* Used primarily for timezone conversion and formatting.
|
512 |
|
|
*
|
513 |
|
|
* @return bool
|
514 |
|
|
* TRUE if the date contains time parts, FALSE otherwise.
|
515 |
|
|
*/
|
516 |
|
|
public function hasTime() {
|
517 |
|
|
return $this->hasGranularity('hour');
|
518 |
|
|
}
|
519 |
|
|
|
520 |
|
|
/**
|
521 |
|
|
* Returns whether the input values included a year.
|
522 |
|
|
*
|
523 |
|
|
* Useful to use pseudo date objects when we only are interested in the time.
|
524 |
|
|
*
|
525 |
|
|
* @todo $this->completeDate does not actually exist?
|
526 |
|
|
*/
|
527 |
|
|
public function completeDate() {
|
528 |
|
|
return $this->completeDate;
|
529 |
|
|
}
|
530 |
|
|
|
531 |
|
|
/**
|
532 |
|
|
* Removes unwanted date parts from a date.
|
533 |
|
|
*
|
534 |
|
|
* In common usage we should not unset timezone through this.
|
535 |
|
|
*
|
536 |
|
|
* @param array $granularity
|
537 |
|
|
* An array of date parts.
|
538 |
|
|
*/
|
539 |
|
|
public function limitGranularity($granularity) {
|
540 |
|
|
foreach ($this->granularity as $key => $val) {
|
541 |
|
|
if ($val != 'timezone' && !in_array($val, $granularity)) {
|
542 |
|
|
unset($this->granularity[$key]);
|
543 |
|
|
}
|
544 |
|
|
}
|
545 |
|
|
}
|
546 |
|
|
|
547 |
|
|
/**
|
548 |
|
|
* Determines the granularity of a date based on the constructor's arguments.
|
549 |
|
|
*
|
550 |
|
|
* @param string $time
|
551 |
|
|
* A date string.
|
552 |
|
|
* @param bool $tz
|
553 |
|
|
* TRUE if the date has a timezone, FALSE otherwise.
|
554 |
|
|
*/
|
555 |
|
|
protected function setGranularityFromTime($time, $tz) {
|
556 |
|
|
$this->granularity = array();
|
557 |
|
|
$temp = date_parse($time);
|
558 |
|
|
// Special case for 'now'.
|
559 |
|
|
if ($time == 'now') {
|
560 |
b720ea3e
|
Assos Assos
|
$this->granularity = array(
|
561 |
|
|
'year',
|
562 |
|
|
'month',
|
563 |
|
|
'day',
|
564 |
|
|
'hour',
|
565 |
|
|
'minute',
|
566 |
|
|
'second',
|
567 |
|
|
);
|
568 |
85ad3d82
|
Assos Assos
|
}
|
569 |
|
|
else {
|
570 |
|
|
// This PHP date_parse() method currently doesn't have resolution down to
|
571 |
|
|
// seconds, so if there is some time, all will be set.
|
572 |
|
|
foreach (self::$allgranularity as $g) {
|
573 |
|
|
if ((isset($temp[$g]) && is_numeric($temp[$g])) || ($g == 'timezone' && (isset($temp['zone_type']) && $temp['zone_type'] > 0))) {
|
574 |
|
|
$this->granularity[] = $g;
|
575 |
|
|
}
|
576 |
|
|
}
|
577 |
|
|
}
|
578 |
|
|
if ($tz) {
|
579 |
|
|
$this->addGranularity('timezone');
|
580 |
|
|
}
|
581 |
|
|
}
|
582 |
|
|
|
583 |
|
|
/**
|
584 |
|
|
* Converts a date string into a date object.
|
585 |
|
|
*
|
586 |
|
|
* @param string $date
|
587 |
|
|
* The date string to parse.
|
588 |
|
|
* @param object $tz
|
589 |
|
|
* A timezone object.
|
590 |
|
|
* @param string $format
|
591 |
|
|
* The date format string.
|
592 |
|
|
*
|
593 |
|
|
* @return object
|
594 |
|
|
* Returns the date object.
|
595 |
|
|
*/
|
596 |
|
|
protected function parse($date, $tz, $format) {
|
597 |
|
|
$array = date_format_patterns();
|
598 |
|
|
foreach ($array as $key => $value) {
|
599 |
|
|
// The letter with no preceding '\'.
|
600 |
|
|
$patterns[] = "`(^|[^\\\\\\\\])" . $key . "`";
|
601 |
|
|
// A single character.
|
602 |
|
|
$repl1[] = '${1}(.)';
|
603 |
|
|
// The.
|
604 |
|
|
$repl2[] = '${1}(' . $value . ')';
|
605 |
|
|
}
|
606 |
|
|
$patterns[] = "`\\\\\\\\([" . implode(array_keys($array)) . "])`";
|
607 |
|
|
$repl1[] = '${1}';
|
608 |
|
|
$repl2[] = '${1}';
|
609 |
|
|
|
610 |
|
|
$format_regexp = preg_quote($format);
|
611 |
|
|
|
612 |
|
|
// Extract letters.
|
613 |
|
|
$regex1 = preg_replace($patterns, $repl1, $format_regexp, 1);
|
614 |
|
|
$regex1 = str_replace('A', '(.)', $regex1);
|
615 |
|
|
$regex1 = str_replace('a', '(.)', $regex1);
|
616 |
|
|
preg_match('`^' . $regex1 . '$`', stripslashes($format), $letters);
|
617 |
|
|
array_shift($letters);
|
618 |
|
|
// Extract values.
|
619 |
|
|
$regex2 = preg_replace($patterns, $repl2, $format_regexp, 1);
|
620 |
|
|
$regex2 = str_replace('A', '(AM|PM)', $regex2);
|
621 |
|
|
$regex2 = str_replace('a', '(am|pm)', $regex2);
|
622 |
|
|
preg_match('`^' . $regex2 . '$`u', $date, $values);
|
623 |
|
|
array_shift($values);
|
624 |
|
|
// If we did not find all the values for the patterns in the format, abort.
|
625 |
|
|
if (count($letters) != count($values)) {
|
626 |
|
|
$this->errors['invalid'] = t('The value @date does not match the expected format.', array('@date' => $date));
|
627 |
|
|
return FALSE;
|
628 |
|
|
}
|
629 |
|
|
$this->granularity = array();
|
630 |
b720ea3e
|
Assos Assos
|
$final_date = array(
|
631 |
|
|
'hour' => 0,
|
632 |
|
|
'minute' => 0,
|
633 |
|
|
'second' => 0,
|
634 |
|
|
'month' => 1,
|
635 |
|
|
'day' => 1,
|
636 |
|
|
'year' => 0,
|
637 |
|
|
);
|
638 |
85ad3d82
|
Assos Assos
|
foreach ($letters as $i => $letter) {
|
639 |
|
|
$value = $values[$i];
|
640 |
|
|
switch ($letter) {
|
641 |
|
|
case 'd':
|
642 |
|
|
case 'j':
|
643 |
|
|
$final_date['day'] = intval($value);
|
644 |
|
|
$this->addGranularity('day');
|
645 |
|
|
break;
|
646 |
b720ea3e
|
Assos Assos
|
|
647 |
85ad3d82
|
Assos Assos
|
case 'n':
|
648 |
|
|
case 'm':
|
649 |
|
|
$final_date['month'] = intval($value);
|
650 |
|
|
$this->addGranularity('month');
|
651 |
|
|
break;
|
652 |
b720ea3e
|
Assos Assos
|
|
653 |
85ad3d82
|
Assos Assos
|
case 'F':
|
654 |
|
|
$array_month_long = array_flip(date_month_names());
|
655 |
|
|
$final_date['month'] = array_key_exists($value, $array_month_long) ? $array_month_long[$value] : -1;
|
656 |
|
|
$this->addGranularity('month');
|
657 |
|
|
break;
|
658 |
b720ea3e
|
Assos Assos
|
|
659 |
85ad3d82
|
Assos Assos
|
case 'M':
|
660 |
|
|
$array_month = array_flip(date_month_names_abbr());
|
661 |
|
|
$final_date['month'] = array_key_exists($value, $array_month) ? $array_month[$value] : -1;
|
662 |
|
|
$this->addGranularity('month');
|
663 |
|
|
break;
|
664 |
b720ea3e
|
Assos Assos
|
|
665 |
85ad3d82
|
Assos Assos
|
case 'Y':
|
666 |
|
|
$final_date['year'] = $value;
|
667 |
|
|
$this->addGranularity('year');
|
668 |
|
|
if (strlen($value) < 4) {
|
669 |
|
|
$this->errors['year'] = t('The year is invalid. Please check that entry includes four digits.');
|
670 |
|
|
}
|
671 |
|
|
break;
|
672 |
b720ea3e
|
Assos Assos
|
|
673 |
85ad3d82
|
Assos Assos
|
case 'y':
|
674 |
|
|
$year = $value;
|
675 |
|
|
// If no century, we add the current one ("06" => "2006").
|
676 |
|
|
$final_date['year'] = str_pad($year, 4, substr(date("Y"), 0, 2), STR_PAD_LEFT);
|
677 |
|
|
$this->addGranularity('year');
|
678 |
|
|
break;
|
679 |
b720ea3e
|
Assos Assos
|
|
680 |
85ad3d82
|
Assos Assos
|
case 'a':
|
681 |
|
|
case 'A':
|
682 |
|
|
$ampm = strtolower($value);
|
683 |
|
|
break;
|
684 |
b720ea3e
|
Assos Assos
|
|
685 |
85ad3d82
|
Assos Assos
|
case 'g':
|
686 |
|
|
case 'h':
|
687 |
|
|
case 'G':
|
688 |
|
|
case 'H':
|
689 |
|
|
$final_date['hour'] = intval($value);
|
690 |
|
|
$this->addGranularity('hour');
|
691 |
|
|
break;
|
692 |
b720ea3e
|
Assos Assos
|
|
693 |
85ad3d82
|
Assos Assos
|
case 'i':
|
694 |
|
|
$final_date['minute'] = intval($value);
|
695 |
|
|
$this->addGranularity('minute');
|
696 |
|
|
break;
|
697 |
b720ea3e
|
Assos Assos
|
|
698 |
85ad3d82
|
Assos Assos
|
case 's':
|
699 |
|
|
$final_date['second'] = intval($value);
|
700 |
|
|
$this->addGranularity('second');
|
701 |
|
|
break;
|
702 |
b720ea3e
|
Assos Assos
|
|
703 |
85ad3d82
|
Assos Assos
|
case 'U':
|
704 |
|
|
parent::__construct($value, $tz ? $tz : new DateTimeZone("UTC"));
|
705 |
|
|
$this->addGranularity('year');
|
706 |
|
|
$this->addGranularity('month');
|
707 |
|
|
$this->addGranularity('day');
|
708 |
|
|
$this->addGranularity('hour');
|
709 |
|
|
$this->addGranularity('minute');
|
710 |
|
|
$this->addGranularity('second');
|
711 |
|
|
return $this;
|
712 |
b720ea3e
|
Assos Assos
|
|
713 |
85ad3d82
|
Assos Assos
|
}
|
714 |
|
|
}
|
715 |
|
|
if (isset($ampm) && $ampm == 'pm' && $final_date['hour'] < 12) {
|
716 |
|
|
$final_date['hour'] += 12;
|
717 |
|
|
}
|
718 |
|
|
elseif (isset($ampm) && $ampm == 'am' && $final_date['hour'] == 12) {
|
719 |
|
|
$final_date['hour'] -= 12;
|
720 |
|
|
}
|
721 |
|
|
|
722 |
|
|
// Blank becomes current time, given TZ.
|
723 |
|
|
parent::__construct('', $tz ? $tz : new DateTimeZone("UTC"));
|
724 |
|
|
if ($tz) {
|
725 |
|
|
$this->addGranularity('timezone');
|
726 |
|
|
}
|
727 |
|
|
|
728 |
|
|
// SetDate expects an integer value for the year, results can be unexpected
|
729 |
|
|
// if we feed it something like '0100' or '0000'.
|
730 |
|
|
$final_date['year'] = intval($final_date['year']);
|
731 |
|
|
|
732 |
|
|
$this->errors += $this->arrayErrors($final_date);
|
733 |
|
|
$granularity = drupal_map_assoc($this->granularity);
|
734 |
|
|
|
735 |
|
|
// If the input value is '0000-00-00', PHP's date class will later
|
736 |
|
|
// incorrectly convert it to something like '-0001-11-30' if we do setDate()
|
737 |
|
|
// here. If we don't do setDate() here, it will default to the current date
|
738 |
|
|
// and we will lose any way to tell that there was no date in the orignal
|
739 |
|
|
// input values. So set a flag we can use later to tell that this date
|
740 |
|
|
// object was created using only time values, and that the date values are
|
741 |
|
|
// artifical.
|
742 |
|
|
if (empty($final_date['year']) && empty($final_date['month']) && empty($final_date['day'])) {
|
743 |
|
|
$this->timeOnly = TRUE;
|
744 |
|
|
}
|
745 |
|
|
elseif (empty($this->errors)) {
|
746 |
|
|
// setDate() expects a valid year, month, and day.
|
747 |
|
|
// Set some defaults for dates that don't use this to
|
748 |
|
|
// keep PHP from interpreting it as the last day of
|
749 |
|
|
// the previous month or last month of the previous year.
|
750 |
|
|
if (empty($granularity['month'])) {
|
751 |
|
|
$final_date['month'] = 1;
|
752 |
|
|
}
|
753 |
|
|
if (empty($granularity['day'])) {
|
754 |
|
|
$final_date['day'] = 1;
|
755 |
|
|
}
|
756 |
|
|
$this->setDate($final_date['year'], $final_date['month'], $final_date['day']);
|
757 |
|
|
}
|
758 |
|
|
|
759 |
|
|
if (!isset($final_date['hour']) && !isset($final_date['minute']) && !isset($final_date['second'])) {
|
760 |
|
|
$this->dateOnly = TRUE;
|
761 |
|
|
}
|
762 |
|
|
elseif (empty($this->errors)) {
|
763 |
|
|
$this->setTime($final_date['hour'], $final_date['minute'], $final_date['second']);
|
764 |
|
|
}
|
765 |
|
|
return $this;
|
766 |
|
|
}
|
767 |
|
|
|
768 |
|
|
/**
|
769 |
|
|
* Returns all standard date parts in an array.
|
770 |
|
|
*
|
771 |
|
|
* Will return '' for parts in which it lacks granularity.
|
772 |
|
|
*
|
773 |
|
|
* @param bool $force
|
774 |
|
|
* Whether or not to limit the granularity. Defaults to FALSE.
|
775 |
|
|
*
|
776 |
|
|
* @return array
|
777 |
|
|
* An array of formatted date part values, keyed by date parts.
|
778 |
|
|
*/
|
779 |
|
|
public function toArray($force = FALSE) {
|
780 |
|
|
return array(
|
781 |
|
|
'year' => $this->format('Y', $force),
|
782 |
|
|
'month' => $this->format('n', $force),
|
783 |
|
|
'day' => $this->format('j', $force),
|
784 |
|
|
'hour' => intval($this->format('H', $force)),
|
785 |
|
|
'minute' => intval($this->format('i', $force)),
|
786 |
|
|
'second' => intval($this->format('s', $force)),
|
787 |
|
|
'timezone' => $this->format('e', $force),
|
788 |
|
|
);
|
789 |
|
|
}
|
790 |
|
|
|
791 |
|
|
/**
|
792 |
|
|
* Creates an ISO date from an array of values.
|
793 |
|
|
*
|
794 |
|
|
* @param array $arr
|
795 |
|
|
* An array of date values keyed by date part.
|
796 |
|
|
* @param bool $full
|
797 |
|
|
* (optional) Whether to force a full date by filling in missing values.
|
798 |
|
|
* Defaults to FALSE.
|
799 |
|
|
*/
|
800 |
|
|
public function toISO($arr, $full = FALSE) {
|
801 |
|
|
// Add empty values to avoid errors. The empty values must create a valid
|
802 |
|
|
// date or we will get date slippage, i.e. a value of 2011-00-00 will get
|
803 |
|
|
// interpreted as November of 2010 by PHP.
|
804 |
|
|
if ($full) {
|
805 |
b720ea3e
|
Assos Assos
|
$arr += array(
|
806 |
|
|
'year' => 0,
|
807 |
|
|
'month' => 1,
|
808 |
|
|
'day' => 1,
|
809 |
|
|
'hour' => 0,
|
810 |
|
|
'minute' => 0,
|
811 |
|
|
'second' => 0,
|
812 |
|
|
);
|
813 |
85ad3d82
|
Assos Assos
|
}
|
814 |
|
|
else {
|
815 |
b720ea3e
|
Assos Assos
|
$arr += array(
|
816 |
|
|
'year' => '',
|
817 |
|
|
'month' => '',
|
818 |
|
|
'day' => '',
|
819 |
|
|
'hour' => '',
|
820 |
|
|
'minute' => '',
|
821 |
|
|
'second' => '',
|
822 |
|
|
);
|
823 |
85ad3d82
|
Assos Assos
|
}
|
824 |
|
|
$datetime = '';
|
825 |
|
|
if ($arr['year'] !== '') {
|
826 |
|
|
$datetime = date_pad(intval($arr['year']), 4);
|
827 |
|
|
if ($full || $arr['month'] !== '') {
|
828 |
|
|
$datetime .= '-' . date_pad(intval($arr['month']));
|
829 |
|
|
if ($full || $arr['day'] !== '') {
|
830 |
|
|
$datetime .= '-' . date_pad(intval($arr['day']));
|
831 |
|
|
}
|
832 |
|
|
}
|
833 |
|
|
}
|
834 |
|
|
if ($arr['hour'] !== '') {
|
835 |
|
|
$datetime .= $datetime ? 'T' : '';
|
836 |
|
|
$datetime .= date_pad(intval($arr['hour']));
|
837 |
|
|
if ($full || $arr['minute'] !== '') {
|
838 |
|
|
$datetime .= ':' . date_pad(intval($arr['minute']));
|
839 |
|
|
if ($full || $arr['second'] !== '') {
|
840 |
|
|
$datetime .= ':' . date_pad(intval($arr['second']));
|
841 |
|
|
}
|
842 |
|
|
}
|
843 |
|
|
}
|
844 |
|
|
return $datetime;
|
845 |
|
|
}
|
846 |
|
|
|
847 |
|
|
/**
|
848 |
|
|
* Forces an incomplete date to be valid.
|
849 |
|
|
*
|
850 |
|
|
* E.g., add a valid year, month, and day if only the time has been defined.
|
851 |
|
|
*
|
852 |
|
|
* @param array|string $date
|
853 |
|
|
* An array of date parts or a datetime string with values to be massaged
|
854 |
|
|
* into a valid date object.
|
855 |
|
|
* @param string $format
|
856 |
|
|
* (optional) The format of the date. Defaults to NULL.
|
857 |
|
|
* @param string $default
|
858 |
|
|
* (optional) If the fallback should use the first value of the date part,
|
859 |
|
|
* or the current value of the date part. Defaults to 'first'.
|
860 |
|
|
*/
|
861 |
|
|
public function setFuzzyDate($date, $format = NULL, $default = 'first') {
|
862 |
|
|
$timezone = $this->getTimeZone() ? $this->getTimeZone()->getName() : NULL;
|
863 |
|
|
$comp = new DateObject($date, $timezone, $format);
|
864 |
|
|
$arr = $comp->toArray(TRUE);
|
865 |
|
|
foreach ($arr as $key => $value) {
|
866 |
|
|
// Set to intval here and then test that it is still an integer.
|
867 |
|
|
// Needed because sometimes valid integers come through as strings.
|
868 |
|
|
$arr[$key] = $this->forceValid($key, intval($value), $default, $arr['month'], $arr['year']);
|
869 |
|
|
}
|
870 |
|
|
$this->setDate($arr['year'], $arr['month'], $arr['day']);
|
871 |
|
|
$this->setTime($arr['hour'], $arr['minute'], $arr['second']);
|
872 |
|
|
}
|
873 |
|
|
|
874 |
|
|
/**
|
875 |
|
|
* Converts a date part into something that will produce a valid date.
|
876 |
|
|
*
|
877 |
|
|
* @param string $part
|
878 |
|
|
* The date part.
|
879 |
|
|
* @param int $value
|
880 |
|
|
* The date value for this part.
|
881 |
|
|
* @param string $default
|
882 |
|
|
* (optional) If the fallback should use the first value of the date part,
|
883 |
|
|
* or the current value of the date part. Defaults to 'first'.
|
884 |
|
|
* @param int $month
|
885 |
|
|
* (optional) Used when the date part is less than 'month' to specify the
|
886 |
|
|
* date. Defaults to NULL.
|
887 |
|
|
* @param int $year
|
888 |
|
|
* (optional) Used when the date part is less than 'year' to specify the
|
889 |
|
|
* date. Defaults to NULL.
|
890 |
|
|
*
|
891 |
|
|
* @return int
|
892 |
|
|
* A valid date value.
|
893 |
|
|
*/
|
894 |
|
|
protected function forceValid($part, $value, $default = 'first', $month = NULL, $year = NULL) {
|
895 |
|
|
$now = date_now();
|
896 |
|
|
switch ($part) {
|
897 |
|
|
case 'year':
|
898 |
|
|
$fallback = $now->format('Y');
|
899 |
|
|
return !is_int($value) || empty($value) || $value < variable_get('date_min_year', 1) || $value > variable_get('date_max_year', 4000) ? $fallback : $value;
|
900 |
b720ea3e
|
Assos Assos
|
|
901 |
85ad3d82
|
Assos Assos
|
case 'month':
|
902 |
|
|
$fallback = $default == 'first' ? 1 : $now->format('n');
|
903 |
|
|
return !is_int($value) || empty($value) || $value <= 0 || $value > 12 ? $fallback : $value;
|
904 |
b720ea3e
|
Assos Assos
|
|
905 |
85ad3d82
|
Assos Assos
|
case 'day':
|
906 |
|
|
$fallback = $default == 'first' ? 1 : $now->format('j');
|
907 |
|
|
$max_day = isset($year) && isset($month) ? date_days_in_month($year, $month) : 31;
|
908 |
|
|
return !is_int($value) || empty($value) || $value <= 0 || $value > $max_day ? $fallback : $value;
|
909 |
b720ea3e
|
Assos Assos
|
|
910 |
85ad3d82
|
Assos Assos
|
case 'hour':
|
911 |
|
|
$fallback = $default == 'first' ? 0 : $now->format('G');
|
912 |
|
|
return !is_int($value) || $value < 0 || $value > 23 ? $fallback : $value;
|
913 |
b720ea3e
|
Assos Assos
|
|
914 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
915 |
|
|
$fallback = $default == 'first' ? 0 : $now->format('i');
|
916 |
|
|
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
|
917 |
b720ea3e
|
Assos Assos
|
|
918 |
85ad3d82
|
Assos Assos
|
case 'second':
|
919 |
|
|
$fallback = $default == 'first' ? 0 : $now->format('s');
|
920 |
|
|
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
|
921 |
|
|
}
|
922 |
|
|
}
|
923 |
|
|
|
924 |
|
|
/**
|
925 |
|
|
* Finds possible errors in an array of date part values.
|
926 |
|
|
*
|
927 |
|
|
* The forceValid() function will change an invalid value to a valid one, so
|
928 |
|
|
* we just need to see if the value got altered.
|
929 |
|
|
*
|
930 |
|
|
* @param array $arr
|
931 |
|
|
* An array of date values, keyed by date part.
|
932 |
|
|
*
|
933 |
|
|
* @return array
|
934 |
|
|
* An array of error messages, keyed by date part.
|
935 |
|
|
*/
|
936 |
|
|
public function arrayErrors($arr) {
|
937 |
|
|
$errors = array();
|
938 |
|
|
$now = date_now();
|
939 |
|
|
$default_month = !empty($arr['month']) ? $arr['month'] : $now->format('n');
|
940 |
|
|
$default_year = !empty($arr['year']) ? $arr['year'] : $now->format('Y');
|
941 |
|
|
|
942 |
|
|
$this->granularity = array();
|
943 |
|
|
foreach ($arr as $part => $value) {
|
944 |
|
|
// Explicitly set the granularity to the values in the input array.
|
945 |
|
|
if (is_numeric($value)) {
|
946 |
|
|
$this->addGranularity($part);
|
947 |
|
|
}
|
948 |
|
|
// Avoid false errors when a numeric value is input as a string by casting
|
949 |
|
|
// as an integer.
|
950 |
|
|
$value = intval($value);
|
951 |
|
|
if (!empty($value) && $this->forceValid($part, $value, 'now', $default_month, $default_year) != $value) {
|
952 |
|
|
// Use a switch/case to make translation easier by providing a different
|
953 |
|
|
// message for each part.
|
954 |
|
|
switch ($part) {
|
955 |
|
|
case 'year':
|
956 |
|
|
$errors['year'] = t('The year is invalid.');
|
957 |
|
|
break;
|
958 |
b720ea3e
|
Assos Assos
|
|
959 |
85ad3d82
|
Assos Assos
|
case 'month':
|
960 |
|
|
$errors['month'] = t('The month is invalid.');
|
961 |
|
|
break;
|
962 |
b720ea3e
|
Assos Assos
|
|
963 |
85ad3d82
|
Assos Assos
|
case 'day':
|
964 |
|
|
$errors['day'] = t('The day is invalid.');
|
965 |
|
|
break;
|
966 |
b720ea3e
|
Assos Assos
|
|
967 |
85ad3d82
|
Assos Assos
|
case 'hour':
|
968 |
|
|
$errors['hour'] = t('The hour is invalid.');
|
969 |
|
|
break;
|
970 |
b720ea3e
|
Assos Assos
|
|
971 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
972 |
|
|
$errors['minute'] = t('The minute is invalid.');
|
973 |
|
|
break;
|
974 |
b720ea3e
|
Assos Assos
|
|
975 |
85ad3d82
|
Assos Assos
|
case 'second':
|
976 |
|
|
$errors['second'] = t('The second is invalid.');
|
977 |
|
|
break;
|
978 |
|
|
}
|
979 |
|
|
}
|
980 |
|
|
}
|
981 |
|
|
if ($this->hasTime()) {
|
982 |
|
|
$this->addGranularity('timezone');
|
983 |
|
|
}
|
984 |
|
|
return $errors;
|
985 |
|
|
}
|
986 |
|
|
|
987 |
|
|
/**
|
988 |
|
|
* Computes difference between two days using a given measure.
|
989 |
|
|
*
|
990 |
|
|
* @param object $date2_in
|
991 |
|
|
* The stop date.
|
992 |
|
|
* @param string $measure
|
993 |
|
|
* (optional) A granularity date part. Defaults to 'seconds'.
|
994 |
b720ea3e
|
Assos Assos
|
* @param bool $absolute
|
995 |
85ad3d82
|
Assos Assos
|
* (optional) Indicate whether the absolute value of the difference should
|
996 |
|
|
* be returned or if the sign should be retained. Defaults to TRUE.
|
997 |
|
|
*/
|
998 |
|
|
public function difference($date2_in, $measure = 'seconds', $absolute = TRUE) {
|
999 |
|
|
// Create cloned objects or original dates will be impacted by the
|
1000 |
|
|
// date_modify() operations done in this code.
|
1001 |
|
|
$date1 = clone($this);
|
1002 |
|
|
$date2 = clone($date2_in);
|
1003 |
|
|
if (is_object($date1) && is_object($date2)) {
|
1004 |
|
|
$diff = date_format($date2, 'U') - date_format($date1, 'U');
|
1005 |
|
|
if ($diff == 0) {
|
1006 |
|
|
return 0;
|
1007 |
|
|
}
|
1008 |
|
|
elseif ($diff < 0 && $absolute) {
|
1009 |
|
|
// Make sure $date1 is the smaller date.
|
1010 |
|
|
$temp = $date2;
|
1011 |
|
|
$date2 = $date1;
|
1012 |
|
|
$date1 = $temp;
|
1013 |
|
|
$diff = date_format($date2, 'U') - date_format($date1, 'U');
|
1014 |
|
|
}
|
1015 |
|
|
$year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y'));
|
1016 |
|
|
switch ($measure) {
|
1017 |
|
|
// The easy cases first.
|
1018 |
|
|
case 'seconds':
|
1019 |
|
|
return $diff;
|
1020 |
b720ea3e
|
Assos Assos
|
|
1021 |
85ad3d82
|
Assos Assos
|
case 'minutes':
|
1022 |
|
|
return $diff / 60;
|
1023 |
b720ea3e
|
Assos Assos
|
|
1024 |
85ad3d82
|
Assos Assos
|
case 'hours':
|
1025 |
|
|
return $diff / 3600;
|
1026 |
b720ea3e
|
Assos Assos
|
|
1027 |
85ad3d82
|
Assos Assos
|
case 'years':
|
1028 |
|
|
return $year_diff;
|
1029 |
|
|
|
1030 |
|
|
case 'months':
|
1031 |
|
|
$format = 'n';
|
1032 |
|
|
$item1 = date_format($date1, $format);
|
1033 |
|
|
$item2 = date_format($date2, $format);
|
1034 |
|
|
if ($year_diff == 0) {
|
1035 |
|
|
return intval($item2 - $item1);
|
1036 |
|
|
}
|
1037 |
|
|
elseif ($year_diff < 0) {
|
1038 |
|
|
$item_diff = 0 - $item1;
|
1039 |
|
|
$item_diff -= intval((abs($year_diff) - 1) * 12);
|
1040 |
|
|
return $item_diff - (12 - $item2);
|
1041 |
|
|
}
|
1042 |
|
|
else {
|
1043 |
|
|
$item_diff = 12 - $item1;
|
1044 |
|
|
$item_diff += intval(($year_diff - 1) * 12);
|
1045 |
|
|
return $item_diff + $item2;
|
1046 |
|
|
}
|
1047 |
|
|
break;
|
1048 |
|
|
|
1049 |
|
|
case 'days':
|
1050 |
|
|
$format = 'z';
|
1051 |
|
|
$item1 = date_format($date1, $format);
|
1052 |
|
|
$item2 = date_format($date2, $format);
|
1053 |
|
|
if ($year_diff == 0) {
|
1054 |
|
|
return intval($item2 - $item1);
|
1055 |
|
|
}
|
1056 |
|
|
elseif ($year_diff < 0) {
|
1057 |
|
|
$item_diff = 0 - $item1;
|
1058 |
|
|
for ($i = 1; $i < abs($year_diff); $i++) {
|
1059 |
|
|
date_modify($date1, '-1 year');
|
1060 |
|
|
$item_diff -= date_days_in_year($date1);
|
1061 |
|
|
}
|
1062 |
|
|
return $item_diff - (date_days_in_year($date2) - $item2);
|
1063 |
|
|
}
|
1064 |
|
|
else {
|
1065 |
|
|
$item_diff = date_days_in_year($date1) - $item1;
|
1066 |
|
|
for ($i = 1; $i < $year_diff; $i++) {
|
1067 |
|
|
date_modify($date1, '+1 year');
|
1068 |
|
|
$item_diff += date_days_in_year($date1);
|
1069 |
|
|
}
|
1070 |
|
|
return $item_diff + $item2;
|
1071 |
|
|
}
|
1072 |
|
|
break;
|
1073 |
|
|
|
1074 |
|
|
case 'weeks':
|
1075 |
|
|
$week_diff = date_format($date2, 'W') - date_format($date1, 'W');
|
1076 |
|
|
$year_diff = date_format($date2, 'o') - date_format($date1, 'o');
|
1077 |
|
|
|
1078 |
|
|
$sign = ($year_diff < 0) ? -1 : 1;
|
1079 |
|
|
|
1080 |
|
|
for ($i = 1; $i <= abs($year_diff); $i++) {
|
1081 |
b720ea3e
|
Assos Assos
|
date_modify($date1, (($sign > 0) ? '+' : '-') . '1 year');
|
1082 |
85ad3d82
|
Assos Assos
|
$week_diff += (date_iso_weeks_in_year($date1) * $sign);
|
1083 |
|
|
}
|
1084 |
|
|
return $week_diff;
|
1085 |
|
|
}
|
1086 |
|
|
}
|
1087 |
|
|
return NULL;
|
1088 |
|
|
}
|
1089 |
|
|
}
|
1090 |
|
|
|
1091 |
|
|
/**
|
1092 |
|
|
* Determines if the date element needs to be processed.
|
1093 |
|
|
*
|
1094 |
|
|
* Helper function to see if date element has been hidden by FAPI to see if it
|
1095 |
|
|
* needs to be processed or just pass the value through. This is needed since
|
1096 |
|
|
* normal date processing explands the date element into parts and then
|
1097 |
|
|
* reconstructs it, which is not needed or desirable if the field is hidden.
|
1098 |
|
|
*
|
1099 |
|
|
* @param array $element
|
1100 |
|
|
* The date element to check.
|
1101 |
|
|
*
|
1102 |
|
|
* @return bool
|
1103 |
|
|
* TRUE if the element is effectively hidden, FALSE otherwise.
|
1104 |
|
|
*/
|
1105 |
|
|
function date_hidden_element($element) {
|
1106 |
|
|
// @TODO What else needs to be tested to see if dates are hidden or disabled?
|
1107 |
|
|
if ((isset($element['#access']) && empty($element['#access']))
|
1108 |
|
|
|| !empty($element['#programmed'])
|
1109 |
|
|
|| in_array($element['#type'], array('hidden', 'value'))) {
|
1110 |
|
|
return TRUE;
|
1111 |
|
|
}
|
1112 |
|
|
return FALSE;
|
1113 |
|
|
}
|
1114 |
|
|
|
1115 |
|
|
/**
|
1116 |
|
|
* Helper function for getting the format string for a date type.
|
1117 |
|
|
*
|
1118 |
|
|
* @param string $type
|
1119 |
|
|
* A date type format name.
|
1120 |
|
|
*
|
1121 |
|
|
* @return string
|
1122 |
|
|
* A date type format, like 'Y-m-d H:i:s'.
|
1123 |
|
|
*/
|
1124 |
|
|
function date_type_format($type) {
|
1125 |
|
|
switch ($type) {
|
1126 |
|
|
case DATE_ISO:
|
1127 |
|
|
return DATE_FORMAT_ISO;
|
1128 |
b720ea3e
|
Assos Assos
|
|
1129 |
85ad3d82
|
Assos Assos
|
case DATE_UNIX:
|
1130 |
|
|
return DATE_FORMAT_UNIX;
|
1131 |
b720ea3e
|
Assos Assos
|
|
1132 |
85ad3d82
|
Assos Assos
|
case DATE_DATETIME:
|
1133 |
|
|
return DATE_FORMAT_DATETIME;
|
1134 |
b720ea3e
|
Assos Assos
|
|
1135 |
85ad3d82
|
Assos Assos
|
case DATE_ICAL:
|
1136 |
|
|
return DATE_FORMAT_ICAL;
|
1137 |
|
|
}
|
1138 |
|
|
}
|
1139 |
|
|
|
1140 |
|
|
/**
|
1141 |
|
|
* Constructs an untranslated array of month names.
|
1142 |
|
|
*
|
1143 |
|
|
* Needed for CSS, translation functions, strtotime(), and other places
|
1144 |
|
|
* that use the English versions of these words.
|
1145 |
|
|
*
|
1146 |
|
|
* @return array
|
1147 |
|
|
* An array of month names.
|
1148 |
|
|
*/
|
1149 |
|
|
function date_month_names_untranslated() {
|
1150 |
|
|
static $month_names;
|
1151 |
|
|
if (empty($month_names)) {
|
1152 |
|
|
$month_names = array(
|
1153 |
|
|
1 => 'January',
|
1154 |
|
|
2 => 'February',
|
1155 |
|
|
3 => 'March',
|
1156 |
|
|
4 => 'April',
|
1157 |
|
|
5 => 'May',
|
1158 |
|
|
6 => 'June',
|
1159 |
|
|
7 => 'July',
|
1160 |
|
|
8 => 'August',
|
1161 |
|
|
9 => 'September',
|
1162 |
|
|
10 => 'October',
|
1163 |
|
|
11 => 'November',
|
1164 |
|
|
12 => 'December',
|
1165 |
|
|
);
|
1166 |
|
|
}
|
1167 |
|
|
return $month_names;
|
1168 |
|
|
}
|
1169 |
|
|
|
1170 |
|
|
/**
|
1171 |
|
|
* Returns a translated array of month names.
|
1172 |
|
|
*
|
1173 |
|
|
* @param bool $required
|
1174 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1175 |
|
|
* Defaults to FALSE.
|
1176 |
|
|
*
|
1177 |
|
|
* @return array
|
1178 |
|
|
* An array of month names.
|
1179 |
|
|
*/
|
1180 |
|
|
function date_month_names($required = FALSE) {
|
1181 |
|
|
$month_names = array();
|
1182 |
|
|
foreach (date_month_names_untranslated() as $key => $month) {
|
1183 |
|
|
$month_names[$key] = t($month, array(), array('context' => 'Long month name'));
|
1184 |
|
|
}
|
1185 |
|
|
$none = array('' => '');
|
1186 |
|
|
return !$required ? $none + $month_names : $month_names;
|
1187 |
|
|
}
|
1188 |
|
|
|
1189 |
|
|
/**
|
1190 |
b720ea3e
|
Assos Assos
|
* Constructs a translated array of month name abbreviations.
|
1191 |
85ad3d82
|
Assos Assos
|
*
|
1192 |
|
|
* @param bool $required
|
1193 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1194 |
|
|
* Defaults to FALSE.
|
1195 |
|
|
* @param int $length
|
1196 |
|
|
* (optional) The length of the abbreviation. Defaults to 3.
|
1197 |
|
|
*
|
1198 |
|
|
* @return array
|
1199 |
|
|
* An array of month abbreviations.
|
1200 |
|
|
*/
|
1201 |
|
|
function date_month_names_abbr($required = FALSE, $length = 3) {
|
1202 |
|
|
$month_names = array();
|
1203 |
|
|
foreach (date_month_names_untranslated() as $key => $month) {
|
1204 |
|
|
if ($length == 3) {
|
1205 |
|
|
$month_names[$key] = t(substr($month, 0, $length), array());
|
1206 |
|
|
}
|
1207 |
|
|
else {
|
1208 |
|
|
$month_names[$key] = t(substr($month, 0, $length), array(), array('context' => 'month_abbr'));
|
1209 |
|
|
}
|
1210 |
|
|
}
|
1211 |
|
|
$none = array('' => '');
|
1212 |
|
|
return !$required ? $none + $month_names : $month_names;
|
1213 |
|
|
}
|
1214 |
|
|
|
1215 |
|
|
/**
|
1216 |
|
|
* Constructs an untranslated array of week days.
|
1217 |
|
|
*
|
1218 |
|
|
* Needed for CSS, translation functions, strtotime(), and other places
|
1219 |
|
|
* that use the English versions of these words.
|
1220 |
|
|
*
|
1221 |
|
|
* @param bool $refresh
|
1222 |
|
|
* (optional) Whether to refresh the list. Defaults to TRUE.
|
1223 |
|
|
*
|
1224 |
|
|
* @return array
|
1225 |
|
|
* An array of week day names
|
1226 |
|
|
*/
|
1227 |
|
|
function date_week_days_untranslated($refresh = TRUE) {
|
1228 |
|
|
static $weekdays;
|
1229 |
|
|
if ($refresh || empty($weekdays)) {
|
1230 |
|
|
$weekdays = array(
|
1231 |
|
|
'Sunday',
|
1232 |
|
|
'Monday',
|
1233 |
|
|
'Tuesday',
|
1234 |
|
|
'Wednesday',
|
1235 |
|
|
'Thursday',
|
1236 |
|
|
'Friday',
|
1237 |
|
|
'Saturday',
|
1238 |
|
|
);
|
1239 |
|
|
}
|
1240 |
|
|
return $weekdays;
|
1241 |
|
|
}
|
1242 |
|
|
|
1243 |
|
|
/**
|
1244 |
|
|
* Returns a translated array of week names.
|
1245 |
|
|
*
|
1246 |
|
|
* @param bool $required
|
1247 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1248 |
|
|
* Defaults to FALSE.
|
1249 |
|
|
*
|
1250 |
|
|
* @return array
|
1251 |
|
|
* An array of week day names
|
1252 |
|
|
*/
|
1253 |
|
|
function date_week_days($required = FALSE, $refresh = TRUE) {
|
1254 |
|
|
$weekdays = array();
|
1255 |
|
|
foreach (date_week_days_untranslated() as $key => $day) {
|
1256 |
|
|
$weekdays[$key] = t($day, array(), array('context' => ''));
|
1257 |
|
|
}
|
1258 |
|
|
$none = array('' => '');
|
1259 |
|
|
return !$required ? $none + $weekdays : $weekdays;
|
1260 |
|
|
}
|
1261 |
|
|
|
1262 |
|
|
/**
|
1263 |
|
|
* Constructs a translated array of week day abbreviations.
|
1264 |
|
|
*
|
1265 |
|
|
* @param bool $required
|
1266 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1267 |
|
|
* Defaults to FALSE.
|
1268 |
|
|
* @param bool $refresh
|
1269 |
|
|
* (optional) Whether to refresh the list. Defaults to TRUE.
|
1270 |
|
|
* @param int $length
|
1271 |
|
|
* (optional) The length of the abbreviation. Defaults to 3.
|
1272 |
|
|
*
|
1273 |
|
|
* @return array
|
1274 |
|
|
* An array of week day abbreviations
|
1275 |
|
|
*/
|
1276 |
|
|
function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
|
1277 |
|
|
$weekdays = array();
|
1278 |
|
|
switch ($length) {
|
1279 |
|
|
case 1:
|
1280 |
|
|
$context = 'day_abbr1';
|
1281 |
|
|
break;
|
1282 |
b720ea3e
|
Assos Assos
|
|
1283 |
85ad3d82
|
Assos Assos
|
case 2:
|
1284 |
|
|
$context = 'day_abbr2';
|
1285 |
|
|
break;
|
1286 |
b720ea3e
|
Assos Assos
|
|
1287 |
85ad3d82
|
Assos Assos
|
default:
|
1288 |
|
|
$context = '';
|
1289 |
|
|
break;
|
1290 |
|
|
}
|
1291 |
|
|
foreach (date_week_days_untranslated() as $key => $day) {
|
1292 |
|
|
$weekdays[$key] = t(substr($day, 0, $length), array(), array('context' => $context));
|
1293 |
|
|
}
|
1294 |
|
|
$none = array('' => '');
|
1295 |
|
|
return !$required ? $none + $weekdays : $weekdays;
|
1296 |
|
|
}
|
1297 |
|
|
|
1298 |
|
|
/**
|
1299 |
|
|
* Reorders weekdays to match the first day of the week.
|
1300 |
|
|
*
|
1301 |
|
|
* @param array $weekdays
|
1302 |
|
|
* An array of weekdays.
|
1303 |
|
|
*
|
1304 |
|
|
* @return array
|
1305 |
|
|
* An array of weekdays reordered to match the first day of the week.
|
1306 |
|
|
*/
|
1307 |
|
|
function date_week_days_ordered($weekdays) {
|
1308 |
|
|
$first_day = variable_get('date_first_day', 0);
|
1309 |
|
|
if ($first_day > 0) {
|
1310 |
|
|
for ($i = 1; $i <= $first_day; $i++) {
|
1311 |
|
|
$last = array_shift($weekdays);
|
1312 |
|
|
array_push($weekdays, $last);
|
1313 |
|
|
}
|
1314 |
|
|
}
|
1315 |
|
|
return $weekdays;
|
1316 |
|
|
}
|
1317 |
|
|
|
1318 |
|
|
/**
|
1319 |
|
|
* Constructs an array of years.
|
1320 |
|
|
*
|
1321 |
b720ea3e
|
Assos Assos
|
* @param int $start
|
1322 |
|
|
* The start year in the array.
|
1323 |
|
|
* @param int $end
|
1324 |
|
|
* The end year in the array.
|
1325 |
85ad3d82
|
Assos Assos
|
* @param bool $required
|
1326 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1327 |
|
|
* Defaults to FALSE.
|
1328 |
|
|
*
|
1329 |
|
|
* @return array
|
1330 |
|
|
* An array of years in the selected range.
|
1331 |
|
|
*/
|
1332 |
b720ea3e
|
Assos Assos
|
function date_years($start = 0, $end = 0, $required = FALSE) {
|
1333 |
85ad3d82
|
Assos Assos
|
// Ensure $min and $max are valid values.
|
1334 |
b720ea3e
|
Assos Assos
|
if (empty($start)) {
|
1335 |
|
|
$start = intval(date('Y', REQUEST_TIME) - 3);
|
1336 |
85ad3d82
|
Assos Assos
|
}
|
1337 |
b720ea3e
|
Assos Assos
|
if (empty($end)) {
|
1338 |
|
|
$end = intval(date('Y', REQUEST_TIME) + 3);
|
1339 |
85ad3d82
|
Assos Assos
|
}
|
1340 |
|
|
$none = array(0 => '');
|
1341 |
b720ea3e
|
Assos Assos
|
return !$required ? $none + drupal_map_assoc(range($start, $end)) : drupal_map_assoc(range($start, $end));
|
1342 |
85ad3d82
|
Assos Assos
|
}
|
1343 |
|
|
|
1344 |
|
|
/**
|
1345 |
|
|
* Constructs an array of days in a month.
|
1346 |
|
|
*
|
1347 |
|
|
* @param bool $required
|
1348 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1349 |
|
|
* Defaults to FALSE.
|
1350 |
|
|
* @param int $month
|
1351 |
|
|
* (optional) The month in which to find the number of days.
|
1352 |
|
|
* @param int $year
|
1353 |
|
|
* (optional) The year in which to find the number of days.
|
1354 |
|
|
*
|
1355 |
|
|
* @return array
|
1356 |
|
|
* An array of days for the selected month.
|
1357 |
|
|
*/
|
1358 |
|
|
function date_days($required = FALSE, $month = NULL, $year = NULL) {
|
1359 |
|
|
// If we have a month and year, find the right last day of the month.
|
1360 |
|
|
if (!empty($month) && !empty($year)) {
|
1361 |
|
|
$date = new DateObject($year . '-' . $month . '-01 00:00:00', 'UTC');
|
1362 |
|
|
$max = $date->format('t');
|
1363 |
|
|
}
|
1364 |
|
|
// If there is no month and year given, default to 31.
|
1365 |
|
|
if (empty($max)) {
|
1366 |
|
|
$max = 31;
|
1367 |
|
|
}
|
1368 |
|
|
$none = array(0 => '');
|
1369 |
|
|
return !$required ? $none + drupal_map_assoc(range(1, $max)) : drupal_map_assoc(range(1, $max));
|
1370 |
|
|
}
|
1371 |
|
|
|
1372 |
|
|
/**
|
1373 |
|
|
* Constructs an array of hours.
|
1374 |
|
|
*
|
1375 |
|
|
* @param string $format
|
1376 |
|
|
* A date format string.
|
1377 |
|
|
* @param bool $required
|
1378 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1379 |
|
|
* Defaults to FALSE.
|
1380 |
|
|
*
|
1381 |
|
|
* @return array
|
1382 |
|
|
* An array of hours in the selected format.
|
1383 |
|
|
*/
|
1384 |
|
|
function date_hours($format = 'H', $required = FALSE) {
|
1385 |
|
|
$hours = array();
|
1386 |
|
|
if ($format == 'h' || $format == 'g') {
|
1387 |
|
|
$min = 1;
|
1388 |
|
|
$max = 12;
|
1389 |
|
|
}
|
1390 |
|
|
else {
|
1391 |
|
|
$min = 0;
|
1392 |
|
|
$max = 23;
|
1393 |
|
|
}
|
1394 |
|
|
for ($i = $min; $i <= $max; $i++) {
|
1395 |
|
|
$hours[$i] = $i < 10 && ($format == 'H' || $format == 'h') ? "0$i" : $i;
|
1396 |
|
|
}
|
1397 |
|
|
$none = array('' => '');
|
1398 |
|
|
return !$required ? $none + $hours : $hours;
|
1399 |
|
|
}
|
1400 |
|
|
|
1401 |
|
|
/**
|
1402 |
|
|
* Constructs an array of minutes.
|
1403 |
|
|
*
|
1404 |
|
|
* @param string $format
|
1405 |
|
|
* A date format string.
|
1406 |
|
|
* @param bool $required
|
1407 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1408 |
|
|
* Defaults to FALSE.
|
1409 |
|
|
*
|
1410 |
|
|
* @return array
|
1411 |
|
|
* An array of minutes in the selected format.
|
1412 |
|
|
*/
|
1413 |
|
|
function date_minutes($format = 'i', $required = FALSE, $increment = 1) {
|
1414 |
|
|
$minutes = array();
|
1415 |
|
|
// Ensure $increment has a value so we don't loop endlessly.
|
1416 |
|
|
if (empty($increment)) {
|
1417 |
|
|
$increment = 1;
|
1418 |
|
|
}
|
1419 |
|
|
for ($i = 0; $i < 60; $i += $increment) {
|
1420 |
|
|
$minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i;
|
1421 |
|
|
}
|
1422 |
|
|
$none = array('' => '');
|
1423 |
|
|
return !$required ? $none + $minutes : $minutes;
|
1424 |
|
|
}
|
1425 |
|
|
|
1426 |
|
|
/**
|
1427 |
|
|
* Constructs an array of seconds.
|
1428 |
|
|
*
|
1429 |
|
|
* @param string $format
|
1430 |
|
|
* A date format string.
|
1431 |
|
|
* @param bool $required
|
1432 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1433 |
|
|
* Defaults to FALSE.
|
1434 |
|
|
*
|
1435 |
|
|
* @return array
|
1436 |
|
|
* An array of seconds in the selected format.
|
1437 |
|
|
*/
|
1438 |
|
|
function date_seconds($format = 's', $required = FALSE, $increment = 1) {
|
1439 |
|
|
$seconds = array();
|
1440 |
|
|
// Ensure $increment has a value so we don't loop endlessly.
|
1441 |
|
|
if (empty($increment)) {
|
1442 |
|
|
$increment = 1;
|
1443 |
|
|
}
|
1444 |
|
|
for ($i = 0; $i < 60; $i += $increment) {
|
1445 |
|
|
$seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i;
|
1446 |
|
|
}
|
1447 |
|
|
$none = array('' => '');
|
1448 |
|
|
return !$required ? $none + $seconds : $seconds;
|
1449 |
|
|
}
|
1450 |
|
|
|
1451 |
|
|
/**
|
1452 |
|
|
* Constructs an array of AM and PM options.
|
1453 |
|
|
*
|
1454 |
|
|
* @param bool $required
|
1455 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1456 |
|
|
* Defaults to FALSE.
|
1457 |
|
|
*
|
1458 |
|
|
* @return array
|
1459 |
|
|
* An array of AM and PM options.
|
1460 |
|
|
*/
|
1461 |
|
|
function date_ampm($required = FALSE) {
|
1462 |
|
|
$none = array('' => '');
|
1463 |
|
|
$ampm = array(
|
1464 |
|
|
'am' => t('am', array(), array('context' => 'ampm')),
|
1465 |
|
|
'pm' => t('pm', array(), array('context' => 'ampm')),
|
1466 |
|
|
);
|
1467 |
|
|
return !$required ? $none + $ampm : $ampm;
|
1468 |
|
|
}
|
1469 |
|
|
|
1470 |
|
|
/**
|
1471 |
|
|
* Constructs an array of regex replacement strings for date format elements.
|
1472 |
|
|
*
|
1473 |
|
|
* @param bool $strict
|
1474 |
|
|
* Whether or not to force 2 digits for elements that sometimes allow either
|
1475 |
|
|
* 1 or 2 digits.
|
1476 |
|
|
*
|
1477 |
|
|
* @return array
|
1478 |
|
|
* An array of date() format letters and their regex equivalents.
|
1479 |
|
|
*/
|
1480 |
|
|
function date_format_patterns($strict = FALSE) {
|
1481 |
|
|
return array(
|
1482 |
|
|
'd' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1483 |
|
|
'm' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1484 |
|
|
'h' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1485 |
|
|
'H' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1486 |
|
|
'i' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1487 |
|
|
's' => '\d{' . ($strict ? '2' : '1,2') . '}',
|
1488 |
|
|
'j' => '\d{1,2}',
|
1489 |
|
|
'N' => '\d',
|
1490 |
|
|
'S' => '\w{2}',
|
1491 |
|
|
'w' => '\d',
|
1492 |
|
|
'z' => '\d{1,3}',
|
1493 |
|
|
'W' => '\d{1,2}',
|
1494 |
|
|
'n' => '\d{1,2}',
|
1495 |
|
|
't' => '\d{2}',
|
1496 |
|
|
'L' => '\d',
|
1497 |
|
|
'o' => '\d{4}',
|
1498 |
|
|
'Y' => '-?\d{1,6}',
|
1499 |
|
|
'y' => '\d{2}',
|
1500 |
|
|
'B' => '\d{3}',
|
1501 |
|
|
'g' => '\d{1,2}',
|
1502 |
|
|
'G' => '\d{1,2}',
|
1503 |
|
|
'e' => '\w*',
|
1504 |
|
|
'I' => '\d',
|
1505 |
|
|
'T' => '\w*',
|
1506 |
|
|
'U' => '\d*',
|
1507 |
|
|
'z' => '[+-]?\d*',
|
1508 |
|
|
'O' => '[+-]?\d{4}',
|
1509 |
|
|
// Using S instead of w and 3 as well as 4 to pick up non-ASCII chars like
|
1510 |
|
|
// German umlaut. Per http://drupal.org/node/1101284, we may need as little
|
1511 |
|
|
// as 2 and as many as 5 characters in some languages.
|
1512 |
|
|
'D' => '\S{2,5}',
|
1513 |
|
|
'l' => '\S*',
|
1514 |
|
|
'M' => '\S{2,5}',
|
1515 |
|
|
'F' => '\S*',
|
1516 |
|
|
'P' => '[+-]?\d{2}\:\d{2}',
|
1517 |
|
|
'O' => '[+-]\d{4}',
|
1518 |
|
|
'c' => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]?\d{2}\:\d{2})',
|
1519 |
|
|
'r' => '(\w{3}), (\d{2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):(\d{2})([+-]?\d{4})?',
|
1520 |
|
|
);
|
1521 |
|
|
}
|
1522 |
|
|
|
1523 |
|
|
/**
|
1524 |
|
|
* Constructs an array of granularity options and their labels.
|
1525 |
|
|
*
|
1526 |
|
|
* @return array
|
1527 |
|
|
* An array of translated date parts, keyed by their machine name.
|
1528 |
|
|
*/
|
1529 |
|
|
function date_granularity_names() {
|
1530 |
|
|
return array(
|
1531 |
|
|
'year' => t('Year', array(), array('context' => 'datetime')),
|
1532 |
|
|
'month' => t('Month', array(), array('context' => 'datetime')),
|
1533 |
|
|
'day' => t('Day', array(), array('context' => 'datetime')),
|
1534 |
|
|
'hour' => t('Hour', array(), array('context' => 'datetime')),
|
1535 |
|
|
'minute' => t('Minute', array(), array('context' => 'datetime')),
|
1536 |
|
|
'second' => t('Second', array(), array('context' => 'datetime')),
|
1537 |
|
|
);
|
1538 |
|
|
}
|
1539 |
|
|
|
1540 |
|
|
/**
|
1541 |
|
|
* Sorts a granularity array.
|
1542 |
|
|
*
|
1543 |
|
|
* @param array $granularity
|
1544 |
|
|
* An array of date parts.
|
1545 |
|
|
*/
|
1546 |
|
|
function date_granularity_sorted($granularity) {
|
1547 |
b720ea3e
|
Assos Assos
|
return array_intersect(array(
|
1548 |
|
|
'year',
|
1549 |
|
|
'month',
|
1550 |
|
|
'day',
|
1551 |
|
|
'hour',
|
1552 |
|
|
'minute',
|
1553 |
|
|
'second',
|
1554 |
|
|
), $granularity);
|
1555 |
85ad3d82
|
Assos Assos
|
}
|
1556 |
|
|
|
1557 |
|
|
/**
|
1558 |
|
|
* Constructs an array of granularity based on a given precision.
|
1559 |
|
|
*
|
1560 |
|
|
* @param string $precision
|
1561 |
|
|
* A granularity item.
|
1562 |
|
|
*
|
1563 |
|
|
* @return array
|
1564 |
|
|
* A granularity array containing the given precision and all those above it.
|
1565 |
|
|
* For example, passing in 'month' will return array('year', 'month').
|
1566 |
|
|
*/
|
1567 |
|
|
function date_granularity_array_from_precision($precision) {
|
1568 |
|
|
$granularity_array = array('year', 'month', 'day', 'hour', 'minute', 'second');
|
1569 |
|
|
switch ($precision) {
|
1570 |
|
|
case 'year':
|
1571 |
|
|
return array_slice($granularity_array, -6, 1);
|
1572 |
b720ea3e
|
Assos Assos
|
|
1573 |
85ad3d82
|
Assos Assos
|
case 'month':
|
1574 |
|
|
return array_slice($granularity_array, -6, 2);
|
1575 |
b720ea3e
|
Assos Assos
|
|
1576 |
85ad3d82
|
Assos Assos
|
case 'day':
|
1577 |
|
|
return array_slice($granularity_array, -6, 3);
|
1578 |
b720ea3e
|
Assos Assos
|
|
1579 |
85ad3d82
|
Assos Assos
|
case 'hour':
|
1580 |
|
|
return array_slice($granularity_array, -6, 4);
|
1581 |
b720ea3e
|
Assos Assos
|
|
1582 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
1583 |
|
|
return array_slice($granularity_array, -6, 5);
|
1584 |
b720ea3e
|
Assos Assos
|
|
1585 |
85ad3d82
|
Assos Assos
|
default:
|
1586 |
|
|
return $granularity_array;
|
1587 |
|
|
}
|
1588 |
|
|
}
|
1589 |
|
|
|
1590 |
|
|
/**
|
1591 |
|
|
* Give a granularity array, return the highest precision.
|
1592 |
|
|
*
|
1593 |
|
|
* @param array $granularity_array
|
1594 |
|
|
* An array of date parts.
|
1595 |
|
|
*
|
1596 |
|
|
* @return string
|
1597 |
|
|
* The most precise element in a granularity array.
|
1598 |
|
|
*/
|
1599 |
|
|
function date_granularity_precision($granularity_array) {
|
1600 |
|
|
$input = date_granularity_sorted($granularity_array);
|
1601 |
|
|
return array_pop($input);
|
1602 |
|
|
}
|
1603 |
|
|
|
1604 |
|
|
/**
|
1605 |
|
|
* Constructs a valid DATETIME format string for the granularity of an item.
|
1606 |
|
|
*
|
1607 |
|
|
* @todo This function is no longer used as of
|
1608 |
|
|
* http://drupalcode.org/project/date.git/commit/07efbb5.
|
1609 |
|
|
*/
|
1610 |
|
|
function date_granularity_format($granularity) {
|
1611 |
|
|
if (is_array($granularity)) {
|
1612 |
|
|
$granularity = date_granularity_precision($granularity);
|
1613 |
|
|
}
|
1614 |
|
|
$format = 'Y-m-d H:i:s';
|
1615 |
|
|
switch ($granularity) {
|
1616 |
|
|
case 'year':
|
1617 |
|
|
return substr($format, 0, 1);
|
1618 |
b720ea3e
|
Assos Assos
|
|
1619 |
85ad3d82
|
Assos Assos
|
case 'month':
|
1620 |
|
|
return substr($format, 0, 3);
|
1621 |
b720ea3e
|
Assos Assos
|
|
1622 |
85ad3d82
|
Assos Assos
|
case 'day':
|
1623 |
|
|
return substr($format, 0, 5);
|
1624 |
b720ea3e
|
Assos Assos
|
|
1625 |
85ad3d82
|
Assos Assos
|
case 'hour';
|
1626 |
|
|
return substr($format, 0, 7);
|
1627 |
b720ea3e
|
Assos Assos
|
|
1628 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
1629 |
|
|
return substr($format, 0, 9);
|
1630 |
b720ea3e
|
Assos Assos
|
|
1631 |
85ad3d82
|
Assos Assos
|
default:
|
1632 |
|
|
return $format;
|
1633 |
|
|
}
|
1634 |
|
|
}
|
1635 |
|
|
|
1636 |
|
|
/**
|
1637 |
|
|
* Returns a translated array of timezone names.
|
1638 |
|
|
*
|
1639 |
|
|
* Cache the untranslated array, make the translated array a static variable.
|
1640 |
|
|
*
|
1641 |
|
|
* @param bool $required
|
1642 |
|
|
* (optional) If FALSE, the returned array will include a blank value.
|
1643 |
|
|
* Defaults to FALSE.
|
1644 |
|
|
* @param bool $refresh
|
1645 |
|
|
* (optional) Whether to refresh the list. Defaults to TRUE.
|
1646 |
|
|
*
|
1647 |
|
|
* @return array
|
1648 |
|
|
* An array of timezone names.
|
1649 |
|
|
*/
|
1650 |
|
|
function date_timezone_names($required = FALSE, $refresh = FALSE) {
|
1651 |
|
|
static $zonenames;
|
1652 |
|
|
if (empty($zonenames) || $refresh) {
|
1653 |
|
|
$cached = cache_get('date_timezone_identifiers_list');
|
1654 |
|
|
$zonenames = !empty($cached) ? $cached->data : array();
|
1655 |
|
|
if ($refresh || empty($cached) || empty($zonenames)) {
|
1656 |
|
|
$data = timezone_identifiers_list();
|
1657 |
|
|
asort($data);
|
1658 |
|
|
foreach ($data as $delta => $zone) {
|
1659 |
|
|
// Because many timezones exist in PHP only for backward compatibility
|
1660 |
|
|
// reasons and should not be used, the list is filtered by a regular
|
1661 |
|
|
// expression.
|
1662 |
|
|
if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
|
1663 |
|
|
$zonenames[$zone] = $zone;
|
1664 |
|
|
}
|
1665 |
|
|
}
|
1666 |
|
|
|
1667 |
|
|
if (!empty($zonenames)) {
|
1668 |
|
|
cache_set('date_timezone_identifiers_list', $zonenames);
|
1669 |
|
|
}
|
1670 |
|
|
}
|
1671 |
|
|
foreach ($zonenames as $zone) {
|
1672 |
|
|
$zonenames[$zone] = t('!timezone', array('!timezone' => t($zone)));
|
1673 |
|
|
}
|
1674 |
|
|
}
|
1675 |
|
|
$none = array('' => '');
|
1676 |
|
|
return !$required ? $none + $zonenames : $zonenames;
|
1677 |
|
|
}
|
1678 |
|
|
|
1679 |
|
|
/**
|
1680 |
|
|
* Returns an array of system-allowed timezone abbreviations.
|
1681 |
|
|
*
|
1682 |
|
|
* Cache an array of just the abbreviation names because the whole
|
1683 |
|
|
* timezone_abbreviations_list() is huge, so we don't want to retrieve it more
|
1684 |
|
|
* than necessary.
|
1685 |
|
|
*
|
1686 |
|
|
* @param bool $refresh
|
1687 |
|
|
* (optional) Whether to refresh the list. Defaults to TRUE.
|
1688 |
|
|
*
|
1689 |
|
|
* @return array
|
1690 |
|
|
* An array of allowed timezone abbreviations.
|
1691 |
|
|
*/
|
1692 |
|
|
function date_timezone_abbr($refresh = FALSE) {
|
1693 |
|
|
$cached = cache_get('date_timezone_abbreviations');
|
1694 |
|
|
$data = isset($cached->data) ? $cached->data : array();
|
1695 |
|
|
if (empty($data) || $refresh) {
|
1696 |
|
|
$data = array_keys(timezone_abbreviations_list());
|
1697 |
|
|
cache_set('date_timezone_abbreviations', $data);
|
1698 |
|
|
}
|
1699 |
|
|
return $data;
|
1700 |
|
|
}
|
1701 |
|
|
|
1702 |
|
|
/**
|
1703 |
|
|
* Formats a date, using a date type or a custom date format string.
|
1704 |
|
|
*
|
1705 |
|
|
* Reworked from Drupal's format_date function to handle pre-1970 and
|
1706 |
|
|
* post-2038 dates and accept a date object instead of a timestamp as input.
|
1707 |
|
|
* Translates formatted date results, unlike PHP function date_format().
|
1708 |
|
|
* Should only be used for display, not input, because it can't be parsed.
|
1709 |
|
|
*
|
1710 |
|
|
* @param object $date
|
1711 |
|
|
* A date object.
|
1712 |
|
|
* @param string $type
|
1713 |
|
|
* (optional) The date format to use. Can be 'small', 'medium' or 'large' for
|
1714 |
|
|
* the preconfigured date formats. If 'custom' is specified, then $format is
|
1715 |
|
|
* required as well. Defaults to 'medium'.
|
1716 |
|
|
* @param string $format
|
1717 |
|
|
* (optional) A PHP date format string as required by date(). A backslash
|
1718 |
|
|
* should be used before a character to avoid interpreting the character as
|
1719 |
|
|
* part of a date format. Defaults to an empty string.
|
1720 |
|
|
* @param string $langcode
|
1721 |
|
|
* (optional) Language code to translate to. Defaults to NULL.
|
1722 |
|
|
*
|
1723 |
|
|
* @return string
|
1724 |
|
|
* A translated date string in the requested format.
|
1725 |
|
|
*
|
1726 |
|
|
* @see format_date()
|
1727 |
|
|
*/
|
1728 |
|
|
function date_format_date($date, $type = 'medium', $format = '', $langcode = NULL) {
|
1729 |
|
|
if (empty($date)) {
|
1730 |
|
|
return '';
|
1731 |
|
|
}
|
1732 |
|
|
if ($type != 'custom') {
|
1733 |
|
|
$format = variable_get('date_format_' . $type);
|
1734 |
|
|
}
|
1735 |
|
|
if ($type != 'custom' && empty($format)) {
|
1736 |
|
|
$format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
|
1737 |
|
|
}
|
1738 |
|
|
$format = date_limit_format($format, $date->granularity);
|
1739 |
|
|
$max = strlen($format);
|
1740 |
|
|
$datestring = '';
|
1741 |
|
|
for ($i = 0; $i < $max; $i++) {
|
1742 |
|
|
$c = $format[$i];
|
1743 |
|
|
switch ($c) {
|
1744 |
|
|
case 'l':
|
1745 |
|
|
$datestring .= t($date->format('l'), array(), array('context' => '', 'langcode' => $langcode));
|
1746 |
|
|
break;
|
1747 |
b720ea3e
|
Assos Assos
|
|
1748 |
85ad3d82
|
Assos Assos
|
case 'D':
|
1749 |
|
|
$datestring .= t($date->format('D'), array(), array('context' => '', 'langcode' => $langcode));
|
1750 |
|
|
break;
|
1751 |
b720ea3e
|
Assos Assos
|
|
1752 |
85ad3d82
|
Assos Assos
|
case 'F':
|
1753 |
|
|
$datestring .= t($date->format('F'), array(), array('context' => 'Long month name', 'langcode' => $langcode));
|
1754 |
|
|
break;
|
1755 |
b720ea3e
|
Assos Assos
|
|
1756 |
85ad3d82
|
Assos Assos
|
case 'M':
|
1757 |
|
|
$datestring .= t($date->format('M'), array(), array('langcode' => $langcode));
|
1758 |
|
|
break;
|
1759 |
b720ea3e
|
Assos Assos
|
|
1760 |
85ad3d82
|
Assos Assos
|
case 'A':
|
1761 |
|
|
case 'a':
|
1762 |
|
|
$datestring .= t($date->format($c), array(), array('context' => 'ampm', 'langcode' => $langcode));
|
1763 |
|
|
break;
|
1764 |
b720ea3e
|
Assos Assos
|
|
1765 |
85ad3d82
|
Assos Assos
|
// The timezone name translations can use t().
|
1766 |
|
|
case 'e':
|
1767 |
|
|
case 'T':
|
1768 |
|
|
$datestring .= t($date->format($c));
|
1769 |
|
|
break;
|
1770 |
b720ea3e
|
Assos Assos
|
|
1771 |
85ad3d82
|
Assos Assos
|
// Remaining date parts need no translation.
|
1772 |
|
|
case 'O':
|
1773 |
|
|
$datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
|
1774 |
|
|
break;
|
1775 |
b720ea3e
|
Assos Assos
|
|
1776 |
85ad3d82
|
Assos Assos
|
case 'P':
|
1777 |
|
|
$datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
|
1778 |
|
|
break;
|
1779 |
b720ea3e
|
Assos Assos
|
|
1780 |
85ad3d82
|
Assos Assos
|
case 'Z':
|
1781 |
|
|
$datestring .= date_offset_get($date);
|
1782 |
|
|
break;
|
1783 |
b720ea3e
|
Assos Assos
|
|
1784 |
85ad3d82
|
Assos Assos
|
case '\\':
|
1785 |
|
|
$datestring .= $format[++$i];
|
1786 |
|
|
break;
|
1787 |
b720ea3e
|
Assos Assos
|
|
1788 |
85ad3d82
|
Assos Assos
|
case 'r':
|
1789 |
b720ea3e
|
Assos Assos
|
$datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', 'en');
|
1790 |
85ad3d82
|
Assos Assos
|
break;
|
1791 |
b720ea3e
|
Assos Assos
|
|
1792 |
85ad3d82
|
Assos Assos
|
default:
|
1793 |
|
|
if (strpos('BdcgGhHiIjLmnNosStTuUwWYyz', $c) !== FALSE) {
|
1794 |
|
|
$datestring .= $date->format($c);
|
1795 |
|
|
}
|
1796 |
|
|
else {
|
1797 |
|
|
$datestring .= $c;
|
1798 |
|
|
}
|
1799 |
|
|
}
|
1800 |
|
|
}
|
1801 |
|
|
return $datestring;
|
1802 |
|
|
}
|
1803 |
|
|
|
1804 |
|
|
/**
|
1805 |
|
|
* Formats a time interval with granularity, including past and future context.
|
1806 |
|
|
*
|
1807 |
|
|
* @param object $date
|
1808 |
|
|
* The current date object.
|
1809 |
|
|
* @param int $granularity
|
1810 |
|
|
* (optional) Number of units to display in the string. Defaults to 2.
|
1811 |
|
|
*
|
1812 |
|
|
* @return string
|
1813 |
|
|
* A translated string representation of the interval.
|
1814 |
|
|
*
|
1815 |
|
|
* @see format_interval()
|
1816 |
|
|
*/
|
1817 |
|
|
function date_format_interval($date, $granularity = 2, $display_ago = TRUE) {
|
1818 |
|
|
// If no date is sent, then return nothing.
|
1819 |
|
|
if (empty($date)) {
|
1820 |
|
|
return NULL;
|
1821 |
|
|
}
|
1822 |
|
|
|
1823 |
|
|
$interval = REQUEST_TIME - $date->format('U');
|
1824 |
|
|
if ($interval > 0) {
|
1825 |
|
|
return $display_ago ? t('!time ago', array('!time' => format_interval($interval, $granularity))) :
|
1826 |
|
|
t('!time', array('!time' => format_interval($interval, $granularity)));
|
1827 |
|
|
}
|
1828 |
|
|
else {
|
1829 |
|
|
return format_interval(abs($interval), $granularity);
|
1830 |
|
|
}
|
1831 |
|
|
}
|
1832 |
|
|
|
1833 |
|
|
/**
|
1834 |
|
|
* A date object for the current time.
|
1835 |
|
|
*
|
1836 |
|
|
* @param object $timezone
|
1837 |
|
|
* (optional) Optionally force time to a specific timezone, defaults to user
|
1838 |
|
|
* timezone, if set, otherwise site timezone. Defaults to NULL.
|
1839 |
|
|
*
|
1840 |
b720ea3e
|
Assos Assos
|
* @param bool $reset
|
1841 |
|
|
* (optional) Static cache reset.
|
1842 |
85ad3d82
|
Assos Assos
|
*
|
1843 |
|
|
* @return object
|
1844 |
|
|
* The current time as a date object.
|
1845 |
|
|
*/
|
1846 |
|
|
function date_now($timezone = NULL, $reset = FALSE) {
|
1847 |
db9ffd17
|
Assos Assos
|
if ($reset) {
|
1848 |
|
|
drupal_static_reset(__FUNCTION__ . $timezone);
|
1849 |
|
|
}
|
1850 |
|
|
|
1851 |
|
|
$now = &drupal_static(__FUNCTION__ . $timezone);
|
1852 |
85ad3d82
|
Assos Assos
|
|
1853 |
db9ffd17
|
Assos Assos
|
if (!isset($now)) {
|
1854 |
85ad3d82
|
Assos Assos
|
$now = new DateObject('now', $timezone);
|
1855 |
|
|
}
|
1856 |
|
|
|
1857 |
db9ffd17
|
Assos Assos
|
// Avoid unexpected manipulation of cached $now object
|
1858 |
|
|
// by subsequent code execution
|
1859 |
|
|
// @see https://drupal.org/node/2261395
|
1860 |
|
|
$clone = clone $now;
|
1861 |
|
|
return $clone;
|
1862 |
85ad3d82
|
Assos Assos
|
}
|
1863 |
db9ffd17
|
Assos Assos
|
|
1864 |
85ad3d82
|
Assos Assos
|
/**
|
1865 |
|
|
* Determines if a timezone string is valid.
|
1866 |
|
|
*
|
1867 |
|
|
* @param string $timezone
|
1868 |
|
|
* A potentially invalid timezone string.
|
1869 |
|
|
*
|
1870 |
|
|
* @return bool
|
1871 |
|
|
* TRUE if the timezone is valid, FALSE otherwise.
|
1872 |
|
|
*/
|
1873 |
|
|
function date_timezone_is_valid($timezone) {
|
1874 |
|
|
static $timezone_names;
|
1875 |
|
|
if (empty($timezone_names)) {
|
1876 |
|
|
$timezone_names = array_keys(date_timezone_names(TRUE));
|
1877 |
|
|
}
|
1878 |
|
|
return in_array($timezone, $timezone_names);
|
1879 |
|
|
}
|
1880 |
|
|
|
1881 |
|
|
/**
|
1882 |
|
|
* Returns a timezone name to use as a default.
|
1883 |
|
|
*
|
1884 |
|
|
* @param bool $check_user
|
1885 |
|
|
* (optional) Whether or not to check for a user-configured timezone.
|
1886 |
|
|
* Defaults to TRUE.
|
1887 |
|
|
*
|
1888 |
|
|
* @return string
|
1889 |
|
|
* The default timezone for a user, if available, otherwise the site.
|
1890 |
|
|
*/
|
1891 |
|
|
function date_default_timezone($check_user = TRUE) {
|
1892 |
|
|
global $user;
|
1893 |
|
|
if ($check_user && variable_get('configurable_timezones', 1) && !empty($user->timezone)) {
|
1894 |
|
|
return $user->timezone;
|
1895 |
|
|
}
|
1896 |
|
|
else {
|
1897 |
|
|
$default = variable_get('date_default_timezone', '');
|
1898 |
|
|
return empty($default) ? 'UTC' : $default;
|
1899 |
|
|
}
|
1900 |
|
|
}
|
1901 |
|
|
|
1902 |
|
|
/**
|
1903 |
|
|
* Returns a timezone object for the default timezone.
|
1904 |
|
|
*
|
1905 |
|
|
* @param bool $check_user
|
1906 |
|
|
* (optional) Whether or not to check for a user-configured timezone.
|
1907 |
|
|
* Defaults to TRUE.
|
1908 |
|
|
*
|
1909 |
|
|
* @return object
|
1910 |
|
|
* The default timezone for a user, if available, otherwise the site.
|
1911 |
|
|
*/
|
1912 |
|
|
function date_default_timezone_object($check_user = TRUE) {
|
1913 |
|
|
return timezone_open(date_default_timezone($check_user));
|
1914 |
|
|
}
|
1915 |
|
|
|
1916 |
|
|
/**
|
1917 |
|
|
* Identifies the number of days in a month for a date.
|
1918 |
|
|
*/
|
1919 |
|
|
function date_days_in_month($year, $month) {
|
1920 |
|
|
// Pick a day in the middle of the month to avoid timezone shifts.
|
1921 |
|
|
$datetime = date_pad($year, 4) . '-' . date_pad($month) . '-15 00:00:00';
|
1922 |
|
|
$date = new DateObject($datetime);
|
1923 |
|
|
return $date->format('t');
|
1924 |
|
|
}
|
1925 |
|
|
|
1926 |
|
|
/**
|
1927 |
|
|
* Identifies the number of days in a year for a date.
|
1928 |
|
|
*
|
1929 |
|
|
* @param mixed $date
|
1930 |
|
|
* (optional) The current date object, or a date string. Defaults to NULL.
|
1931 |
|
|
*
|
1932 |
b720ea3e
|
Assos Assos
|
* @return int
|
1933 |
85ad3d82
|
Assos Assos
|
* The number of days in the year.
|
1934 |
|
|
*/
|
1935 |
|
|
function date_days_in_year($date = NULL) {
|
1936 |
|
|
if (empty($date)) {
|
1937 |
|
|
$date = date_now();
|
1938 |
|
|
}
|
1939 |
|
|
elseif (!is_object($date)) {
|
1940 |
|
|
$date = new DateObject($date);
|
1941 |
|
|
}
|
1942 |
|
|
if (is_object($date)) {
|
1943 |
|
|
if ($date->format('L')) {
|
1944 |
|
|
return 366;
|
1945 |
|
|
}
|
1946 |
|
|
else {
|
1947 |
|
|
return 365;
|
1948 |
|
|
}
|
1949 |
|
|
}
|
1950 |
|
|
return NULL;
|
1951 |
|
|
}
|
1952 |
|
|
|
1953 |
|
|
/**
|
1954 |
|
|
* Identifies the number of ISO weeks in a year for a date.
|
1955 |
|
|
*
|
1956 |
|
|
* December 28 is always in the last ISO week of the year.
|
1957 |
|
|
*
|
1958 |
|
|
* @param mixed $date
|
1959 |
|
|
* (optional) The current date object, or a date string. Defaults to NULL.
|
1960 |
|
|
*
|
1961 |
b720ea3e
|
Assos Assos
|
* @return int
|
1962 |
85ad3d82
|
Assos Assos
|
* The number of ISO weeks in a year.
|
1963 |
|
|
*/
|
1964 |
|
|
function date_iso_weeks_in_year($date = NULL) {
|
1965 |
|
|
if (empty($date)) {
|
1966 |
|
|
$date = date_now();
|
1967 |
|
|
}
|
1968 |
|
|
elseif (!is_object($date)) {
|
1969 |
|
|
$date = new DateObject($date);
|
1970 |
|
|
}
|
1971 |
|
|
|
1972 |
|
|
if (is_object($date)) {
|
1973 |
|
|
date_date_set($date, $date->format('Y'), 12, 28);
|
1974 |
|
|
return $date->format('W');
|
1975 |
|
|
}
|
1976 |
|
|
return NULL;
|
1977 |
|
|
}
|
1978 |
|
|
|
1979 |
|
|
/**
|
1980 |
|
|
* Returns day of week for a given date (0 = Sunday).
|
1981 |
|
|
*
|
1982 |
|
|
* @param mixed $date
|
1983 |
|
|
* (optional) A date, default is current local day. Defaults to NULL.
|
1984 |
|
|
*
|
1985 |
|
|
* @return int
|
1986 |
|
|
* The number of the day in the week.
|
1987 |
|
|
*/
|
1988 |
|
|
function date_day_of_week($date = NULL) {
|
1989 |
|
|
if (empty($date)) {
|
1990 |
|
|
$date = date_now();
|
1991 |
|
|
}
|
1992 |
|
|
elseif (!is_object($date)) {
|
1993 |
|
|
$date = new DateObject($date);
|
1994 |
|
|
}
|
1995 |
|
|
|
1996 |
|
|
if (is_object($date)) {
|
1997 |
|
|
return $date->format('w');
|
1998 |
|
|
}
|
1999 |
|
|
return NULL;
|
2000 |
|
|
}
|
2001 |
|
|
|
2002 |
|
|
/**
|
2003 |
|
|
* Returns translated name of the day of week for a given date.
|
2004 |
|
|
*
|
2005 |
|
|
* @param mixed $date
|
2006 |
|
|
* (optional) A date, default is current local day. Defaults to NULL.
|
2007 |
|
|
* @param string $abbr
|
2008 |
|
|
* (optional) Whether to return the abbreviated name for that day.
|
2009 |
|
|
* Defaults to TRUE.
|
2010 |
|
|
*
|
2011 |
|
|
* @return string
|
2012 |
|
|
* The name of the day in the week for that date.
|
2013 |
|
|
*/
|
2014 |
|
|
function date_day_of_week_name($date = NULL, $abbr = TRUE) {
|
2015 |
|
|
if (!is_object($date)) {
|
2016 |
|
|
$date = new DateObject($date);
|
2017 |
|
|
}
|
2018 |
|
|
$dow = date_day_of_week($date);
|
2019 |
|
|
$days = $abbr ? date_week_days_abbr() : date_week_days();
|
2020 |
|
|
return $days[$dow];
|
2021 |
|
|
}
|
2022 |
|
|
|
2023 |
|
|
/**
|
2024 |
|
|
* Calculates the start and end dates for a calendar week.
|
2025 |
|
|
*
|
2026 |
|
|
* The dates are adjusted to use the chosen first day of week for this site.
|
2027 |
|
|
*
|
2028 |
|
|
* @param int $week
|
2029 |
|
|
* The week value.
|
2030 |
|
|
* @param int $year
|
2031 |
|
|
* The year value.
|
2032 |
|
|
*
|
2033 |
|
|
* @return array
|
2034 |
|
|
* A numeric array containing the start and end dates of a week.
|
2035 |
|
|
*/
|
2036 |
|
|
function date_week_range($week, $year) {
|
2037 |
|
|
if (variable_get('date_api_use_iso8601', FALSE)) {
|
2038 |
|
|
return date_iso_week_range($week, $year);
|
2039 |
|
|
}
|
2040 |
|
|
$min_date = new DateObject($year . '-01-01 00:00:00');
|
2041 |
|
|
$min_date->setTimezone(date_default_timezone_object());
|
2042 |
|
|
|
2043 |
|
|
// Move to the right week.
|
2044 |
|
|
date_modify($min_date, '+' . strval(7 * ($week - 1)) . ' days');
|
2045 |
|
|
|
2046 |
|
|
// Move backwards to the first day of the week.
|
2047 |
|
|
$first_day = variable_get('date_first_day', 0);
|
2048 |
|
|
$day_wday = date_format($min_date, 'w');
|
2049 |
|
|
date_modify($min_date, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
|
2050 |
|
|
|
2051 |
|
|
// Move forwards to the last day of the week.
|
2052 |
|
|
$max_date = clone($min_date);
|
2053 |
b720ea3e
|
Assos Assos
|
date_modify($max_date, '+6 days');
|
2054 |
85ad3d82
|
Assos Assos
|
|
2055 |
|
|
if (date_format($min_date, 'Y') != $year) {
|
2056 |
|
|
$min_date = new DateObject($year . '-01-01 00:00:00');
|
2057 |
|
|
}
|
2058 |
|
|
return array($min_date, $max_date);
|
2059 |
|
|
}
|
2060 |
|
|
|
2061 |
|
|
/**
|
2062 |
|
|
* Calculates the start and end dates for an ISO week.
|
2063 |
|
|
*
|
2064 |
|
|
* @param int $week
|
2065 |
|
|
* The week value.
|
2066 |
|
|
* @param int $year
|
2067 |
|
|
* The year value.
|
2068 |
|
|
*
|
2069 |
|
|
* @return array
|
2070 |
|
|
* A numeric array containing the start and end dates of an ISO week.
|
2071 |
|
|
*/
|
2072 |
|
|
function date_iso_week_range($week, $year) {
|
2073 |
|
|
// Get to the last ISO week of the previous year.
|
2074 |
|
|
$min_date = new DateObject(($year - 1) . '-12-28 00:00:00');
|
2075 |
|
|
date_timezone_set($min_date, date_default_timezone_object());
|
2076 |
|
|
|
2077 |
|
|
// Find the first day of the first ISO week in the year.
|
2078 |
|
|
date_modify($min_date, '+1 Monday');
|
2079 |
|
|
|
2080 |
|
|
// Jump ahead to the desired week for the beginning of the week range.
|
2081 |
|
|
if ($week > 1) {
|
2082 |
|
|
date_modify($min_date, '+ ' . ($week - 1) . ' weeks');
|
2083 |
|
|
}
|
2084 |
|
|
|
2085 |
|
|
// Move forwards to the last day of the week.
|
2086 |
|
|
$max_date = clone($min_date);
|
2087 |
b720ea3e
|
Assos Assos
|
date_modify($max_date, '+6 days');
|
2088 |
85ad3d82
|
Assos Assos
|
return array($min_date, $max_date);
|
2089 |
|
|
}
|
2090 |
|
|
|
2091 |
|
|
/**
|
2092 |
|
|
* The number of calendar weeks in a year.
|
2093 |
|
|
*
|
2094 |
|
|
* PHP week functions return the ISO week, not the calendar week.
|
2095 |
|
|
*
|
2096 |
|
|
* @param int $year
|
2097 |
|
|
* A year value.
|
2098 |
|
|
*
|
2099 |
|
|
* @return int
|
2100 |
|
|
* Number of calendar weeks in selected year.
|
2101 |
|
|
*/
|
2102 |
|
|
function date_weeks_in_year($year) {
|
2103 |
|
|
$date = new DateObject(($year + 1) . '-01-01 12:00:00', 'UTC');
|
2104 |
|
|
date_modify($date, '-1 day');
|
2105 |
|
|
return date_week($date->format('Y-m-d'));
|
2106 |
|
|
}
|
2107 |
|
|
|
2108 |
|
|
/**
|
2109 |
|
|
* The calendar week number for a date.
|
2110 |
|
|
*
|
2111 |
|
|
* PHP week functions return the ISO week, not the calendar week.
|
2112 |
|
|
*
|
2113 |
|
|
* @param string $date
|
2114 |
|
|
* A date string in the format Y-m-d.
|
2115 |
|
|
*
|
2116 |
|
|
* @return int
|
2117 |
|
|
* The calendar week number.
|
2118 |
|
|
*/
|
2119 |
|
|
function date_week($date) {
|
2120 |
|
|
$date = substr($date, 0, 10);
|
2121 |
|
|
$parts = explode('-', $date);
|
2122 |
|
|
|
2123 |
|
|
$date = new DateObject($date . ' 12:00:00', 'UTC');
|
2124 |
|
|
|
2125 |
|
|
// If we are using ISO weeks, this is easy.
|
2126 |
|
|
if (variable_get('date_api_use_iso8601', FALSE)) {
|
2127 |
|
|
return intval($date->format('W'));
|
2128 |
|
|
}
|
2129 |
|
|
|
2130 |
|
|
$year_date = new DateObject($parts[0] . '-01-01 12:00:00', 'UTC');
|
2131 |
|
|
$week = intval($date->format('W'));
|
2132 |
|
|
$year_week = intval(date_format($year_date, 'W'));
|
2133 |
|
|
$date_year = intval($date->format('o'));
|
2134 |
|
|
|
2135 |
|
|
// Remove the leap week if it's present.
|
2136 |
|
|
if ($date_year > intval($parts[0])) {
|
2137 |
|
|
$last_date = clone($date);
|
2138 |
|
|
date_modify($last_date, '-7 days');
|
2139 |
|
|
$week = date_format($last_date, 'W') + 1;
|
2140 |
|
|
}
|
2141 |
|
|
elseif ($date_year < intval($parts[0])) {
|
2142 |
|
|
$week = 0;
|
2143 |
|
|
}
|
2144 |
|
|
|
2145 |
|
|
if ($year_week != 1) {
|
2146 |
|
|
$week++;
|
2147 |
|
|
}
|
2148 |
|
|
|
2149 |
|
|
// Convert to ISO-8601 day number, to match weeks calculated above.
|
2150 |
|
|
$iso_first_day = 1 + (variable_get('date_first_day', 0) + 6) % 7;
|
2151 |
|
|
|
2152 |
|
|
// If it's before the starting day, it's the previous week.
|
2153 |
|
|
if (intval($date->format('N')) < $iso_first_day) {
|
2154 |
|
|
$week--;
|
2155 |
|
|
}
|
2156 |
|
|
|
2157 |
|
|
// If the year starts before, it's an extra week at the beginning.
|
2158 |
|
|
if (intval(date_format($year_date, 'N')) < $iso_first_day) {
|
2159 |
|
|
$week++;
|
2160 |
|
|
}
|
2161 |
|
|
|
2162 |
|
|
return $week;
|
2163 |
|
|
}
|
2164 |
|
|
|
2165 |
|
|
/**
|
2166 |
|
|
* Helper function to left pad date parts with zeros.
|
2167 |
|
|
*
|
2168 |
|
|
* Provided because this is needed so often with dates.
|
2169 |
|
|
*
|
2170 |
|
|
* @param int $value
|
2171 |
|
|
* The value to pad.
|
2172 |
|
|
* @param int $size
|
2173 |
|
|
* (optional) Total size expected, usually 2 or 4. Defaults to 2.
|
2174 |
|
|
*
|
2175 |
|
|
* @return string
|
2176 |
|
|
* The padded value.
|
2177 |
|
|
*/
|
2178 |
|
|
function date_pad($value, $size = 2) {
|
2179 |
|
|
return sprintf("%0" . $size . "d", $value);
|
2180 |
|
|
}
|
2181 |
|
|
|
2182 |
|
|
/**
|
2183 |
|
|
* Determines if the granularity contains a time portion.
|
2184 |
|
|
*
|
2185 |
|
|
* @param array $granularity
|
2186 |
|
|
* An array of allowed date parts, all others will be removed.
|
2187 |
|
|
*
|
2188 |
|
|
* @return bool
|
2189 |
|
|
* TRUE if the granularity contains a time portion, FALSE otherwise.
|
2190 |
|
|
*/
|
2191 |
|
|
function date_has_time($granularity) {
|
2192 |
|
|
if (!is_array($granularity)) {
|
2193 |
|
|
$granularity = array();
|
2194 |
|
|
}
|
2195 |
b720ea3e
|
Assos Assos
|
$options = array('hour', 'minute', 'second');
|
2196 |
|
|
return (bool) count(array_intersect($granularity, $options));
|
2197 |
85ad3d82
|
Assos Assos
|
}
|
2198 |
|
|
|
2199 |
|
|
/**
|
2200 |
|
|
* Determines if the granularity contains a date portion.
|
2201 |
|
|
*
|
2202 |
|
|
* @param array $granularity
|
2203 |
|
|
* An array of allowed date parts, all others will be removed.
|
2204 |
|
|
*
|
2205 |
|
|
* @return bool
|
2206 |
|
|
* TRUE if the granularity contains a date portion, FALSE otherwise.
|
2207 |
|
|
*/
|
2208 |
|
|
function date_has_date($granularity) {
|
2209 |
|
|
if (!is_array($granularity)) {
|
2210 |
|
|
$granularity = array();
|
2211 |
|
|
}
|
2212 |
b720ea3e
|
Assos Assos
|
$options = array('year', 'month', 'day');
|
2213 |
|
|
return (bool) count(array_intersect($granularity, $options));
|
2214 |
85ad3d82
|
Assos Assos
|
}
|
2215 |
|
|
|
2216 |
|
|
/**
|
2217 |
|
|
* Helper function to get a format for a specific part of a date field.
|
2218 |
|
|
*
|
2219 |
|
|
* @param string $part
|
2220 |
|
|
* The date field part, either 'time' or 'date'.
|
2221 |
|
|
* @param string $format
|
2222 |
|
|
* A date format string.
|
2223 |
|
|
*
|
2224 |
|
|
* @return string
|
2225 |
|
|
* The date format for the given part.
|
2226 |
|
|
*/
|
2227 |
|
|
function date_part_format($part, $format) {
|
2228 |
|
|
switch ($part) {
|
2229 |
|
|
case 'date':
|
2230 |
|
|
return date_limit_format($format, array('year', 'month', 'day'));
|
2231 |
b720ea3e
|
Assos Assos
|
|
2232 |
85ad3d82
|
Assos Assos
|
case 'time':
|
2233 |
|
|
return date_limit_format($format, array('hour', 'minute', 'second'));
|
2234 |
b720ea3e
|
Assos Assos
|
|
2235 |
85ad3d82
|
Assos Assos
|
default:
|
2236 |
|
|
return date_limit_format($format, array($part));
|
2237 |
|
|
}
|
2238 |
|
|
}
|
2239 |
|
|
|
2240 |
|
|
/**
|
2241 |
|
|
* Limits a date format to include only elements from a given granularity array.
|
2242 |
|
|
*
|
2243 |
|
|
* Example:
|
2244 |
|
|
* date_limit_format('F j, Y - H:i', array('year', 'month', 'day'));
|
2245 |
|
|
* returns 'F j, Y'
|
2246 |
|
|
*
|
2247 |
|
|
* @param string $format
|
2248 |
|
|
* A date format string.
|
2249 |
|
|
* @param array $granularity
|
2250 |
|
|
* An array of allowed date parts, all others will be removed.
|
2251 |
|
|
*
|
2252 |
|
|
* @return string
|
2253 |
|
|
* The format string with all other elements removed.
|
2254 |
|
|
*/
|
2255 |
|
|
function date_limit_format($format, $granularity) {
|
2256 |
|
|
// Use the advanced drupal_static() pattern to improve performance.
|
2257 |
|
|
static $drupal_static_fast;
|
2258 |
|
|
if (!isset($drupal_static_fast)) {
|
2259 |
|
|
$drupal_static_fast['formats'] = &drupal_static(__FUNCTION__);
|
2260 |
|
|
}
|
2261 |
|
|
$formats = &$drupal_static_fast['formats'];
|
2262 |
b720ea3e
|
Assos Assos
|
$format_granularity_cid = $format . '|' . implode(',', $granularity);
|
2263 |
85ad3d82
|
Assos Assos
|
if (isset($formats[$format_granularity_cid])) {
|
2264 |
|
|
return $formats[$format_granularity_cid];
|
2265 |
|
|
}
|
2266 |
|
|
|
2267 |
|
|
// If punctuation has been escaped, remove the escaping. Done using strtr()
|
2268 |
|
|
// because it is easier than getting the escape character extracted using
|
2269 |
|
|
// preg_replace().
|
2270 |
|
|
$replace = array(
|
2271 |
|
|
'\-' => '-',
|
2272 |
|
|
'\:' => ':',
|
2273 |
|
|
"\'" => "'",
|
2274 |
|
|
'\. ' => ' . ',
|
2275 |
|
|
'\,' => ',',
|
2276 |
|
|
);
|
2277 |
|
|
$format = strtr($format, $replace);
|
2278 |
|
|
|
2279 |
|
|
// Get the 'T' out of ISO date formats that don't have both date and time.
|
2280 |
|
|
if (!date_has_time($granularity) || !date_has_date($granularity)) {
|
2281 |
|
|
$format = str_replace('\T', ' ', $format);
|
2282 |
|
|
$format = str_replace('T', ' ', $format);
|
2283 |
|
|
}
|
2284 |
|
|
|
2285 |
|
|
$regex = array();
|
2286 |
|
|
if (!date_has_time($granularity)) {
|
2287 |
|
|
$regex[] = '((?<!\\\\)[a|A])';
|
2288 |
|
|
}
|
2289 |
|
|
// Create regular expressions to remove selected values from string.
|
2290 |
|
|
// Use (?<!\\\\) to keep escaped letters from being removed.
|
2291 |
|
|
foreach (date_nongranularity($granularity) as $element) {
|
2292 |
|
|
switch ($element) {
|
2293 |
|
|
case 'year':
|
2294 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[Yy])';
|
2295 |
|
|
break;
|
2296 |
b720ea3e
|
Assos Assos
|
|
2297 |
85ad3d82
|
Assos Assos
|
case 'day':
|
2298 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[l|D|d|dS|j|jS|N|w|W|z]{1,2})';
|
2299 |
|
|
break;
|
2300 |
b720ea3e
|
Assos Assos
|
|
2301 |
85ad3d82
|
Assos Assos
|
case 'month':
|
2302 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[FMmn])';
|
2303 |
|
|
break;
|
2304 |
b720ea3e
|
Assos Assos
|
|
2305 |
85ad3d82
|
Assos Assos
|
case 'hour':
|
2306 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[HhGg])';
|
2307 |
|
|
break;
|
2308 |
b720ea3e
|
Assos Assos
|
|
2309 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
2310 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[i])';
|
2311 |
|
|
break;
|
2312 |
b720ea3e
|
Assos Assos
|
|
2313 |
85ad3d82
|
Assos Assos
|
case 'second':
|
2314 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[s])';
|
2315 |
|
|
break;
|
2316 |
b720ea3e
|
Assos Assos
|
|
2317 |
85ad3d82
|
Assos Assos
|
case 'timezone':
|
2318 |
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[TOZPe])';
|
2319 |
|
|
break;
|
2320 |
|
|
|
2321 |
|
|
}
|
2322 |
|
|
}
|
2323 |
|
|
// Remove empty parentheses, brackets, pipes.
|
2324 |
|
|
$regex[] = '(\(\))';
|
2325 |
|
|
$regex[] = '(\[\])';
|
2326 |
|
|
$regex[] = '(\|\|)';
|
2327 |
|
|
|
2328 |
|
|
// Remove selected values from string.
|
2329 |
|
|
$format = trim(preg_replace($regex, array(), $format));
|
2330 |
|
|
// Remove orphaned punctuation at the beginning of the string.
|
2331 |
|
|
$format = preg_replace('`^([\-/\.,:\'])`', '', $format);
|
2332 |
|
|
// Remove orphaned punctuation at the end of the string.
|
2333 |
|
|
$format = preg_replace('([\-/,:\']$)', '', $format);
|
2334 |
|
|
$format = preg_replace('(\\$)', '', $format);
|
2335 |
|
|
|
2336 |
|
|
// Trim any whitespace from the result.
|
2337 |
|
|
$format = trim($format);
|
2338 |
|
|
|
2339 |
|
|
// After removing the non-desired parts of the format, test if the only things
|
2340 |
|
|
// left are escaped, non-date, characters. If so, return nothing.
|
2341 |
|
|
// Using S instead of w to pick up non-ASCII characters.
|
2342 |
|
|
$test = trim(preg_replace('(\\\\\S{1,3})u', '', $format));
|
2343 |
|
|
if (empty($test)) {
|
2344 |
|
|
$format = '';
|
2345 |
|
|
}
|
2346 |
|
|
|
2347 |
|
|
// Store the return value in the static array for performance.
|
2348 |
|
|
$formats[$format_granularity_cid] = $format;
|
2349 |
|
|
|
2350 |
|
|
return $format;
|
2351 |
|
|
}
|
2352 |
|
|
|
2353 |
|
|
/**
|
2354 |
|
|
* Converts a format to an ordered array of granularity parts.
|
2355 |
|
|
*
|
2356 |
|
|
* Example:
|
2357 |
|
|
* date_format_order('m/d/Y H:i')
|
2358 |
|
|
* returns
|
2359 |
|
|
* array(
|
2360 |
|
|
* 0 => 'month',
|
2361 |
|
|
* 1 => 'day',
|
2362 |
|
|
* 2 => 'year',
|
2363 |
|
|
* 3 => 'hour',
|
2364 |
|
|
* 4 => 'minute',
|
2365 |
|
|
* );
|
2366 |
|
|
*
|
2367 |
|
|
* @param string $format
|
2368 |
|
|
* A date format string.
|
2369 |
|
|
*
|
2370 |
|
|
* @return array
|
2371 |
|
|
* An array of ordered granularity elements from the given format string.
|
2372 |
|
|
*/
|
2373 |
|
|
function date_format_order($format) {
|
2374 |
|
|
$order = array();
|
2375 |
|
|
if (empty($format)) {
|
2376 |
|
|
return $order;
|
2377 |
|
|
}
|
2378 |
|
|
|
2379 |
|
|
$max = strlen($format);
|
2380 |
|
|
for ($i = 0; $i <= $max; $i++) {
|
2381 |
|
|
if (!isset($format[$i])) {
|
2382 |
|
|
break;
|
2383 |
|
|
}
|
2384 |
|
|
switch ($format[$i]) {
|
2385 |
|
|
case 'd':
|
2386 |
|
|
case 'j':
|
2387 |
|
|
$order[] = 'day';
|
2388 |
|
|
break;
|
2389 |
b720ea3e
|
Assos Assos
|
|
2390 |
85ad3d82
|
Assos Assos
|
case 'F':
|
2391 |
|
|
case 'M':
|
2392 |
|
|
case 'm':
|
2393 |
|
|
case 'n':
|
2394 |
|
|
$order[] = 'month';
|
2395 |
|
|
break;
|
2396 |
b720ea3e
|
Assos Assos
|
|
2397 |
85ad3d82
|
Assos Assos
|
case 'Y':
|
2398 |
|
|
case 'y':
|
2399 |
|
|
$order[] = 'year';
|
2400 |
|
|
break;
|
2401 |
b720ea3e
|
Assos Assos
|
|
2402 |
85ad3d82
|
Assos Assos
|
case 'g':
|
2403 |
|
|
case 'G':
|
2404 |
|
|
case 'h':
|
2405 |
|
|
case 'H':
|
2406 |
|
|
$order[] = 'hour';
|
2407 |
|
|
break;
|
2408 |
b720ea3e
|
Assos Assos
|
|
2409 |
85ad3d82
|
Assos Assos
|
case 'i':
|
2410 |
|
|
$order[] = 'minute';
|
2411 |
|
|
break;
|
2412 |
b720ea3e
|
Assos Assos
|
|
2413 |
85ad3d82
|
Assos Assos
|
case 's':
|
2414 |
|
|
$order[] = 'second';
|
2415 |
|
|
break;
|
2416 |
|
|
}
|
2417 |
|
|
}
|
2418 |
|
|
return $order;
|
2419 |
|
|
}
|
2420 |
|
|
|
2421 |
|
|
/**
|
2422 |
|
|
* Strips out unwanted granularity elements.
|
2423 |
|
|
*
|
2424 |
|
|
* @param array $granularity
|
2425 |
|
|
* An array like ('year', 'month', 'day', 'hour', 'minute', 'second');
|
2426 |
|
|
*
|
2427 |
|
|
* @return array
|
2428 |
|
|
* A reduced set of granularitiy elements.
|
2429 |
|
|
*/
|
2430 |
|
|
function date_nongranularity($granularity) {
|
2431 |
b720ea3e
|
Assos Assos
|
$options = array(
|
2432 |
|
|
'year',
|
2433 |
|
|
'month',
|
2434 |
|
|
'day',
|
2435 |
|
|
'hour',
|
2436 |
|
|
'minute',
|
2437 |
|
|
'second',
|
2438 |
|
|
'timezone',
|
2439 |
|
|
);
|
2440 |
|
|
return array_diff($options, (array) $granularity);
|
2441 |
85ad3d82
|
Assos Assos
|
}
|
2442 |
|
|
|
2443 |
|
|
/**
|
2444 |
|
|
* Implements hook_element_info().
|
2445 |
|
|
*/
|
2446 |
|
|
function date_api_element_info() {
|
2447 |
|
|
module_load_include('inc', 'date_api', 'date_api_elements');
|
2448 |
|
|
return _date_api_element_info();
|
2449 |
|
|
}
|
2450 |
|
|
|
2451 |
|
|
/**
|
2452 |
|
|
* Implements hook_theme().
|
2453 |
|
|
*/
|
2454 |
|
|
function date_api_theme($existing, $type, $theme, $path) {
|
2455 |
|
|
$base = array(
|
2456 |
|
|
'file' => 'theme.inc',
|
2457 |
|
|
'path' => "$path/theme",
|
2458 |
|
|
);
|
2459 |
|
|
return array(
|
2460 |
b720ea3e
|
Assos Assos
|
'date_nav_title' => $base + array(
|
2461 |
|
|
'variables' => array(
|
2462 |
|
|
'granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL
|
2463 |
|
|
),
|
2464 |
|
|
),
|
2465 |
85ad3d82
|
Assos Assos
|
'date_timezone' => $base + array('render element' => 'element'),
|
2466 |
|
|
'date_select' => $base + array('render element' => 'element'),
|
2467 |
|
|
'date_text' => $base + array('render element' => 'element'),
|
2468 |
|
|
'date_select_element' => $base + array('render element' => 'element'),
|
2469 |
|
|
'date_textfield_element' => $base + array('render element' => 'element'),
|
2470 |
|
|
'date_part_hour_prefix' => $base + array('render element' => 'element'),
|
2471 |
|
|
'date_part_minsec_prefix' => $base + array('render element' => 'element'),
|
2472 |
|
|
'date_part_label_year' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2473 |
|
|
'date_part_label_month' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2474 |
|
|
'date_part_label_day' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2475 |
|
|
'date_part_label_hour' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2476 |
|
|
'date_part_label_minute' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2477 |
|
|
'date_part_label_second' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2478 |
|
|
'date_part_label_ampm' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2479 |
|
|
'date_part_label_timezone' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2480 |
|
|
'date_part_label_date' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2481 |
|
|
'date_part_label_time' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
|
2482 |
|
|
'date_views_filter_form' => $base + array('template' => 'date-views-filter-form', 'render element' => 'form'),
|
2483 |
|
|
'date_calendar_day' => $base + array('variables' => array('date' => NULL)),
|
2484 |
b720ea3e
|
Assos Assos
|
'date_time_ago' => $base + array(
|
2485 |
|
|
'variables' => array(
|
2486 |
|
|
'start_date' => NULL, 'end_date' => NULL, 'interval' => NULL
|
2487 |
|
|
),
|
2488 |
|
|
),
|
2489 |
85ad3d82
|
Assos Assos
|
);
|
2490 |
|
|
}
|
2491 |
|
|
|
2492 |
|
|
/**
|
2493 |
|
|
* Function to figure out which local timezone applies to a date and select it.
|
2494 |
|
|
*
|
2495 |
|
|
* @param string $handling
|
2496 |
|
|
* The timezone handling.
|
2497 |
|
|
* @param string $timezone
|
2498 |
|
|
* (optional) A timezone string. Defaults to an empty string.
|
2499 |
|
|
*
|
2500 |
|
|
* @return string
|
2501 |
|
|
* The timezone string.
|
2502 |
|
|
*/
|
2503 |
|
|
function date_get_timezone($handling, $timezone = '') {
|
2504 |
|
|
switch ($handling) {
|
2505 |
|
|
case 'date':
|
2506 |
|
|
$timezone = !empty($timezone) ? $timezone : date_default_timezone();
|
2507 |
|
|
break;
|
2508 |
b720ea3e
|
Assos Assos
|
|
2509 |
85ad3d82
|
Assos Assos
|
case 'utc':
|
2510 |
|
|
$timezone = 'UTC';
|
2511 |
|
|
break;
|
2512 |
b720ea3e
|
Assos Assos
|
|
2513 |
85ad3d82
|
Assos Assos
|
default:
|
2514 |
|
|
$timezone = date_default_timezone();
|
2515 |
|
|
}
|
2516 |
|
|
return $timezone > '' ? $timezone : date_default_timezone();
|
2517 |
|
|
}
|
2518 |
|
|
|
2519 |
|
|
/**
|
2520 |
db9ffd17
|
Assos Assos
|
* Function to figure out which db timezone applies to a date.
|
2521 |
85ad3d82
|
Assos Assos
|
*
|
2522 |
|
|
* @param string $handling
|
2523 |
|
|
* The timezone handling.
|
2524 |
|
|
* @param string $timezone
|
2525 |
db9ffd17
|
Assos Assos
|
* (optional) When $handling is 'date', date_get_timezone_db() returns this
|
2526 |
|
|
* value.
|
2527 |
85ad3d82
|
Assos Assos
|
*
|
2528 |
|
|
* @return string
|
2529 |
|
|
* The timezone string.
|
2530 |
|
|
*/
|
2531 |
db9ffd17
|
Assos Assos
|
function date_get_timezone_db($handling, $timezone = NULL) {
|
2532 |
85ad3d82
|
Assos Assos
|
switch ($handling) {
|
2533 |
db9ffd17
|
Assos Assos
|
case ('utc'):
|
2534 |
|
|
case ('site'):
|
2535 |
|
|
case ('user'):
|
2536 |
|
|
// These handling modes all convert to UTC before storing in the DB.
|
2537 |
|
|
$timezone = 'UTC';
|
2538 |
|
|
break;
|
2539 |
b720ea3e
|
Assos Assos
|
|
2540 |
db9ffd17
|
Assos Assos
|
case ('date'):
|
2541 |
|
|
if ($timezone == NULL) {
|
2542 |
|
|
// This shouldn't happen, since it's meaning is undefined. But we need
|
2543 |
|
|
// to fall back to *something* that's a legal timezone.
|
2544 |
|
|
$timezone = date_default_timezone();
|
2545 |
|
|
}
|
2546 |
85ad3d82
|
Assos Assos
|
break;
|
2547 |
b720ea3e
|
Assos Assos
|
|
2548 |
db9ffd17
|
Assos Assos
|
case ('none'):
|
2549 |
85ad3d82
|
Assos Assos
|
default:
|
2550 |
db9ffd17
|
Assos Assos
|
$timezone = date_default_timezone();
|
2551 |
85ad3d82
|
Assos Assos
|
break;
|
2552 |
|
|
}
|
2553 |
db9ffd17
|
Assos Assos
|
return $timezone;
|
2554 |
85ad3d82
|
Assos Assos
|
}
|
2555 |
|
|
|
2556 |
|
|
/**
|
2557 |
|
|
* Helper function for converting back and forth from '+1' to 'First'.
|
2558 |
|
|
*/
|
2559 |
|
|
function date_order_translated() {
|
2560 |
|
|
return array(
|
2561 |
|
|
'+1' => t('First', array(), array('context' => 'date_order')),
|
2562 |
|
|
'+2' => t('Second', array(), array('context' => 'date_order')),
|
2563 |
|
|
'+3' => t('Third', array(), array('context' => 'date_order')),
|
2564 |
|
|
'+4' => t('Fourth', array(), array('context' => 'date_order')),
|
2565 |
|
|
'+5' => t('Fifth', array(), array('context' => 'date_order')),
|
2566 |
|
|
'-1' => t('Last', array(), array('context' => 'date_order_reverse')),
|
2567 |
|
|
'-2' => t('Next to last', array(), array('context' => 'date_order_reverse')),
|
2568 |
|
|
'-3' => t('Third from last', array(), array('context' => 'date_order_reverse')),
|
2569 |
|
|
'-4' => t('Fourth from last', array(), array('context' => 'date_order_reverse')),
|
2570 |
|
|
'-5' => t('Fifth from last', array(), array('context' => 'date_order_reverse')),
|
2571 |
|
|
);
|
2572 |
|
|
}
|
2573 |
|
|
|
2574 |
|
|
/**
|
2575 |
|
|
* Creates an array of ordered strings, using English text when possible.
|
2576 |
|
|
*/
|
2577 |
|
|
function date_order() {
|
2578 |
|
|
return array(
|
2579 |
|
|
'+1' => 'First',
|
2580 |
|
|
'+2' => 'Second',
|
2581 |
|
|
'+3' => 'Third',
|
2582 |
|
|
'+4' => 'Fourth',
|
2583 |
|
|
'+5' => 'Fifth',
|
2584 |
|
|
'-1' => 'Last',
|
2585 |
|
|
'-2' => '-2',
|
2586 |
|
|
'-3' => '-3',
|
2587 |
|
|
'-4' => '-4',
|
2588 |
|
|
'-5' => '-5',
|
2589 |
|
|
);
|
2590 |
|
|
}
|
2591 |
|
|
|
2592 |
|
|
/**
|
2593 |
|
|
* Tests validity of a date range string.
|
2594 |
|
|
*
|
2595 |
|
|
* @param string $string
|
2596 |
|
|
* A min and max year string like '-3:+1'a.
|
2597 |
|
|
*
|
2598 |
|
|
* @return bool
|
2599 |
|
|
* TRUE if the date range is valid, FALSE otherwise.
|
2600 |
|
|
*/
|
2601 |
|
|
function date_range_valid($string) {
|
2602 |
b720ea3e
|
Assos Assos
|
$matches = preg_match('@^([\+\-][0-9]+|[0-9]{4}):([\+\-][0-9]+|[0-9]{4})$@', $string);
|
2603 |
85ad3d82
|
Assos Assos
|
return $matches < 1 ? FALSE : TRUE;
|
2604 |
|
|
}
|
2605 |
|
|
|
2606 |
|
|
/**
|
2607 |
b720ea3e
|
Assos Assos
|
* Splits a string like -3:+3 or 2001:2010 into an array of start and end years.
|
2608 |
85ad3d82
|
Assos Assos
|
*
|
2609 |
|
|
* Center the range around the current year, if any, but expand it far
|
2610 |
|
|
* enough so it will pick up the year value in the field in case
|
2611 |
|
|
* the value in the field is outside the initial range.
|
2612 |
|
|
*
|
2613 |
|
|
* @param string $string
|
2614 |
|
|
* A min and max year string like '-3:+1'.
|
2615 |
|
|
* @param object $date
|
2616 |
|
|
* (optional) A date object. Defaults to NULL.
|
2617 |
|
|
*
|
2618 |
|
|
* @return array
|
2619 |
b720ea3e
|
Assos Assos
|
* A numerically indexed array, containing a start and end year.
|
2620 |
85ad3d82
|
Assos Assos
|
*/
|
2621 |
|
|
function date_range_years($string, $date = NULL) {
|
2622 |
|
|
$this_year = date_format(date_now(), 'Y');
|
2623 |
b720ea3e
|
Assos Assos
|
list($start_year, $end_year) = explode(':', $string);
|
2624 |
85ad3d82
|
Assos Assos
|
|
2625 |
|
|
// Valid patterns would be -5:+5, 0:+1, 2008:2010.
|
2626 |
b720ea3e
|
Assos Assos
|
$plus_pattern = '@[\+\-][0-9]{1,4}@';
|
2627 |
85ad3d82
|
Assos Assos
|
$year_pattern = '@^[0-9]{4}@';
|
2628 |
b720ea3e
|
Assos Assos
|
if (!preg_match($year_pattern, $start_year, $matches)) {
|
2629 |
|
|
if (preg_match($plus_pattern, $start_year, $matches)) {
|
2630 |
|
|
$start_year = $this_year + $matches[0];
|
2631 |
85ad3d82
|
Assos Assos
|
}
|
2632 |
|
|
else {
|
2633 |
b720ea3e
|
Assos Assos
|
$start_year = $this_year;
|
2634 |
85ad3d82
|
Assos Assos
|
}
|
2635 |
|
|
}
|
2636 |
b720ea3e
|
Assos Assos
|
if (!preg_match($year_pattern, $end_year, $matches)) {
|
2637 |
|
|
if (preg_match($plus_pattern, $end_year, $matches)) {
|
2638 |
|
|
$end_year = $this_year + $matches[0];
|
2639 |
85ad3d82
|
Assos Assos
|
}
|
2640 |
|
|
else {
|
2641 |
b720ea3e
|
Assos Assos
|
$end_year = $this_year;
|
2642 |
85ad3d82
|
Assos Assos
|
}
|
2643 |
|
|
}
|
2644 |
|
|
// If there is a current value, stretch the range to include it.
|
2645 |
|
|
$value_year = is_object($date) ? $date->format('Y') : '';
|
2646 |
|
|
if (!empty($value_year)) {
|
2647 |
b720ea3e
|
Assos Assos
|
if ($start_year <= $end_year) {
|
2648 |
|
|
$start_year = min($value_year, $start_year);
|
2649 |
|
|
$end_year = max($value_year, $end_year);
|
2650 |
|
|
}
|
2651 |
|
|
else {
|
2652 |
|
|
$start_year = max($value_year, $start_year);
|
2653 |
|
|
$end_year = min($value_year, $end_year);
|
2654 |
|
|
}
|
2655 |
85ad3d82
|
Assos Assos
|
}
|
2656 |
b720ea3e
|
Assos Assos
|
return array($start_year, $end_year);
|
2657 |
85ad3d82
|
Assos Assos
|
}
|
2658 |
|
|
|
2659 |
|
|
/**
|
2660 |
|
|
* Converts a min and max year into a string like '-3:+1'.
|
2661 |
|
|
*
|
2662 |
|
|
* @param array $years
|
2663 |
|
|
* A numerically indexed array, containing a minimum and maximum year.
|
2664 |
|
|
*
|
2665 |
|
|
* @return string
|
2666 |
|
|
* A min and max year string like '-3:+1'.
|
2667 |
|
|
*/
|
2668 |
|
|
function date_range_string($years) {
|
2669 |
|
|
$this_year = date_format(date_now(), 'Y');
|
2670 |
|
|
|
2671 |
|
|
if ($years[0] < $this_year) {
|
2672 |
|
|
$min = '-' . ($this_year - $years[0]);
|
2673 |
|
|
}
|
2674 |
|
|
else {
|
2675 |
|
|
$min = '+' . ($years[0] - $this_year);
|
2676 |
|
|
}
|
2677 |
|
|
|
2678 |
|
|
if ($years[1] < $this_year) {
|
2679 |
|
|
$max = '-' . ($this_year - $years[1]);
|
2680 |
|
|
}
|
2681 |
|
|
else {
|
2682 |
|
|
$max = '+' . ($years[1] - $this_year);
|
2683 |
|
|
}
|
2684 |
|
|
|
2685 |
|
|
return $min . ':' . $max;
|
2686 |
|
|
}
|
2687 |
|
|
|
2688 |
|
|
/**
|
2689 |
|
|
* Temporary helper to re-create equivalent of content_database_info().
|
2690 |
|
|
*/
|
2691 |
|
|
function date_api_database_info($field, $revision = FIELD_LOAD_CURRENT) {
|
2692 |
|
|
return array(
|
2693 |
|
|
'columns' => $field['storage']['details']['sql'][$revision],
|
2694 |
|
|
'table' => _field_sql_storage_tablename($field),
|
2695 |
|
|
);
|
2696 |
|
|
}
|
2697 |
|
|
|
2698 |
|
|
/**
|
2699 |
|
|
* Implements hook_form_FORM_ID_alter() for system_regional_settings().
|
2700 |
|
|
*
|
2701 |
|
|
* Add a form element to configure whether or not week numbers are ISO-8601, the
|
2702 |
|
|
* default is FALSE (US/UK/AUS norm).
|
2703 |
|
|
*/
|
2704 |
|
|
function date_api_form_system_regional_settings_alter(&$form, &$form_state, $form_id) {
|
2705 |
|
|
$form['locale']['date_api_use_iso8601'] = array(
|
2706 |
|
|
'#type' => 'checkbox',
|
2707 |
|
|
'#title' => t('Use ISO-8601 week numbers'),
|
2708 |
|
|
'#default_value' => variable_get('date_api_use_iso8601', FALSE),
|
2709 |
|
|
'#description' => t('IMPORTANT! If checked, First day of week MUST be set to Monday'),
|
2710 |
|
|
);
|
2711 |
|
|
$form['#validate'][] = 'date_api_form_system_settings_validate';
|
2712 |
|
|
}
|
2713 |
|
|
|
2714 |
|
|
/**
|
2715 |
|
|
* Validate that the option to use ISO weeks matches first day of week choice.
|
2716 |
|
|
*/
|
2717 |
|
|
function date_api_form_system_settings_validate(&$form, &$form_state) {
|
2718 |
|
|
$form_values = $form_state['values'];
|
2719 |
|
|
if ($form_values['date_api_use_iso8601'] && $form_values['date_first_day'] != 1) {
|
2720 |
|
|
form_set_error('date_first_day', t('When using ISO-8601 week numbers, the first day of the week must be set to Monday.'));
|
2721 |
|
|
}
|
2722 |
|
|
}
|
2723 |
|
|
|
2724 |
|
|
/**
|
2725 |
|
|
* Creates an array of date format types for use as an options list.
|
2726 |
|
|
*/
|
2727 |
|
|
function date_format_type_options() {
|
2728 |
|
|
$options = array();
|
2729 |
|
|
$format_types = system_get_date_types();
|
2730 |
|
|
if (!empty($format_types)) {
|
2731 |
|
|
foreach ($format_types as $type => $type_info) {
|
2732 |
|
|
$options[$type] = $type_info['title'] . ' (' . date_format_date(date_example_date(), $type) . ')';
|
2733 |
|
|
}
|
2734 |
|
|
}
|
2735 |
|
|
return $options;
|
2736 |
|
|
}
|
2737 |
|
|
|
2738 |
|
|
/**
|
2739 |
|
|
* Creates an example date.
|
2740 |
|
|
*
|
2741 |
|
|
* This ensures a clear difference between month and day, and 12 and 24 hours.
|
2742 |
|
|
*/
|
2743 |
|
|
function date_example_date() {
|
2744 |
|
|
$now = date_now();
|
2745 |
|
|
if (date_format($now, 'M') == date_format($now, 'F')) {
|
2746 |
|
|
date_modify($now, '+1 month');
|
2747 |
|
|
}
|
2748 |
|
|
if (date_format($now, 'm') == date_format($now, 'd')) {
|
2749 |
|
|
date_modify($now, '+1 day');
|
2750 |
|
|
}
|
2751 |
|
|
if (date_format($now, 'H') == date_format($now, 'h')) {
|
2752 |
|
|
date_modify($now, '+12 hours');
|
2753 |
|
|
}
|
2754 |
|
|
return $now;
|
2755 |
|
|
}
|
2756 |
|
|
|
2757 |
|
|
/**
|
2758 |
|
|
* Determine if a start/end date combination qualify as 'All day'.
|
2759 |
|
|
*
|
2760 |
|
|
* @param string $string1
|
2761 |
|
|
* A string date in datetime format for the 'start' date.
|
2762 |
|
|
* @param string $string2
|
2763 |
|
|
* A string date in datetime format for the 'end' date.
|
2764 |
|
|
* @param string $granularity
|
2765 |
|
|
* (optional) The granularity of the date. Defaults to 'second'.
|
2766 |
|
|
* @param int $increment
|
2767 |
|
|
* (optional) The increment of the date. Defaults to 1.
|
2768 |
|
|
*
|
2769 |
|
|
* @return bool
|
2770 |
|
|
* TRUE if the date is all day, FALSE otherwise.
|
2771 |
|
|
*/
|
2772 |
|
|
function date_is_all_day($string1, $string2, $granularity = 'second', $increment = 1) {
|
2773 |
|
|
if (empty($string1) || empty($string2)) {
|
2774 |
|
|
return FALSE;
|
2775 |
|
|
}
|
2776 |
|
|
elseif (!in_array($granularity, array('hour', 'minute', 'second'))) {
|
2777 |
|
|
return FALSE;
|
2778 |
|
|
}
|
2779 |
|
|
|
2780 |
|
|
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string1, $matches);
|
2781 |
|
|
$count = count($matches);
|
2782 |
|
|
$date1 = $count > 1 ? $matches[1] : '';
|
2783 |
|
|
$time1 = $count > 2 ? $matches[2] : '';
|
2784 |
|
|
$hour1 = $count > 3 ? intval($matches[3]) : 0;
|
2785 |
|
|
$min1 = $count > 4 ? intval($matches[4]) : 0;
|
2786 |
|
|
$sec1 = $count > 5 ? intval($matches[5]) : 0;
|
2787 |
|
|
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string2, $matches);
|
2788 |
|
|
$count = count($matches);
|
2789 |
|
|
$date2 = $count > 1 ? $matches[1] : '';
|
2790 |
|
|
$time2 = $count > 2 ? $matches[2] : '';
|
2791 |
|
|
$hour2 = $count > 3 ? intval($matches[3]) : 0;
|
2792 |
|
|
$min2 = $count > 4 ? intval($matches[4]) : 0;
|
2793 |
|
|
$sec2 = $count > 5 ? intval($matches[5]) : 0;
|
2794 |
|
|
if (empty($date1) || empty($date2)) {
|
2795 |
|
|
return FALSE;
|
2796 |
|
|
}
|
2797 |
|
|
if (empty($time1) || empty($time2)) {
|
2798 |
|
|
return FALSE;
|
2799 |
|
|
}
|
2800 |
|
|
|
2801 |
|
|
$tmp = date_seconds('s', TRUE, $increment);
|
2802 |
|
|
$max_seconds = intval(array_pop($tmp));
|
2803 |
|
|
$tmp = date_minutes('i', TRUE, $increment);
|
2804 |
|
|
$max_minutes = intval(array_pop($tmp));
|
2805 |
|
|
|
2806 |
|
|
// See if minutes and seconds are the maximum allowed for an increment or the
|
2807 |
|
|
// maximum possible (59), or 0.
|
2808 |
|
|
switch ($granularity) {
|
2809 |
|
|
case 'second':
|
2810 |
|
|
$min_match = $time1 == '00:00:00'
|
2811 |
|
|
|| ($hour1 == 0 && $min1 == 0 && $sec1 == 0);
|
2812 |
|
|
$max_match = $time2 == '00:00:00'
|
2813 |
|
|
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59)))
|
2814 |
|
|
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0);
|
2815 |
|
|
break;
|
2816 |
b720ea3e
|
Assos Assos
|
|
2817 |
85ad3d82
|
Assos Assos
|
case 'minute':
|
2818 |
|
|
$min_match = $time1 == '00:00:00'
|
2819 |
|
|
|| ($hour1 == 0 && $min1 == 0);
|
2820 |
|
|
$max_match = $time2 == '00:00:00'
|
2821 |
|
|
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
|
2822 |
|
|
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
|
2823 |
|
|
break;
|
2824 |
b720ea3e
|
Assos Assos
|
|
2825 |
85ad3d82
|
Assos Assos
|
case 'hour':
|
2826 |
|
|
$min_match = $time1 == '00:00:00'
|
2827 |
|
|
|| ($hour1 == 0);
|
2828 |
|
|
$max_match = $time2 == '00:00:00'
|
2829 |
|
|
|| ($hour2 == 23)
|
2830 |
|
|
|| ($hour1 == 0 && $hour2 == 0);
|
2831 |
|
|
break;
|
2832 |
b720ea3e
|
Assos Assos
|
|
2833 |
85ad3d82
|
Assos Assos
|
default:
|
2834 |
|
|
$min_match = TRUE;
|
2835 |
|
|
$max_match = FALSE;
|
2836 |
|
|
}
|
2837 |
|
|
|
2838 |
|
|
if ($min_match && $max_match) {
|
2839 |
|
|
return TRUE;
|
2840 |
|
|
}
|
2841 |
|
|
|
2842 |
|
|
return FALSE;
|
2843 |
|
|
}
|
2844 |
|
|
|
2845 |
|
|
/**
|
2846 |
|
|
* Helper function to round minutes and seconds to requested value.
|
2847 |
|
|
*/
|
2848 |
|
|
function date_increment_round(&$date, $increment) {
|
2849 |
|
|
// Round minutes and seconds, if necessary.
|
2850 |
|
|
if (is_object($date) && $increment > 1) {
|
2851 |
|
|
$day = intval(date_format($date, 'j'));
|
2852 |
|
|
$hour = intval(date_format($date, 'H'));
|
2853 |
|
|
$second = intval(round(intval(date_format($date, 's')) / $increment) * $increment);
|
2854 |
|
|
$minute = intval(date_format($date, 'i'));
|
2855 |
|
|
if ($second == 60) {
|
2856 |
|
|
$minute += 1;
|
2857 |
|
|
$second = 0;
|
2858 |
|
|
}
|
2859 |
|
|
$minute = intval(round($minute / $increment) * $increment);
|
2860 |
|
|
if ($minute == 60) {
|
2861 |
|
|
$hour += 1;
|
2862 |
|
|
$minute = 0;
|
2863 |
|
|
}
|
2864 |
|
|
date_time_set($date, $hour, $minute, $second);
|
2865 |
|
|
if ($hour == 24) {
|
2866 |
|
|
$day += 1;
|
2867 |
|
|
$hour = 0;
|
2868 |
|
|
$year = date_format($date, 'Y');
|
2869 |
|
|
$month = date_format($date, 'n');
|
2870 |
|
|
date_date_set($date, $year, $month, $day);
|
2871 |
|
|
}
|
2872 |
|
|
}
|
2873 |
|
|
return $date;
|
2874 |
|
|
}
|
2875 |
|
|
|
2876 |
|
|
/**
|
2877 |
|
|
* Determines if a date object is valid.
|
2878 |
|
|
*
|
2879 |
|
|
* @param object $date
|
2880 |
|
|
* The date object to check.
|
2881 |
|
|
*
|
2882 |
|
|
* @return bool
|
2883 |
|
|
* TRUE if the date is a valid date object, FALSE otherwise.
|
2884 |
|
|
*/
|
2885 |
|
|
function date_is_date($date) {
|
2886 |
|
|
if (empty($date) || !is_object($date) || !empty($date->errors)) {
|
2887 |
|
|
return FALSE;
|
2888 |
|
|
}
|
2889 |
|
|
return TRUE;
|
2890 |
|
|
}
|
2891 |
|
|
|
2892 |
|
|
/**
|
2893 |
b720ea3e
|
Assos Assos
|
* Replace specific ISO values using patterns.
|
2894 |
|
|
*
|
2895 |
|
|
* Function will replace ISO values that have the pattern 9999-00-00T00:00:00
|
2896 |
|
|
* with a pattern like 9999-01-01T00:00:00, to match the behavior of non-ISO dates
|
2897 |
|
|
* and ensure that date objects created from this value contain a valid month
|
2898 |
|
|
* and day.
|
2899 |
|
|
* Without this fix, the ISO date '2020-00-00T00:00:00' would be created as
|
2900 |
85ad3d82
|
Assos Assos
|
* November 30, 2019 (the previous day in the previous month).
|
2901 |
|
|
*
|
2902 |
|
|
* @param string $iso_string
|
2903 |
|
|
* An ISO string that needs to be made into a complete, valid date.
|
2904 |
|
|
*
|
2905 |
b720ea3e
|
Assos Assos
|
* @return mixed|string
|
2906 |
|
|
* replaced value, or incoming value.
|
2907 |
|
|
*
|
2908 |
85ad3d82
|
Assos Assos
|
* @TODO Expand on this to work with all sorts of partial ISO dates.
|
2909 |
|
|
*/
|
2910 |
|
|
function date_make_iso_valid($iso_string) {
|
2911 |
|
|
// If this isn't a value that uses an ISO pattern, there is nothing to do.
|
2912 |
|
|
if (is_numeric($iso_string) || !preg_match(DATE_REGEX_ISO, $iso_string)) {
|
2913 |
|
|
return $iso_string;
|
2914 |
|
|
}
|
2915 |
|
|
// First see if month and day parts are '-00-00'.
|
2916 |
|
|
if (substr($iso_string, 4, 6) == '-00-00') {
|
2917 |
|
|
return preg_replace('/([\d]{4}-)(00-00)(T[\d]{2}:[\d]{2}:[\d]{2})/', '${1}01-01${3}', $iso_string);
|
2918 |
|
|
}
|
2919 |
|
|
// Then see if the day part is '-00'.
|
2920 |
|
|
elseif (substr($iso_string, 7, 3) == '-00') {
|
2921 |
|
|
return preg_replace('/([\d]{4}-[\d]{2}-)(00)(T[\d]{2}:[\d]{2}:[\d]{2})/', '${1}01${3}', $iso_string);
|
2922 |
|
|
}
|
2923 |
|
|
|
2924 |
|
|
// Fall through, no changes required.
|
2925 |
|
|
return $iso_string;
|
2926 |
|
|
} |