Newer
Older
Karen Stevenson
committed
<?php
// $Id$
/**
* @file
* This module will make the date API available to other modules.
* Designed to provide a light but flexible assortment of functions
* and constants, with more functionality in additional files that
* are not loaded unless other modules specifically include them.
*/
/**
* Set up some constants.
*
* Includes standard date types, format strings, strict regex strings for ISO
Karen Stevenson
committed
* and DATETIME formats (seconds are optional).
*
* The loose regex will find any variety of ISO date and time, with or
* without time, with or without dashes and colons separating the elements,
* and with either a 'T' or a space separating date and time.
Karen Stevenson
committed
*/
define('DATE_ISO', 'date');
define('DATE_UNIX', 'datestamp');
define('DATE_DATETIME', 'datetime');
define('DATE_ARRAY', 'array');
define('DATE_OBJECT', 'object');
Karen Stevenson
committed
define('DATE_ICAL', 'ical');
Karen Stevenson
committed
define('DATE_FORMAT_ISO', "Y-m-d\TH:i:s");
Karen Stevenson
committed
define('DATE_FORMAT_UNIX', "U");
Karen Stevenson
committed
define('DATE_FORMAT_DATETIME', "Y-m-d H:i:s");
Karen Stevenson
committed
define('DATE_FORMAT_ICAL', "Ymd\THis");
Karen Stevenson
committed
Karen Stevenson
committed
define('DATE_REGEX_ISO', '/(\d{4})?(-(\d{2}))?(-(\d{2}))?([T\s](\d{2}))?(:(\d{2}))?(:(\d{2}))?/');
Karen Stevenson
committed
define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/');
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})?)?/');
define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/');
define('DATE_REGEX_ICAL_DATETIME', '/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?/');
Karen Stevenson
committed
Karen Stevenson
committed
/**
Karen Stevenson
committed
* Core DateTime extension module used for as many date operations as possible in this new version.
Karen Stevenson
committed
*/
Karen Stevenson
committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* Extend PHP DateTime class with granularity handling, merge functionality and
* slightly more flexible initialization parameters.
*
* This class is a Drupal independent extension of the >= PHP 5.2 DateTime
* class.
*
* @see FeedsDateTimeElement class
*/
class DateObject extends DateTime {
public $granularity = array();
protected static $allgranularity = array('year', 'month', 'day', 'hour', 'minute', 'second', 'timezone');
private $_serialized_time;
private $_serialized_timezone;
/**
* Helper function to prepare the object during serialization.
*
* We are extending a core class and core classes cannot be serialized.
*
* Ref: http://bugs.php.net/41334, http://bugs.php.net/39821
*/
public function __sleep(){
$this->_serialized_time = $this->format('c');
$this->_serialized_timezone = $this->getTimezone()->getName();
return array('_serialized_time', '_serialized_timezone');
}
/**
* Upon unserializing, we must re-build ourselves using local variables.
*/
public function __wakeup() {
$this->__construct($this->_serialized_time, new DateTimeZone($this->_serialized_timezone));
}
Karen Stevenson
committed
public function __toString() {
return $this->format(DATE_FORMAT_DATETIME) . ' '. $this->getTimeZone()->getName();
}
Karen Stevenson
committed
/**
* Overridden constructor.
*
* @param $time
* time string, flexible format including timestamp.
* @param $tz
* PHP DateTimeZone object, string or NULL allowed, defaults to site timezone.
Karen Stevenson
committed
* @param $format
* PHP date() type format for parsing. Doesn't support timezones; if you have a timezone, send NULL
* and the default constructor method will hopefully parse it.
* $format is recommended in order to use negative or large years, which php's parser fails on.
*/
public function __construct($time = 'now', $tz = NULL, $format = NULL) {
Karen Stevenson
committed
$this->timeOnly = FALSE;
$this->dateOnly = FALSE;
// Allow string timezones
if (!empty($tz) && !is_object($tz)) {
$tz = new DateTimeZone($tz);
}
// Default to the site timezone when not explicitly provided.
elseif (empty($tz)) {
$tz = date_default_timezone_object();
}
// Special handling for Unix timestamps expressed in the local timezone.
// Create a date object in UTC and convert it to the local timezone.
// Don't try to turn things like '2010' with a format of 'Y' into a timestamp.
if (is_numeric($time) && (empty($format) || $format == 'U')) {
Karen Stevenson
committed
// Assume timestamp.
$time = "@". $time;
if ($tz->getName() != 'UTC') {
$date = new DateObject($time, 'UTC');
$date->setTimezone($tz);
$time = $date->format(DATE_FORMAT_DATETIME);
$format = DATE_FORMAT_DATETIME;
}
Karen Stevenson
committed
}
Karen Stevenson
committed
if (is_array($time)) {
Karen Stevenson
committed
// Assume we were passed an indexed array.
if (empty($time['year']) && empty($time['month']) && empty($time['day'])) {
$this->timeOnly = TRUE;
Karen Stevenson
committed
}
if (empty($time['hour']) && empty($time['minute']) && empty($time['second'])) {
$this->dateOnly = TRUE;
}
Karen Stevenson
committed
$time = $this->toISO($time);
Karen Stevenson
committed
$format = DATE_FORMAT_ISO;
}
Karen Stevenson
committed
Karen Stevenson
committed
if (!empty($format)) {
Karen Stevenson
committed
$arg = self::$allgranularity;
$element = array_pop($arg);
while(!$this->parse($time, $tz, $format) && $element != 'year') {
$element = array_pop($arg);
$format = date_limit_format($format, $arg);
Karen Stevenson
committed
}
if ($element == 'year') {
Karen Stevenson
committed
return FALSE;
}
Karen Stevenson
committed
elseif (is_string($time)) {
Karen Stevenson
committed
// PHP < 5.3 doesn't like the GMT- notation for parsing timezones.
$time = str_replace("GMT-", "-", $time);
$time = str_replace("GMT+", "+", $time);
parent::__construct($time, $tz ? $tz : new DateTimeZone("UTC"));
$this->setGranularityFromTime($time, $tz);
}
Karen Stevenson
committed
// This tz was given as just an offset, which causes problems,
// or the timezone was invalid.
if (!$this->getTimezone() || !preg_match('/[a-zA-Z]/', $this->getTimezone()->getName())) {
Karen Stevenson
committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
$this->setTimezone(new DateTimeZone("UTC"));
}
}
/**
* This function will keep this object's values by default.
*/
public function merge(FeedsDateTime $other) {
$other_tz = $other->getTimezone();
$this_tz = $this->getTimezone();
// Figure out which timezone to use for combination.
$use_tz = ($this->hasGranularity('timezone') || !$other->hasGranularity('timezone')) ? $this_tz : $other_tz;
$this2 = clone $this;
$this2->setTimezone($use_tz);
$other->setTimezone($use_tz);
$val = $this2->toArray(TRUE);
$otherval = $other->toArray();
foreach (self::$allgranularity as $g) {
if ($other->hasGranularity($g) && !$this2->hasGranularity($g)) {
// The other class has a property we don't; steal it.
$this2->addGranularity($g);
$val[$g] = $otherval[$g];
}
}
$other->setTimezone($other_tz);
$this2->setDate($val['year'], $val['month'], $val['day']);
$this2->setTime($val['hour'], $val['minute'], $val['second']);
return $this2;
}
/**
* Overrides default DateTime function. Only changes output values if
* actually had time granularity. This should be used as a "converter" for
* output, to switch tzs.
*
* In order to set a timezone for a datetime that doesn't have such
* granularity, merge() it with one that does.
*/
public function setTimezone(DateTimeZone $tz, $force = FALSE) {
// PHP 5.2.6 has a fatal error when setting a date's timezone to itself.
// http://bugs.php.net/bug.php?id=45038
if (version_compare(PHP_VERSION, '5.2.7', '<') && $tz == $this->getTimezone()) {
$tz = new DateTimeZone($tz->getName());
}
if (!$this->hasTime() || !$this->hasGranularity('timezone') || $force) {
// this has no time or timezone granularity, so timezone doesn't mean much
// We set the timezone using the method, which will change the day/hour, but then we switch back
$arr = $this->toArray(TRUE);
parent::setTimezone($tz);
$this->setDate($arr['year'], $arr['month'], $arr['day']);
$this->setTime($arr['hour'], $arr['minute'], $arr['second']);
$this->addGranularity('timezone');
return;
}
parent::setTimezone($tz);
}
/**
* Overrides base format function, formats this date according to its available granularity,
Karen Stevenson
committed
* unless $force'ed not to limit to granularity.
*
* @TODO Incorporate translation into this so translated names will be provided.
Karen Stevenson
committed
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
*/
public function format($format, $force = FALSE) {
return parent::format($force ? $format : date_limit_format($format, $this->granularity));
}
/**
* Safely adds a granularity entry to the array.
*/
public function addGranularity($g) {
$this->granularity[] = $g;
$this->granularity = array_unique($this->granularity);
}
/**
* Removes a granularity entry from the array.
*/
public function removeGranularity($g) {
if ($key = array_search($g, $this->granularity)) {
unset($this->granularity[$key]);
}
}
/**
* Checks granularity array for a given entry.
* Accepts an array, in which case all items must be present (AND's the query)
*/
public function hasGranularity($g = NULL) {
if ($g === NULL) {
//just want to know if it has something valid
//means no lower granularities without higher ones
$last = TRUE;
foreach(self::$allgranularity AS $arg) {
if($arg == 'timezone') {
Karen Stevenson
committed
continue;
}
if(in_array($arg, $this->granularity) && !$last) {
Karen Stevenson
committed
return FALSE;
}
$last = in_array($arg, $this->granularity);
Karen Stevenson
committed
}
return in_array('year', $this->granularity);
}
if (is_array($g)) {
foreach($g as $gran) {
if (!in_array($gran, $this->granularity)) {
return FALSE;
}
}
return TRUE;
}
return in_array($g, $this->granularity);
}
// whether a date is valid for a given $granularity array, depending on if it's allowed to be flexible.
public function valid($granularity = NULL, $flexible = FALSE) {
return $this->hasGranularity() && (!$granularity || $flexible || $this->hasGranularity($granularity));
}
/**
* Returns whether this object has time set. Used primarily for timezone
Karen Stevenson
committed
* conversion and formatting.
Karen Stevenson
committed
*/
public function hasTime() {
return $this->hasGranularity('hour');
}
Karen Stevenson
committed
/**
* Returns whether the input values included a year.
* Useful to use pseudo date objects when we only are interested in the time.
*/
public function completeDate() {
return $this->completeDate;
}
Karen Stevenson
committed
/**
* In common usage we should not unset timezone through this.
*/
public function limitGranularity($gran) {
foreach($this->granularity AS $key => $val){
if ($val != 'timezone' && !in_array($val, $gran)) {
unset($this->granularity[$key]);
}
}
}
/**
* Protected function to find the granularity given by the arguments to the
* constructor.
*/
protected function setGranularityFromTime($time, $tz) {
$this->granularity = array();
$temp = date_parse($time);
// Special case for "now"
if ($time == 'now') {
$this->granularity = array('year', 'month', 'day', 'hour', 'minute', 'second');
Karen Stevenson
committed
}
else {
Karen Stevenson
committed
// This PHP date_parse() method currently doesn't have resolution down to seconds, so if
// there is some time, all will be set.
foreach (self::$allgranularity AS $g) {
if ((isset($temp[$g]) && is_numeric($temp[$g])) || ($g == 'timezone' && (isset($temp['zone_type']) && $temp['zone_type'] > 0))) {
$this->granularity[] = $g;
}
}
}
if ($tz) {
$this->addGranularity('timezone');
}
}
Karen Stevenson
committed
Karen Stevenson
committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
protected function parse($date, $tz, $format) {
$array = date_format_patterns();
foreach ($array as $key => $value) {
$patterns[] = "`(^|[^\\\\\\\\])" . $key . "`"; // the letter with no preceding '\'
$repl1[] = '${1}(.)'; // a single character
$repl2[] = '${1}(' . $value . ')'; // the
}
$patterns[] = "`\\\\\\\\([" . implode(array_keys($array)) . "])`";
$repl1[] = '${1}';
$repl2[] = '${1}';
$format_regexp = preg_quote($format);
// extract letters
$regex1 = preg_replace($patterns, $repl1, $format_regexp, 1);
$regex1 = str_replace('A', '(.)', $regex1);
$regex1 = str_replace('a', '(.)', $regex1);
preg_match('`^' . $regex1 . '$`', stripslashes($format), $letters);
array_shift($letters);
// extract values
$regex2 = preg_replace($patterns, $repl2, $format_regexp, 1);
$regex2 = str_replace('A', '(AM|PM)', $regex2);
$regex2 = str_replace('a', '(am|pm)', $regex2);
preg_match('`^' . $regex2 . '$`', $date, $values);
array_shift($values);
// if we did not find all the values for the patterns in the format, abort
if (count($letters) != count($values)) {
return FALSE;
}
$this->granularity = array();
$final_date = array('hour' => 0, 'minute' => 0, 'second' => 0,
'month' => 1, 'day' => 1, 'year' => 0);
foreach ($letters as $i => $letter) {
$value = $values[$i];
switch ($letter) {
case 'd':
case 'j':
$final_date['day'] = intval($value);
$this->addGranularity('day');
break;
case 'n':
case 'm':
$final_date['month'] = intval($value);
$this->addGranularity('month');
break;
case 'F':
$array_month_long = array_flip(date_month_names());
$final_date['month'] = $array_month_long[$value];
$this->addGranularity('month');
break;
case 'M':
$array_month = array_flip(date_month_names_abbr());
$final_date['month'] = $array_month[$value];
$this->addGranularity('month');
break;
case 'Y':
$final_date['year'] = $value;
$this->addGranularity('year');
break;
case 'y':
$year = $value;
// if no century, we add the current one ("06" => "2006")
$final_date['year'] = str_pad($year, 4, substr(date("Y"), 0, 2), STR_PAD_LEFT);
$this->addGranularity('year');
break;
case 'a':
case 'A':
$ampm = strtolower($value);
break;
case 'g':
case 'h':
case 'G':
case 'H':
$final_date['hour'] = intval($value);
$this->addGranularity('hour');
break;
case 'i':
$final_date['minute'] = intval($value);
$this->addGranularity('minute');
break;
case 's':
$final_date['second'] = intval($value);
$this->addGranularity('second');
break;
case 'U':
parent::__construct($value, $tz ? $tz : new DateTimeZone("UTC"));
$this->addGranularity('year');
$this->addGranularity('month');
$this->addGranularity('day');
$this->addGranularity('hour');
$this->addGranularity('minute');
$this->addGranularity('second');
return $this;
break;
}
}
if (isset($ampm) && $ampm == 'pm' && $final_date['hour'] < 12) {
$final_date['hour'] += 12;
}
elseif (isset($ampm) && $ampm == 'am' && $final_date['hour'] == 12) {
$final_date['hour'] -= 12;
}
Karen Stevenson
committed
Karen Stevenson
committed
// Blank becomes current time, given TZ.
parent::__construct('', $tz ? $tz : new DateTimeZone("UTC"));
if ($tz) {
$this->addGranularity('timezone');
}
Karen Stevenson
committed
// SetDate expects an integer value for the year, results can
// be unexpected if we feed it something like '0100' or '0000';
$final_date['year'] = intval($final_date['year']);
// If the input value is '0000-00-00', PHP's date class will later incorrectly convert
// it to something like '-0001-11-30' if we do setDate() here. If we don't do
// setDate() here, it will default to the current date and we will lose any way to
// tell that there was no date in the orignal input values. So set a flag we can use
// later to tell that this date object was created using only time and that the date
// values are artifical.
if (empty($final_date['year']) && empty($final_date['month']) && empty($final_date['day'])) {
$this->timeOnly = TRUE;
}
else {
$this->setDate($final_date['year'], $final_date['month'], $final_date['day']);
}
if (!isset($final_date['hour']) && !isset($final_date['minute']) && !isset($final_date['second'])) {
Karen Stevenson
committed
$this->dateOnly = TRUE;
}
else {
$this->setTime($final_date['hour'], $final_date['minute'], $final_date['second']);
}
Karen Stevenson
committed
return $this;
}
/**
* Helper to return all standard date parts in an array.
* Will return '' for parts in which it lacks granularity.
*/
Karen Stevenson
committed
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
public function toArray($force = FALSE) {
return array(
'year' => $this->format('Y', $force),
'month' => $this->format('m', $force),
'day' => $this->format('d', $force),
'hour' => $this->format('H', $force),
'minute' => $this->format('i', $force),
'second' => $this->format('s', $force),
'timezone' => $this->format('e', $force),
);
}
/**
* Create an ISO date from an array of values.
*/
public function toISO($arr) {
// Add empty values to avoid errors
$arr += array('year' => '', 'month' => '', 'day' => '', 'hour' => '', 'minute' => '', 'second' => '');
$datetime = '';
if ($arr['year'] !== '') {
$datetime = date_pad(intval($arr['year']), 4);
if ($arr['month'] !== '') {
$datetime .= '-'. date_pad(intval($arr['month']));
if ($arr['day'] !== '') {
$datetime .= '-'. date_pad(intval($arr['day']));
}
}
}
if ($arr['hour'] !== '') {
$datetime .= $datetime ? 'T' : '';
$datetime .= date_pad(intval($arr['hour']));
if ($arr['minute'] !== '') {
$datetime.= ':'. date_pad(intval($arr['minute']));
if ($arr['second'] !== '') {
$datetime .= ':'. date_pad(intval($arr['second']));
}
}
}
return $datetime;
}
/**
* Force an incomplete date to be valid, for instance to add
* a valid year, month, and day if only the time has been defined.
*
* @param $date
* An array of date parts or a datetime string with values to be forced into date.
Karen Stevenson
committed
* @param $granularity
* The granularity of the date.
Karen Stevenson
committed
* @param $default
* 'current' - default to current day values.
* 'first' - default to the first possible valid value.
*/
Karen Stevenson
committed
public function setFuzzyDate($date, $granularity, $default = 'first') {
Karen Stevenson
committed
$comp = new DateObject($date, $this->getTimeZone()->getName());
$arr = $comp->toArray(TRUE);
foreach ($arr as $key => $value) {
Karen Stevenson
committed
// Set to intval here and then test that it is still an integer.
// Needed because sometimes valid integers come through as strings.
$arr[$key] = $this->forceValid($key, intval($value), $default);
Karen Stevenson
committed
}
$this->setDate($arr['year'], $arr['month'], $arr['day']);
$this->setTime($arr['hour'], $arr['minute'], $arr['second']);
Karen Stevenson
committed
}
Karen Stevenson
committed
/**
* Convert a date part into something that will produce a valid date.
*/
protected function forceValid($part, $value, $default = 'first') {
$now = date_now();
switch ($part) {
case 'year':
$fallback = $now->format('Y');
return !is_int($value) || empty($value) || $value <= 0 ? $fallback : $value;
Karen Stevenson
committed
break;
Karen Stevenson
committed
case 'month':
$fallback = $default == 'first' ? 1 : $now->format('n');
return !is_int($value) || empty($value) || $value <= 0 || $value > 12 ? $fallback : $value;
Karen Stevenson
committed
break;
Karen Stevenson
committed
case 'day':
$fallback = $default == 'first' ? 1 : $now->format('j');
return !is_int($value) || empty($value) || $value <= 0 || $value > 31 ? $fallback : $value;
Karen Stevenson
committed
break;
Karen Stevenson
committed
case 'hour':
$fallback = $default == 'first' ? 0 : $now->format('G');
return !is_int($value) || $value < 0 || $value > 23 ? $fallback : $value;
Karen Stevenson
committed
break;
Karen Stevenson
committed
case 'minute':
$fallback = $default == 'first' ? 0 : $now->format('i');
Karen Stevenson
committed
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
break;
Karen Stevenson
committed
case 'second':
$fallback = $default == 'first' ? 0 : $now->format('s');
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
Karen Stevenson
committed
break;
Karen Stevenson
committed
}
}
Karen Stevenson
committed
/**
* Compute difference between two days using a given measure.
*
* @param mixed $date1
* the starting date
* @param mixed $date2
* the ending date
* @param string $measure
* 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'
* @param string $type
* the type of dates provided:
* DATE_OBJECT, DATE_DATETIME, DATE_ISO, DATE_UNIX, DATE_ARRAY
*/
Karen Stevenson
committed
public function difference($date2_in, $measure = 'seconds') {
Karen Stevenson
committed
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
// Create cloned objects or original dates will be impacted by
// the date_modify() operations done in this code.
$date1 = clone($this);
$date2 = clone($date2_in);
if (is_object($date1) && is_object($date2)) {
$diff = date_format($date2, 'U') - date_format($date1, 'U');
if ($diff == 0 ) {
return 0;
}
elseif ($diff < 0) {
// Make sure $date1 is the smaller date.
$temp = $date2;
$date2 = $date1;
$date1 = $temp;
$diff = date_format($date2, 'U') - date_format($date1, 'U');
}
$year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y'));
switch ($measure) {
// The easy cases first.
case 'seconds':
return $diff;
case 'minutes':
return $diff / 60;
case 'hours':
return $diff / 3600;
case 'years':
return $year_diff;
case 'months':
$format = 'n';
$item1 = date_format($date1, $format);
$item2 = date_format($date2, $format);
if ($year_diff == 0) {
return intval($item2 - $item1);
}
else {
$item_diff = 12 - $item1;
$item_diff += intval(($year_diff - 1) * 12);
return $item_diff + $item2;
}
break;
case 'days':
$format = 'z';
$item1 = date_format($date1, $format);
$item2 = date_format($date2, $format);
if ($year_diff == 0) {
return intval($item2 - $item1);
}
else {
$item_diff = date_days_in_year($date1) - $item1;
for ($i = 1; $i < $year_diff; $i++) {
date_modify($date1, '+1 year');
$item_diff += date_days_in_year($date1);
}
return $item_diff + $item2;
}
break;
case 'weeks':
$week_diff = date_format($date2, 'W') - date_format($date1, 'W');
$year_diff = date_format($date2, 'o') - date_format($date1, 'o');
for ($i = 1; $i <= $year_diff; $i++) {
date_modify($date1, '+1 year');
$week_diff += date_iso_weeks_in_year($date1);
}
return $week_diff;
}
}
return NULL;
}
Karen Stevenson
committed
}
Karen Stevenson
committed
function date_db_type() {
return $GLOBALS['databases']['default']['default']['driver'];
}
Karen Stevenson
committed
/**
* Helper function for getting the format string for a date type.
*/
function date_type_format($type) {
switch ($type) {
case DATE_ISO:
return DATE_FORMAT_ISO;
case DATE_UNIX:
return DATE_FORMAT_UNIX;
case DATE_DATETIME:
return DATE_FORMAT_DATETIME;
Karen Stevenson
committed
case DATE_ICAL:
return DATE_FORMAT_ICAL;
Karen Stevenson
committed
}
}
Karen Stevenson
committed
/**
* Implement hook_init().
*/
function date_api_init() {
drupal_add_css(drupal_get_path('module', 'date_api') . '/date.css', array('weight' => CSS_THEME));
}
Karen Stevenson
committed
/**
* An untranslated array of month names
*
* Needed for css, translation functions, strtotime(), and other places
* that use the English versions of these words.
*
* @return
* an array of month names
*/
function date_month_names_untranslated() {
static $month_names;
if (empty($month_names)) {
$month_names = array(1 => 'January', 2 => 'February', 3 => 'March',
4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July',
8 => 'August', 9 => 'September', 10 => 'October',
11 => 'November', 12 => 'December');
}
return $month_names;
}
/**
* A translated array of month names
*
* @param $required
Karen Stevenson
committed
* If not required, will include a blank value at the beginning of the list.
Karen Stevenson
committed
* @return
* an array of month names
*/
function date_month_names($required = FALSE) {
Karen Stevenson
committed
foreach (date_month_names_untranslated() as $key => $month) {
$month_names[$key] = t($month, array(), array('context' => 'month_name'));
Karen Stevenson
committed
}
Karen Stevenson
committed
$none = array('' => '');
Karen Stevenson
committed
return !$required ? $none + $month_names : $month_names;
}
/**
* A translated array of month name abbreviations
*
* @param $required
Karen Stevenson
committed
* If not required, will include a blank value at the beginning of the list.
Karen Stevenson
committed
* @return
* an array of month abbreviations
*/
Karen Stevenson
committed
function date_month_names_abbr($required = FALSE, $length = 3) {
Karen Stevenson
committed
foreach (date_month_names_untranslated() as $key => $month) {
$month_names[$key] = t(substr($month, 0, $length), array(), array('context' => 'month_abbr'));
Karen Stevenson
committed
}
Karen Stevenson
committed
$none = array('' => '');
Karen Stevenson
committed
return !$required ? $none + $month_names : $month_names;
}
/**
* An untranslated array of week days
*
* Needed for css, translation functions, strtotime(), and other places
* that use the English versions of these words.
*
* @return
* an array of week day names
*/
function date_week_days_untranslated($refresh = TRUE) {
Karen Stevenson
committed
static $weekdays;
Karen Stevenson
committed
if ($refresh || empty($weekdays)) {
$weekdays = array(0 => 'Sunday', 1 => 'Monday', 2 => 'Tuesday',
3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday',
6 => 'Saturday');
}
return $weekdays;
}
/**
* A translated array of week days
*
* @param $required
Karen Stevenson
committed
* If not required, will include a blank value at the beginning of the array.
Karen Stevenson
committed
* @return
* an array of week day names
*/
function date_week_days($required = FALSE, $refresh = TRUE) {
$weekdays = array();
foreach (date_week_days_untranslated() as $key => $day) {
$weekdays[$key] = t($day, array(), array('context' => 'day_name'));
Karen Stevenson
committed
}
Karen Stevenson
committed
$none = array('' => '');
Karen Stevenson
committed
return !$required ? $none + $weekdays : $weekdays;
}
/**
* An translated array of week day abbreviations.
*
* @param $required
Karen Stevenson
committed
* If not required, will include a blank value at the beginning of the array.
Karen Stevenson
committed
* @return
* an array of week day abbreviations
*/
Karen Stevenson
committed
function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
$weekdays = array();
switch ($length) {
case 1:
$context = 'day_abbr1';
break;
case 2:
$context = 'day_abbr2';
break;
default:
$context = 'day_abbr';
break;
}
foreach (date_week_days_untranslated() as $key => $day) {
Karen Stevenson
committed
$weekdays[$key] = t(substr($day, 0, $length), array(), array('context' => $context));
Karen Stevenson
committed
}
Karen Stevenson
committed
$none = array('' => '');
Karen Stevenson
committed
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
return !$required ? $none + $weekdays : $weekdays;
}
/**
* Order weekdays
* Correct weekdays array so first day in array matches the first day of
* the week. Use to create things like calendar headers.
*
* @param array $weekdays
* @return array
*/
function date_week_days_ordered($weekdays) {
if (variable_get('date_first_day', 1) > 0) {
for ($i = 1; $i <= variable_get('date_first_day', 1); $i++) {
$last = array_shift($weekdays);
array_push($weekdays, $last);
}
}
return $weekdays;
}
/**
* An array of years.
*
* @param int $min
* the minimum year in the array
* @param int $max
* the maximum year in the array
* @param $required
Karen Stevenson
committed
* If not required, will include a blank value at the beginning of the array.
Karen Stevenson
committed
* @return
* an array of years in the selected range
*/
Karen Stevenson
committed
function date_years($min = 0, $max = 0, $required = FALSE) {
// Have to be sure $min and $max are valid values;
if (empty($min)) $min = intval(date('Y', REQUEST_TIME) - 3);
if (empty($max)) $max = intval(date('Y', REQUEST_TIME) + 3);
Karen Stevenson
committed
$none = array(0 => '');
Karen Stevenson
committed
return !$required ? $none + drupal_map_assoc(range($min, $max)) : drupal_map_assoc(range($min, $max));
Karen Stevenson
committed
}
/**
* An array of days.
*
* @param $required
Karen Stevenson
committed
* If not required, returned array will include a blank value.
Karen Stevenson
committed
* @param integer $month (optional)
* @param integer $year (optional)
* @return
* an array of days for the selected month.
*/
function date_days($required = FALSE, $month = NULL, $year = NULL) {
Karen Stevenson
committed
// If we have a month and year, find the right last day of the month.
if (!empty($month) && !empty($year)) {
Karen Stevenson
committed
$date = new DateObject($year . '-' . $month . '-01 00:00:00', 'UTC');
$max = $date->format('t');
Karen Stevenson
committed
}
// If there is no month and year given, default to 31.
Karen Stevenson
committed
if (empty($max)) $max = 31;
$none = array(0 => '');
return !$required ? $none + drupal_map_assoc(range(1, $max)) : drupal_map_assoc(range(1, $max));
}
/**
* An array of hours.
*
* @param string $format
* @param $required
Karen Stevenson
committed
* If not required, returned array will include a blank value.
Karen Stevenson
committed
* @return
* an array of hours in the selected format.
*/
function date_hours($format = 'H', $required = FALSE) {
$hours = array();
if ($format == 'h' || $format == 'g') {
$min = 1;
$max = 12;
}
else {
$min = 0;
$max = 23;
}
for ($i = $min; $i <= $max; $i++) {
$hours[$i] = $i < 10 && ($format == 'H' || $format == 'h') ? "0$i" : $i;
Karen Stevenson
committed
}
$none = array('' => '');
return !$required ? $none + $hours : $hours;
}
/**
* An array of minutes.
*
* @param string $format
* @param $required
Karen Stevenson
committed
* If not required, returned array will include a blank value.
Karen Stevenson
committed
* @return
* an array of minutes in the selected format.
*/
function date_minutes($format = 'i', $required = FALSE, $increment = 1) {
$minutes = array();
Karen Stevenson
committed
// Have to be sure $increment has a value so we don't loop endlessly;
if (empty($increment)) $increment = 1;
Karen Stevenson
committed
for ($i = 0; $i < 60; $i += $increment) {
$minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i;
}
$none = array('' => '');
return !$required ? $none + $minutes : $minutes;
}
/**
* An array of seconds.
*
* @param string $format
* @param $required
Karen Stevenson
committed
* If not required, returned array will include a blank value.
Karen Stevenson
committed
* @return array an array of seconds in the selected format.
*/
function date_seconds($format = 's', $required = FALSE, $increment = 1) {
$seconds = array();
Karen Stevenson
committed
// Have to be sure $increment has a value so we don't loop endlessly;
if (empty($increment)) $increment = 1;
Karen Stevenson
committed
for ($i = 0; $i < 60; $i += $increment) {
$seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i;
}
$none = array('' => '');
return !$required ? $none + $seconds : $seconds;
}
/**
* An array of am and pm options.
* @param $required
Karen Stevenson
committed
* If not required, returned array will include a blank value.
Karen Stevenson
committed
* @return array an array of am pm options.
*/
function date_ampm($required = FALSE) {
$none = array('' => '');
$ampm = array('am' => t('am', array(), array('context' => 'ampm')), 'pm' => t('pm', array(), array('context' => 'ampm')));
Karen Stevenson
committed
return !$required ? $none + $ampm : $ampm;
}
/**
Karen Stevenson
committed
* Array of regex replacement strings for date format elements.
Karen Stevenson
committed
* Used to allow input in custom formats. Based on work done for
* the Date module by Yves Chedemois (yched).
*
* @return array of date() format letters and their regex equivalents.
*/
function date_format_patterns($strict = FALSE) {
Karen Stevenson
committed
return array(
'd' => '\d{' . ($strict ? '2' : '1,2') . '}',
'm' => '\d{' . ($strict ? '2' : '1,2') . '}',
'h' => '\d{' . ($strict ? '2' : '1,2') . '}',
'H' => '\d{' . ($strict ? '2' : '1,2') . '}',
'i' => '\d{' . ($strict ? '2' : '1,2') . '}',
's' => '\d{' . ($strict ? '2' : '1,2') . '}',
'j' => '\d{1,2}', 'N' => '\d', 'S' => '\w{2}',
'w' => '\d', 'z' => '\d{1,3}', 'W' => '\d{1,2}',
Karen Stevenson
committed
'n' => '\d{1,2}', 't' => '\d{2}', 'L' => '\d', 'o' => '\d{4}',
Karen Stevenson
committed
'Y' => '-?\d{1,6}', 'y' => '\d{2}', 'B' => '\d{3}', 'g' => '\d{1,2}',
'G' => '\d{1,2}', 'e' => '\w*', 'I' => '\d', 'T' => '\w*',
'U' => '\d*', 'z' => '[+-]?\d*', 'O' => '[+-]?\d{4}',
Karen Stevenson
committed
//Using S instead of w and 3 as well as 4 to pick up non-ASCII chars like German umlaute
'D' => '\S{3,4}', 'l' => '\S*', 'M' => '\S{3,4}', 'F' => '\S*',
'P' => '[+-]?\d{2}\:\d{2}',
Karen Stevenson
committed
'O' => '[+-]\d{4}',
Karen Stevenson
committed
'c' => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]?\d{2}\:\d{2})',
'r' => '(\w{3}), (\d{2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):(\d{2})([+-]?\d{4})?',
);
}
Karen Stevenson
committed
/**
Karen Stevenson
committed
* Array of granularity options and their labels
Karen Stevenson
committed
*
* @return array
*/
function date_granularity_names() {
'year' => t('Year', array(), array('context' => 'datetime')),
'month' => t('Month', array(), array('context' => 'datetime')),
'day' => t('Day', array(), array('context' => 'datetime')),
'hour' => t('Hour', array(), array('context' => 'datetime')),
'minute' => t('Minute', array(), array('context' => 'datetime')),
'second' => t('Second', array(), array('context' => 'datetime')),
Karen Stevenson
committed
);
}
Karen Stevenson
committed
/**
* Give a granularity $precision, return an array of
* all the possible granularity elements.
*/
function date_granularity_array_from_precision($precision) {
$granularity_array = array('year', 'month', 'day', 'hour', 'minute', 'second');
switch(($precision)) {
case 'year':
return array_slice($granularity_array, -6);
case 'month':
return array_slice($granularity_array, -5);
case 'day':
return array_slice($granularity_array, -4);