symphony/lib/core/class.datetimeobj.php
<?php
/**
* @package core
*/
/**
* The DateTimeObj provides static functions regarding dates in Symphony.
* Symphony will set the default timezone of the system using the value from
* the Configuration values. Alternatively a new settings can be set using the
* `setSettings` function. Symphony parses all input dates against the Configuration
* date formats by default for better support with non English dates.
*/
class DateTimeObj
{
/**
* Holds the various settings for the formats that the `DateTimeObj` should
* use when parsing input dates.
*
* @since Symphony 2.7.0 it contains default values to prevent the case where
* no settings are set
* @since Symphony 2.2.4
* @var array
*/
private static $settings = array(
'time_format' => 'g:i a',
'date_format' => 'm/d/Y',
);
/**
* Mapping PHP to Moment date formats.
*/
protected static $date_mappings = array(
'Y/m/d' => 'YYYY/MM/DD', // e. g. 2014/01/02
'd/m/Y' => 'DD/MM/YYYY', // e. g. 01/02/2014
'm/d/Y' => 'MM/DD/YYYY', // e. g. 01/02/2014
'm/d/y' => 'MM/DD/YY', // e. g. 01/02/14
'Y-m-d' => 'YYYY-MM-DD', // e. g. 2014-01-02
'm-d-Y' => 'MM-DD-YYYY', // e. g. 01-02-2014
'm-d-y' => 'MM-DD-YY', // e. g. 01-02-14
'j.n.Y' => 'D.M.YYYY', // e. g. 2.1.2014 - no leading zeros
'j.n.y' => 'D.M.YY', // e. g. 2.1.14 - no leading zeros
'd.m.Y' => 'DD.MM.YYYY', // e. g. 02.01.2014
'd.m.y' => 'DD.MM.YYYY', // e. g. 02.01.14
'd F Y' => 'DD MMMM YYYY', // e. g. 02 January 2014
'd. F Y' => 'DD. MMMM YYYY', // e. g. 02. January 2014
'd M Y' => 'DD MMM YYYY', // e. g. 02 Jan 2014
'd. M Y' => 'DD. MMM. YYYY', // e. g. 02. Jan 2014
'j. F Y' => 'D. MMMM YYYY', // e. g. 2. January 2014 - no leading zeros
'j. M. Y' => 'D. MMM. YYYY', // e. g. 2. Jan. 2014 - no leading zeros
);
/**
* Mapping PHP to Moment time formats.
*/
protected static $time_mappings = array(
'H:i:s' => 'HH:mm:ss', // e. g. 20:45:32
'H:i' => 'HH:mm', // e. g. 20:45
'g:i:s a' => 'h:mm:ss a', // e. g. 8:45:32 pm
'g:i a' => 'h:mm a', // e. g. 8:45 pm
);
/**
* This function takes an array of settings for `DateTimeObj` to use when parsing
* input dates. The following settings are supported, `time_format`, `date_format`,
* `datetime_separator` and `timezone`. This equates to Symphony's default `region`
* group set in the `Configuration` class. If any of these values are not provided
* the class will fallback to existing `self::$settings` values
*
* @since Symphony 2.2.4
* @param array $settings
* An associative array of formats for this class to use to format
* dates
*/
public static function setSettings(array $settings = array())
{
// Date format
if (isset($settings['date_format'])) {
self::$settings['date_format'] = $settings['date_format'];
}
// Time format
if (isset($settings['time_format'])) {
self::$settings['time_format'] = $settings['time_format'];
}
// Datetime separator
if (isset($settings['datetime_separator'])) {
self::$settings['datetime_separator'] = $settings['datetime_separator'];
} elseif (!isset(self::$settings['datetime_separator'])) {
self::$settings['datetime_separator'] = ' ';
}
// Datetime format
if (isset($settings['datetime_format'])) {
self::$settings['datetime_format'] = $settings['datetime_format'];
} elseif (!isset(self::$settings['datetime_format'])) {
self::$settings['datetime_format'] = self::$settings['date_format'] . self::$settings['datetime_separator'] . self::$settings['time_format'];
}
// Timezone
if (isset($settings['timezone']) && !empty($settings['timezone'])) {
self::$settings['timezone'] = $settings['timezone'];
self::setDefaultTimezone($settings['timezone']);
} elseif (!isset(self::$settings['timezone'])) {
self::$settings['timezone'] = ini_get('date.timezone');
if (empty(self::$settings['timezone'])) {
self::$settings['timezone'] = 'UTC';
}
}
}
/**
* Accessor function for the settings of the DateTimeObj. Currently
* the available settings are `time_format`, `date_format`,
* `datetime_format` and `datetime_separator`. If `$name` is not
* provided, the entire `$settings` array is returned.
*
* @since Symphony 2.2.4
* @param string $name
* @return array|string|null
* If `$name` is omitted this function returns array.
* If `$name` is not set, this fucntion returns `null`
* If `$name` is set, this function returns string
*/
public static function getSetting($name = null)
{
if (is_null($name)) {
return self::$settings;
}
if (isset(self::$settings[$name])) {
return self::$settings[$name];
}
return null;
}
/**
* Uses PHP's date_default_timezone_set function to set the system
* timezone. If the timezone provided is invalid, an exception is thrown.
*
* @link http://php.net/manual/en/function.date-default-timezone-set.php
* @link http://www.php.net/manual/en/timezones.php
* @param string $timezone
* A valid timezone identifier, such as UTC or Europe/Lisbon
* @throws Exception
* If the timezone is not valid.
*/
public static function setDefaultTimezone($timezone)
{
if (!@date_default_timezone_set($timezone)) {
throw new Exception(__('Invalid timezone %s', array($timezone)));
}
}
/**
* Validate a given date and time string
*
* @param string $string
* A date and time string or timestamp to validate
* @return boolean
* Returns true for valid dates, otherwise false
*/
public static function validate($string)
{
try {
if (is_numeric($string) && (int)$string == $string) {
$date = new DateTime('@' . $string);
} else {
$date = self::parse($string);
}
} catch (Exception $ex) {
return false;
}
// String is empty or not a valid date
if (empty($string) || $date === false) {
return false;
// String is a valid date
} else {
return true;
}
}
/**
* Given a `$format`, and a `$timestamp`,
* return the date in the format provided. This function is a basic
* wrapper for PHP's DateTime object. If the `$timestamp` is omitted,
* the current timestamp will be used. Optionally, you pass a
* timezone identifier with this function to localise the output
*
* If you like to display a date in the backend, please make use
* of `DateTimeObj::format()` which allows date and time localization
*
* @see class.datetimeobj.php#format()
* @link http://www.php.net/manual/en/book.datetime.php
* @param string $format
* A valid PHP date format
* @param null|string $timestamp (optional)
* A unix timestamp to format. 'now' or omitting this parameter will
* result in the current time being used
* @param string $timezone (optional)
* The timezone associated with the timestamp
* @return string|boolean
* The formatted date, of if the date could not be parsed, false.
*/
public static function get($format, $timestamp = 'now', $timezone = null)
{
return self::format($timestamp, $format, false, $timezone);
}
/**
* Formats the given date and time `$string` based on the given `$format`.
* Optionally the result will be localized and respect a timezone differing
* from the system default. The default output is ISO 8601.
*
* @since Symphony 2.2.1
* @param string $string (optional)
* A string containing date and time, defaults to the current date and time
* @param string $format (optional)
* A valid PHP date format, defaults to ISO 8601
* @param boolean $localize (optional)
* Localizes the output, if true, defaults to true
* @param string $timezone (optional)
* The timezone associated with the timestamp
* @return string|boolean
* The formatted date, or if the date could not be parsed, false.
*/
public static function format($string = 'now', $format = DateTime::ISO8601, $localize = true, $timezone = null)
{
// Parse date
$date = self::parse($string);
if ($date === false) {
return false;
}
// Timezone
// If a timezone was given, apply it
if (!is_null($timezone)) {
$date->setTimezone(new DateTimeZone($timezone));
// No timezone given, apply the default timezone
} elseif (isset(self::$settings['timezone'])) {
$date->setTimezone(new DateTimeZone(self::$settings['timezone']));
}
// Format date
$date = $date->format($format);
// Localize date
// Convert date string from English back to the activated Language
if ($localize === true) {
$date = Lang::localizeDate($date);
}
// Return custom formatted date, use ISO 8601 date by default
return $date;
}
/**
* Parses the given string and returns a DateTime object.
*
* @since Symphony 2.3
* @param string $string (optional)
* A string containing date and time, defaults to the current date and time
* @return DateTime|boolean
* The DateTime object, or if the date could not be parsed, false.
*/
public static function parse($string)
{
// Current date and time
if ($string == 'now' || empty($string)) {
$date = new DateTime();
// Timestamp
} elseif (is_numeric($string)) {
$date = new DateTime('@' . $string);
// Attempt to parse the date provided against the Symphony configuration setting
// in an effort to better support multilingual date formats. Should this fail
// this block will fallback to just passing the date to DateTime constructor,
// which will parse the date assuming it's in an American format.
} else {
// Standardize date
// Convert date string to English
$string = Lang::standardizeDate($string);
// PHP 5.3: Apply Symphony date format using `createFromFormat`
$date = DateTime::createFromFormat(self::$settings['datetime_format'], $string);
if ($date === false) {
$date = DateTime::createFromFormat(self::$settings['date_format'], $string);
}
// Handle dates that are in a different format to Symphony's config
// DateTime is much the same as `strtotime` and will handle relative
// dates.
if ($date === false) {
try {
$date = new DateTime($string);
} catch (Exception $ex) {
// Invalid date, it can't be parsed
return false;
}
}
// If the date is still invalid, just return false.
if ($date === false || $date->format('Y') < 0) {
return false;
}
}
// Return custom formatted date, use ISO 8601 date by default
return $date;
}
/**
* A wrapper for get, this function will force the GMT timezone.
*
* @param string $format
* A valid PHP date format
* @param null|string $timestamp (optional)
* A unix timestamp to format. Omitting this parameter will
* result in the current time being used
* @return string
* The formatted date in GMT
*/
public static function getGMT($format, $timestamp = 'now')
{
return self::format($timestamp, $format, false, 'GMT');
}
/**
* This functions acts as a standard way to get the zones
* available on the system.
*
* @since Symphony 2.3
* @link http://au2.php.net/manual/en/class.datetimezone.php
* @return array
*/
public static function getZones()
{
$ref = new ReflectionClass('DateTimeZone');
return $ref->getConstants();
}
/**
* This functions acts as a standard way to get the timezones
* regardless of PHP version. It accepts a single parameter,
* zone, which returns the timezones associated with that 'zone'
*
* @since Symphony 2.3
* @link http://au2.php.net/manual/en/class.datetimezone.php
* @link http://au2.php.net/manual/en/datetimezone.listidentifiers.php
* @param string $zone
* The zone for the timezones the field wants. This maps to the
* DateTimeZone constants
* @return array
*/
public static function getTimezones($zone = null)
{
return DateTimeZone::listIdentifiers(constant('DateTimeZone::' . $zone));
}
/**
* Loads all available timezones using `getTimezones()` and builds an
* array where timezones are grouped by their region (Europe/America etc.)
* The options array that is returned is designed to be used with
* `Widget::Select`
*
* @since Symphony 2.3
* @see core.DateTimeObj#getTimezones()
* @see core.Widget#Select()
* @param string $selected
* A preselected timezone, defaults to null
* @return array
* An associative array, for use with `Widget::Select`
*/
public static function getTimezonesSelectOptions($selected = null)
{
$zones = self::getZones();
$groups = array();
foreach ($zones as $zone => $value) {
if ($value >= 1024) {
break;
}
$timezones = self::getTimezones($zone);
$options = array();
foreach ($timezones as $timezone) {
$tz = new DateTime('now', new DateTimeZone($timezone));
$options[] = array($timezone, ($timezone == $selected), sprintf(
"%s %s",
str_replace('_', ' ', substr(strrchr($timezone, '/'), 1)),
$tz->format('P')
));
}
$groups[] = array('label' => ucwords(strtolower($zone)), 'options' => $options);
}
return $groups;
}
/**
* Returns an array of PHP date formats Symphony supports mapped to
* their Moment equivalent.
*
* @since Symphony 2.6
* @return array
*/
public static function getDateFormatMappings()
{
return self::$date_mappings;
}
/**
* Returns an array of the date formats Symphony supports. These
* formats are a combination of valid PHP format tokens.
*
* @link http://au2.php.net/manual/en/function.date.php
* @since Symphony 2.3
* @return array
*/
public static function getDateFormats()
{
return array_keys(self::$date_mappings);
}
/**
* Returns an array of the date formats Symphony supports. These
* formats are a combination of valid Moment format tokens.
*
* @link http://momentjs.com/docs/#/parsing/
* @since Symphony 2.6
* @return array
*/
public static function getMomentDateFormats()
{
return array_values(self::$date_mappings);
}
/**
* Returns the Moment representation of a given PHP format token.
*
* @since Symphony 2.6
* @param string $format
* A valid PHP date token
* @return string
*/
public static function convertDateToMoment($format)
{
return self::$date_mappings[$format];
}
/**
* Returns the PHP representation of a given Moment format token.
*
* @since Symphony 2.6
* @param string $format
* A valid Moment date token
* @return string
*/
public static function convertMomentToDate($format)
{
$formats = array_flip(self::$date_mappings);
return $formats[$format];
}
/**
* Returns an array of the date formats Symphony supports by applying
* the format to the current datetime. The array returned is for use with
* `Widget::Select()`
*
* @since Symphony 2.3
* @see core.Widget#Select()
* @param string $selected
* A preselected date format, defaults to null
* @return array
* An associative array, for use with `Widget::Select`
*/
public static function getDateFormatsSelectOptions($selected = null)
{
$formats = self::getDateFormats();
$options = array();
foreach ($formats as $option) {
$leadingZero = '';
if (strpos($option, 'j') !== false || strpos($option, 'n') !== false) {
$leadingZero = ' (' . __('no leading zeros') . ')';
}
$options[] = array($option, $option == $selected, self::format('now', $option) . $leadingZero);
}
return $options;
}
/**
* Returns an array of the time formats Symphony supports. These
* formats are a combination of valid PHP format tokens.
*
* @link http://au2.php.net/manual/en/function.date.php
* @since Symphony 2.3
* @return array
*/
public static function getTimeFormats()
{
return array_keys(self::$time_mappings);
}
/**
* Returns an array of the time formats Symphony supports. These
* formats are a combination of valid Moment format tokens.
*
* @link http://momentjs.com/docs/#/parsing/
* @since Symphony 2.6
* @return array
*/
public static function getMomentTimeFormats()
{
return array_keys(self::$time_mappings);
}
/**
* Returns the Moment time representation of a given PHP format token.
*
* @since Symphony 2.6
* @param string $format
* A valid PHP time token
* @return string
*/
public static function convertTimeToMoment($format)
{
return self::$time_mappings[$format];
}
/**
* Returns the PHP time representation of a given Moment format token.
*
* @since Symphony 2.6
* @param string $format
* A valid Moment time token
* @return string
*/
public static function convertMomentToTime($format)
{
$formats = array_flip(self::$time_mappings);
return $formats[$format];
}
/**
* Returns an array of the time formats Symphony supports by applying
* the format to the current datetime. The array returned is for use with
* `Widget::Select()`
*
* @since Symphony 2.3
* @see core.Widget#Select()
* @param string $selected
* A preselected time format, defaults to null
* @return array
* An associative array, for use with `Widget::Select`
*/
public static function getTimeFormatsSelectOptions($selected = null)
{
$formats = self::getTimeFormats();
$options = array();
foreach ($formats as $option) {
$options[] = array($option, $option == $selected, self::get($option));
}
return $options;
}
}