1 | <?php |
---|
2 | /** |
---|
3 | * @file |
---|
4 | * This module will make the date API available to other modules. |
---|
5 | * Designed to provide a light but flexible assortment of functions |
---|
6 | * and constants, with more functionality in additional files that |
---|
7 | * are not loaded unless other modules specifically include them. |
---|
8 | */ |
---|
9 | |
---|
10 | /** |
---|
11 | * Set up some constants. |
---|
12 | * |
---|
13 | * Includes standard date types, format strings, strict regex strings for ISO |
---|
14 | * and DATETIME formats (seconds are optional). |
---|
15 | * |
---|
16 | * The loose regex will find any variety of ISO date and time, with or |
---|
17 | * without time, with or without dashes and colons separating the elements, |
---|
18 | * and with either a 'T' or a space separating date and time. |
---|
19 | */ |
---|
20 | define('DATE_ISO', 'date'); |
---|
21 | define('DATE_UNIX', 'datestamp'); |
---|
22 | define('DATE_DATETIME', 'datetime'); |
---|
23 | define('DATE_ARRAY', 'array'); |
---|
24 | define('DATE_OBJECT', 'object'); |
---|
25 | define('DATE_ICAL', 'ical'); |
---|
26 | |
---|
27 | define('DATE_FORMAT_ISO', "Y-m-d\TH:i:s"); |
---|
28 | define('DATE_FORMAT_UNIX', "U"); |
---|
29 | define('DATE_FORMAT_DATETIME', "Y-m-d H:i:s"); |
---|
30 | define('DATE_FORMAT_ICAL', "Ymd\THis"); |
---|
31 | define('DATE_FORMAT_ICAL_DATE', "Ymd"); |
---|
32 | define('DATE_FORMAT_DATE', 'Y-m-d'); |
---|
33 | |
---|
34 | define('DATE_REGEX_ISO', '/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):?(\d{2})?/'); |
---|
35 | define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/'); |
---|
36 | 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})?)?/'); |
---|
37 | define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/'); |
---|
38 | define('DATE_REGEX_ICAL_DATETIME', '/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?/'); |
---|
39 | |
---|
40 | /** |
---|
41 | * Implementation of hook_init(). |
---|
42 | */ |
---|
43 | function date_api_init() { |
---|
44 | if (function_exists('date_default_timezone_set')) { |
---|
45 | date_default_timezone_set(date_default_timezone_name()); |
---|
46 | } |
---|
47 | drupal_add_css(drupal_get_path('module', 'date_api') .'/date.css'); |
---|
48 | |
---|
49 | } |
---|
50 | |
---|
51 | /** |
---|
52 | * Implementation of hook_menu(). |
---|
53 | */ |
---|
54 | function date_api_menu() { |
---|
55 | $items = array(); |
---|
56 | $items['admin/settings/date-time/formats'] = array( |
---|
57 | 'title' => 'Formats', |
---|
58 | 'description' => 'Allow users to configure date formats', |
---|
59 | 'type' => MENU_LOCAL_TASK, |
---|
60 | 'file' => 'date_api.admin.inc', |
---|
61 | 'page callback' => 'drupal_get_form', |
---|
62 | 'page arguments' => array('date_api_date_formats_form'), |
---|
63 | 'access arguments' => array('administer site configuration'), |
---|
64 | 'weight' => 1, |
---|
65 | ); |
---|
66 | $items['admin/settings/date-time/formats/configure'] = array( |
---|
67 | 'title' => 'Configure', |
---|
68 | 'description' => 'Allow users to configure date formats', |
---|
69 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
---|
70 | 'file' => 'date_api.admin.inc', |
---|
71 | 'page callback' => 'drupal_get_form', |
---|
72 | 'page arguments' => array('date_api_date_formats_form'), |
---|
73 | 'access arguments' => array('administer site configuration'), |
---|
74 | 'weight' => 1, |
---|
75 | ); |
---|
76 | $items['admin/settings/date-time/formats/lookup'] = array( |
---|
77 | 'title' => 'Date and time lookup', |
---|
78 | 'type' => MENU_CALLBACK, |
---|
79 | 'page callback' => 'date_api_date_time_lookup', |
---|
80 | 'access arguments' => array('administer site configuration'), |
---|
81 | ); |
---|
82 | $items['admin/settings/date-time/formats/custom'] = array( |
---|
83 | 'title' => 'Custom formats', |
---|
84 | 'description' => 'Allow users to configure custom date formats.', |
---|
85 | 'type' => MENU_LOCAL_TASK, |
---|
86 | 'file' => 'date_api.admin.inc', |
---|
87 | 'page callback' => 'date_api_configure_custom_date_formats', |
---|
88 | 'access arguments' => array('administer site configuration'), |
---|
89 | 'weight' => 2, |
---|
90 | ); |
---|
91 | $items['admin/settings/date-time/formats/add'] = array( |
---|
92 | 'title' => 'Add format', |
---|
93 | 'description' => 'Allow users to add additional date formats.', |
---|
94 | 'type' => MENU_LOCAL_TASK, |
---|
95 | 'file' => 'date_api.admin.inc', |
---|
96 | 'page callback' => 'drupal_get_form', |
---|
97 | 'page arguments' => array('date_api_add_date_formats_form'), |
---|
98 | 'access arguments' => array('administer site configuration'), |
---|
99 | 'weight' => 3, |
---|
100 | ); |
---|
101 | $items['admin/settings/date-time/formats/delete/%'] = array( |
---|
102 | 'title' => 'Delete date format', |
---|
103 | 'description' => 'Allow users to delete a configured date format.', |
---|
104 | 'type' => MENU_CALLBACK, |
---|
105 | 'file' => 'date_api.admin.inc', |
---|
106 | 'page callback' => 'drupal_get_form', |
---|
107 | 'page arguments' => array('date_api_delete_format_form', 5), |
---|
108 | 'access arguments' => array('administer site configuration'), |
---|
109 | ); |
---|
110 | $items['admin/settings/date-time/delete/%'] = array( |
---|
111 | 'title' => 'Delete date format type', |
---|
112 | 'description' => 'Allow users to delete a configured date format type.', |
---|
113 | 'type' => MENU_CALLBACK, |
---|
114 | 'file' => 'date_api.admin.inc', |
---|
115 | 'page callback' => 'drupal_get_form', |
---|
116 | 'page arguments' => array('date_api_delete_format_type_form', 4), |
---|
117 | 'access arguments' => array('administer site configuration'), |
---|
118 | ); |
---|
119 | |
---|
120 | return $items; |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * Implementation of hook_menu_alter(). |
---|
125 | */ |
---|
126 | function date_api_menu_alter(&$callbacks) { |
---|
127 | // Add a new 'admin/settings/date-time/configure' path and make it the same as |
---|
128 | // the 'admin/settings/date-time'. Also set it to be the default local task - |
---|
129 | // needed to make tabs work as expected. |
---|
130 | $callbacks['admin/settings/date-time/configure'] = $callbacks['admin/settings/date-time']; |
---|
131 | $callbacks['admin/settings/date-time/configure']['type'] = MENU_DEFAULT_LOCAL_TASK; |
---|
132 | } |
---|
133 | |
---|
134 | /** |
---|
135 | * Helper function for getting the format string for a date type. |
---|
136 | */ |
---|
137 | function date_type_format($type) { |
---|
138 | switch ($type) { |
---|
139 | case DATE_ISO: |
---|
140 | return DATE_FORMAT_ISO; |
---|
141 | case DATE_UNIX: |
---|
142 | return DATE_FORMAT_UNIX; |
---|
143 | case DATE_DATETIME: |
---|
144 | return DATE_FORMAT_DATETIME; |
---|
145 | case DATE_ICAL: |
---|
146 | return DATE_FORMAT_ICAL; |
---|
147 | } |
---|
148 | } |
---|
149 | |
---|
150 | /** |
---|
151 | * An untranslated array of month names |
---|
152 | * |
---|
153 | * Needed for css, translation functions, strtotime(), and other places |
---|
154 | * that use the English versions of these words. |
---|
155 | * |
---|
156 | * @return |
---|
157 | * an array of month names |
---|
158 | */ |
---|
159 | function date_month_names_untranslated() { |
---|
160 | static $month_names; |
---|
161 | if (empty($month_names)) { |
---|
162 | $month_names = array(1 => 'January', 2 => 'February', 3 => 'March', |
---|
163 | 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', |
---|
164 | 8 => 'August', 9 => 'September', 10 => 'October', |
---|
165 | 11 => 'November', 12 => 'December'); |
---|
166 | } |
---|
167 | return $month_names; |
---|
168 | } |
---|
169 | |
---|
170 | /** |
---|
171 | * A translated array of month names |
---|
172 | * |
---|
173 | * @param $required |
---|
174 | * If not required, will include a blank value at the beginning of the list. |
---|
175 | * @return |
---|
176 | * an array of month names |
---|
177 | */ |
---|
178 | function date_month_names($required = FALSE) { |
---|
179 | $month_names = array(); |
---|
180 | foreach (date_month_names_untranslated() as $key => $day) { |
---|
181 | $month_names[$key] = date_t($day, 'month_name'); |
---|
182 | } |
---|
183 | $none = array('' => ''); |
---|
184 | return !$required ? $none + $month_names : $month_names; |
---|
185 | } |
---|
186 | |
---|
187 | /** |
---|
188 | * A translated array of month name abbreviations |
---|
189 | * |
---|
190 | * @param $required |
---|
191 | * If not required, will include a blank value at the beginning of the list. |
---|
192 | * @return |
---|
193 | * an array of month abbreviations |
---|
194 | */ |
---|
195 | function date_month_names_abbr($required = FALSE) { |
---|
196 | $month_names = array(); |
---|
197 | foreach (date_month_names_untranslated() as $key => $day) { |
---|
198 | $month_names[$key] = date_t($day, 'month_abbr'); |
---|
199 | } |
---|
200 | $none = array('' => ''); |
---|
201 | return !$required ? $none + $month_names : $month_names; |
---|
202 | } |
---|
203 | |
---|
204 | /** |
---|
205 | * An untranslated array of week days |
---|
206 | * |
---|
207 | * Needed for css, translation functions, strtotime(), and other places |
---|
208 | * that use the English versions of these words. |
---|
209 | * |
---|
210 | * @return |
---|
211 | * an array of week day names |
---|
212 | */ |
---|
213 | function date_week_days_untranslated($refresh = TRUE) { |
---|
214 | static $weekdays; |
---|
215 | if ($refresh || empty($weekdays)) { |
---|
216 | $weekdays = array(0 => 'Sunday', 1 => 'Monday', 2 => 'Tuesday', |
---|
217 | 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', |
---|
218 | 6 => 'Saturday'); |
---|
219 | } |
---|
220 | return $weekdays; |
---|
221 | } |
---|
222 | |
---|
223 | /** |
---|
224 | * A translated array of week days |
---|
225 | * |
---|
226 | * @param $required |
---|
227 | * If not required, will include a blank value at the beginning of the array. |
---|
228 | * @return |
---|
229 | * an array of week day names |
---|
230 | */ |
---|
231 | function date_week_days($required = FALSE, $refresh = TRUE) { |
---|
232 | $weekdays = array(); |
---|
233 | foreach (date_week_days_untranslated() as $key => $day) { |
---|
234 | $weekdays[$key] = date_t($day, 'day_name'); |
---|
235 | } |
---|
236 | $none = array('' => ''); |
---|
237 | return !$required ? $none + $weekdays : $weekdays; |
---|
238 | } |
---|
239 | |
---|
240 | /** |
---|
241 | * An translated array of week day abbreviations. |
---|
242 | * |
---|
243 | * @param $required |
---|
244 | * If not required, will include a blank value at the beginning of the array. |
---|
245 | * @return |
---|
246 | * an array of week day abbreviations |
---|
247 | */ |
---|
248 | function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) { |
---|
249 | $weekdays = array(); |
---|
250 | switch ($length) { |
---|
251 | case 1: |
---|
252 | $context = 'day_abbr1'; |
---|
253 | break; |
---|
254 | case 2: |
---|
255 | $context = 'day_abbr2'; |
---|
256 | break; |
---|
257 | default: |
---|
258 | $context = 'day_abbr'; |
---|
259 | break; |
---|
260 | } |
---|
261 | foreach (date_week_days_untranslated() as $key => $day) { |
---|
262 | $weekdays[$key] = date_t($day, $context); |
---|
263 | } |
---|
264 | $none = array('' => ''); |
---|
265 | return !$required ? $none + $weekdays : $weekdays; |
---|
266 | } |
---|
267 | |
---|
268 | /** |
---|
269 | * Order weekdays |
---|
270 | * Correct weekdays array so first day in array matches the first day of |
---|
271 | * the week. Use to create things like calendar headers. |
---|
272 | * |
---|
273 | * @param array $weekdays |
---|
274 | * @return array |
---|
275 | */ |
---|
276 | function date_week_days_ordered($weekdays) { |
---|
277 | if (variable_get('date_first_day', 1) > 0) { |
---|
278 | for ($i = 1; $i <= variable_get('date_first_day', 1); $i++) { |
---|
279 | $last = array_shift($weekdays); |
---|
280 | array_push($weekdays, $last); |
---|
281 | } |
---|
282 | } |
---|
283 | return $weekdays; |
---|
284 | } |
---|
285 | |
---|
286 | /** |
---|
287 | * An array of years. |
---|
288 | * |
---|
289 | * @param int $min |
---|
290 | * the minimum year in the array |
---|
291 | * @param int $max |
---|
292 | * the maximum year in the array |
---|
293 | * @param $required |
---|
294 | * If not required, will include a blank value at the beginning of the array. |
---|
295 | * @return |
---|
296 | * an array of years in the selected range |
---|
297 | */ |
---|
298 | function date_years($min = 0, $max = 0, $required = FALSE) { |
---|
299 | // Have to be sure $min and $max are valid values; |
---|
300 | if (empty($min)) $min = intval(date('Y', time()) - 3); |
---|
301 | if (empty($max)) $max = intval(date('Y', time()) + 3); |
---|
302 | $none = array(0 => ''); |
---|
303 | return !$required ? $none + drupal_map_assoc(range($min, $max)) : drupal_map_assoc(range($min, $max)); |
---|
304 | } |
---|
305 | |
---|
306 | /** |
---|
307 | * An array of days. |
---|
308 | * |
---|
309 | * @param $required |
---|
310 | * If not required, returned array will include a blank value. |
---|
311 | * @param integer $month (optional) |
---|
312 | * @param integer $year (optional) |
---|
313 | * @return |
---|
314 | * an array of days for the selected month. |
---|
315 | */ |
---|
316 | function date_days($required = FALSE, $month = NULL, $year = NULL) { |
---|
317 | // If we have a month and year, find the right last day of the month. |
---|
318 | if (!empty($month) && !empty($year)) { |
---|
319 | $date = date_make_date($year .'-'. $month .'-01 00:00:00', 'UTC'); |
---|
320 | $max = date_format('t', $date); |
---|
321 | } |
---|
322 | // If there is no month and year given, default to 31. |
---|
323 | if (empty($max)) $max = 31; |
---|
324 | $none = array(0 => ''); |
---|
325 | return !$required ? $none + drupal_map_assoc(range(1, $max)) : drupal_map_assoc(range(1, $max)); |
---|
326 | } |
---|
327 | |
---|
328 | /** |
---|
329 | * An array of hours. |
---|
330 | * |
---|
331 | * @param string $format |
---|
332 | * @param $required |
---|
333 | * If not required, returned array will include a blank value. |
---|
334 | * @return |
---|
335 | * an array of hours in the selected format. |
---|
336 | */ |
---|
337 | function date_hours($format = 'H', $required = FALSE) { |
---|
338 | $hours = array(); |
---|
339 | if ($format == 'h' || $format == 'g') { |
---|
340 | $min = 1; |
---|
341 | $max = 12; |
---|
342 | } |
---|
343 | else { |
---|
344 | $min = 0; |
---|
345 | $max = 23; |
---|
346 | } |
---|
347 | for ($i = $min; $i <= $max; $i++) { |
---|
348 | $hours[$i] = $i < 10 && ($format == 'H' || $format == 'h') ? "0$i" : $i; |
---|
349 | } |
---|
350 | $none = array('' => ''); |
---|
351 | return !$required ? $none + $hours : $hours; |
---|
352 | } |
---|
353 | |
---|
354 | /** |
---|
355 | * An array of minutes. |
---|
356 | * |
---|
357 | * @param string $format |
---|
358 | * @param $required |
---|
359 | * If not required, returned array will include a blank value. |
---|
360 | * @return |
---|
361 | * an array of minutes in the selected format. |
---|
362 | */ |
---|
363 | function date_minutes($format = 'i', $required = FALSE, $increment = 1) { |
---|
364 | $minutes = array(); |
---|
365 | // Have to be sure $increment has a value so we don't loop endlessly; |
---|
366 | if (empty($increment)) $increment = 1; |
---|
367 | for ($i = 0; $i < 60; $i += $increment) { |
---|
368 | $minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i; |
---|
369 | } |
---|
370 | $none = array('' => ''); |
---|
371 | return !$required ? $none + $minutes : $minutes; |
---|
372 | } |
---|
373 | |
---|
374 | /** |
---|
375 | * An array of seconds. |
---|
376 | * |
---|
377 | * @param string $format |
---|
378 | * @param $required |
---|
379 | * If not required, returned array will include a blank value. |
---|
380 | * @return array an array of seconds in the selected format. |
---|
381 | */ |
---|
382 | function date_seconds($format = 's', $required = FALSE, $increment = 1) { |
---|
383 | $seconds = array(); |
---|
384 | // Have to be sure $increment has a value so we don't loop endlessly; |
---|
385 | if (empty($increment)) $increment = 1; |
---|
386 | for ($i = 0; $i < 60; $i += $increment) { |
---|
387 | $seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i; |
---|
388 | } |
---|
389 | $none = array('' => ''); |
---|
390 | return !$required ? $none + $seconds : $seconds; |
---|
391 | } |
---|
392 | |
---|
393 | /** |
---|
394 | * An array of am and pm options. |
---|
395 | * @param $required |
---|
396 | * If not required, returned array will include a blank value. |
---|
397 | * @return array an array of am pm options. |
---|
398 | */ |
---|
399 | function date_ampm($required = FALSE) { |
---|
400 | $none = array('' => ''); |
---|
401 | $ampm = array('am' => date_t('am', 'ampm'), 'pm' => date_t('pm', 'ampm')); |
---|
402 | return !$required ? $none + $ampm : $ampm; |
---|
403 | } |
---|
404 | |
---|
405 | /** |
---|
406 | * Implementation of hook_date_formats(). |
---|
407 | * |
---|
408 | * @return |
---|
409 | * An array of date formats with attributes 'type' (short, medium or long), |
---|
410 | * 'format' (the format string) and 'locales'. The 'locales' attribute is an |
---|
411 | * array of locales, which can include both 2 character language codes like |
---|
412 | * 'en', 'fr', but also 5 character language codes like 'en-gb' and 'en-us'. |
---|
413 | */ |
---|
414 | function date_api_date_formats() { |
---|
415 | include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_formats_list.inc'); |
---|
416 | return _date_api_date_formats_list(); |
---|
417 | } |
---|
418 | |
---|
419 | /** |
---|
420 | * Implementation of hook_date_format_types(). |
---|
421 | */ |
---|
422 | function date_api_date_format_types() { |
---|
423 | return array( |
---|
424 | 'long' => t('Long'), |
---|
425 | 'medium' => t('Medium'), |
---|
426 | 'short' => t('Short'), |
---|
427 | ); |
---|
428 | } |
---|
429 | |
---|
430 | /** |
---|
431 | * Array of regex replacement strings for date format elements. |
---|
432 | * Used to allow input in custom formats. Based on work done for |
---|
433 | * the Date module by Yves Chedemois (yched). |
---|
434 | * |
---|
435 | * @return array of date() format letters and their regex equivalents. |
---|
436 | */ |
---|
437 | function date_format_patterns($strict = FALSE) { |
---|
438 | return array( |
---|
439 | 'd' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
440 | 'm' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
441 | 'h' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
442 | 'H' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
443 | 'i' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
444 | 's' => '\d{'. ($strict ? '2' : '1,2') .'}', |
---|
445 | 'j' => '\d{1,2}', 'N' => '\d', 'S' => '\w{2}', |
---|
446 | 'w' => '\d', 'z' => '\d{1,3}', 'W' => '\d{1,2}', |
---|
447 | 'n' => '\d{1,2}', 't' => '\d{2}', 'L' => '\d', 'o' => '\d{4}', |
---|
448 | 'Y' => '\d{4}', 'y' => '\d{2}', 'B' => '\d{3}', 'g' => '\d{1,2}', |
---|
449 | 'G' => '\d{1,2}', 'e' => '\w*', 'I' => '\d', 'T' => '\w*', |
---|
450 | 'U' => '\d*', 'z' => '[+-]?\d*', 'O' => '[+-]?\d{4}', |
---|
451 | //Using S instead of w and 3 as well as 4 to pick up non-ASCII chars like German umlaute. |
---|
452 | // Per http://drupal.org/node/1101294, we may need as little as 2 and as many as 5 characters |
---|
453 | // in some languages. |
---|
454 | 'D' => '\S{2,5}', 'l' => '\S*', 'M' => '\S{2,5}', 'F' => '\S*', |
---|
455 | 'P' => '[+-]?\d{2}\:\d{2}', |
---|
456 | 'c' => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]?\d{2}\:\d{2})', |
---|
457 | 'r' => '(\w{3}), (\d{2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):(\d{2})([+-]?\d{4})?', |
---|
458 | ); |
---|
459 | } |
---|
460 | |
---|
461 | /** |
---|
462 | * Array of granularity options and their labels |
---|
463 | * |
---|
464 | * @return array |
---|
465 | */ |
---|
466 | function date_granularity_names() { |
---|
467 | return array( |
---|
468 | 'year' => date_t('Year', 'datetime'), |
---|
469 | 'month' => date_t('Month', 'datetime'), |
---|
470 | 'day' => date_t('Day', 'datetime'), |
---|
471 | 'hour' => date_t('Hour', 'datetime'), |
---|
472 | 'minute' => date_t('Minute', 'datetime'), |
---|
473 | 'second' => date_t('Second', 'datetime'), |
---|
474 | ); |
---|
475 | } |
---|
476 | |
---|
477 | /** |
---|
478 | * Sort a granularity array. |
---|
479 | */ |
---|
480 | function date_granularity_sorted($granularity) { |
---|
481 | return array_intersect(array('year', 'month', 'day', 'hour', 'minute', 'second'), $granularity); |
---|
482 | } |
---|
483 | |
---|
484 | /** |
---|
485 | * Give a granularity $precision, return an array of |
---|
486 | * all the possible granularity elements. |
---|
487 | */ |
---|
488 | function date_granularity_array_from_precision($precision) { |
---|
489 | $granularity_array = array('year', 'month', 'day', 'hour', 'minute', 'second'); |
---|
490 | switch (($precision)) { |
---|
491 | case 'year': |
---|
492 | return array_slice($granularity_array, -6); |
---|
493 | case 'month': |
---|
494 | return array_slice($granularity_array, -5); |
---|
495 | case 'day': |
---|
496 | return array_slice($granularity_array, -4); |
---|
497 | case 'hour': |
---|
498 | return array_slice($granularity_array, -3); |
---|
499 | case 'minute': |
---|
500 | return array_slice($granularity_array, -2); |
---|
501 | default: |
---|
502 | return $granularity_array; |
---|
503 | } |
---|
504 | } |
---|
505 | |
---|
506 | /** |
---|
507 | * Give a granularity array, return the highest precision. |
---|
508 | */ |
---|
509 | function date_granularity_precision($granularity_array) { |
---|
510 | $input = date_granularity_sorted($granularity_array); |
---|
511 | return array_pop($input); |
---|
512 | } |
---|
513 | |
---|
514 | /** |
---|
515 | * Construct an appropriate DATETIME format string for the granularity of an item. |
---|
516 | */ |
---|
517 | function date_granularity_format($granularity) { |
---|
518 | if (is_array($granularity)) { |
---|
519 | $granularity = date_granularity_precision($granularity); |
---|
520 | } |
---|
521 | $format = 'Y-m-d H:i:s'; |
---|
522 | switch ($granularity) { |
---|
523 | case 'year': |
---|
524 | return drupal_substr($format, 0, 1); |
---|
525 | case 'month': |
---|
526 | return drupal_substr($format, 0, 3); |
---|
527 | case 'day': |
---|
528 | return drupal_substr($format, 0, 5); |
---|
529 | case 'hour'; |
---|
530 | return drupal_substr($format, 0, 7); |
---|
531 | case 'minute': |
---|
532 | return drupal_substr($format, 0, 9); |
---|
533 | default: |
---|
534 | return $format; |
---|
535 | } |
---|
536 | } |
---|
537 | |
---|
538 | /** |
---|
539 | * A translated array of timezone names. |
---|
540 | * Cache the untranslated array, make the translated array a static variable. |
---|
541 | * |
---|
542 | * @param $required |
---|
543 | * If not required, returned array will include a blank value. |
---|
544 | * @return |
---|
545 | * an array of timezone names |
---|
546 | */ |
---|
547 | function date_timezone_names($required = FALSE, $refresh = FALSE) { |
---|
548 | static $zonenames; |
---|
549 | if (empty($zonenames) || $refresh) { |
---|
550 | $cached = cache_get('date_timezone_identifiers_list'); |
---|
551 | $zonenames = !empty($cached) ? $cached->data : array(); |
---|
552 | if ($refresh || empty($cached) || empty($zonenames)) { |
---|
553 | $data = timezone_identifiers_list(); |
---|
554 | asort($data); |
---|
555 | // Use include instead of include once in case the function gets |
---|
556 | // refreshed via devel or other API and is called more than once. |
---|
557 | if (module_exists('date_php4')) { |
---|
558 | include('./'. drupal_get_path('module', 'date_php4') .'/date_php4_missing_data.inc'); |
---|
559 | } |
---|
560 | foreach ($data as $delta => $zone) { |
---|
561 | // Because many time zones exist in PHP only for backward |
---|
562 | // compatibility reasons and should not be used, the list is |
---|
563 | // filtered by a regular expression. |
---|
564 | if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) { |
---|
565 | $zonenames[$zone] = $zone; |
---|
566 | } |
---|
567 | } |
---|
568 | // If using PHP4, filter the list down to only the timezones |
---|
569 | // the PHP4 wrapper has data for. |
---|
570 | if (module_exists('date_php4')) { |
---|
571 | foreach ($missing_timezone_data as $zone) { |
---|
572 | unset($zonenames[$zone]); |
---|
573 | } |
---|
574 | } |
---|
575 | |
---|
576 | // If using Event, further filter the list down to only |
---|
577 | // zones that exist in the event module. |
---|
578 | if (module_exists('event') && db_table_exists('event_timezones')) { |
---|
579 | $result = db_query("SELECT name FROM {event_timezones} ORDER BY name"); |
---|
580 | $names = array(); |
---|
581 | while ($row = db_fetch_array($result)) { |
---|
582 | $names[] = str_replace(' ', '_', $row['name']); |
---|
583 | } |
---|
584 | foreach ($zonenames as $name => $zone) { |
---|
585 | if (!in_array($name, $names)) { |
---|
586 | unset($zonenames[$name]); |
---|
587 | } |
---|
588 | } |
---|
589 | } |
---|
590 | if (!empty($zonenames)) { |
---|
591 | cache_set('date_timezone_identifiers_list', $zonenames); |
---|
592 | } |
---|
593 | } |
---|
594 | foreach ($zonenames as $zone) { |
---|
595 | $zonenames[$zone] = t('!timezone', array('!timezone' => t($zone))); |
---|
596 | } |
---|
597 | } |
---|
598 | $none = array('' => ''); |
---|
599 | return !$required ? $none + $zonenames : $zonenames; |
---|
600 | } |
---|
601 | |
---|
602 | /** |
---|
603 | * An array of timezone abbreviations that the system allows. |
---|
604 | * Cache an array of just the abbreviation names because the |
---|
605 | * whole timezone_abbreviations_list is huge so we don't want |
---|
606 | * to get it more than necessary. |
---|
607 | * |
---|
608 | * @return array |
---|
609 | */ |
---|
610 | function date_timezone_abbr($refresh = FALSE) { |
---|
611 | $cached = cache_get('date_timezone_abbreviations'); |
---|
612 | $data = isset($cached->data) ? $cached->data : array(); |
---|
613 | if (empty($data) || $refresh) { |
---|
614 | $data = array_keys(timezone_abbreviations_list()); |
---|
615 | cache_set('date_timezone_abbreviations', $data); |
---|
616 | } |
---|
617 | return $data; |
---|
618 | } |
---|
619 | |
---|
620 | /** |
---|
621 | * A function to translate ambiguous short date strings. |
---|
622 | * |
---|
623 | * Example: Pass in 'Monday', 'day_abbr' and get the translated |
---|
624 | * abbreviation for Monday. |
---|
625 | * |
---|
626 | * @param string $string |
---|
627 | * @param string $context |
---|
628 | * @param int $langcode |
---|
629 | * @return translated value of the string |
---|
630 | */ |
---|
631 | function date_t($string, $context, $langcode = NULL) { |
---|
632 | static $replace = array(); |
---|
633 | |
---|
634 | if (empty($replace[$langcode])) { |
---|
635 | // The function to create the date string arrays is kept separate |
---|
636 | // so those arrays can be directly accessed by other functions. |
---|
637 | date_t_strings($replace, $langcode); |
---|
638 | } |
---|
639 | switch ($context) { |
---|
640 | case 'day_name': |
---|
641 | case 'day_abbr': |
---|
642 | case 'day_abbr1': |
---|
643 | case 'day_abbr2': |
---|
644 | $untranslated = array_flip(date_week_days_untranslated()); |
---|
645 | break; |
---|
646 | case 'month_name': |
---|
647 | case 'month_abbr': |
---|
648 | $untranslated = array_flip(date_month_names_untranslated()); |
---|
649 | break; |
---|
650 | case 'ampm': |
---|
651 | $untranslated = array_flip(array('am', 'pm', 'AM', 'PM')); |
---|
652 | break; |
---|
653 | case 'datetime': |
---|
654 | $untranslated = array_flip(array('Year', 'Month', 'Day', 'Week', 'Hour', 'Minute', 'Second', 'All Day', 'All day')); |
---|
655 | break; |
---|
656 | case 'datetime_plural': |
---|
657 | $untranslated = array_flip(array('Years', 'Months', 'Days', 'Weeks', 'Hours', 'Minutes', 'Seconds')); |
---|
658 | break; |
---|
659 | case 'date_order': |
---|
660 | $untranslated = array_flip(array('Every', 'First', 'Second', 'Third', 'Fourth', 'Fifth')); |
---|
661 | break; |
---|
662 | case 'date_order_reverse': |
---|
663 | $untranslated = array_flip(array('', 'Last', 'Next to last', 'Third from last', 'Fourth from last', 'Fifth from last')); |
---|
664 | break; |
---|
665 | case 'date_nav': |
---|
666 | $untranslated = array_flip(array('Prev', 'Next', 'Today')); |
---|
667 | break; |
---|
668 | } |
---|
669 | $pos = $untranslated[$string]; |
---|
670 | return $replace[$langcode][$context][$pos]; |
---|
671 | } |
---|
672 | |
---|
673 | /** |
---|
674 | * Construct translation arrays from pipe-delimited strings. |
---|
675 | * |
---|
676 | * Combining these strings into a single t() gives them the context needed |
---|
677 | * for better translation. |
---|
678 | */ |
---|
679 | function date_t_strings(&$replace, $langcode) { |
---|
680 | $replace[$langcode]['day_name'] = explode('|', trim(t('!day-name Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday', array('!day-name' => ''), $langcode))); |
---|
681 | $replace[$langcode]['day_abbr'] = explode('|', trim(t('!day-abbreviation Sun|Mon|Tue|Wed|Thu|Fri|Sat', array('!day-abbreviation' => ''), $langcode))); |
---|
682 | $replace[$langcode]['day_abbr1'] = explode('|', trim(t('!day-abbreviation S|M|T|W|T|F|S', array('!day-abbreviation' => ''), $langcode))); |
---|
683 | $replace[$langcode]['day_abbr2'] = explode('|', trim(t('!day-abbreviation SU|MO|TU|WE|TH|FR|SA', array('!day-abbreviation' => ''), $langcode))); |
---|
684 | $replace[$langcode]['ampm'] = explode('|', trim(t('!ampm-abbreviation am|pm|AM|PM', array('!ampm-abbreviation' => ''), $langcode))); |
---|
685 | $replace[$langcode]['datetime'] = explode('|', trim(t('!datetime Year|Month|Day|Week|Hour|Minute|Second|All Day|All day', array('!datetime' => ''), $langcode))); |
---|
686 | $replace[$langcode]['datetime_plural'] = explode('|', trim(t('!datetime_plural Years|Months|Days|Weeks|Hours|Minutes|Seconds', array('!datetime_plural' => ''), $langcode))); |
---|
687 | $replace[$langcode]['date_order'] = explode('|', trim(t('!date_order Every|First|Second|Third|Fourth|Fifth', array('!date_order' => ''), $langcode))); |
---|
688 | $replace[$langcode]['date_order_reverse'] = explode('|', trim(t('!date_order |Last|Next to last|Third from last|Fourth from last|Fifth from last', array('!date_order' => ''), $langcode))); |
---|
689 | $replace[$langcode]['date_nav'] = explode('|', trim(t('!date_nav Prev|Next|Today', array('!date_nav' => ''), $langcode))); |
---|
690 | |
---|
691 | // These start with a pipe so the January value will be in position 1 instead of position 0. |
---|
692 | $replace[$langcode]['month_name'] = explode('|', trim(t('!month-name |January|February|March|April|May|June|July|August|September|October|November|December', array('!month-name' => ''), $langcode))); |
---|
693 | $replace[$langcode]['month_abbr'] = explode('|', trim(t('!month-abbreviation |Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec', array('!month-abbreviation' => ''), $langcode))); |
---|
694 | } |
---|
695 | |
---|
696 | /** |
---|
697 | * Reworked from Drupal's format_date function to handle pre-1970 and |
---|
698 | * post-2038 dates and accept a date object instead of a timestamp as input. |
---|
699 | * |
---|
700 | * Translates formatted date results, unlike PHP function date_format(). |
---|
701 | * |
---|
702 | * @param $date |
---|
703 | * A date object, could be created by date_make_date(). |
---|
704 | * @param $type |
---|
705 | * The format to use. Can be "small", "medium" or "large" for the preconfigured |
---|
706 | * date formats. If "custom" is specified, then $format is required as well. |
---|
707 | * @param $format |
---|
708 | * A PHP date format string as required by date(). A backslash should be used |
---|
709 | * before a character to avoid interpreting the character as part of a date |
---|
710 | * format. |
---|
711 | * @return |
---|
712 | * A translated date string in the requested format. |
---|
713 | */ |
---|
714 | function date_format_date($date, $type = 'medium', $format = '', $langcode = NULL) { |
---|
715 | if (empty($date)) { |
---|
716 | return ''; |
---|
717 | } |
---|
718 | |
---|
719 | if (function_exists('timezone_name_from_abbr') && get_class($date) != 'DateTime') { |
---|
720 | $date = date_make_date($date); |
---|
721 | } |
---|
722 | switch ($type) { |
---|
723 | case 'small': |
---|
724 | case 'short': |
---|
725 | $format = variable_get('date_format_short', 'm/d/Y - H:i'); |
---|
726 | break; |
---|
727 | case 'large': |
---|
728 | case 'long': |
---|
729 | $format = variable_get('date_format_long', 'l, F j, Y - H:i'); |
---|
730 | break; |
---|
731 | case 'custom': |
---|
732 | $format = $format; |
---|
733 | break; |
---|
734 | case 'medium': |
---|
735 | default: |
---|
736 | $format = variable_get('date_format_medium', 'D, m/d/Y - H:i'); |
---|
737 | } |
---|
738 | $max = drupal_strlen($format); |
---|
739 | $datestring = ''; |
---|
740 | for ($i = 0; $i < $max; $i++) { |
---|
741 | $c = $format[$i]; |
---|
742 | switch ($c) { |
---|
743 | // Use date_t() for ambiguous short strings that need translation. |
---|
744 | // We send long day and month names to date_t(), along with context. |
---|
745 | case 'l': |
---|
746 | $datestring .= date_t(date_format($date, 'l'), 'day_name', $langcode); |
---|
747 | break; |
---|
748 | case 'D': |
---|
749 | $datestring .= date_t(date_format($date, 'l'), 'day_abbr', $langcode); |
---|
750 | break; |
---|
751 | case 'F': |
---|
752 | $datestring .= date_t(date_format($date, 'F'), 'month_name', $langcode); |
---|
753 | break; |
---|
754 | case 'M': |
---|
755 | $datestring .= date_t(date_format($date, 'F'), 'month_abbr', $langcode); |
---|
756 | break; |
---|
757 | case 'A': |
---|
758 | case 'a': |
---|
759 | $datestring .= date_t(date_format($date, $c), 'ampm', $langcode); |
---|
760 | break; |
---|
761 | // The timezone name translations can use t(). |
---|
762 | case 'e': |
---|
763 | case 'T': |
---|
764 | $datestring .= t(date_format($date, $c)); |
---|
765 | break; |
---|
766 | // Remaining date parts need no translation. |
---|
767 | case 'O': |
---|
768 | $datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60); |
---|
769 | break; |
---|
770 | case 'P': |
---|
771 | $datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60); |
---|
772 | break; |
---|
773 | case 'Z': |
---|
774 | $datestring .= date_offset_get($date); |
---|
775 | break; |
---|
776 | case '\\': |
---|
777 | $datestring .= $format[++$i]; |
---|
778 | break; |
---|
779 | case 'r': |
---|
780 | $datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', $langcode); |
---|
781 | break; |
---|
782 | default: |
---|
783 | if (strpos('BdcgGhHiIjLmnNosStTuUwWYyz', $c) !== FALSE) { |
---|
784 | $datestring .= date_format($date, $c); |
---|
785 | } |
---|
786 | else { |
---|
787 | $datestring .= $c; |
---|
788 | } |
---|
789 | } |
---|
790 | } |
---|
791 | return $datestring; |
---|
792 | } |
---|
793 | |
---|
794 | /** |
---|
795 | * An override for interval formatting that adds past and future context |
---|
796 | * |
---|
797 | * @param DateTime $date |
---|
798 | * @param integer $granularity |
---|
799 | * @return formatted string |
---|
800 | */ |
---|
801 | function date_format_interval($date, $granularity = 2) { |
---|
802 | // If no date is sent, then return nothing |
---|
803 | if (empty($date)) { |
---|
804 | return NULL; |
---|
805 | } |
---|
806 | |
---|
807 | $interval = time() - date_format($date, 'U'); |
---|
808 | if ($interval > 0 ) { |
---|
809 | return t('!time ago', array('!time' => format_interval($interval, $granularity))); |
---|
810 | } |
---|
811 | else { |
---|
812 | return format_interval(abs($interval), $granularity); |
---|
813 | } |
---|
814 | } |
---|
815 | |
---|
816 | /** |
---|
817 | * A date object for the current time. |
---|
818 | * |
---|
819 | * @param $timezone |
---|
820 | * Optional method to force time to a specific timezone, |
---|
821 | * defaults to user timezone, if set, otherwise site timezone. |
---|
822 | * @return object date |
---|
823 | */ |
---|
824 | function date_now($timezone = NULL) { |
---|
825 | return date_make_date('now', $timezone); |
---|
826 | } |
---|
827 | |
---|
828 | /** |
---|
829 | * Convert a date of any type or an array of date parts into a valid date |
---|
830 | * object. |
---|
831 | |
---|
832 | * @param $date |
---|
833 | * A date in any format or the string 'now'. |
---|
834 | * @param $timezone |
---|
835 | * Optional, the name of the timezone this date is in, defaults |
---|
836 | * to the user timezone, if set, otherwise the site timezone. |
---|
837 | * Accepts either a timezone name or a timezone object as input. |
---|
838 | * @param $type |
---|
839 | * The type of date provided, could be |
---|
840 | * DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT. |
---|
841 | * @param $granularity |
---|
842 | * The granularity of the date value provided. Set this for partial |
---|
843 | * dates so they pass validation and don't get reset to 'now'. |
---|
844 | */ |
---|
845 | function date_make_date($date, $timezone = NULL, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) { |
---|
846 | |
---|
847 | // Make sure some value is set for the date and timezone even if the |
---|
848 | // site timezone is not yet set up to avoid fatal installation |
---|
849 | // errors. |
---|
850 | if (empty($timezone) || !date_timezone_is_valid($timezone)) { |
---|
851 | $timezone = date_default_timezone_name(); |
---|
852 | } |
---|
853 | |
---|
854 | // Special handling for a unix timestamp of '0', since it will fail 'empty' tests below. |
---|
855 | if ($date === 0 && $type == DATE_UNIX) { |
---|
856 | $date = date_convert($date, $type, DATE_DATETIME, $timezone); |
---|
857 | $type = DATE_DATETIME; |
---|
858 | } |
---|
859 | |
---|
860 | // No value or one with unexpected array keys. |
---|
861 | if (empty($date) || (is_array($date) && array_diff($granularity, array_keys($date)))) { |
---|
862 | return NULL; |
---|
863 | } |
---|
864 | |
---|
865 | // Special handling for partial dates that don't need precision. |
---|
866 | $granularity_sorted = date_granularity_sorted($granularity); |
---|
867 | $max_granularity = end($granularity_sorted); |
---|
868 | if (in_array($max_granularity, array('year', 'month')) || $type == DATE_ISO || $type == DATE_ARRAY) { |
---|
869 | if ($type == DATE_UNIX) { |
---|
870 | $date = date_convert($date, $type, DATE_DATETIME); |
---|
871 | } |
---|
872 | $date = date_fuzzy_datetime($date); |
---|
873 | $type = DATE_DATETIME; |
---|
874 | } |
---|
875 | |
---|
876 | if (!date_is_valid($date, $type, $granularity)) { |
---|
877 | $date = 'now'; |
---|
878 | } |
---|
879 | if (!empty($timezone) && !empty($date)) { |
---|
880 | if ($date == 'now') { |
---|
881 | return date_create('now', timezone_open($timezone)); |
---|
882 | } |
---|
883 | elseif ($datetime = date_convert($date, $type, DATE_DATETIME, $timezone)) { |
---|
884 | return date_create($datetime, timezone_open($timezone)); |
---|
885 | } |
---|
886 | } |
---|
887 | return NULL; |
---|
888 | } |
---|
889 | |
---|
890 | function date_timezone_is_valid($timezone) { |
---|
891 | static $timezone_names; |
---|
892 | if (empty($timezone_names)) { |
---|
893 | $timezone_names = array_keys(date_timezone_names(TRUE)); |
---|
894 | } |
---|
895 | if (!in_array($timezone, $timezone_names)) { |
---|
896 | return FALSE; |
---|
897 | } |
---|
898 | return TRUE; |
---|
899 | } |
---|
900 | |
---|
901 | /** |
---|
902 | * Return a timezone name to use as a default. |
---|
903 | * |
---|
904 | * @return a timezone name |
---|
905 | * Identify the default timezone for a user, if available, otherwise the site. |
---|
906 | * Must return a value even if no timezone info has been set up. |
---|
907 | */ |
---|
908 | function date_default_timezone_name($check_user = TRUE) { |
---|
909 | global $user; |
---|
910 | if ($check_user && variable_get('configurable_timezones', 1) && !empty($user->timezone_name)) { |
---|
911 | return $user->timezone_name; |
---|
912 | } |
---|
913 | else { |
---|
914 | $default = variable_get('date_default_timezone_name', ''); |
---|
915 | return empty($default) ? 'UTC' : $default; |
---|
916 | } |
---|
917 | } |
---|
918 | |
---|
919 | /** |
---|
920 | * A timezone object for the default timezone. |
---|
921 | * |
---|
922 | * @return a timezone object |
---|
923 | * Identify the default timezone for a user, if available, otherwise the site. |
---|
924 | */ |
---|
925 | function date_default_timezone($check_user = TRUE) { |
---|
926 | $timezone = date_default_timezone_name($check_user); |
---|
927 | return timezone_open(date_default_timezone_name($check_user)); |
---|
928 | } |
---|
929 | |
---|
930 | /** |
---|
931 | * Identify the number of days in a month for a date. |
---|
932 | */ |
---|
933 | function date_days_in_month($year, $month) { |
---|
934 | // Pick a day in the middle of the month to avoid timezone shifts. |
---|
935 | $datetime = date_pad($year, 4) .'-'. date_pad($month) .'-15 00:00:00'; |
---|
936 | $date = date_make_date($datetime); |
---|
937 | return date_format($date, 't'); |
---|
938 | } |
---|
939 | |
---|
940 | /** |
---|
941 | * Identify the number of days in a year for a date. |
---|
942 | * |
---|
943 | * @param mixed $date |
---|
944 | * @param string $type |
---|
945 | * @return integer |
---|
946 | */ |
---|
947 | function date_days_in_year($date = NULL, $type = DATE_OBJECT) { |
---|
948 | if (empty($date)) { |
---|
949 | $date = date_now(); |
---|
950 | } |
---|
951 | if (!is_object($date)) { |
---|
952 | $date = date_convert($date, $type, DATE_OBJECT); |
---|
953 | } |
---|
954 | if (is_object($date)) { |
---|
955 | if (date_format($date, 'L')) { |
---|
956 | return 366; |
---|
957 | } |
---|
958 | else { |
---|
959 | return 365; |
---|
960 | } |
---|
961 | } |
---|
962 | return NULL; |
---|
963 | } |
---|
964 | |
---|
965 | /** |
---|
966 | * Identify the number of ISO weeks in a year for a date. |
---|
967 | * |
---|
968 | * December 28 is always in the last ISO week of the year. |
---|
969 | * |
---|
970 | * @param mixed $date |
---|
971 | * @param string $type |
---|
972 | * @return integer |
---|
973 | */ |
---|
974 | function date_iso_weeks_in_year($date = NULL, $type = DATE_OBJECT) { |
---|
975 | if (empty($date)) { |
---|
976 | $date = date_now(); |
---|
977 | } |
---|
978 | if (!is_object($date)) { |
---|
979 | $date = date_convert($date, $type, DATE_OBJECT); |
---|
980 | } |
---|
981 | if (is_object($date)) { |
---|
982 | date_date_set($date, date_format($date, 'Y'), 12, 28); |
---|
983 | return date_format($date, 'W'); |
---|
984 | } |
---|
985 | return NULL; |
---|
986 | } |
---|
987 | |
---|
988 | /** |
---|
989 | * Returns day of week for a given date (0 = Sunday). |
---|
990 | * |
---|
991 | * @param mixed $date |
---|
992 | * a date, default is current local day |
---|
993 | * @param string $type |
---|
994 | * The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX |
---|
995 | * @return |
---|
996 | * the number of the day in the week |
---|
997 | */ |
---|
998 | function date_day_of_week($date = NULL, $type = DATE_OBJECT) { |
---|
999 | if (empty($date)) { |
---|
1000 | $date = date_now(); |
---|
1001 | $type = DATE_OBJECT; |
---|
1002 | } |
---|
1003 | $date = date_convert($date, $type, DATE_OBJECT); |
---|
1004 | if (is_object($date)) { |
---|
1005 | return date_format($date, 'w'); |
---|
1006 | } |
---|
1007 | return NULL; |
---|
1008 | } |
---|
1009 | |
---|
1010 | /** |
---|
1011 | * Returns translated name of the day of week for a given date. |
---|
1012 | * |
---|
1013 | * @param mixed $date |
---|
1014 | * a date, default is current local day |
---|
1015 | * @param string $type |
---|
1016 | * The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX |
---|
1017 | * @param string $abbr |
---|
1018 | * Whether to return the abbreviated name for that day |
---|
1019 | * @return |
---|
1020 | * the name of the day in the week for that date |
---|
1021 | */ |
---|
1022 | function date_day_of_week_name($date = NULL, $abbr = TRUE, $type = DATE_DATETIME) { |
---|
1023 | $dow = date_day_of_week($date, $type); |
---|
1024 | $days = $abbr ? date_week_days_abbr() : date_week_days(); |
---|
1025 | return $days[$dow]; |
---|
1026 | } |
---|
1027 | |
---|
1028 | /** |
---|
1029 | * Compute difference between two days using a given measure. |
---|
1030 | * |
---|
1031 | * @param mixed $date1 |
---|
1032 | * the starting date |
---|
1033 | * @param mixed $date2 |
---|
1034 | * the ending date |
---|
1035 | * @param string $measure |
---|
1036 | * 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds' |
---|
1037 | * @param string $type |
---|
1038 | * the type of dates provided: |
---|
1039 | * DATE_OBJECT, DATE_DATETIME, DATE_ISO, DATE_UNIX, DATE_ARRAY |
---|
1040 | */ |
---|
1041 | function date_difference($date1_in, $date2_in, $measure = 'seconds', $type = DATE_OBJECT) { |
---|
1042 | // Create cloned objects or original dates will be impacted by |
---|
1043 | // the date_modify() operations done in this code. |
---|
1044 | $date1 = drupal_clone(date_convert($date1_in, $type, DATE_OBJECT)); |
---|
1045 | $date2 = drupal_clone(date_convert($date2_in, $type, DATE_OBJECT)); |
---|
1046 | if (is_object($date1) && is_object($date2)) { |
---|
1047 | $diff = date_format($date2, 'U') - date_format($date1, 'U'); |
---|
1048 | if ($diff == 0 ) { |
---|
1049 | return 0; |
---|
1050 | } |
---|
1051 | elseif ($diff < 0) { |
---|
1052 | // Make sure $date1 is the smaller date. |
---|
1053 | $temp = $date2; |
---|
1054 | $date2 = $date1; |
---|
1055 | $date1 = $temp; |
---|
1056 | $diff = date_format($date2, 'U') - date_format($date1, 'U'); |
---|
1057 | } |
---|
1058 | $year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y')); |
---|
1059 | switch ($measure) { |
---|
1060 | |
---|
1061 | // The easy cases first. |
---|
1062 | case 'seconds': |
---|
1063 | return $diff; |
---|
1064 | case 'minutes': |
---|
1065 | return $diff / 60; |
---|
1066 | case 'hours': |
---|
1067 | return $diff / 3600; |
---|
1068 | case 'years': |
---|
1069 | return $year_diff; |
---|
1070 | |
---|
1071 | case 'months': |
---|
1072 | $format = 'n'; |
---|
1073 | $item1 = date_format($date1, $format); |
---|
1074 | $item2 = date_format($date2, $format); |
---|
1075 | if ($year_diff == 0) { |
---|
1076 | return intval($item2 - $item1); |
---|
1077 | } |
---|
1078 | else { |
---|
1079 | $item_diff = 12 - $item1; |
---|
1080 | $item_diff += intval(($year_diff - 1) * 12); |
---|
1081 | return $item_diff + $item2; |
---|
1082 | } |
---|
1083 | break; |
---|
1084 | |
---|
1085 | case 'days': |
---|
1086 | $format = 'z'; |
---|
1087 | $item1 = date_format($date1, $format); |
---|
1088 | $item2 = date_format($date2, $format); |
---|
1089 | if ($year_diff == 0) { |
---|
1090 | return intval($item2 - $item1); |
---|
1091 | } |
---|
1092 | else { |
---|
1093 | $item_diff = date_days_in_year($date1) - $item1; |
---|
1094 | for ($i = 1; $i < $year_diff; $i++) { |
---|
1095 | date_modify($date1, '+1 year'); |
---|
1096 | $item_diff += date_days_in_year($date1); |
---|
1097 | } |
---|
1098 | return $item_diff + $item2; |
---|
1099 | } |
---|
1100 | break; |
---|
1101 | |
---|
1102 | case 'weeks': |
---|
1103 | $week_diff = date_format($date2, 'W') - date_format($date1, 'W'); |
---|
1104 | $year_diff = date_format($date2, 'o') - date_format($date1, 'o'); |
---|
1105 | for ($i = 1; $i <= $year_diff; $i++) { |
---|
1106 | date_modify($date1, '+1 year'); |
---|
1107 | $week_diff += date_iso_weeks_in_year($date1); |
---|
1108 | } |
---|
1109 | return $week_diff; |
---|
1110 | } |
---|
1111 | } |
---|
1112 | return NULL; |
---|
1113 | } |
---|
1114 | |
---|
1115 | /** |
---|
1116 | * Start and end dates for a calendar week, adjusted to use the |
---|
1117 | * chosen first day of week for this site. |
---|
1118 | */ |
---|
1119 | function date_week_range($week, $year) { |
---|
1120 | if (variable_get('date_api_use_iso8601', FALSE)) { |
---|
1121 | return date_iso_week_range($week, $year); |
---|
1122 | } |
---|
1123 | |
---|
1124 | $min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone_name()); |
---|
1125 | date_timezone_set($min_date, date_default_timezone()); |
---|
1126 | |
---|
1127 | // move to the right week |
---|
1128 | date_modify($min_date, '+' . strval(7 * ($week - 1)) . ' days'); |
---|
1129 | |
---|
1130 | // move backwards to the first day of the week |
---|
1131 | $first_day = variable_get('date_first_day', 1); |
---|
1132 | $day_wday = date_format($min_date, 'w'); |
---|
1133 | date_modify($min_date, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days'); |
---|
1134 | |
---|
1135 | // move forwards to the last day of the week |
---|
1136 | $max_date = drupal_clone($min_date); |
---|
1137 | date_modify($max_date, '+7 days'); |
---|
1138 | |
---|
1139 | if (date_format($min_date, 'Y') != $year) { |
---|
1140 | $min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone()); |
---|
1141 | } |
---|
1142 | return array($min_date, $max_date); |
---|
1143 | } |
---|
1144 | |
---|
1145 | /** |
---|
1146 | * Start and end dates for an ISO week. |
---|
1147 | */ |
---|
1148 | function date_iso_week_range($week, $year) { |
---|
1149 | |
---|
1150 | // Get to the last ISO week of the previous year. |
---|
1151 | $min_date = date_make_date(($year - 1) .'-12-28 00:00:00', date_default_timezone_name()); |
---|
1152 | date_timezone_set($min_date, date_default_timezone()); |
---|
1153 | |
---|
1154 | // Find the first day of the first ISO week in the year. |
---|
1155 | date_modify($min_date, '+1 Monday'); |
---|
1156 | |
---|
1157 | // Jump ahead to the desired week for the beginning of the week range. |
---|
1158 | if ($week > 1) { |
---|
1159 | date_modify($min_date, '+ '. ($week - 1) .' weeks'); |
---|
1160 | } |
---|
1161 | |
---|
1162 | // move forwards to the last day of the week |
---|
1163 | $max_date = drupal_clone($min_date); |
---|
1164 | date_modify($max_date, '+7 days'); |
---|
1165 | return array($min_date, $max_date); |
---|
1166 | } |
---|
1167 | |
---|
1168 | /** |
---|
1169 | * The number of calendar weeks in a year. |
---|
1170 | * |
---|
1171 | * PHP week functions return the ISO week, not the calendar week. |
---|
1172 | * |
---|
1173 | * @param int $year |
---|
1174 | * @return int number of calendar weeks in selected year. |
---|
1175 | */ |
---|
1176 | function date_weeks_in_year($year) { |
---|
1177 | $date = date_make_date(($year + 1) . '-01-01 12:00:00', 'UTC'); |
---|
1178 | date_modify($date, '-1 day'); |
---|
1179 | return date_week(date_format($date, 'Y-m-d')); |
---|
1180 | } |
---|
1181 | |
---|
1182 | /** |
---|
1183 | * The calendar week number for a date. |
---|
1184 | * |
---|
1185 | * PHP week functions return the ISO week, not the calendar week. |
---|
1186 | * |
---|
1187 | * @param string $date, in the format Y-m-d |
---|
1188 | * @return int calendar week number. |
---|
1189 | */ |
---|
1190 | function date_week($date) { |
---|
1191 | $date = drupal_substr($date, 0, 10); |
---|
1192 | $parts = explode('-', $date); |
---|
1193 | $date = date_make_date($date . ' 12:00:00', 'UTC'); |
---|
1194 | |
---|
1195 | // If we are using ISO weeks, this is easy. |
---|
1196 | if (variable_get('date_api_use_iso8601', FALSE)) { |
---|
1197 | return intval(date_format($date, 'W')); |
---|
1198 | } |
---|
1199 | |
---|
1200 | $year_date = date_make_date($parts[0] . '-01-01 12:00:00', 'UTC'); |
---|
1201 | $week = intval(date_format($date, 'W')); |
---|
1202 | $year_week = intval(date_format($year_date, 'W')); |
---|
1203 | $date_year = intval(date_format($date, 'o')); |
---|
1204 | |
---|
1205 | // remove the leap week if it's present |
---|
1206 | if ($date_year > intval($parts[0])) { |
---|
1207 | $last_date = drupal_clone($date); |
---|
1208 | date_modify($last_date, '-7 days'); |
---|
1209 | $week = date_format($last_date, 'W') + 1; |
---|
1210 | } |
---|
1211 | elseif ($date_year < intval($parts[0])) { |
---|
1212 | $week = 0; |
---|
1213 | } |
---|
1214 | |
---|
1215 | if ($year_week != 1) $week++; |
---|
1216 | |
---|
1217 | // convert to ISO-8601 day number, to match weeks calculated above |
---|
1218 | $iso_first_day = 1 + (variable_get('date_first_day', 1) + 6) % 7; |
---|
1219 | |
---|
1220 | // if it's before the starting day, it's the previous week |
---|
1221 | if (intval(date_format($date, 'N')) < $iso_first_day) $week--; |
---|
1222 | |
---|
1223 | // if the year starts before, it's an extra week at the beginning |
---|
1224 | if (intval(date_format($year_date, 'N')) < $iso_first_day) $week++; |
---|
1225 | |
---|
1226 | return $week; |
---|
1227 | } |
---|
1228 | |
---|
1229 | /** |
---|
1230 | * Date conversion helper function. |
---|
1231 | * |
---|
1232 | * A variety of ways to convert dates from one type to another. |
---|
1233 | * No timezone conversion is done in this operation, except |
---|
1234 | * when handling timestamps because create_date() assumes |
---|
1235 | * timestamps hold the UTC value for the time. |
---|
1236 | * |
---|
1237 | * @param mixed $date |
---|
1238 | * the date to convert |
---|
1239 | * @param string $from_type |
---|
1240 | * the type of date to convert from |
---|
1241 | * @param string $to_type |
---|
1242 | * the type of date to convert to |
---|
1243 | * @param string $tz |
---|
1244 | * the timezone of the supplied value, only needed when using timestamps |
---|
1245 | * for dates not set to UTC. |
---|
1246 | */ |
---|
1247 | function date_convert($date, $from_type, $to_type, $tz = 'UTC') { |
---|
1248 | if (empty($date) && !$date === 0) return NULL; |
---|
1249 | if (empty($from_type) || empty($to_type) || $from_type == $to_type) return $date; |
---|
1250 | switch ($from_type) { |
---|
1251 | case DATE_ARRAY: |
---|
1252 | if (!is_array($date)) return NULL; |
---|
1253 | // Make sure all parts exist to avoid PHP notices. |
---|
1254 | foreach (array('month', 'day', 'hour', 'minute', 'second') as $part) { |
---|
1255 | if (!isset($date[$part])) { |
---|
1256 | $date[$part] = ''; |
---|
1257 | } |
---|
1258 | } |
---|
1259 | if (isset($date['ampm'])) { |
---|
1260 | if ($date['ampm'] == 'pm' && $date['hour'] < 12) $date['hour'] += 12; |
---|
1261 | if ($date['ampm'] == 'am' && $date['hour'] == 12) $date['hour'] -= 12; |
---|
1262 | } |
---|
1263 | $datetime = date_pad(intval($date['year']), 4) .'-'. date_pad(intval($date['month'])) . |
---|
1264 | '-'. date_pad(intval($date['day'])) .' '. date_pad(intval($date['hour'])) . |
---|
1265 | ':'. date_pad(intval($date['minute'])) .':'. date_pad(intval($date['second'])); |
---|
1266 | switch ($to_type) { |
---|
1267 | case DATE_ISO: |
---|
1268 | return str_replace(' ', 'T', $datetime); |
---|
1269 | case DATE_DATETIME: |
---|
1270 | return $datetime; |
---|
1271 | case DATE_ICAL: |
---|
1272 | $replace = array(' ' => 'T', '-' => '', ':' => ''); |
---|
1273 | return strtr($datetime, $replace); |
---|
1274 | case DATE_OBJECT: |
---|
1275 | return date_create($datetime, timezone_open($tz)); |
---|
1276 | case DATE_UNIX: |
---|
1277 | $obj = date_create($datetime, timezone_open($tz)); |
---|
1278 | return date_format($obj, 'U'); |
---|
1279 | } |
---|
1280 | break; |
---|
1281 | case DATE_OBJECT: |
---|
1282 | if (!is_object($date)) return NULL; |
---|
1283 | $obj = $date; |
---|
1284 | break; |
---|
1285 | case DATE_DATETIME: |
---|
1286 | case DATE_ISO: |
---|
1287 | if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL; |
---|
1288 | $date = date_fuzzy_datetime($date); |
---|
1289 | $obj = date_create($date, timezone_open($tz)); |
---|
1290 | break; |
---|
1291 | case DATE_ICAL: |
---|
1292 | if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL; |
---|
1293 | preg_match(DATE_REGEX_LOOSE, $date, $regs); |
---|
1294 | $datetime = date_pad($regs[1], 4) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]) . |
---|
1295 | 'T'. date_pad($regs[5]) .':'. date_pad($regs[6]) .':'. date_pad($regs[7]); |
---|
1296 | $obj = date_create($datetime, timezone_open($tz)); |
---|
1297 | break; |
---|
1298 | case DATE_UNIX: |
---|
1299 | if (!is_numeric($date)) return NULL; |
---|
1300 | // Special case when creating dates with timestamps. |
---|
1301 | // The date_create() function will assume date is UTC value |
---|
1302 | // and will ignore our timezone. |
---|
1303 | $obj = date_create("@$date", timezone_open('UTC')); |
---|
1304 | date_timezone_set($obj, timezone_open($tz)); |
---|
1305 | break; |
---|
1306 | } |
---|
1307 | switch ($to_type) { |
---|
1308 | case DATE_OBJECT: |
---|
1309 | return $obj; |
---|
1310 | case DATE_DATETIME: |
---|
1311 | return date_format($obj, DATE_FORMAT_DATETIME); |
---|
1312 | case DATE_ISO: |
---|
1313 | return date_format($obj, DATE_FORMAT_ISO); |
---|
1314 | case DATE_ICAL: |
---|
1315 | return date_format($obj, DATE_FORMAT_ICAL); |
---|
1316 | case DATE_UNIX: |
---|
1317 | return date_format($obj, 'U'); |
---|
1318 | case DATE_ARRAY: |
---|
1319 | $date_array = date_array($obj); |
---|
1320 | // ISO dates may contain zero values for some date parts, |
---|
1321 | // make sure they don't get lost in the conversion. |
---|
1322 | if ($from_type == DATE_ISO) { |
---|
1323 | $date_array = array_merge($date_array, date_iso_array($date)); |
---|
1324 | } |
---|
1325 | return $date_array; |
---|
1326 | default: |
---|
1327 | return NULL; |
---|
1328 | } |
---|
1329 | } |
---|
1330 | |
---|
1331 | /** |
---|
1332 | * Create valid datetime value from incomplete ISO dates or arrays. |
---|
1333 | */ |
---|
1334 | function date_fuzzy_datetime($date) { |
---|
1335 | // A text ISO date, like MMMM-YY-DD HH:MM:SS |
---|
1336 | if (!is_array($date)) { |
---|
1337 | $date = date_iso_array($date); |
---|
1338 | } |
---|
1339 | // An date/time value in the format: |
---|
1340 | // array('date' => MMMM-YY-DD, 'time' => HH:MM:SS). |
---|
1341 | elseif (array_key_exists('date', $date) || array_key_exists('time', $date)) { |
---|
1342 | $date_part = array_key_exists('date', $date) ? $date['date'] : ''; |
---|
1343 | $time_part = array_key_exists('time', $date) ? $date['time'] : ''; |
---|
1344 | $date = date_iso_array(trim($date_part .' '. $time_part)); |
---|
1345 | } |
---|
1346 | // Otherwise date must in in format: |
---|
1347 | // array('year' => YYYY, 'month' => MM, 'day' => DD). |
---|
1348 | if (empty($date['year'])) { |
---|
1349 | $date['year'] = date('Y'); |
---|
1350 | } |
---|
1351 | if (empty($date['month'])) { |
---|
1352 | $date['month'] = 1; |
---|
1353 | } |
---|
1354 | if (empty($date['day'])) { |
---|
1355 | $date['day'] = 1; |
---|
1356 | } |
---|
1357 | foreach (array('hour', 'minute', 'second') as $part) { |
---|
1358 | if (empty($date[$part])) { |
---|
1359 | $date[$part] = 0; |
---|
1360 | } |
---|
1361 | } |
---|
1362 | $value = date_pad($date['year'], 4) .'-'. date_pad($date['month']) .'-'. |
---|
1363 | date_pad($date['day']) .' '. date_pad($date['hour']) .':'. |
---|
1364 | date_pad($date['minute']) .':'. date_pad($date['second']); |
---|
1365 | return $value; |
---|
1366 | } |
---|
1367 | |
---|
1368 | /** |
---|
1369 | * Create an array of date parts from an ISO date. |
---|
1370 | */ |
---|
1371 | function date_iso_array($date) { |
---|
1372 | preg_match(DATE_REGEX_LOOSE, $date, $regs); |
---|
1373 | return array( |
---|
1374 | 'year' => isset($regs[1]) ? intval($regs[1]) : '', |
---|
1375 | 'month' => isset($regs[2]) ? intval($regs[2]) : '', |
---|
1376 | 'day' => isset($regs[3]) ? intval($regs[3]) : '', |
---|
1377 | 'hour' => isset($regs[5]) ? intval($regs[5]) : '', |
---|
1378 | 'minute' => isset($regs[6]) ? intval($regs[6]) : '', |
---|
1379 | 'second' => isset($regs[7]) ? intval($regs[7]) : '', |
---|
1380 | ); |
---|
1381 | } |
---|
1382 | |
---|
1383 | /** |
---|
1384 | * Create an array of values from a date object. Structured like the |
---|
1385 | * results of getdate() but not limited to the 32-bit signed range. |
---|
1386 | * |
---|
1387 | * @param object $obj |
---|
1388 | * @return array |
---|
1389 | */ |
---|
1390 | function date_array($obj) { |
---|
1391 | $year = intval(date_format($obj, 'Y')); |
---|
1392 | $dow = date_format($obj, 'w'); |
---|
1393 | $days = date_week_days(); |
---|
1394 | return array( |
---|
1395 | 'second' => (integer) date_format($obj, 's'), |
---|
1396 | 'minute' => (integer) date_format($obj, 'i'), |
---|
1397 | 'hour' => date_format($obj, 'G'), |
---|
1398 | 'day' => date_format($obj, 'j'), |
---|
1399 | 'wday' => $dow, |
---|
1400 | 'month' => date_format($obj, 'n'), |
---|
1401 | 'year' => date_format($obj, 'Y'), |
---|
1402 | 'yday' => date_format($obj, 'z'), |
---|
1403 | 'weekday' => $days[$dow], |
---|
1404 | 'month_name' => date_format($obj, 'F'), |
---|
1405 | 0 => date_format($obj, 'U')); |
---|
1406 | } |
---|
1407 | |
---|
1408 | /** |
---|
1409 | * Extract integer value of any date part from any type of date. |
---|
1410 | * |
---|
1411 | * Example: |
---|
1412 | * date_part_extract('2007-03-15 00:00', 'month', DATE_DATETIME) |
---|
1413 | * returns: 3 |
---|
1414 | * |
---|
1415 | * @param mixed $date |
---|
1416 | * the date value to analyze. |
---|
1417 | * @param string $part |
---|
1418 | * the part of the date to extract, 'year', 'month', 'day', 'hour', 'minute', 'second' |
---|
1419 | * @param string $type |
---|
1420 | * the type of date supplied, DATE_ISO, DATE_UNIX, DATE_DATETIME, or DATE_OBJECT; |
---|
1421 | * @return integer |
---|
1422 | * the integer value of the requested date part. |
---|
1423 | */ |
---|
1424 | function date_part_extract($date, $part, $type = DATE_DATETIME, $tz = 'UTC') { |
---|
1425 | $formats = array('year' => 'Y', 'month' => 'n', 'day' => 'j', |
---|
1426 | 'hour' => 'G', 'minute' => 'i', 'second' => 's'); |
---|
1427 | $positions = array('year' => 0, 'month' => 5, 'day' => 8, |
---|
1428 | 'hour' => 11, 'minute' => 14, 'second' => 17); |
---|
1429 | $ipositions = array('year' => 0, 'month' => 4, 'day' => 6, |
---|
1430 | 'hour' => 9, 'minute' => 11, 'second' => 13); |
---|
1431 | switch ($type) { |
---|
1432 | case DATE_ARRAY: |
---|
1433 | return (integer) array_key_exists($part, $date) ? $date[$part] : NULL; |
---|
1434 | case DATE_DATETIME: |
---|
1435 | case DATE_ISO: |
---|
1436 | return (integer) drupal_substr($date, $positions[$part], $part == 'year' ? 4 : 2); |
---|
1437 | case DATE_ICAL: |
---|
1438 | return (integer) drupal_substr($date, $ipositions[$part], $part == 'year' ? 4 : 2); |
---|
1439 | case DATE_UNIX: |
---|
1440 | // Special case when creating dates with timestamps. |
---|
1441 | // The date_create() function will assume date is UTC value |
---|
1442 | // and will ignore our timezone. |
---|
1443 | $date = date_create("@$date", timezone_open('UTC')); |
---|
1444 | date_timezone_set($date, timezone_open($tz)); |
---|
1445 | return date_format($date, $formats[$part]); |
---|
1446 | case DATE_OBJECT: |
---|
1447 | return date_format($date, $formats[$part]); |
---|
1448 | } |
---|
1449 | } |
---|
1450 | |
---|
1451 | /** |
---|
1452 | * Functions to test the validity of a date in various formats. |
---|
1453 | * Has special case for ISO dates and arrays which can be missing |
---|
1454 | * month and day and still be valid. |
---|
1455 | * |
---|
1456 | * @param $type |
---|
1457 | * could be DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT |
---|
1458 | * @param $granularity |
---|
1459 | * The granularity of the date value provided. Set this for partial |
---|
1460 | * dates so they pass validation. |
---|
1461 | */ |
---|
1462 | function date_is_valid($date, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) { |
---|
1463 | |
---|
1464 | // Check that the value is properly structured. |
---|
1465 | // Remember that DATE_UNIX can have a valid value of '0', which is 'empty'. |
---|
1466 | if (empty($date) && $type != DATE_UNIX) return FALSE; |
---|
1467 | if ($type == DATE_OBJECT && !is_object($date)) return FALSE; |
---|
1468 | if (($type == DATE_ISO || $type == DATE_DATETIME) && (!is_string($date) || !preg_match(DATE_REGEX_LOOSE, $date))) return FALSE; |
---|
1469 | if ($type == DATE_UNIX and !is_numeric($date)) return FALSE; |
---|
1470 | if ($type == DATE_ARRAY and !is_array($date)) return FALSE; |
---|
1471 | |
---|
1472 | // Make sure integer values are sent to checkdate. |
---|
1473 | $year = intval(date_part_extract($date, 'year', $type)); |
---|
1474 | $month = intval(date_part_extract($date, 'month', $type)); |
---|
1475 | $day = intval(date_part_extract($date, 'day', $type)); |
---|
1476 | if (checkdate($month, $day, $year)) { |
---|
1477 | return TRUE; |
---|
1478 | } |
---|
1479 | |
---|
1480 | // If this is an incomplete date (year only or year and month only), |
---|
1481 | // need special handling, partial dates can have empty date parts. |
---|
1482 | $max_granularity = $granularity; |
---|
1483 | $max_granularity = array_pop($max_granularity); |
---|
1484 | if (!in_array($max_granularity, array('year', 'month'))) { |
---|
1485 | if (in_array('year', $granularity) && !date_valid_year($year)) { |
---|
1486 | return FALSE; |
---|
1487 | } |
---|
1488 | elseif (in_array('month', $granularity) && !date_valid_month($month)) { |
---|
1489 | return FALSE; |
---|
1490 | } |
---|
1491 | elseif (in_array('day', $granularity) && !date_valid_day($day, $month, $year)) { |
---|
1492 | return FALSE; |
---|
1493 | } |
---|
1494 | } |
---|
1495 | // ISO dates and arrays can have empty date parts. |
---|
1496 | elseif ($type == DATE_ISO || $type == DATE_ARRAY) { |
---|
1497 | if (!date_valid_year($year)) { |
---|
1498 | return FALSE; |
---|
1499 | } |
---|
1500 | elseif (!empty($month) && !date_valid_month($month)) { |
---|
1501 | return FALSE; |
---|
1502 | } |
---|
1503 | elseif (!empty($day) && !date_valid_day($day, $month, $year)) { |
---|
1504 | return FALSE; |
---|
1505 | } |
---|
1506 | } |
---|
1507 | elseif (!date_valid_year($year) || !date_valid_month($month) || !date_valid_day($day, $month, $year)) { |
---|
1508 | // Unix and datetime are expected to have at least a year, month, and day. |
---|
1509 | return FALSE; |
---|
1510 | } |
---|
1511 | |
---|
1512 | return TRUE; |
---|
1513 | } |
---|
1514 | |
---|
1515 | function date_valid_year($year) { |
---|
1516 | if (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year) { |
---|
1517 | return FALSE; |
---|
1518 | } |
---|
1519 | else { |
---|
1520 | return TRUE; |
---|
1521 | } |
---|
1522 | } |
---|
1523 | |
---|
1524 | function date_valid_month($month) { |
---|
1525 | if (12 < $month || 0 > $month) { |
---|
1526 | return FALSE; |
---|
1527 | } |
---|
1528 | else { |
---|
1529 | return TRUE; |
---|
1530 | } |
---|
1531 | } |
---|
1532 | |
---|
1533 | function date_valid_day($day, $month = NULL, $year = NULL) { |
---|
1534 | $days_in_month = !empty($month) && !empty($year) ? date_days_in_month($year, $month) : 31; |
---|
1535 | if ($days_in_month < $day || 1 > $day) { |
---|
1536 | return FALSE; |
---|
1537 | } |
---|
1538 | else { |
---|
1539 | return TRUE; |
---|
1540 | } |
---|
1541 | } |
---|
1542 | |
---|
1543 | /** |
---|
1544 | * Helper function to left pad date parts with zeros. |
---|
1545 | * Provided because this is needed so often with dates. |
---|
1546 | * |
---|
1547 | * @param int $value |
---|
1548 | * the value to pad |
---|
1549 | * @param int $size |
---|
1550 | * total size expected, usually 2 or 4 |
---|
1551 | * @return string the padded value |
---|
1552 | */ |
---|
1553 | function date_pad($value, $size = 2) { |
---|
1554 | return sprintf("%0". $size ."d", $value); |
---|
1555 | } |
---|
1556 | |
---|
1557 | /** |
---|
1558 | * Function to figure out if any time data is to be collected or displayed. |
---|
1559 | * |
---|
1560 | * @param granularity |
---|
1561 | * an array like ('year', 'month', 'day', 'hour', 'minute', 'second'); |
---|
1562 | */ |
---|
1563 | function date_has_time($granularity) { |
---|
1564 | if (!is_array($granularity)) $granularity = array(); |
---|
1565 | return sizeof(array_intersect($granularity, array('hour', 'minute', 'second'))) > 0 ? TRUE : FALSE; |
---|
1566 | } |
---|
1567 | |
---|
1568 | function date_has_date($granularity) { |
---|
1569 | if (!is_array($granularity)) $granularity = array(); |
---|
1570 | return sizeof(array_intersect($granularity, array('year', 'month', 'day'))) > 0 ? TRUE : FALSE; |
---|
1571 | } |
---|
1572 | /** |
---|
1573 | * Recalculate a date so it only includes elements from a granularity |
---|
1574 | * array. Helps prevent errors when unwanted values round up and ensures |
---|
1575 | * that unwanted date part values don't get stored in the database. |
---|
1576 | * |
---|
1577 | * Example: |
---|
1578 | * date_limit_value('2007-05-15 04:45:59', array('year', 'month', 'day')) |
---|
1579 | * returns '2007-05-15 00:00:00' |
---|
1580 | * |
---|
1581 | * @param $date |
---|
1582 | * a date value |
---|
1583 | * @param $granularity |
---|
1584 | * an array of allowed date parts, like ('year', 'month', 'day', 'hour', 'minute', 'second'); |
---|
1585 | * @param $type |
---|
1586 | * the type of date value provided, |
---|
1587 | * DATE_DATETIME, DATE_ISO, DATE_UNIX, or DATE_ARRAY |
---|
1588 | * @return |
---|
1589 | * the date with the unwanted parts reset to zeros (or ones if zeros are |
---|
1590 | * invalid for that date type). |
---|
1591 | */ |
---|
1592 | function date_limit_value($date, $granularity, $type = DATE_DATETIME) { |
---|
1593 | if (!date_is_valid($date, $type, $granularity) || !$nongranularity = date_nongranularity($granularity)) { |
---|
1594 | return $date; |
---|
1595 | } |
---|
1596 | else { |
---|
1597 | $date = date_convert($date, $type, DATE_ARRAY); |
---|
1598 | foreach ($nongranularity as $level) { |
---|
1599 | switch ($level) { |
---|
1600 | case 'second': |
---|
1601 | $date['second'] = 0; |
---|
1602 | break; |
---|
1603 | case 'minute': |
---|
1604 | $date['minute'] = 0; |
---|
1605 | break; |
---|
1606 | case 'hour': |
---|
1607 | $date['hour'] = 0; |
---|
1608 | break; |
---|
1609 | case 'month': |
---|
1610 | $date['month'] = $type != DATE_ISO ? 1 : 0; |
---|
1611 | break; |
---|
1612 | case 'day': |
---|
1613 | $date['day'] = $type != DATE_ISO ? 1 : 0; |
---|
1614 | break; |
---|
1615 | } |
---|
1616 | } |
---|
1617 | return date_convert($date, DATE_ARRAY, $type); |
---|
1618 | } |
---|
1619 | } |
---|
1620 | |
---|
1621 | /** |
---|
1622 | * Rewrite a format string so it only includes elements from a |
---|
1623 | * specified granularity array. |
---|
1624 | * |
---|
1625 | * Example: |
---|
1626 | * date_limit_format('F j, Y - H:i', array('year', 'month', 'day')); |
---|
1627 | * returns 'F j, Y' |
---|
1628 | * |
---|
1629 | * @param $format |
---|
1630 | * a format string |
---|
1631 | * @param $granularity |
---|
1632 | * an array of allowed date parts, all others will be removed |
---|
1633 | * array('year', 'month', 'day', 'hour', 'minute', 'second'); |
---|
1634 | * @return |
---|
1635 | * a format string with all other elements removed |
---|
1636 | */ |
---|
1637 | function date_limit_format($format, $granularity) { |
---|
1638 | // If punctuation has been escaped, remove the escaping. |
---|
1639 | // Done using strtr because it is easier than getting the |
---|
1640 | // escape character extracted using preg_replace. |
---|
1641 | $replace = array( |
---|
1642 | '\-' => '-', |
---|
1643 | '\:' => ':', |
---|
1644 | "\'" => "'", |
---|
1645 | '\.' => '.', |
---|
1646 | '\,' => ',', |
---|
1647 | ); |
---|
1648 | $format = strtr($format, $replace); |
---|
1649 | |
---|
1650 | // Get the 'T' out of ISO date formats that don't have |
---|
1651 | // both date and time. |
---|
1652 | if (!date_has_time($granularity) || !date_has_date($granularity)) { |
---|
1653 | $format = str_replace('\T', ' ', $format); |
---|
1654 | $format = str_replace('T', ' ', $format); |
---|
1655 | } |
---|
1656 | |
---|
1657 | $regex = array(); |
---|
1658 | if (!date_has_time($granularity)) { |
---|
1659 | $regex[] = '((?<!\\\\)[a|A])'; |
---|
1660 | } |
---|
1661 | // Create regular expressions to remove selected values from string. |
---|
1662 | // Use (?<!\\\\) to keep escaped letters from being removed. |
---|
1663 | foreach (date_nongranularity($granularity) as $element) { |
---|
1664 | switch ($element) { |
---|
1665 | case 'year': |
---|
1666 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[Yy])'; |
---|
1667 | break; |
---|
1668 | case 'day': |
---|
1669 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[l|D|d|dS|j|jS|N|w|W|z]{1,2})'; |
---|
1670 | break; |
---|
1671 | case 'month': |
---|
1672 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[FMmn])'; |
---|
1673 | break; |
---|
1674 | case 'hour': |
---|
1675 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[HhGg])'; |
---|
1676 | break; |
---|
1677 | case 'minute': |
---|
1678 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[i])'; |
---|
1679 | break; |
---|
1680 | case 'second': |
---|
1681 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[s])'; |
---|
1682 | break; |
---|
1683 | case 'timezone': |
---|
1684 | $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[TOZPe])'; |
---|
1685 | break; |
---|
1686 | |
---|
1687 | } |
---|
1688 | } |
---|
1689 | // Remove empty parentheses, brackets, pipes. |
---|
1690 | $regex[] = '(\(\))'; |
---|
1691 | $regex[] = '(\[\])'; |
---|
1692 | $regex[] = '(\|\|)'; |
---|
1693 | |
---|
1694 | // Remove selected values from string. |
---|
1695 | $format = trim(preg_replace($regex, array(), $format)); |
---|
1696 | // Remove orphaned punctuation at the beginning of the string. |
---|
1697 | $format = preg_replace('`^([\-/\.,:\'])`', '', $format); |
---|
1698 | // Remove orphaned punctuation at the end of the string. |
---|
1699 | $format = preg_replace('([\-/\.,:\']$)', '', $format); |
---|
1700 | $format = preg_replace('(\\$)', '', $format); |
---|
1701 | |
---|
1702 | // Trim any whitespace from the result. |
---|
1703 | $format = trim($format); |
---|
1704 | |
---|
1705 | // After removing the non-desired parts of the format, test if the only |
---|
1706 | // things left are escaped, non-date, characters. If so, return nothing. |
---|
1707 | // Using S instead of w to pick up non-ASCII characters. |
---|
1708 | $test = trim(preg_replace('(\\\\\S{1,3})', '', $format)); |
---|
1709 | if (empty($test)) { |
---|
1710 | $format = ''; |
---|
1711 | } |
---|
1712 | return $format; |
---|
1713 | } |
---|
1714 | |
---|
1715 | /** |
---|
1716 | * Convert a format to an ordered array of granularity parts. |
---|
1717 | * |
---|
1718 | * Example: |
---|
1719 | * date_format_order('m/d/Y H:i') |
---|
1720 | * returns |
---|
1721 | * array( |
---|
1722 | * 0 => 'month', |
---|
1723 | * 1 => 'day', |
---|
1724 | * 2 => 'year', |
---|
1725 | * 3 => 'hour', |
---|
1726 | * 4 => 'minute', |
---|
1727 | * ); |
---|
1728 | * |
---|
1729 | * @param string $format |
---|
1730 | * @return array of ordered granularity elements in this format string |
---|
1731 | */ |
---|
1732 | function date_format_order($format) { |
---|
1733 | $order = array(); |
---|
1734 | if (empty($format)) return $order; |
---|
1735 | $max = drupal_strlen($format); |
---|
1736 | for ($i = 0; $i <= $max; $i++) { |
---|
1737 | if (!isset($format[$i])) break; |
---|
1738 | $c = $format[$i]; |
---|
1739 | switch ($c) { |
---|
1740 | case 'd': |
---|
1741 | case 'j': |
---|
1742 | $order[] = 'day'; |
---|
1743 | break; |
---|
1744 | case 'F': |
---|
1745 | case 'M': |
---|
1746 | case 'm': |
---|
1747 | case 'n': |
---|
1748 | $order[] = 'month'; |
---|
1749 | break; |
---|
1750 | case 'Y': |
---|
1751 | case 'y': |
---|
1752 | $order[] = 'year'; |
---|
1753 | break; |
---|
1754 | case 'g': |
---|
1755 | case 'G': |
---|
1756 | case 'h': |
---|
1757 | case 'H': |
---|
1758 | $order[] = 'hour'; |
---|
1759 | break; |
---|
1760 | case 'i': |
---|
1761 | $order[] = 'minute'; |
---|
1762 | break; |
---|
1763 | case 's': |
---|
1764 | $order[] = 'second'; |
---|
1765 | break; |
---|
1766 | } |
---|
1767 | } |
---|
1768 | return $order; |
---|
1769 | } |
---|
1770 | |
---|
1771 | /** |
---|
1772 | * An difference array of granularity elements that are NOT in the |
---|
1773 | * granularity array. Used by functions that strip unwanted |
---|
1774 | * granularity elements out of formats and values. |
---|
1775 | * |
---|
1776 | * @param $granularity |
---|
1777 | * an array like ('year', 'month', 'day', 'hour', 'minute', 'second'); |
---|
1778 | */ |
---|
1779 | function date_nongranularity($granularity) { |
---|
1780 | return array_diff(array('year', 'month', 'day', 'hour', 'minute', 'second', 'timezone'), (array) $granularity); |
---|
1781 | } |
---|
1782 | |
---|
1783 | /** |
---|
1784 | * Implementation of hook_simpletest(). |
---|
1785 | */ |
---|
1786 | function date_api_simpletest() { |
---|
1787 | $dir = drupal_get_path('module', 'date_api') .'/tests'; |
---|
1788 | $tests = file_scan_directory($dir, '\.test$'); |
---|
1789 | return array_keys($tests); |
---|
1790 | } |
---|
1791 | |
---|
1792 | /** |
---|
1793 | * Implementation of hook_elements(). |
---|
1794 | */ |
---|
1795 | function date_api_elements() { |
---|
1796 | require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_elements.inc'); |
---|
1797 | return _date_api_elements(); |
---|
1798 | } |
---|
1799 | |
---|
1800 | function date_api_theme() { |
---|
1801 | $path = drupal_get_path('module', 'date_api'); |
---|
1802 | $base = array( |
---|
1803 | 'file' => 'theme.inc', |
---|
1804 | 'path' => "$path/theme", |
---|
1805 | ); |
---|
1806 | return array( |
---|
1807 | 'date_nav_title' => $base + array('arguments' => array('type' => NULL, 'view' => NULL)), |
---|
1808 | 'date_vcalendar' => $base + array('arguments' => array('events' => NULL, 'calname' => NULL)), |
---|
1809 | 'date_vevent' => $base + array('arguments' => array('event' => NULL)), |
---|
1810 | 'date_valarm' => $base + array('arguments' => array('alarm' => NULL)), |
---|
1811 | 'date_timezone' => $base + array('arguments' => array('element' => NULL)), |
---|
1812 | 'date_select' => $base + array('arguments' => array('element' => NULL)), |
---|
1813 | 'date_text' => $base + array('arguments' => array('element' => NULL)), |
---|
1814 | 'date_select_element' => $base + array('arguments' => array('element' => NULL)), |
---|
1815 | 'date_textfield_element' => $base + array('arguments' => array('element' => NULL)), |
---|
1816 | 'date_date_part_hour_prefix' => $base + array('arguments' => array('element' => NULL)), |
---|
1817 | 'date_part_minsec_prefix' => $base + array('arguments' => array('element' => NULL)), |
---|
1818 | 'date_part_label_year' => $base + array('arguments' => array('element' => NULL)), |
---|
1819 | 'date_part_label_month' => $base + array('arguments' => array('element' => NULL)), |
---|
1820 | 'date_part_label_day' => $base + array('arguments' => array('element' => NULL)), |
---|
1821 | 'date_part_label_hour' => $base + array('arguments' => array('element' => NULL)), |
---|
1822 | 'date_part_label_minute' => $base + array('arguments' => array('element' => NULL)), |
---|
1823 | 'date_part_label_second' => $base + array('arguments' => array('element' => NULL)), |
---|
1824 | 'date_part_label_ampm' => $base + array('arguments' => array('element' => NULL)), |
---|
1825 | 'date_part_label_timezone' => $base + array('arguments' => array('element' => NULL)), |
---|
1826 | 'date_views_filter_form' => $base + array( |
---|
1827 | 'template' => 'date-views-filter-form', |
---|
1828 | 'arguments' => array('form' => NULL)), |
---|
1829 | 'date_calendar_day' => $base + array('arguments' => array('date' => NULL)), |
---|
1830 | 'date_time_ago' => $base + array('arguments' => array('start_date' => NULL, 'end_date' => NULL, 'interval' => NULL))); |
---|
1831 | } |
---|
1832 | |
---|
1833 | /** |
---|
1834 | * Wrapper around date handler setting for timezone. |
---|
1835 | */ |
---|
1836 | function date_api_set_db_timezone($offset = '+00:00') { |
---|
1837 | require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc'); |
---|
1838 | $handler = new date_sql_handler(); |
---|
1839 | return $handler->set_db_timezone($offset); |
---|
1840 | } |
---|
1841 | |
---|
1842 | /** |
---|
1843 | * Function to figure out which local timezone applies to a date and select it |
---|
1844 | */ |
---|
1845 | function date_get_timezone($handling, $timezone = '') { |
---|
1846 | switch ($handling) { |
---|
1847 | case ('date'): |
---|
1848 | $timezone = !empty($timezone) ? $timezone : date_default_timezone_name(); |
---|
1849 | break; |
---|
1850 | case ('utc'): |
---|
1851 | $timezone = 'UTC'; |
---|
1852 | break; |
---|
1853 | default: |
---|
1854 | $timezone = date_default_timezone_name(); |
---|
1855 | } |
---|
1856 | return $timezone > '' ? $timezone : date_default_timezone_name(); |
---|
1857 | } |
---|
1858 | |
---|
1859 | /** |
---|
1860 | * Function to figure out which db timezone applies to a date and select it |
---|
1861 | */ |
---|
1862 | function date_get_timezone_db($handling, $timezone = '') { |
---|
1863 | switch ($handling) { |
---|
1864 | case ('none'): |
---|
1865 | $timezone = date_default_timezone_name(); |
---|
1866 | break; |
---|
1867 | default: |
---|
1868 | $timezone = 'UTC'; |
---|
1869 | break; |
---|
1870 | } |
---|
1871 | return $timezone > '' ? $timezone : 'UTC'; |
---|
1872 | } |
---|
1873 | |
---|
1874 | /** |
---|
1875 | * Wrapper function to make sure this function will always work. |
---|
1876 | */ |
---|
1877 | function date_api_views_fetch_fields($base, $type) { |
---|
1878 | if (!module_exists('views')) { |
---|
1879 | return array(); |
---|
1880 | } |
---|
1881 | require_once('./'. drupal_get_path('module', 'views') .'/includes/admin.inc'); |
---|
1882 | return views_fetch_fields($base, $type); |
---|
1883 | } |
---|
1884 | |
---|
1885 | /** |
---|
1886 | * Get the list of date formats for a particular format length. |
---|
1887 | * |
---|
1888 | * @param $type |
---|
1889 | * The format type: 'short', 'medium', 'long', 'custom'. If empty, then all |
---|
1890 | * available formats will be returned. |
---|
1891 | * @param $reset |
---|
1892 | * Whether or not to reset this function's internal cache (defaults to FALSE). |
---|
1893 | * @return |
---|
1894 | * Array of date formats. |
---|
1895 | */ |
---|
1896 | function date_get_formats($type = NULL, $reset = FALSE) { |
---|
1897 | static $_date_formats; |
---|
1898 | |
---|
1899 | if ($reset || !isset($_date_formats)) { |
---|
1900 | $_date_formats = _date_formats_build(); |
---|
1901 | } |
---|
1902 | |
---|
1903 | return $type ? (isset($_date_formats[$type]) ? $_date_formats[$type] : FALSE) : $_date_formats; |
---|
1904 | } |
---|
1905 | |
---|
1906 | /** |
---|
1907 | * Get the format details for a particular id. |
---|
1908 | * |
---|
1909 | * @param $dfid |
---|
1910 | * Identifier of a date format string. |
---|
1911 | * @return |
---|
1912 | * Array of date format details. |
---|
1913 | */ |
---|
1914 | function date_get_format($dfid) { |
---|
1915 | $result = db_query('SELECT df.dfid, df.format, df.type, df.locked FROM {date_formats} df WHERE df.dfid = %d', $dfid); |
---|
1916 | return db_fetch_array($result); |
---|
1917 | } |
---|
1918 | |
---|
1919 | /** |
---|
1920 | * Get the list of available date format types and attributes. |
---|
1921 | * |
---|
1922 | * @param $type |
---|
1923 | * The format type, e.g. 'short', 'medium', 'long', 'custom'. If empty, then |
---|
1924 | * all attributes for that type will be returned. |
---|
1925 | * @param $reset |
---|
1926 | * Whether or not to reset this function's internal cache (defaults to FALSE). |
---|
1927 | * @return |
---|
1928 | * Array of date format types. |
---|
1929 | */ |
---|
1930 | function date_get_format_types($type = NULL, $reset = FALSE) { |
---|
1931 | static $_date_format_types; |
---|
1932 | |
---|
1933 | if ($reset || !isset($_date_format_types)) { |
---|
1934 | $_date_format_types = _date_format_types_build(); |
---|
1935 | } |
---|
1936 | |
---|
1937 | return $type ? (isset($_date_format_types[$type]) ? $_date_format_types[$type] : FALSE) : $_date_format_types; |
---|
1938 | } |
---|
1939 | |
---|
1940 | /** |
---|
1941 | * Implementation of hook_flush_caches(). |
---|
1942 | */ |
---|
1943 | function date_api_flush_caches() { |
---|
1944 | // Rebuild list of date formats. |
---|
1945 | date_formats_rebuild(); |
---|
1946 | return array(); |
---|
1947 | } |
---|
1948 | |
---|
1949 | /** |
---|
1950 | * Resets the database cache of date formats, and saves all new date formats to |
---|
1951 | * the database. |
---|
1952 | */ |
---|
1953 | function date_formats_rebuild() { |
---|
1954 | $date_formats = date_get_formats(NULL, TRUE); |
---|
1955 | |
---|
1956 | foreach ($date_formats as $format_type => $formats) { |
---|
1957 | foreach ($formats as $format => $info) { |
---|
1958 | date_format_save($info); |
---|
1959 | } |
---|
1960 | } |
---|
1961 | |
---|
1962 | // Rebuild configured date formats locale list. |
---|
1963 | date_format_locale(NULL, NULL, TRUE); |
---|
1964 | |
---|
1965 | _date_formats_build(); |
---|
1966 | } |
---|
1967 | |
---|
1968 | /** |
---|
1969 | * Save a date format type to the database. |
---|
1970 | * |
---|
1971 | * @param $date_format_type |
---|
1972 | * An array of attributes for a date format type. |
---|
1973 | */ |
---|
1974 | function date_format_type_save($date_format_type) { |
---|
1975 | $type = array(); |
---|
1976 | $type['type'] = $date_format_type['type']; |
---|
1977 | $type['title'] = $date_format_type['title']; |
---|
1978 | $type['locked'] = $date_format_type['locked']; |
---|
1979 | |
---|
1980 | // Update date_format table. |
---|
1981 | if (isset($date_format_type['is_new']) && !empty($date_format_type['is_new'])) { |
---|
1982 | drupal_write_record('date_format_types', $type); |
---|
1983 | } |
---|
1984 | else { |
---|
1985 | drupal_write_record('date_format_types', $type, 'type'); |
---|
1986 | } |
---|
1987 | } |
---|
1988 | |
---|
1989 | /** |
---|
1990 | * Delete a date format type from the database. |
---|
1991 | * |
---|
1992 | * @param $date_format_type |
---|
1993 | * The date format type name. |
---|
1994 | */ |
---|
1995 | function date_format_type_delete($date_format_type) { |
---|
1996 | db_query("DELETE FROM {date_formats} WHERE type = '%s'", $date_format_type); |
---|
1997 | db_query("DELETE FROM {date_format_types} WHERE type = '%s'", $date_format_type); |
---|
1998 | db_query("DELETE FROM {date_format_locale} WHERE type = '%s'", $date_format_type); |
---|
1999 | } |
---|
2000 | |
---|
2001 | /** |
---|
2002 | * Save a date format to the database. |
---|
2003 | * |
---|
2004 | * @param $date_format |
---|
2005 | * An array of attributes for a date format. |
---|
2006 | */ |
---|
2007 | function date_format_save($date_format) { |
---|
2008 | $format = array(); |
---|
2009 | $format['type'] = $date_format['type']; |
---|
2010 | $format['format'] = $date_format['format']; |
---|
2011 | $format['locked'] = $date_format['locked']; |
---|
2012 | |
---|
2013 | // Update date_format table. |
---|
2014 | if (isset($date_format['is_new']) && !empty($date_format['is_new'])) { |
---|
2015 | drupal_write_record('date_formats', $format); |
---|
2016 | } |
---|
2017 | else { |
---|
2018 | drupal_write_record('date_formats', $format, array('format', 'type')); |
---|
2019 | } |
---|
2020 | |
---|
2021 | $languages = language_list('enabled'); |
---|
2022 | $languages = $languages[1]; |
---|
2023 | // If site_country module is enabled, add country specific languages to |
---|
2024 | // languages array. |
---|
2025 | if (module_exists('site_country')) { |
---|
2026 | $country_code = variable_get('site_country_default_country', ''); |
---|
2027 | if (!empty($country_code)) { |
---|
2028 | foreach ($languages as $langcode => $details) { |
---|
2029 | $country_language = $langcode . '-' . $country_code; |
---|
2030 | if (drupal_strlen($langcode) == 2 && !in_array($country_language, array_keys($languages))) { |
---|
2031 | $name = $details->name; |
---|
2032 | $languages[$country_language] = "$name ($country_code)"; |
---|
2033 | } |
---|
2034 | } |
---|
2035 | } |
---|
2036 | } |
---|
2037 | |
---|
2038 | $locale_format = array(); |
---|
2039 | $locale_format['type'] = $date_format['type']; |
---|
2040 | $locale_format['format'] = $date_format['format']; |
---|
2041 | |
---|
2042 | // Check if the suggested language codes are configured and enabled. |
---|
2043 | if (!empty($date_format['locales'])) { |
---|
2044 | foreach ($date_format['locales'] as $langcode) { |
---|
2045 | // Only proceed if language is enabled. |
---|
2046 | if (in_array($langcode, $languages)) { |
---|
2047 | $is_existing = db_result(db_query("SELECT COUNT(*) FROM {date_format_locale} WHERE type = '%s' AND language = '%s'", $date_format['type'], $langcode)); |
---|
2048 | if (!$is_existing) { |
---|
2049 | $locale_format['language'] = $langcode; |
---|
2050 | drupal_write_record('date_format_locale', $locale_format); |
---|
2051 | } |
---|
2052 | } |
---|
2053 | } |
---|
2054 | } |
---|
2055 | } |
---|
2056 | |
---|
2057 | /** |
---|
2058 | * Delete a date format from the database. |
---|
2059 | * |
---|
2060 | * @param $date_format_id |
---|
2061 | * The date format string identifier. |
---|
2062 | */ |
---|
2063 | function date_format_delete($date_format_id) { |
---|
2064 | db_query("DELETE FROM {date_formats} WHERE dfid = '%d'", $date_format_id); |
---|
2065 | } |
---|
2066 | |
---|
2067 | /** |
---|
2068 | * Builds and returns the list of available date format types. |
---|
2069 | * |
---|
2070 | * @return |
---|
2071 | * Array of date format types. |
---|
2072 | */ |
---|
2073 | function _date_format_types_build() { |
---|
2074 | $types = array(); |
---|
2075 | |
---|
2076 | // Prevent errors in the upgrade before the date_format_types table exists. |
---|
2077 | if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format_types')) { |
---|
2078 | return $types; |
---|
2079 | } |
---|
2080 | |
---|
2081 | // Get list of modules which implement hook_date_format_types(). |
---|
2082 | $modules = module_implements('date_format_types'); |
---|
2083 | |
---|
2084 | foreach ($modules as $module) { |
---|
2085 | $module_types = module_invoke($module, 'date_format_types'); |
---|
2086 | foreach ($module_types as $module_type => $type_title) { |
---|
2087 | $type = array(); |
---|
2088 | $type['module'] = $module; |
---|
2089 | $type['type'] = $module_type; |
---|
2090 | $type['title'] = $type_title; |
---|
2091 | $type['locked'] = 1; |
---|
2092 | $type['is_new'] = TRUE; // Will be over-ridden later if in the db. |
---|
2093 | $types[$module_type] = $type; |
---|
2094 | } |
---|
2095 | } |
---|
2096 | |
---|
2097 | // Get custom formats added to the database by the end user. |
---|
2098 | $result = db_query('SELECT dft.type, dft.title, dft.locked FROM {date_format_types} dft ORDER BY dft.title'); |
---|
2099 | while ($object = db_fetch_object($result)) { |
---|
2100 | if (!in_array($object->type, $types)) { |
---|
2101 | $type = array(); |
---|
2102 | $type['is_new'] = FALSE; |
---|
2103 | $type['module'] = ''; |
---|
2104 | $type['type'] = $object->type; |
---|
2105 | $type['title'] = $object->title; |
---|
2106 | $type['locked'] = $object->locked; |
---|
2107 | $types[$object->type] = $type; |
---|
2108 | } |
---|
2109 | else { |
---|
2110 | $type = array(); |
---|
2111 | $type['is_new'] = FALSE; // Over-riding previous setting. |
---|
2112 | $types[$object->type] = array_merge($types[$object->type], $type); |
---|
2113 | } |
---|
2114 | } |
---|
2115 | |
---|
2116 | // Allow other modules to modify these format types. |
---|
2117 | drupal_alter('date_format_types', $types); |
---|
2118 | |
---|
2119 | return $types; |
---|
2120 | } |
---|
2121 | |
---|
2122 | /** |
---|
2123 | * Builds and returns the list of available date formats. |
---|
2124 | * |
---|
2125 | * @return |
---|
2126 | * Array of date formats. |
---|
2127 | */ |
---|
2128 | function _date_formats_build() { |
---|
2129 | $date_formats = array(); |
---|
2130 | |
---|
2131 | // Prevent errors in the upgrade before the date_format table exists. |
---|
2132 | if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format')) { |
---|
2133 | return $date_formats; |
---|
2134 | } |
---|
2135 | |
---|
2136 | // First handle hook_date_format_types(). |
---|
2137 | $types = _date_format_types_build(); |
---|
2138 | foreach ($types as $type => $info) { |
---|
2139 | date_format_type_save($info); |
---|
2140 | } |
---|
2141 | |
---|
2142 | // Get formats supplied by various contrib modules. |
---|
2143 | $module_formats = module_invoke_all('date_formats'); |
---|
2144 | |
---|
2145 | foreach ($module_formats as $module_format) { |
---|
2146 | $module_format['locked'] = 1; // System types are locked. |
---|
2147 | // If no format type is specified, assign 'custom'. |
---|
2148 | if (!isset($module_format['type'])) { |
---|
2149 | $module_format['type'] = 'custom'; |
---|
2150 | } |
---|
2151 | if (!in_array($module_format['type'], array_keys($types))) { |
---|
2152 | continue; |
---|
2153 | } |
---|
2154 | if (!isset($date_formats[$module_format['type']])) { |
---|
2155 | $date_formats[$module_format['type']] = array(); |
---|
2156 | } |
---|
2157 | |
---|
2158 | // If another module already set this format, merge in the new settings. |
---|
2159 | if (isset($date_formats[$module_format['type']][$module_format['format']])) { |
---|
2160 | $date_formats[$module_format['type']][$module_format['format']] = array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format); |
---|
2161 | } |
---|
2162 | else { |
---|
2163 | // This setting will be overridden later if it already exists in the db. |
---|
2164 | $module_format['is_new'] = TRUE; |
---|
2165 | $date_formats[$module_format['type']][$module_format['format']] = $module_format; |
---|
2166 | } |
---|
2167 | } |
---|
2168 | |
---|
2169 | // Get custom formats added to the database by the end user. |
---|
2170 | $result = db_query('SELECT df.dfid, df.format, df.type, df.locked, dfl.language FROM {date_formats} df LEFT JOIN {date_format_types} dft ON df.type = dft.type LEFT JOIN {date_format_locale} dfl ON df.format = dfl.format AND df.type = dfl.type ORDER BY df.type, df.format'); |
---|
2171 | while ($object = db_fetch_object($result)) { |
---|
2172 | // If this format type isn't set, initialise the array. |
---|
2173 | if (!isset($date_formats[$object->type])) { |
---|
2174 | $date_formats[$object->type] = array(); |
---|
2175 | } |
---|
2176 | // If this format not already present, add it to the array. |
---|
2177 | if (!isset($date_formats[$object->type][$object->format])) { |
---|
2178 | // We don't set 'is_new' as it is already in the db. |
---|
2179 | $format = array(); |
---|
2180 | $format['module'] = ''; |
---|
2181 | $format['dfid'] = $object->dfid; |
---|
2182 | $format['format'] = $object->format; |
---|
2183 | $format['type'] = $object->type; |
---|
2184 | $format['locked'] = $object->locked; |
---|
2185 | $format['locales'] = array($object->language); |
---|
2186 | $date_formats[$object->type][$object->format] = $format; |
---|
2187 | } |
---|
2188 | // Format already present, so merge in settings. |
---|
2189 | else { |
---|
2190 | $format = array(); |
---|
2191 | $format['is_new'] = FALSE; // It's in the db, so override this setting. |
---|
2192 | $format['dfid'] = $object->dfid; |
---|
2193 | $format['format'] = $object->format; |
---|
2194 | $format['type'] = $object->type; |
---|
2195 | $format['locked'] = $object->locked; |
---|
2196 | if (!empty($object->language)) { |
---|
2197 | $format['locales'] = array_merge($date_formats[$object->type][$object->format]['locales'], array($object->language)); |
---|
2198 | } |
---|
2199 | $date_formats[$object->type][$object->format] = array_merge($date_formats[$object->type][$object->format], $format); |
---|
2200 | } |
---|
2201 | } |
---|
2202 | |
---|
2203 | // Allow other modules to modify these formats. |
---|
2204 | drupal_alter('date_formats', $date_formats); |
---|
2205 | |
---|
2206 | return $date_formats; |
---|
2207 | } |
---|
2208 | |
---|
2209 | /** |
---|
2210 | * Get the appropriate date format for a type and locale. |
---|
2211 | * |
---|
2212 | * @param $langcode |
---|
2213 | * Language code for the current locale. This can be a 2 character language |
---|
2214 | * code like 'en', 'fr', or a longer 5 character code like 'en-gb'. |
---|
2215 | * @param $type |
---|
2216 | * Date format type: short, medium, long, custom. |
---|
2217 | * @param $reset |
---|
2218 | * Whether or not to reset this function's internal cache (defaults to FALSE). |
---|
2219 | * @return |
---|
2220 | * The format string, or NULL if no matching format found. |
---|
2221 | */ |
---|
2222 | function date_format_locale($langcode = NULL, $type = NULL, $reset = FALSE) { |
---|
2223 | static $formats; |
---|
2224 | |
---|
2225 | if ($reset || empty($formats)) { |
---|
2226 | $formats = array(); |
---|
2227 | $result = db_query("SELECT format, type, language FROM {date_format_locale}"); |
---|
2228 | while ($object = db_fetch_object($result)) { |
---|
2229 | if (!isset($formats[$object->language])) { |
---|
2230 | $formats[$object->language] = array(); |
---|
2231 | } |
---|
2232 | $formats[$object->language][$object->type] = $object->format; |
---|
2233 | } |
---|
2234 | } |
---|
2235 | |
---|
2236 | if ($type && $langcode && !empty($formats[$langcode][$type])) { |
---|
2237 | return $formats[$langcode][$type]; |
---|
2238 | } |
---|
2239 | elseif ($langcode && !empty($formats[$langcode])) { |
---|
2240 | return $formats[$langcode]; |
---|
2241 | } |
---|
2242 | |
---|
2243 | return FALSE; |
---|
2244 | } |
---|
2245 | |
---|
2246 | /** |
---|
2247 | * Helper function for BYDAY options in Date Repeat |
---|
2248 | * and for converting back and forth from '+1' to 'First'. |
---|
2249 | */ |
---|
2250 | function date_order_translated() { |
---|
2251 | return array( |
---|
2252 | '+1' => date_t('First', 'date_order'), |
---|
2253 | '+2' => date_t('Second', 'date_order'), |
---|
2254 | '+3' => date_t('Third', 'date_order'), |
---|
2255 | '+4' => date_t('Fourth', 'date_order'), |
---|
2256 | '+5' => date_t('Fifth', 'date_order'), |
---|
2257 | '-1' => date_t('Last', 'date_order_reverse'), |
---|
2258 | '-2' => date_t('Next to last', 'date_order_reverse'), |
---|
2259 | '-3' => date_t('Third from last', 'date_order_reverse'), |
---|
2260 | '-4' => date_t('Fourth from last', 'date_order_reverse'), |
---|
2261 | '-5' => date_t('Fifth from last', 'date_order_reverse') |
---|
2262 | ); |
---|
2263 | } |
---|
2264 | |
---|
2265 | function date_order() { |
---|
2266 | return array( |
---|
2267 | '+1' => 'First', |
---|
2268 | '+2' => 'Second', |
---|
2269 | '+3' => 'Third', |
---|
2270 | '+4' => 'Fourth', |
---|
2271 | '+5' => 'Fifth', |
---|
2272 | '-1' => 'Last', |
---|
2273 | '-2' => '-2', |
---|
2274 | '-3' => '-3', |
---|
2275 | '-4' => '-4', |
---|
2276 | '-5' => '-5' |
---|
2277 | ); |
---|
2278 | } |
---|
2279 | |
---|
2280 | /** |
---|
2281 | * Implementation of hook_views_api(). |
---|
2282 | * |
---|
2283 | * This one is used as the base to reduce errors when updating. |
---|
2284 | */ |
---|
2285 | function date_api_views_api() { |
---|
2286 | return array( |
---|
2287 | 'api' => 2, |
---|
2288 | 'path' => drupal_get_path('module', 'date_api') .'/includes', |
---|
2289 | ); |
---|
2290 | } |
---|
2291 | |
---|
2292 | /** |
---|
2293 | * Implementation of hook_date_api_fields(). |
---|
2294 | * on behalf of core fields. |
---|
2295 | * |
---|
2296 | * All modules that create custom fields that use the |
---|
2297 | * 'views_handler_field_date' handler can provide |
---|
2298 | * additional information here about the type of |
---|
2299 | * date they create so the date can be used by |
---|
2300 | * the Date API views date argument and date filter. |
---|
2301 | */ |
---|
2302 | function date_api_date_api_fields($field) { |
---|
2303 | $values = array( |
---|
2304 | // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME. |
---|
2305 | 'sql_type' => DATE_UNIX, |
---|
2306 | // Timezone handling options: 'none', 'site', 'date', 'utc'. |
---|
2307 | 'tz_handling' => 'site', |
---|
2308 | // Needed only for dates that use 'date' tz_handling. |
---|
2309 | 'timezone_field' => '', |
---|
2310 | // Needed only for dates that use 'date' tz_handling. |
---|
2311 | 'offset_field' => '', |
---|
2312 | // Array of "table.field" values for related fields that should be |
---|
2313 | // loaded automatically in the Views SQL. |
---|
2314 | 'related_fields' => array(), |
---|
2315 | // Granularity of this date field's db data. |
---|
2316 | 'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'), |
---|
2317 | ); |
---|
2318 | |
---|
2319 | switch ($field) { |
---|
2320 | case 'users.created': |
---|
2321 | case 'users.access': |
---|
2322 | case 'users.login': |
---|
2323 | case 'node.created': |
---|
2324 | case 'node.changed': |
---|
2325 | case 'node_revisions.timestamp': |
---|
2326 | case 'files.timestamp': |
---|
2327 | case 'node_counter.timestamp': |
---|
2328 | case 'accesslog.timestamp': |
---|
2329 | case 'comments.timestamp': |
---|
2330 | case 'node_comment_statistics.last_comment_timestamp': |
---|
2331 | return $values; |
---|
2332 | } |
---|
2333 | } |
---|
2334 | |
---|
2335 | /** |
---|
2336 | * Rebuild the theme registry and all the caches. |
---|
2337 | * needed to pick up changes created by updated Views API |
---|
2338 | * and other changes to Views definitions. |
---|
2339 | */ |
---|
2340 | function date_api_views_clear() { |
---|
2341 | if (db_table_exists('cache_content')) { |
---|
2342 | db_query('DELETE FROM {cache_content}'); |
---|
2343 | } |
---|
2344 | if (db_table_exists('cache_views')) { |
---|
2345 | db_query('DELETE FROM {cache_views}'); |
---|
2346 | } |
---|
2347 | if (db_table_exists('views_object_cache')) { |
---|
2348 | db_query('DELETE FROM {views_object_cache}'); |
---|
2349 | } |
---|
2350 | db_query("DELETE FROM {cache} where cid LIKE 'theme_registry%'"); |
---|
2351 | } |
---|
2352 | |
---|
2353 | /** |
---|
2354 | * Embed a view using a PHP snippet. |
---|
2355 | * |
---|
2356 | * This function is meant to be called from PHP snippets, should one wish to |
---|
2357 | * embed a view in a node or something. It's meant to provide the simplest |
---|
2358 | * solution and doesn't really offer a lot of options, but breaking the function |
---|
2359 | * apart is pretty easy, and this provides a worthwhile guide to doing so. |
---|
2360 | * |
---|
2361 | * Note that this function does NOT display the title of the view. If you want |
---|
2362 | * to do that, you will need to do what this function does manually, by |
---|
2363 | * loading the view, getting the preview and then getting $view->get_title(). |
---|
2364 | * |
---|
2365 | * @param $name |
---|
2366 | * The name of the view to embed. |
---|
2367 | * |
---|
2368 | * @param $display_id |
---|
2369 | * 'calendar_1' will display the calendar page, |
---|
2370 | * 'calendar_block_1' will display the calendar block. |
---|
2371 | * |
---|
2372 | * @param $settings |
---|
2373 | * an array of view settings to use to override view default values; |
---|
2374 | * |
---|
2375 | * Include a setting for 'block_identifier, the identifier to use |
---|
2376 | * for this embedded view. All embedded views that use the same |
---|
2377 | * identifier will move together, or provide different identifiers |
---|
2378 | * to keep them independent. The identifier will be used in the url |
---|
2379 | * as a querystring, like: node/27?mini=calendar/2008-10. |
---|
2380 | * |
---|
2381 | * @param ... |
---|
2382 | * Any additional parameters will be passed as arguments. |
---|
2383 | */ |
---|
2384 | function date_embed_view($name, $display_id = 'default', $settings = array(), $args = array()) { |
---|
2385 | $view = views_get_view($name); |
---|
2386 | if (!$view) { |
---|
2387 | return; |
---|
2388 | } |
---|
2389 | if (!empty($settings)) { |
---|
2390 | foreach ($settings as $key => $setting) { |
---|
2391 | $view->$key = $setting; |
---|
2392 | } |
---|
2393 | } |
---|
2394 | if (!isset($view->date_info->block_identifier)) { |
---|
2395 | $view->date_info->block_identifier = 'mini'; |
---|
2396 | } |
---|
2397 | return $view->preview($display_id, $args); |
---|
2398 | } |
---|
2399 | |
---|
2400 | /** |
---|
2401 | * Figure out the URL of the date view we're currently looking at, |
---|
2402 | * adapted to various date types or specific date arguments. |
---|
2403 | * |
---|
2404 | * @param $date_type |
---|
2405 | * - if not empty, return the url of a specific date type. |
---|
2406 | * @param $date_arg |
---|
2407 | * - if not empty, return the url for a view with a specific date argument. |
---|
2408 | * @param $force_view_url |
---|
2409 | * - always use the view url, even if embedded. |
---|
2410 | * @return |
---|
2411 | * return the requested view url. |
---|
2412 | */ |
---|
2413 | function date_real_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE) { |
---|
2414 | $args = $view->args; |
---|
2415 | $pos = $view->date_info->date_arg_pos; |
---|
2416 | |
---|
2417 | // The View arguments array is indexed numerically but is not necessarily |
---|
2418 | // in numerical order. Sort the arguments to ensure the correct order. |
---|
2419 | ksort($args); |
---|
2420 | |
---|
2421 | // If there are empty arguments before the date argument, |
---|
2422 | // pad them with the wildcard so the date argument will be in |
---|
2423 | // the right position. |
---|
2424 | if (count($args) < $pos) { |
---|
2425 | foreach ($view->argument as $name => $argument) { |
---|
2426 | if ($argument->position == $pos) { |
---|
2427 | break; |
---|
2428 | } |
---|
2429 | $args[] = $argument->options['wildcard']; |
---|
2430 | } |
---|
2431 | } |
---|
2432 | |
---|
2433 | if (!empty($date_type)) { |
---|
2434 | switch ($date_type) { |
---|
2435 | case 'year': |
---|
2436 | $args[$pos] = date_pad($view->date_info->year, 4); |
---|
2437 | break; |
---|
2438 | case 'week': |
---|
2439 | $args[$pos] = date_pad($view->date_info->year, 4) .'-W'. date_pad($view->date_info->week); |
---|
2440 | break; |
---|
2441 | case 'day': |
---|
2442 | $args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month) .'-'. date_pad($view->date_info->day); |
---|
2443 | break; |
---|
2444 | default: |
---|
2445 | $args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month); |
---|
2446 | break; |
---|
2447 | } |
---|
2448 | } |
---|
2449 | elseif (!empty($date_arg)) { |
---|
2450 | $args[$pos] = $date_arg; |
---|
2451 | } |
---|
2452 | else { |
---|
2453 | $args = $view->args; |
---|
2454 | } |
---|
2455 | // Is this an embedded or a block view? |
---|
2456 | if (!$force_view_url && |
---|
2457 | (!empty($view->preview) || !empty($view->date_info->block_identifier))) { |
---|
2458 | $url = $view->get_url($args); |
---|
2459 | $key = date_block_identifier($view); |
---|
2460 | if (!empty($key)) { |
---|
2461 | return url($_GET['q'], array( |
---|
2462 | 'query' => date_querystring($view, array($key => $url)), |
---|
2463 | 'absolute' => TRUE)); |
---|
2464 | } |
---|
2465 | } |
---|
2466 | // Normal views may need querystrings appended to them |
---|
2467 | // if they use exposed filters. |
---|
2468 | return url($view->get_url($args), array( |
---|
2469 | 'query' => date_querystring($view), |
---|
2470 | 'absolute' => TRUE)); |
---|
2471 | } |
---|
2472 | |
---|
2473 | /** |
---|
2474 | * Pick up filter and sort info from url. |
---|
2475 | */ |
---|
2476 | function date_querystring($view, $extra_params = array()) { |
---|
2477 | $query_params = array_merge($_GET, $extra_params); |
---|
2478 | // Allow NULL params to be removed from the query string. |
---|
2479 | foreach ($extra_params AS $key => $value) { |
---|
2480 | if (!isset($value)) { |
---|
2481 | unset($query_params[$key]); |
---|
2482 | } |
---|
2483 | } |
---|
2484 | // Filter the special "q" and "view" variables out of the query string. |
---|
2485 | $exclude = array('q'); |
---|
2486 | $query = drupal_query_string_encode($query_params, $exclude); |
---|
2487 | // To prevent an empty query string from adding a "?" on to the end of a URL, |
---|
2488 | // we return NULL. |
---|
2489 | return !empty($query) ? $query : NULL; |
---|
2490 | } |
---|
2491 | |
---|
2492 | function date_block_identifier($view) { |
---|
2493 | if (!empty($view->block_identifier)) { |
---|
2494 | return $view->block_identifier; |
---|
2495 | } |
---|
2496 | return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL; |
---|
2497 | } |
---|
2498 | |
---|
2499 | /** |
---|
2500 | * Implementation of hook_form_alter(). |
---|
2501 | * |
---|
2502 | * Add new submit handler for system_modules form. |
---|
2503 | */ |
---|
2504 | function date_api_form_system_modules_alter(&$form, $form_state, $form_id = 'system_modules') { |
---|
2505 | $form['#submit'][] = 'date_api_system_modules_submit'; |
---|
2506 | } |
---|
2507 | |
---|
2508 | /** |
---|
2509 | * Rebuild list of date formats when modules list is saved. |
---|
2510 | */ |
---|
2511 | function date_api_system_modules_submit($form, &$form_state) { |
---|
2512 | date_formats_rebuild(); |
---|
2513 | } |
---|
2514 | |
---|
2515 | /** |
---|
2516 | * Implementation of hook_form_alter(). |
---|
2517 | * |
---|
2518 | * Remove the 'date_formats' section from the 'admin/settings/date-time' page. |
---|
2519 | * This form section is now part of the form at 'admin/settings/date-time/formats'. |
---|
2520 | * We add the formats as values to the form to avoid errors on submission |
---|
2521 | * of the form when expected values are missing in system_date_time_settings_submit(). |
---|
2522 | * |
---|
2523 | * Add a form element to configure whether or not week numbers are ISO-8601 (default: FALSE == US/UK/AUS norm). |
---|
2524 | */ |
---|
2525 | function date_api_form_system_date_time_settings_alter(&$form, $form_state, $form_id = 'system_date_time_settings') { |
---|
2526 | include_once(drupal_get_path('module', 'date_api') .'/date_api.admin.inc'); |
---|
2527 | $formats_form = date_api_date_formats_form($form_state); |
---|
2528 | $form['date_formats'] = $formats_form['date_formats']; |
---|
2529 | foreach ($form['date_formats'] as $key => $value) { |
---|
2530 | if (drupal_substr($key, 0, 1) != '#') { |
---|
2531 | $form['date_formats'][$key]['#type'] = 'value'; |
---|
2532 | } |
---|
2533 | else { |
---|
2534 | unset($form['date_formats'][$key]); |
---|
2535 | } |
---|
2536 | } |
---|
2537 | $form['locale']['date_api_use_iso8601'] = array( |
---|
2538 | '#type' => 'checkbox', |
---|
2539 | '#title' => t('Use ISO-8601 week numbers'), |
---|
2540 | '#default_value' => variable_get('date_api_use_iso8601', FALSE), |
---|
2541 | '#description' => t('IMPORTANT! If checked, First day of week MUST be set to Monday'), |
---|
2542 | ); |
---|
2543 | $form['#validate'][] = 'date_api_form_system_settings_validate'; |
---|
2544 | } |
---|
2545 | |
---|
2546 | /** |
---|
2547 | * Validate that the option to use ISO weeks matches first day of week choice. |
---|
2548 | */ |
---|
2549 | function date_api_form_system_settings_validate(&$form, &$form_state) { |
---|
2550 | $form_values = $form_state['values']; |
---|
2551 | if ($form_values['date_api_use_iso8601'] && $form_values['date_first_day'] != 1) { |
---|
2552 | form_set_error('date_first_day', t('When using ISO-8601 week numbers, the first day of the week must be set to Monday.')); |
---|
2553 | } |
---|
2554 | } |
---|
2555 | |
---|
2556 | /** |
---|
2557 | * Helper function; add system.js and javascript settings. |
---|
2558 | */ |
---|
2559 | function date_api_add_system_javascript() { |
---|
2560 | drupal_add_js(drupal_get_path('module', 'date_api') .'/date_api.js', 'module'); |
---|
2561 | drupal_add_js(array('dateDateTime' => array('lookup' => url('admin/settings/date-time/formats/lookup'))), 'setting'); |
---|
2562 | } |
---|
2563 | |
---|
2564 | /** |
---|
2565 | * Return the date for a given format string via Ajax. |
---|
2566 | */ |
---|
2567 | function date_api_date_time_lookup() { |
---|
2568 | $result = date_format_date(date_now(), 'custom', $_GET['format']); |
---|
2569 | echo drupal_to_js($result); |
---|
2570 | exit; |
---|
2571 | } |
---|
2572 | |
---|
2573 | /* |
---|
2574 | * Test validity of a date range string. |
---|
2575 | */ |
---|
2576 | function date_range_valid($string) { |
---|
2577 | $matches = preg_match('@^(\-[0-9]+|[0-9]{4}):([\+|\-][0-9]+|[0-9]{4})$@', $string); |
---|
2578 | return $matches < 1 ? FALSE : TRUE; |
---|
2579 | } |
---|
2580 | |
---|
2581 | /** |
---|
2582 | * Split a string like -3:+3 or 2001:2010 into |
---|
2583 | * an array of min and max years. |
---|
2584 | * |
---|
2585 | * Center the range around the current year, if any, but expand it far |
---|
2586 | * enough so it will pick up the year value in the field in case |
---|
2587 | * the value in the field is outside the initial range. |
---|
2588 | */ |
---|
2589 | function date_range_years($string, $date = NULL) { |
---|
2590 | $this_year = date_format(date_now(), 'Y'); |
---|
2591 | list($min_year, $max_year) = explode(':', $string); |
---|
2592 | |
---|
2593 | // Valid patterns would be -5:+5, 0:+1, 2008:2010. |
---|
2594 | $plus_pattern = '@[\+|\-][0-9]{1,4}@'; |
---|
2595 | $year_pattern = '@[0-9]{4}@'; |
---|
2596 | if (!preg_match($year_pattern, $min_year, $matches)) { |
---|
2597 | if (preg_match($plus_pattern, $min_year, $matches)) { |
---|
2598 | $min_year = $this_year + $matches[0]; |
---|
2599 | } |
---|
2600 | else { |
---|
2601 | $min_year = $this_year; |
---|
2602 | } |
---|
2603 | } |
---|
2604 | if (!preg_match($year_pattern, $max_year, $matches)) { |
---|
2605 | if (preg_match($plus_pattern, $max_year, $matches)) { |
---|
2606 | $max_year = $this_year + $matches[0]; |
---|
2607 | } |
---|
2608 | else { |
---|
2609 | $max_year = $this_year; |
---|
2610 | } |
---|
2611 | } |
---|
2612 | // We expect the $min year to be less than the $max year. |
---|
2613 | // Some custom values for -99:+99 might not obey that. |
---|
2614 | if ($min_year > $max_year) { |
---|
2615 | $temp = $max_year; |
---|
2616 | $max_year = $min_year; |
---|
2617 | $min_year = $temp; |
---|
2618 | } |
---|
2619 | // If there is a current value, stretch the range to include it. |
---|
2620 | $value_year = is_object($date) ? date_format($date, 'Y') : ''; |
---|
2621 | if (!empty($value_year)) { |
---|
2622 | $min_year = min($value_year, $min_year); |
---|
2623 | $max_year = max($value_year, $max_year); |
---|
2624 | } |
---|
2625 | return array($min_year, $max_year); |
---|
2626 | } |
---|
2627 | |
---|
2628 | /** |
---|
2629 | * Convert a min and max year into a string like '-3:+1'. |
---|
2630 | * |
---|
2631 | * @param unknown_type $years |
---|
2632 | * @return unknown |
---|
2633 | */ |
---|
2634 | function date_range_string($years) { |
---|
2635 | $this_year = date_format(date_now(), 'Y'); |
---|
2636 | if ($years[0] < $this_year) { |
---|
2637 | $min = '-'. ($this_year - $years[0]); |
---|
2638 | } |
---|
2639 | else { |
---|
2640 | $min = '+'. ($years[0] - $this_year); |
---|
2641 | } |
---|
2642 | if ($years[1] < $this_year) { |
---|
2643 | $max = '-'. ($this_year - $years[1]); |
---|
2644 | } |
---|
2645 | else { |
---|
2646 | $max = '+'. ($years[1] - $this_year); |
---|
2647 | } |
---|
2648 | return $min .':'. $max; |
---|
2649 | } |
---|
2650 | |
---|
2651 | /** |
---|
2652 | * Implement hook_date_api_tables(). |
---|
2653 | */ |
---|
2654 | function date_api_date_api_tables() { |
---|
2655 | return array('node', 'comments', 'users'); |
---|
2656 | } |
---|
2657 | |
---|
2658 | /** |
---|
2659 | * Determine if a from/to date combination qualify as 'All day'. |
---|
2660 | * |
---|
2661 | * @param object $date1, a string date in datetime format for the 'from' date. |
---|
2662 | * @param object $date2, a string date in datetime format for the 'to' date. |
---|
2663 | * @return TRUE or FALSE. |
---|
2664 | */ |
---|
2665 | function date_is_all_day($string1, $string2, $granularity = 'second', $increment = 1) { |
---|
2666 | if (empty($string1) || empty($string2)) { |
---|
2667 | return FALSE; |
---|
2668 | } |
---|
2669 | elseif (!in_array($granularity, array('hour', 'minute', 'second'))) { |
---|
2670 | return FALSE; |
---|
2671 | } |
---|
2672 | |
---|
2673 | preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string1, $matches); |
---|
2674 | $count = count($matches); |
---|
2675 | $date1 = $count > 1 ? $matches[1] : ''; |
---|
2676 | $time1 = $count > 2 ? $matches[2] : ''; |
---|
2677 | $hour1 = $count > 3 ? intval($matches[3]) : 0; |
---|
2678 | $min1 = $count > 4 ? intval($matches[4]) : 0; |
---|
2679 | $sec1 = $count > 5 ? intval($matches[5]) : 0; |
---|
2680 | preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string2, $matches); |
---|
2681 | $count = count($matches); |
---|
2682 | $date2 = $count > 1 ? $matches[1] : ''; |
---|
2683 | $time2 = $count > 2 ? $matches[2] : ''; |
---|
2684 | $hour2 = $count > 3 ? intval($matches[3]) : 0; |
---|
2685 | $min2 = $count > 4 ? intval($matches[4]) : 0; |
---|
2686 | $sec2 = $count > 5 ? intval($matches[5]) : 0; |
---|
2687 | if (empty($date1) || empty($date2)) { |
---|
2688 | return FALSE; |
---|
2689 | } |
---|
2690 | if (empty($time1) || empty($time2)) { |
---|
2691 | return FALSE; |
---|
2692 | } |
---|
2693 | |
---|
2694 | $tmp = date_seconds('s', TRUE, $increment); |
---|
2695 | $max_seconds = intval(array_pop($tmp)); |
---|
2696 | $tmp = date_minutes('i', TRUE, $increment); |
---|
2697 | $max_minutes = intval(array_pop($tmp)); |
---|
2698 | |
---|
2699 | // See if minutes and seconds are the maximum allowed for an increment or the |
---|
2700 | // maximum possible (59), or 0. |
---|
2701 | switch ($granularity) { |
---|
2702 | case 'second': |
---|
2703 | $min_match = $time1 == '00:00:00' |
---|
2704 | || ($hour1 == 0 && $min1 == 0 && $sec1 == 0); |
---|
2705 | $max_match = $time2 == '00:00:00' |
---|
2706 | || ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59))) |
---|
2707 | || ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0); |
---|
2708 | break; |
---|
2709 | case 'minute': |
---|
2710 | $min_match = $time1 == '00:00:00' |
---|
2711 | || ($hour1 == 0 && $min1 == 0); |
---|
2712 | $max_match = $time2 == '00:00:00' |
---|
2713 | || ($hour2 == 23 && in_array($min2, array($max_minutes, 59))) |
---|
2714 | || ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0); |
---|
2715 | break; |
---|
2716 | case 'hour': |
---|
2717 | $min_match = $time1 == '00:00:00' |
---|
2718 | || ($hour1 == 0); |
---|
2719 | $max_match = $time2 == '00:00:00' |
---|
2720 | || ($hour2 == 23) |
---|
2721 | || ($hour1 == 0 && $hour2 == 0); |
---|
2722 | break; |
---|
2723 | default: |
---|
2724 | $min_match = TRUE; |
---|
2725 | $max_match = FALSE; |
---|
2726 | } |
---|
2727 | |
---|
2728 | if ($min_match && $max_match) { |
---|
2729 | return TRUE; |
---|
2730 | } |
---|
2731 | |
---|
2732 | return FALSE; |
---|
2733 | } |
---|
2734 | |
---|
2735 | |
---|
2736 | /** |
---|
2737 | * Helper function to round minutes and seconds to requested value. |
---|
2738 | */ |
---|
2739 | function date_increment_round(&$date, $increment) { |
---|
2740 | // Round minutes and seconds, if necessary. |
---|
2741 | if (is_object($date) && $increment > 1) { |
---|
2742 | $day = intval(date_format($date, 'j')); |
---|
2743 | $hour = intval(date_format($date, 'H')); |
---|
2744 | $second = intval(round(intval(date_format($date, 's')) / $increment) * $increment); |
---|
2745 | $minute = intval(date_format($date, 'i')); |
---|
2746 | if ($second == 60) { |
---|
2747 | $minute += 1; |
---|
2748 | $second = 0; |
---|
2749 | } |
---|
2750 | $minute = intval(round($minute / $increment) * $increment); |
---|
2751 | if ($minute == 60) { |
---|
2752 | $hour += 1; |
---|
2753 | $minute = 0; |
---|
2754 | } |
---|
2755 | date_time_set($date, $hour, $minute, $second); |
---|
2756 | if ($hour == 24) { |
---|
2757 | $day += 1; |
---|
2758 | $hour = 0; |
---|
2759 | $year = date_format($date, 'Y'); |
---|
2760 | $month = date_format($date, 'n'); |
---|
2761 | date_date_set($date, $year, $month, $day); |
---|
2762 | } |
---|
2763 | } |
---|
2764 | return $date; |
---|
2765 | } |
---|
2766 | |
---|
2767 | /** |
---|
2768 | * Return the nested form elements for a field by name. |
---|
2769 | * This can be used either to retrieve the entire sub-element |
---|
2770 | * for a field by name, no matter how deeply nested it is within |
---|
2771 | * fieldgroups or multigroups, or to find the multiple value |
---|
2772 | * sub-elements within a field element by name (i.e. 'value' or |
---|
2773 | * 'rrule'). You can also use this function to see if an item exists |
---|
2774 | * in a form (the return will be an empty array if it does not exist). |
---|
2775 | * |
---|
2776 | * The function returns an array of results. A field will generally |
---|
2777 | * only exist once in a form but the function can also be used to |
---|
2778 | * locate all the 'value' elements within a multiple value field, |
---|
2779 | * so the result is always returned as an array of values. |
---|
2780 | * |
---|
2781 | * For example, for a field named field_custom, the following will |
---|
2782 | * pluck out the form elements for this field from the node form, |
---|
2783 | * no matter how deeply it is nested within fieldgroups or fieldsets: |
---|
2784 | * |
---|
2785 | * $elements = content_get_nested_elements($node_form, 'field_custom'); |
---|
2786 | * |
---|
2787 | * You can prefix the function with '&' to retrieve the element by |
---|
2788 | * reference to alter it directly: |
---|
2789 | * |
---|
2790 | * $elements = &content_get_nested_elements($form, 'field_custom'); |
---|
2791 | * foreach ($elements as $element) { |
---|
2792 | * $element['#after_build'][] = 'my_field_afterbuild'; |
---|
2793 | * } |
---|
2794 | * |
---|
2795 | * During the #after_build you could then do something like the |
---|
2796 | * following to alter each individual part of a multiple value field: |
---|
2797 | * |
---|
2798 | * $sub_elements = &content_get_nested_elements($element, 'value'); |
---|
2799 | * foreach ($sub_elements as $sub_element) { |
---|
2800 | * $sub_element['#element_validate'][] = 'custom_validation'; |
---|
2801 | * } |
---|
2802 | * |
---|
2803 | * @param $form |
---|
2804 | * The form array to search. |
---|
2805 | * @param $field_name |
---|
2806 | * The name or key of the form elements to return. |
---|
2807 | * @return |
---|
2808 | * An array of all matching form elements, returned by reference. |
---|
2809 | */ |
---|
2810 | function &date_get_nested_elements(&$form, $field_name) { |
---|
2811 | $elements = array(); |
---|
2812 | |
---|
2813 | foreach (element_children($form) as $key) { |
---|
2814 | if ($key === $field_name) { |
---|
2815 | $elements[] = &$form[$key]; |
---|
2816 | } |
---|
2817 | elseif (is_array($form[$key])) { |
---|
2818 | $nested_form = &$form[$key]; |
---|
2819 | if ($sub_elements = &date_get_nested_elements($nested_form, $field_name)) { |
---|
2820 | $elements = array_merge($elements, $sub_elements); |
---|
2821 | } |
---|
2822 | } |
---|
2823 | } |
---|
2824 | |
---|
2825 | return $elements; |
---|
2826 | } |
---|