PageRenderTime 22ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/symphony/lib/core/class.datetimeobj.php

http://github.com/symphonycms/symphony-2
PHP | 582 lines | 235 code | 59 blank | 288 comment | 34 complexity | a9c689da854d67bcb8f9fc5818369d44 MD5 | raw file
  1<?php
  2
  3/**
  4 * @package core
  5 */
  6
  7 /**
  8  * The DateTimeObj provides static functions regarding dates in Symphony.
  9  * Symphony will set the default timezone of the system using the value from
 10  * the Configuration values. Alternatively a new settings can be set using the
 11  * `setSettings` function. Symphony parses all input dates against the Configuration
 12  * date formats by default for better support with non English dates.
 13  */
 14class DateTimeObj
 15{
 16    /**
 17     * Holds the various settings for the formats that the `DateTimeObj` should
 18     * use when parsing input dates.
 19     *
 20     * @since Symphony 2.7.0 it contains default values to prevent the case where
 21     * no settings are set
 22     * @since Symphony 2.2.4
 23     * @var array
 24     */
 25    private static $settings = array(
 26        'time_format' => 'g:i a',
 27        'date_format' => 'm/d/Y',
 28    );
 29
 30    /**
 31     * Mapping PHP to Moment date formats.
 32     */
 33    protected static $date_mappings = array(
 34        'Y/m/d' => 'YYYY/MM/DD',    // e. g. 2014/01/02
 35        'd/m/Y' => 'DD/MM/YYYY',    // e. g. 01/02/2014
 36        'm/d/Y' => 'MM/DD/YYYY',    // e. g. 01/02/2014
 37        'm/d/y' => 'MM/DD/YY',      // e. g. 01/02/14
 38        'Y-m-d' => 'YYYY-MM-DD',    // e. g. 2014-01-02
 39        'm-d-Y' => 'MM-DD-YYYY',    // e. g. 01-02-2014
 40        'm-d-y' => 'MM-DD-YY',      // e. g. 01-02-14
 41        'j.n.Y' => 'D.M.YYYY',      // e. g. 2.1.2014 - no leading zeros
 42        'j.n.y' => 'D.M.YY',        // e. g. 2.1.14 - no leading zeros
 43        'd.m.Y' => 'DD.MM.YYYY',    // e. g. 02.01.2014
 44        'd.m.y' => 'DD.MM.YYYY',    // e. g. 02.01.14
 45        'd F Y' => 'DD MMMM YYYY',  // e. g. 02 January 2014
 46        'd. F Y' => 'DD. MMMM YYYY', // e. g. 02. January 2014
 47        'd M Y' => 'DD MMM YYYY',   // e. g. 02 Jan 2014
 48        'd. M Y' => 'DD. MMM. YYYY', // e. g. 02. Jan 2014
 49        'j. F Y' => 'D. MMMM YYYY', // e. g. 2. January 2014 - no leading zeros
 50        'j. M. Y' => 'D. MMM. YYYY', // e. g. 2. Jan. 2014 - no leading zeros
 51    );
 52
 53    /**
 54     * Mapping PHP to Moment time formats.
 55     */
 56    protected static $time_mappings = array(
 57        'H:i:s' => 'HH:mm:ss',      // e. g. 20:45:32
 58        'H:i' => 'HH:mm',           // e. g. 20:45
 59        'g:i:s a' => 'h:mm:ss a',   // e. g. 8:45:32 pm
 60        'g:i a' => 'h:mm a',        // e. g. 8:45 pm
 61    );
 62
 63    /**
 64     * This function takes an array of settings for `DateTimeObj` to use when parsing
 65     * input dates. The following settings are supported, `time_format`, `date_format`,
 66     * `datetime_separator` and `timezone`. This equates to Symphony's default `region`
 67     * group set in the `Configuration` class. If any of these values are not provided
 68     * the class will fallback to existing `self::$settings` values
 69     *
 70     * @since Symphony 2.2.4
 71     * @param array $settings
 72     *  An associative array of formats for this class to use to format
 73     *  dates
 74     */
 75    public static function setSettings(array $settings = array())
 76    {
 77        // Date format
 78        if (isset($settings['date_format'])) {
 79            self::$settings['date_format'] = $settings['date_format'];
 80        }
 81
 82        // Time format
 83        if (isset($settings['time_format'])) {
 84            self::$settings['time_format'] = $settings['time_format'];
 85        }
 86
 87        // Datetime separator
 88        if (isset($settings['datetime_separator'])) {
 89            self::$settings['datetime_separator'] = $settings['datetime_separator'];
 90        } elseif (!isset(self::$settings['datetime_separator'])) {
 91            self::$settings['datetime_separator'] = ' ';
 92        }
 93
 94        // Datetime format
 95        if (isset($settings['datetime_format'])) {
 96            self::$settings['datetime_format'] = $settings['datetime_format'];
 97        } elseif (!isset(self::$settings['datetime_format'])) {
 98            self::$settings['datetime_format'] = self::$settings['date_format'] . self::$settings['datetime_separator'] . self::$settings['time_format'];
 99        }
100
101        // Timezone
102        if (isset($settings['timezone']) && !empty($settings['timezone'])) {
103            self::$settings['timezone'] = $settings['timezone'];
104            self::setDefaultTimezone($settings['timezone']);
105        } elseif (!isset(self::$settings['timezone'])) {
106            self::$settings['timezone'] = ini_get('date.timezone');
107            if (empty(self::$settings['timezone'])) {
108                self::$settings['timezone'] = 'UTC';
109            }
110        }
111    }
112
113    /**
114     * Accessor function for the settings of the DateTimeObj. Currently
115     * the available settings are `time_format`, `date_format`,
116     * `datetime_format` and `datetime_separator`. If `$name` is not
117     * provided, the entire `$settings` array is returned.
118     *
119     * @since Symphony 2.2.4
120     * @param string $name
121     * @return array|string|null
122     *  If `$name` is omitted this function returns array.
123     *  If `$name` is not set, this fucntion returns `null`
124     *  If `$name` is set, this function returns string
125     */
126    public static function getSetting($name = null)
127    {
128        if (is_null($name)) {
129            return self::$settings;
130        }
131
132        if (isset(self::$settings[$name])) {
133            return self::$settings[$name];
134        }
135
136        return null;
137    }
138
139    /**
140     * Uses PHP's date_default_timezone_set function to set the system
141     * timezone. If the timezone provided is invalid, an exception is thrown.
142     *
143     * @link http://php.net/manual/en/function.date-default-timezone-set.php
144     * @link http://www.php.net/manual/en/timezones.php
145     * @param string $timezone
146     *  A valid timezone identifier, such as UTC or Europe/Lisbon
147     * @throws Exception
148     *  If the timezone is not valid.
149     */
150    public static function setDefaultTimezone($timezone)
151    {
152        if (!@date_default_timezone_set($timezone)) {
153            throw new Exception(__('Invalid timezone %s', array($timezone)));
154        }
155    }
156
157    /**
158     * Validate a given date and time string
159     *
160     * @param string $string
161     *  A date and time string or timestamp to validate
162     * @return boolean
163     *  Returns true for valid dates, otherwise false
164     */
165    public static function validate($string)
166    {
167        try {
168            if (is_numeric($string) && (int)$string == $string) {
169                $date = new DateTime('@' . $string);
170            } else {
171                $date = self::parse($string);
172            }
173        } catch (Exception $ex) {
174            return false;
175        }
176
177        // String is empty or not a valid date
178        if (empty($string) || $date === false) {
179            return false;
180
181            // String is a valid date
182        } else {
183            return true;
184        }
185    }
186
187    /**
188     * Given a `$format`, and a `$timestamp`,
189     * return the date in the format provided. This function is a basic
190     * wrapper for PHP's DateTime object. If the `$timestamp` is omitted,
191     * the current timestamp will be used. Optionally, you pass a
192     * timezone identifier with this function to localise the output
193     *
194     * If you like to display a date in the backend, please make use
195     * of `DateTimeObj::format()` which allows date and time localization
196     *
197     * @see class.datetimeobj.php#format()
198     * @link http://www.php.net/manual/en/book.datetime.php
199     * @param string $format
200     *  A valid PHP date format
201     * @param null|string $timestamp (optional)
202     *  A unix timestamp to format. 'now' or omitting this parameter will
203     *  result in the current time being used
204     * @param string $timezone (optional)
205     *  The timezone associated with the timestamp
206     * @return string|boolean
207     *  The formatted date, of if the date could not be parsed, false.
208     */
209    public static function get($format, $timestamp = 'now', $timezone = null)
210    {
211        return self::format($timestamp, $format, false, $timezone);
212    }
213
214    /**
215     * Formats the given date and time `$string` based on the given `$format`.
216     * Optionally the result will be localized and respect a timezone differing
217     * from the system default. The default output is ISO 8601.
218     *
219     * @since Symphony 2.2.1
220     * @param string $string (optional)
221     *  A string containing date and time, defaults to the current date and time
222     * @param string $format (optional)
223     *  A valid PHP date format, defaults to ISO 8601
224     * @param boolean $localize (optional)
225     *  Localizes the output, if true, defaults to true
226     * @param string $timezone (optional)
227     *  The timezone associated with the timestamp
228     * @return string|boolean
229     *  The formatted date, or if the date could not be parsed, false.
230     */
231    public static function format($string = 'now', $format = DateTime::ISO8601, $localize = true, $timezone = null)
232    {
233        // Parse date
234        $date = self::parse($string);
235
236        if ($date === false) {
237            return false;
238        }
239
240        // Timezone
241        // If a timezone was given, apply it
242        if (!is_null($timezone)) {
243            $date->setTimezone(new DateTimeZone($timezone));
244
245            // No timezone given, apply the default timezone
246        } elseif (isset(self::$settings['timezone'])) {
247            $date->setTimezone(new DateTimeZone(self::$settings['timezone']));
248        }
249
250        // Format date
251        $date = $date->format($format);
252
253        // Localize date
254        // Convert date string from English back to the activated Language
255        if ($localize === true) {
256            $date = Lang::localizeDate($date);
257        }
258
259        // Return custom formatted date, use ISO 8601 date by default
260        return $date;
261    }
262
263    /**
264     * Parses the given string and returns a DateTime object.
265     *
266     * @since Symphony 2.3
267     * @param string $string (optional)
268     *  A string containing date and time, defaults to the current date and time
269     * @return DateTime|boolean
270     *  The DateTime object, or if the date could not be parsed, false.
271     */
272    public static function parse($string)
273    {
274
275        // Current date and time
276        if ($string == 'now' || empty($string)) {
277            $date = new DateTime();
278
279            // Timestamp
280        } elseif (is_numeric($string)) {
281            $date = new DateTime('@' . $string);
282
283            // Attempt to parse the date provided against the Symphony configuration setting
284            // in an effort to better support multilingual date formats. Should this fail
285            // this block will fallback to just passing the date to DateTime constructor,
286            // which will parse the date assuming it's in an American format.
287        } else {
288            // Standardize date
289            // Convert date string to English
290            $string = Lang::standardizeDate($string);
291
292            // PHP 5.3: Apply Symphony date format using `createFromFormat`
293            $date = DateTime::createFromFormat(self::$settings['datetime_format'], $string);
294
295            if ($date === false) {
296                $date = DateTime::createFromFormat(self::$settings['date_format'], $string);
297            }
298
299            // Handle dates that are in a different format to Symphony's config
300            // DateTime is much the same as `strtotime` and will handle relative
301            // dates.
302            if ($date === false) {
303                try {
304                    $date = new DateTime($string);
305                } catch (Exception $ex) {
306                    // Invalid date, it can't be parsed
307                    return false;
308                }
309            }
310
311            // If the date is still invalid, just return false.
312            if ($date === false || $date->format('Y') < 0) {
313                return false;
314            }
315        }
316
317        // Return custom formatted date, use ISO 8601 date by default
318        return $date;
319    }
320
321    /**
322     * A wrapper for get, this function will force the GMT timezone.
323     *
324     * @param string $format
325     *  A valid PHP date format
326     * @param null|string $timestamp (optional)
327     *  A unix timestamp to format. Omitting this parameter will
328     *  result in the current time being used
329     * @return string
330     *  The formatted date in GMT
331     */
332    public static function getGMT($format, $timestamp = 'now')
333    {
334        return self::format($timestamp, $format, false, 'GMT');
335    }
336
337    /**
338     * This functions acts as a standard way to get the zones
339     * available on the system.
340     *
341     * @since Symphony 2.3
342     * @link http://au2.php.net/manual/en/class.datetimezone.php
343     * @return array
344     */
345    public static function getZones()
346    {
347        $ref = new ReflectionClass('DateTimeZone');
348        return $ref->getConstants();
349    }
350
351    /**
352     * This functions acts as a standard way to get the timezones
353     * regardless of PHP version. It accepts a single parameter,
354     * zone, which returns the timezones associated with that 'zone'
355     *
356     * @since Symphony 2.3
357     * @link http://au2.php.net/manual/en/class.datetimezone.php
358     * @link http://au2.php.net/manual/en/datetimezone.listidentifiers.php
359     * @param string $zone
360     *  The zone for the timezones the field wants. This maps to the
361     *  DateTimeZone constants
362     * @return array
363     */
364    public static function getTimezones($zone = null)
365    {
366        return DateTimeZone::listIdentifiers(constant('DateTimeZone::' . $zone));
367    }
368
369    /**
370     * Loads all available timezones using `getTimezones()` and builds an
371     * array where timezones are grouped by their region (Europe/America etc.)
372     * The options array that is returned is designed to be used with
373     * `Widget::Select`
374     *
375     * @since Symphony 2.3
376     * @see core.DateTimeObj#getTimezones()
377     * @see core.Widget#Select()
378     * @param string $selected
379     *  A preselected timezone, defaults to null
380     * @return array
381     *  An associative array, for use with `Widget::Select`
382     */
383    public static function getTimezonesSelectOptions($selected = null)
384    {
385        $zones = self::getZones();
386        $groups = array();
387
388        foreach ($zones as $zone => $value) {
389            if ($value >= 1024) {
390                break;
391            }
392
393            $timezones = self::getTimezones($zone);
394            $options = array();
395
396            foreach ($timezones as $timezone) {
397                $tz = new DateTime('now', new DateTimeZone($timezone));
398
399                $options[] = array($timezone, ($timezone == $selected), sprintf(
400                    "%s %s",
401                    str_replace('_', ' ', substr(strrchr($timezone, '/'), 1)),
402                    $tz->format('P')
403                ));
404            }
405
406            $groups[] = array('label' => ucwords(strtolower($zone)), 'options' => $options);
407        }
408
409        return $groups;
410    }
411
412    /**
413     * Returns an array of PHP date formats Symphony supports mapped to 
414     * their Moment equivalent.
415     *
416     * @since Symphony 2.6
417     * @return array
418     */
419    public static function getDateFormatMappings()
420    {
421        return self::$date_mappings;
422    }
423
424    /**
425     * Returns an array of the date formats Symphony supports. These
426     * formats are a combination of valid PHP format tokens.
427     *
428     * @link http://au2.php.net/manual/en/function.date.php
429     * @since Symphony 2.3
430     * @return array
431     */
432    public static function getDateFormats()
433    {
434        return array_keys(self::$date_mappings);
435    }
436
437    /**
438     * Returns an array of the date formats Symphony supports. These
439     * formats are a combination of valid Moment format tokens.
440     *
441     * @link http://momentjs.com/docs/#/parsing/
442     * @since Symphony 2.6
443     * @return array
444     */
445    public static function getMomentDateFormats()
446    {
447        return array_values(self::$date_mappings);
448    }
449
450    /**
451     * Returns the Moment representation of a given PHP format token.
452     *
453     * @since Symphony 2.6
454     * @param string $format
455     *  A valid PHP date token
456     * @return string
457     */
458    public static function convertDateToMoment($format)
459    {
460        return self::$date_mappings[$format];
461    }
462
463    /**
464     * Returns the PHP representation of a given Moment format token.
465     *
466     * @since Symphony 2.6
467     * @param string $format
468     *  A valid Moment date token
469     * @return string
470     */
471    public static function convertMomentToDate($format)
472    {
473        $formats = array_flip(self::$date_mappings);
474        return $formats[$format];
475    }
476
477    /**
478     * Returns an array of the date formats Symphony supports by applying
479     * the format to the current datetime. The array returned is for use with
480     * `Widget::Select()`
481     *
482     * @since Symphony 2.3
483     * @see core.Widget#Select()
484     * @param string $selected
485     *  A preselected date format, defaults to null
486     * @return array
487     *  An associative array, for use with `Widget::Select`
488     */
489    public static function getDateFormatsSelectOptions($selected = null)
490    {
491        $formats = self::getDateFormats();
492        $options = array();
493
494        foreach ($formats as $option) {
495            $leadingZero = '';
496
497            if (strpos($option, 'j') !== false || strpos($option, 'n') !== false) {
498                $leadingZero = ' (' . __('no leading zeros') . ')';
499            }
500
501            $options[] = array($option, $option == $selected, self::format('now', $option) . $leadingZero);
502        }
503
504        return $options;
505    }
506
507    /**
508     * Returns an array of the time formats Symphony supports. These
509     * formats are a combination of valid PHP format tokens.
510     *
511     * @link http://au2.php.net/manual/en/function.date.php
512     * @since Symphony 2.3
513     * @return array
514     */
515    public static function getTimeFormats()
516    {
517        return array_keys(self::$time_mappings);
518    }
519
520    /**
521     * Returns an array of the time formats Symphony supports. These
522     * formats are a combination of valid Moment format tokens.
523     *
524     * @link http://momentjs.com/docs/#/parsing/
525     * @since Symphony 2.6
526     * @return array
527     */
528    public static function getMomentTimeFormats()
529    {
530        return array_keys(self::$time_mappings);
531    }
532
533    /**
534     * Returns the Moment time representation of a given PHP format token.
535     *
536     * @since Symphony 2.6
537     * @param string $format
538     *  A valid PHP time token
539     * @return string
540     */
541    public static function convertTimeToMoment($format)
542    {
543        return self::$time_mappings[$format];
544    }
545
546    /**
547     * Returns the PHP time representation of a given Moment format token.
548     *
549     * @since Symphony 2.6
550     * @param string $format
551     *  A valid Moment time token
552     * @return string
553     */
554    public static function convertMomentToTime($format)
555    {
556        $formats = array_flip(self::$time_mappings);
557        return $formats[$format];
558    }
559    /**
560     * Returns an array of the time formats Symphony supports by applying
561     * the format to the current datetime. The array returned is for use with
562     * `Widget::Select()`
563     *
564     * @since Symphony 2.3
565     * @see core.Widget#Select()
566     * @param string $selected
567     *  A preselected time format, defaults to null
568     * @return array
569     *  An associative array, for use with `Widget::Select`
570     */
571    public static function getTimeFormatsSelectOptions($selected = null)
572    {
573        $formats = self::getTimeFormats();
574        $options = array();
575
576        foreach ($formats as $option) {
577            $options[] = array($option, $option == $selected, self::get($option));
578        }
579
580        return $options;
581    }
582}