diff --git a/core/includes/common.inc b/core/includes/common.inc index 1980f2f..48db270 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3192,7 +3192,7 @@ function drupal_page_set_cache(Response $response, Request $request) { // Use the actual timestamp from an Expires header, if available. if ($date = $response->getExpires()) { - $date = new DrupalDateTime($date); + $date = DrupalDateTime::createFromDateTime($date); $cache->expire = $date->getTimestamp(); } diff --git a/core/lib/Drupal/Component/Datetime/DateTimePlus.php b/core/lib/Drupal/Component/Datetime/DateTimePlus.php index b00124b..cda7f3e 100644 --- a/core/lib/Drupal/Component/Datetime/DateTimePlus.php +++ b/core/lib/Drupal/Component/Datetime/DateTimePlus.php @@ -106,21 +106,85 @@ class DateTimePlus extends \DateTime { protected $errors = array(); /** - * Constructs a date object set to a requested date and timezone. + * A boolean to store whether or not the intl php extension is available. + */ + static $intlExtentionExists = NULL; + + /** + * Creates a date object from an input date object. * - * @param mixed $time - * A DateTime object, a date/time string, a unix timestamp, - * or an array of date parts, like ('year' => 2014, 'month => 4). - * Defaults to 'now'. + * @param \DateTime $datetime + * A DateTime object. + * @param array $settings + * @see __construct() + */ + public static function createFromDateTime(\DateTime $datetime, $settings = array()) { + return new static($datetime->format(static::FORMAT), $datetime->getTimezone(), $settings); + } + + /** + * Creates a date object from an array of date parts. + * + * Converts the input value into an ISO date, forcing a full ISO + * date even if some values are missing. + * + * @param array $date_parts + * An array of date parts, like ('year' => 2014, 'month => 4). * @param mixed $timezone - * PHP DateTimeZone object, string or NULL allowed. - * Defaults to NULL. + * @see __construct() + * @param array $settings + * @see __construct() + */ + public static function createFromArray(array $date_parts, $timezone = NULL, $settings = array()) { + $date_parts = static::prepareArray($date_parts, TRUE); + if (static::checkArray($date_parts)) { + // Even with validation, we can end up with a value that the + // parent class won't handle, like a year outside the range + // of -9999 to 9999, which will pass checkdate() but + // fail to construct a date object. + $iso_date = static::arrayToISO($date_parts); + return new static($iso_date, $timezone, $settings); + } + else { + throw new \Exception('The array contains invalid values.'); + } + } + + /** + * Creates a date object from timestamp input. + * + * The timezone of a timestamp is always UTC. The timezone for a + * timestamp indicates the timezone used by the format() method. + * + * @param int $timestamp + * A UNIX timestamp. + * @param mixed $timezone + * @see __construct() + * @param array $settings + * @see __construct() + */ + public static function createFromTimestamp($timestamp, $timezone = NULL, $settings = array()) { + if (!is_numeric($timestamp)) { + throw new \Exception('The timestamp must be numeric.'); + } + $datetime = new static('', $timezone, $settings); + $datetime->setTimestamp($timestamp); + return $datetime; + } + + /** + * Creates a date object from an input format. + * * @param string $format * PHP date() type format for parsing the input. This is recommended - * for specialized input with a known format. If provided the + * to use things like negative years, which php's parser fails on, or + * any other specialized input with a known format. If provided the * date will be created using the createFromFormat() method. - * Defaults to NULL. * @see http://us3.php.net/manual/en/datetime.createfromformat.php + * @param mixed $time + * @see __construct() + * @param mixed $timezone + * @see __construct() * @param array $settings * - validate_format: (optional) Boolean choice to validate the * created date using the input format. The format used in @@ -129,6 +193,47 @@ class DateTimePlus extends \DateTime { * possible to a validation step to confirm that the date created * from a format string exactly matches the input. This option * indicates the format can be used for validation. Defaults to TRUE. + * @see __construct() + */ + public static function createFromFormat($format, $time, $timezone = NULL, $settings = array()) { + if (!isset($settings['validate_format'])) { + $settings['validate_format'] = TRUE; + } + + // Tries to create a date from the format and use it if possible. + // A regular try/catch won't work right here, if the value is + // invalid it doesn't return an exception. + $datetimeplus = new static('', $timezone, $settings); + + $date = \DateTime::createFromFormat($format, $time, $datetimeplus->getTimezone()); + if (!$date instanceOf \DateTime) { + throw new \Exception('The date cannot be created from a format.'); + } + else { + $datetimeplus->setTimestamp($date->getTimestamp()); + $datetimeplus->setTimezone($date->getTimezone()); + + // The createFromFormat function is forgiving, it might create a date that + // is not exactly a match for the provided value, so test for that. For + // instance, an input value of '11' using a format of Y (4 digits) gets + // created as '0011' instead of '2011'. Use the parent::format() because + // we do not want to use the IntlDateFormatter here. + if ($settings['validate_format'] && $date->format($format) != $time) { + throw new \Exception('The created date does not match the input value.'); + } + } + return $datetimeplus; + } + + /** + * Constructs a date object set to a requested date and timezone. + * + * @param string $time + * A date/time string. Defaults to 'now'. + * @param mixed $timezone + * PHP DateTimeZone object, string or NULL allowed. + * Defaults to NULL. + * @param array $settings * - langcode: (optional) String two letter language code to construct * the locale string by the intlDateFormatter class. Used to control * the result of the format() method if that class is available. @@ -142,66 +247,36 @@ class DateTimePlus extends \DateTime { * - debug: (optional) Boolean choice to leave debug values in the * date object for debugging purposes. Defaults to FALSE. */ - public function __construct($time = 'now', $timezone = NULL, $format = NULL, $settings = array()) { + public function __construct($time = 'now', $timezone = NULL, $settings = array()) { // Unpack settings. - $this->validateFormat = !empty($settings['validate_format']) ? $settings['validate_format'] : TRUE; $this->langcode = !empty($settings['langcode']) ? $settings['langcode'] : NULL; $this->country = !empty($settings['country']) ? $settings['country'] : NULL; $this->calendar = !empty($settings['calendar']) ? $settings['calendar'] : static::CALENDAR; - // Store the original input so it is available for validation. - $this->inputTimeRaw = $time; - $this->inputTimeZoneRaw = $timezone; - $this->inputFormatRaw = $format; - // Massage the input values as necessary. - $this->prepareTime($time); - $this->prepareTimezone($timezone); - $this->prepareFormat($format); - - // Create a date as a clone of an input DateTime object. - if ($this->inputIsObject()) { - $this->constructFromObject(); - } + $prepared_time = $this->prepareTime($time); + $prepared_timezone = $this->prepareTimezone($timezone); - // Create date from array of date parts. - elseif ($this->inputIsArray()) { - $this->constructFromArray(); - } - - // Create a date from a Unix timestamp. - elseif ($this->inputIsTimestamp()) { - $this->constructFromTimestamp(); - } + try { + if (!empty($prepared_time)) { + $test = date_parse($prepared_time); + if (!empty($test['errors'])) { + $this->errors[] = $test['errors']; + } + } - // Create a date from a time string and an expected format. - elseif ($this->inputIsFormat()) { - $this->constructFromFormat(); + if (empty($this->errors)) { + parent::__construct($prepared_time, $prepared_timezone); + } } - - // Create a date from any other input. - else { - $this->constructFallback(); + catch (\Exception $e) { + $this->errors[] = $e->getMessage(); } // Clean up the error messages. $this->checkErrors(); $this->errors = array_unique($this->errors); - - // Now that we've validated the input, clean up the extra values. - if (empty($settings['debug'])) { - unset( - $this->inputTimeRaw, - $this->inputTimeAdjusted, - $this->inputTimeZoneRaw, - $this->inputTimeZoneAdjusted, - $this->inputFormatRaw, - $this->inputFormatAdjusted, - $this->validateFormat - ); - } - } /** @@ -228,7 +303,7 @@ public function __toString() { * or an array of date parts. */ protected function prepareTime($time) { - $this->inputTimeAdjusted = $time; + return $time; } /** @@ -247,12 +322,6 @@ protected function prepareTimezone($timezone) { $timezone_adjusted = $timezone; } - // When the passed-in time is a DateTime object with its own - // timezone, try to use the date's timezone. - elseif (empty($timezone) && $this->inputTimeAdjusted instanceOf \DateTime) { - $timezone_adjusted = $this->inputTimeAdjusted->getTimezone(); - } - // Allow string timezone input, and create a timezone from it. elseif (!empty($timezone) && is_string($timezone)) { $timezone_adjusted = new \DateTimeZone($timezone); @@ -267,7 +336,7 @@ protected function prepareTimezone($timezone) { } // We are finally certain that we have a usable timezone. - $this->inputTimeZoneAdjusted = $timezone_adjusted; + return $timezone_adjusted; } /** @@ -280,179 +349,10 @@ protected function prepareTimezone($timezone) { * A PHP format string. */ protected function prepareFormat($format) { - $this->inputFormatAdjusted = $format; - } - - /** - * Checks whether input is a DateTime object. - * - * @return boolean - * TRUE if the input time is a DateTime object. - */ - public function inputIsObject() { - return $this->inputTimeAdjusted instanceOf \DateTime; - } - - /** - * Creates a date object from an input date object. - */ - protected function constructFromObject() { - try { - $this->inputTimeAdjusted = $this->inputTimeAdjusted->format(static::FORMAT); - parent::__construct($this->inputTimeAdjusted, $this->inputTimeZoneAdjusted); - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } + return $format; } - /** - * Checks whether input time seems to be a timestamp. - * - * Providing an input format will prevent ISO values without separators - * from being mis-interpreted as timestamps. Providing a format can also - * avoid interpreting a value like '2010' with a format of 'Y' as a - * timestamp. The 'U' format indicates this is a timestamp. - * - * @return boolean - * TRUE if the input time is a timestamp. - */ - public function inputIsTimestamp() { - return is_numeric($this->inputTimeAdjusted) && (empty($this->inputFormatAdjusted) || $this->inputFormatAdjusted == 'U'); - } - /** - * Creates a date object from timestamp input. - * - * The timezone of a timestamp is always UTC. The timezone for a - * timestamp indicates the timezone used by the format() method. - */ - protected function constructFromTimestamp() { - try { - parent::__construct('', $this->inputTimeZoneAdjusted); - $this->setTimestamp($this->inputTimeAdjusted); - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } - } - - /** - * Checks if input is an array of date parts. - * - * @return boolean - * TRUE if the input time is a DateTime object. - */ - public function inputIsArray() { - return is_array($this->inputTimeAdjusted); - } - - /** - * Creates a date object from an array of date parts. - * - * Converts the input value into an ISO date, forcing a full ISO - * date even if some values are missing. - */ - protected function constructFromArray() { - try { - parent::__construct('', $this->inputTimeZoneAdjusted); - $this->inputTimeAdjusted = static::prepareArray($this->inputTimeAdjusted, TRUE); - if (static::checkArray($this->inputTimeAdjusted)) { - // Even with validation, we can end up with a value that the - // parent class won't handle, like a year outside the range - // of -9999 to 9999, which will pass checkdate() but - // fail to construct a date object. - $this->inputTimeAdjusted = static::arrayToISO($this->inputTimeAdjusted); - parent::__construct($this->inputTimeAdjusted, $this->inputTimeZoneAdjusted); - } - else { - throw new \Exception('The array contains invalid values.'); - } - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } - } - - /** - * Checks if input is a string with an expected format. - * - * @return boolean - * TRUE if the input time is a string with an expected format. - */ - public function inputIsFormat() { - return is_string($this->inputTimeAdjusted) && !empty($this->inputFormatAdjusted); - } - - /** - * Creates a date object from an input format. - */ - protected function constructFromFormat() { - // Tries to create a date from the format and use it if possible. - // A regular try/catch won't work right here, if the value is - // invalid it doesn't return an exception. - try { - parent::__construct('', $this->inputTimeZoneAdjusted); - $date = parent::createFromFormat($this->inputFormatAdjusted, $this->inputTimeAdjusted, $this->inputTimeZoneAdjusted); - if (!$date instanceOf \DateTime) { - throw new \Exception('The date cannot be created from a format.'); - } - else { - $this->setTimestamp($date->getTimestamp()); - $this->setTimezone($date->getTimezone()); - - try { - // The createFromFormat function is forgiving, it might - // create a date that is not exactly a match for the provided - // value, so test for that. For instance, an input value of - // '11' using a format of Y (4 digits) gets created as - // '0011' instead of '2011'. - // Use the parent::format() because we do not want to use - // the IntlDateFormatter here. - if ($this->validateFormat && parent::format($this->inputFormatAdjusted) != $this->inputTimeRaw) { - throw new \Exception('The created date does not match the input value.'); - } - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } - } - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } - } - - /** - * Creates a date when none of the other methods are appropriate. - * - * Fallback construction for values that don't match any of the - * other patterns. Lets the parent dateTime attempt to turn this string - * into a valid date. - */ - protected function constructFallback() { - - try { - // One last test for invalid input before we try to construct - // a date. If the input contains totally bogus information - // it will blow up badly if we pass it to the constructor. - // The date_parse() function will tell us if the input - // makes sense. - if (!empty($this->inputTimeAdjusted)) { - $test = date_parse($this->inputTimeAdjusted); - if (!empty($test['errors'])) { - $this->errors[] = $test['errors']; - } - } - - if (empty($this->errors)) { - parent::__construct($this->inputTimeAdjusted, $this->inputTimeZoneAdjusted); - } - } - catch (\Exception $e) { - $this->errors[] = $e->getMessage(); - } - } /** * Examines getLastErrors() to see what errors to report. @@ -655,7 +555,14 @@ public function canUseIntl($calendar = NULL, $langcode = NULL, $country = NULL) $country = !empty($country) ? $country : $this->country; $calendar = !empty($calendar) ? $calendar : $this->calendar; - return class_exists('IntlDateFormatter') && !empty($calendar) && !empty($langcode) && !empty($country); + return $this->intlDateFormatterExists() && !empty($calendar) && !empty($langcode) && !empty($country); + } + + public static function intlDateFormatterExists() { + if (static::$intlExtentionExists === NULL) { + static::$intlExtentionExists = class_exists('IntlDateFormatter'); + } + return static::$intlExtentionExists; } /** diff --git a/core/lib/Drupal/Core/Datetime/Date.php b/core/lib/Drupal/Core/Datetime/Date.php index 5567331..dbc237b 100644 --- a/core/lib/Drupal/Core/Datetime/Date.php +++ b/core/lib/Drupal/Core/Datetime/Date.php @@ -39,6 +39,9 @@ class Date { */ protected $languageManager; + protected $country = NULL; + protected $dateFormats = array(); + /** * Constructs a Date object. * @@ -97,19 +100,23 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N } // Create a DrupalDateTime object from the timestamp and timezone. - $date = new DrupalDateTime($timestamp, $this->timezones[$timezone]); + $create_settings = array( + 'langcode' => $langcode, + 'country' => $this->country(), + ); + $date = DrupalDateTime::createFromTimestamp($timestamp, $this->timezones[$timezone], $create_settings); // Find the appropriate format type. $key = $date->canUseIntl() ? DrupalDateTime::INTL : DrupalDateTime::PHP; // If we have a non-custom date format use the provided date format pattern. - if ($date_format = $this->dateFormatStorage->load($type)) { + if ($date_format = $this->dateFormat($type)) { $format = $date_format->getPattern($key); } // Fall back to medium if a format was not found. if (empty($format)) { - $format = $this->dateFormatStorage->load('fallback')->getPattern($key); + $format = $this->dateFormat('fallback')->getPattern($key); } // Call $date->format(). @@ -120,4 +127,24 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N return Xss::filter($date->format($format, $settings)); } + protected function dateFormat($format) { + if (!isset($this->dateFormats[$format])) { + $this->dateFormats[$format] = $this->dateFormatStorage->load($format); + } + return $this->dateFormats[$format]; + } + + /** + * Returns the default country from config. + * + * @return string + * The config setting for country.default. + */ + protected function country() { + if ($this->country === NULL) { + $this->country = config('system.date')->get('country.default'); + } + return $this->country; + } + } diff --git a/core/lib/Drupal/Core/Datetime/DrupalDateTime.php b/core/lib/Drupal/Core/Datetime/DrupalDateTime.php index 0a283ac..18545b4 100644 --- a/core/lib/Drupal/Core/Datetime/DrupalDateTime.php +++ b/core/lib/Drupal/Core/Datetime/DrupalDateTime.php @@ -29,13 +29,6 @@ class DrupalDateTime extends DateTimePlus { * @param mixed $timezone * PHP DateTimeZone object, string or NULL allowed. * Defaults to NULL. - * @param string $format - * PHP date() type format for parsing the input. This is recommended - * to use things like negative years, which php's parser fails on, or - * any other specialized input with a known format. If provided the - * date will be created using the createFromFormat() method. - * Defaults to NULL. - * @see http://us3.php.net/manual/en/datetime.createfromformat.php * @param array $settings * - validate_format: (optional) Boolean choice to validate the * created date using the input format. The format used in @@ -57,14 +50,18 @@ class DrupalDateTime extends DateTimePlus { * - debug: (optional) Boolean choice to leave debug values in the * date object for debugging purposes. Defaults to FALSE. */ - public function __construct($time = 'now', $timezone = NULL, $format = NULL, $settings = array()) { - + public function __construct($time = 'now', $timezone = NULL, $settings = array()) { // We can set the langcode and country using Drupal values. - $settings['langcode'] = !empty($settings['langcode']) ? $settings['langcode'] : language(Language::TYPE_INTERFACE)->id; - $settings['country'] = !empty($settings['country']) ? $settings['country'] : config('system.date')->get('country.default'); + if (!isset($settings['langcode'])) { + $settings['langcode'] = language(Language::TYPE_INTERFACE)->id; + } + + if (!isset($settings['country'])) { + $settings['country'] = config('system.date')->get('country.default'); + } // Instantiate the parent class. - parent::__construct($time, $timezone, $format, $settings); + parent::__construct($time, $timezone, $settings); } @@ -79,7 +76,7 @@ protected function prepareTimezone($timezone) { if (empty($timezone) && !empty($user_timezone)) { $timezone = $user_timezone; } - parent::prepareTimezone($timezone); + return parent::prepareTimezone($timezone); } /** diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php index 906b4eb..629d675 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php @@ -29,7 +29,13 @@ class DateTimeIso8601 extends String implements DateTimeInterface { */ public function getDateTime() { if ($this->value) { - return new DrupalDateTime($this->value); + if (is_array($this->value)) { + $datetime = DrupalDateTime::createFromArray($this->value); + } + else { + $datetime = new DrupalDateTime($this->value); + } + return $datetime; } } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php index c0cad02..c817e25 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php @@ -34,7 +34,7 @@ class Timestamp extends Integer implements DateTimeInterface { */ public function getDateTime() { if ($this->value) { - return new DrupalDateTime($this->value); + return DrupalDateTime::createFromTimestamp($this->value); } } diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php index 7e20010..6d7c39b 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -62,7 +62,7 @@ public function form(array $form, array &$form_state) { if ($is_admin) { $author = $comment->name->value; $status = (isset($comment->status->value) ? $comment->status->value : COMMENT_NOT_PUBLISHED); - $date = (!empty($comment->date) ? $comment->date : new DrupalDateTime($comment->created->value)); + $date = (!empty($comment->date) ? $comment->date : DrupalDateTime::createFromTimestamp($comment->created->value)); } else { if ($user->isAuthenticated()) { diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module index ebbcdd8..eafc616 100644 --- a/core/modules/datetime/datetime.module +++ b/core/modules/datetime/datetime.module @@ -507,11 +507,16 @@ function form_type_datetime_value($element, $input = FALSE) { $time_input .= ':00'; } - $date = new DrupalDateTime(trim($date_input . ' ' . $time_input), $timezone, trim($date_format . ' ' . $time_format)); + try { + $date = DrupalDateTime::createFromFormat(trim($date_format . ' ' . $time_format), trim($date_input . ' ' . $time_input), $timezone); + } + catch (\Exception $e) { + $date = NULL; + } $input = array( 'date' => $date_input, 'time' => $time_input, - 'object' => $date instanceOf DrupalDateTime && !$date->hasErrors() ? $date : NULL, + 'object' => $date, ); } else { @@ -836,7 +841,7 @@ function form_type_datelist_value($element, $input = FALSE, &$form_state = array unset($input['ampm']); } $timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : NULL; - $date = new DrupalDateTime($input, $timezone); + $date = DrupalDateTime::createFromArray($input, $timezone); if ($date instanceOf DrupalDateTime && !$date->hasErrors()) { date_increment_round($date, $increment); } @@ -1044,5 +1049,5 @@ function datetime_form_node_form_alter(&$form, &$form_state, $form_id) { */ function datetime_node_prepare_form(NodeInterface $node, $form_display, $operation, array &$form_state) { // Prepare the 'Authored on' date to use datetime. - $node->date = new DrupalDateTime($node->created); + $node->date = DrupalDateTime::createFromTimestamp($node->created); } diff --git a/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php index 0c51dd4..6f02eda 100644 --- a/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php +++ b/core/modules/datetime/lib/Drupal/datetime/Plugin/field/field_type/DateTimeItem.php @@ -122,9 +122,14 @@ public function prepareCache() { $value = $this->get('value')->getValue(); if (!empty($value)) { $storage_format = $this->getFieldSetting('datetime_type') == 'date' ? DATETIME_DATE_STORAGE_FORMAT : DATETIME_DATETIME_STORAGE_FORMAT; - $date = new DrupalDateTime($value, DATETIME_STORAGE_TIMEZONE, $storage_format); - if ($date instanceOf DrupalDateTime && !$date->hasErrors()) { - $this->set('date', $date); + try { + $date = DrupalDateTime::createFromFormat($storage_format, $value, DATETIME_STORAGE_TIMEZONE); + if ($date instanceOf DrupalDateTime && !$date->hasErrors()) { + $this->set('date', $date); + } + } + catch (\Exception $e) { + // @todo Handle this. } } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Datetime/DateTimePlusIntlTest.php b/core/modules/system/lib/Drupal/system/Tests/Datetime/DateTimePlusIntlTest.php index 89466d4..453cfbd 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Datetime/DateTimePlusIntlTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Datetime/DateTimePlusIntlTest.php @@ -70,8 +70,8 @@ function testDateTimestampIntl() { 'langcode' => 'en', ); - $intl_date = new DateTimePlus($input, $timezone, NULL, $intl_settings); - $php_date = new DateTimePlus($input, $timezone, NULL, $php_settings); + $intl_date = new DateTimePlus($input, $timezone, $intl_settings); + $php_date = new DateTimePlus($input, $timezone, $php_settings); $this->assertTrue($intl_date->canUseIntl(), 'DateTimePlus object can use intl when provided with country and langcode settings.'); $this->assertFalse($php_date->canUseIntl(), 'DateTimePlus object will fallback to use PHP when not provided with country setting.'); diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php index 3558d20..6b1f0c5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -174,7 +174,7 @@ public function testGetAndSet() { // Check implementation of DateTimeInterface. $typed_data = $this->createTypedData(array('type' => 'timestamp'), REQUEST_TIME); $this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime); - $typed_data->setDateTime(new DrupalDateTime(REQUEST_TIME + 1)); + $typed_data->setDateTime(DrupalDateTime::createFromTimestamp(REQUEST_TIME + 1)); $this->assertEqual($typed_data->getValue(), REQUEST_TIME + 1); $typed_data->setValue(NULL); $this->assertNull($typed_data->getDateTime()); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentDateTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentDateTest.php index 545b40a..c5dac2e 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentDateTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentDateTest.php @@ -142,7 +142,7 @@ public function testMonthHandler() { $view->destroy(); $view->setDisplay('embed_2'); - $this->executeView($view, array('23')); + $this->executeView($view, array('12')); $expected = array(); $this->assertIdenticalResultset($view, $expected, $this->columnMap); } @@ -296,7 +296,7 @@ public function testYearMonthHandler() { $view->destroy(); $view->setDisplay('embed_5'); - $this->executeView($view, array('23')); + $this->executeView($view, array('201301')); $expected = array(); $this->assertIdenticalResultset($view, $expected, $this->columnMap); } diff --git a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php index f780877..9e78d63 100644 --- a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php +++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php @@ -51,10 +51,32 @@ public function testDates($input, $timezone, $expected) { } /** - * Test creating dates from timestamps, and manipulating timezones. + * Test creating dates from string and array input. * * @param mixed $input - * Input argument for DateTimePlus(). + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $expected + * Expected output from DateTimePlus::format(). + * + * @dataProvider providerTestDateArrays + */ + public function testDateArrays($input, $timezone, $expected) { + $date = DateTimePlus::createFromArray($input, $timezone); + $value = $date->format('c'); + + if (is_array($input)) { + $input = var_export($input, TRUE); + } + $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value)); + } + + /** + * Test creating dates from timestamps, and manipulating timezones. + * + * @param int $input + * Input argument for DateTimePlus::createFromTimestamp(). * @param array $initial * An array containing: * - 'timezone_initial' - Timezone argument for DateTimePlus. @@ -76,12 +98,46 @@ public function testDates($input, $timezone, $expected) { * - 'expected_transform_offset' - Expected output from * DateTimePlus::getOffset(), after timezone transform. * + * @dataProvider providerTestTimestamp + */ + public function testTimestamp($input, array $initial, array $transform) { + // Initialize a new date object. + $date = DateTimePlus::createFromTimestamp($input, $initial['timezone']); + $this->assertDateTimestamp($date, $input, $initial, $transform); + } + + /** + * Test creating dates from datetime strings. + * + * @param string $input + * Input argument for DateTimePlus(). + * @param array $initial + * @see testTimestamp() + * @param array $transform + * @see testTimestamp() + * * @dataProvider providerTestDateTimestamp */ public function testDateTimestamp($input, array $initial, array $transform) { // Initialize a new date object. $date = new DateTimePlus($input, $initial['timezone']); + $this->assertDateTimestamp($date, $input, $initial, $transform); + } + /** + * Assertion helper for testTimestamp and testDateTimestamp since they need + * different dataProviders. + * + * @param DateTimePlus $date + * DateTimePlus to test. + * @input mixed $input + * The original input passed to the test method. + * @param array $initial + * @see testTimestamp() + * @param array $transform + * @see testTimestamp() + */ + public function assertDateTimestamp($date, $input, $initial, $transform) { // Check format. $value = $date->format($initial['format']); $this->assertEquals($initial['expected_date'], $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $initial['timezone'], $initial['expected_date'], $value)); @@ -108,7 +164,6 @@ public function testDateTimestamp($input, array $initial, array $transform) { // Check transformed offset. $value = $date->getOffset(); $this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value)); - } /** @@ -126,7 +181,7 @@ public function testDateTimestamp($input, array $initial, array $transform) { * @dataProvider providerTestDateFormat */ public function testDateFormat($input, $timezone, $format, $format_date, $expected) { - $date = new DateTimePlus($input, $timezone, $format); + $date = DateTimePlus::createFromFormat($format, $input, $timezone); $value = $date->format($format_date); $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value)); } @@ -144,14 +199,14 @@ public function testDateFormat($input, $timezone, $format, $format_date, $expect * Message to print if no errors are thrown by the invalid dates. * * @dataProvider providerTestInvalidDates + * @expectedException \Exception */ public function testInvalidDates($input, $timezone, $format, $message) { - $date = new DateTimePlus($input, $timezone, $format); - $this->assertNotEquals(count($date->getErrors()), 0, $message); + $date = DateTimePlus::createFromFormat($format, $input, $timezone); } /** - * Test that DrupalDateTime can detect the right timezone to use. + * Tests that DrupalDateTime can detect the right timezone to use. * When specified or not. * * @param mixed $input @@ -172,7 +227,23 @@ public function testDateTimezone($input, $timezone, $expected_timezone, $message } /** - * Provide data for date tests. + * Test that DrupalDateTime can detect the right timezone to use when + * constructed from a datetime object. + */ + public function testDateTimezoneWithDateTimeObject() { + // Create a date object with another date object. + $input = new DateTimePlus('now', 'Pacific/Midway'); + $timezone = NULL; + $expected_timezone = 'Pacific/Midway'; + $message = 'DateTimePlus uses the specified timezone if provided.'; + + $date = DateTimePlus::createFromDateTime($input, $timezone); + $timezone = $date->getTimezone()->getName(); + $this->assertEquals($timezone, $expected_timezone, $message); + } + + /** + * Provides data for date tests. * * @return array * An array of arrays, each containing the input parameters for @@ -195,7 +266,20 @@ public function providerTestDates() { array('2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'), // Same during daylight savings time. array('2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'), + ); + } + /** + * Provides data for date tests. + * + * @return array + * An array of arrays, each containing the input parameters for + * DateTimePlusTest::testDates(). + * + * @see DateTimePlusTest::testDates(). + */ + public function providerTestDateArrays() { + return array( // Array input. // Create date object from date array, date only. array(array('year' => 2010, 'month' => 2, 'day' => 28), 'America/Chicago', '2010-02-28T00:00:00-06:00'), @@ -209,7 +293,7 @@ public function providerTestDates() { } /** - * Provide data for testDateFormats. + * Provides data for testDateFormats. * * @return array * An array of arrays, each containing: @@ -235,7 +319,7 @@ public function providerTestDateFormat() { } /** - * Provide data for testInvalidDates. + * Provides data for testInvalidDates. * * @return array * An array of arrays, each containing: @@ -259,6 +343,24 @@ public function providerTestInvalidDates() { array('0000-75-00T15:30:00', NULL, 'Y-m-d\TH:i:s', "0000-75-00T15:30:00 contains an invalid month and did not produce errors."), // Test for invalid year. array('11-08-01T15:30:00', NULL, 'Y-m-d\TH:i:s', "11-08-01T15:30:00 contains an invalid year and did not produce errors."), + + ); + } + + /** + * Provides data for testInvalidDates. + * + * @return array + * An array of arrays, each containing: + * - 'input' - Input for DateTimePlus. + * - 'timezone' - Timezone for DateTimePlus. + * - 'format' - Format for DateTimePlus. + * - 'message' - Message to display on failure. + * + * @see testInvalidDateArrays + */ + public function providerTestInvalidDateArrays() { + return array( // Test for invalid year from date array. 10000 as a year will // create an exception error in the PHP DateTime object. array(array('year' => 10000, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0), 'America/Chicago', NULL, "array('year' => 10000, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0) contains an invalid year and did not produce errors."), @@ -272,7 +374,7 @@ public function providerTestInvalidDates() { } /** - * Provide data for testDateTimezone. + * Provides data for testDateTimezone. * * @return array * An array of arrays, each containing: @@ -304,15 +406,15 @@ public function providerTestDateTimezone() { } /** - * Provide data for testDateTimestamp. + * Provides data for testTimestamp. * * @return array * An array of arrays, each containing the arguments required for - * self::testDateTimestamp(). + * self::testTimestamp(). * - * @see testDateTimestamp() + * @see testTimestamp() */ - public function providerTestDateTimestamp() { + public function providerTestTimestamp() { return array( // Create date object from a unix timestamp and display it in // local time. @@ -352,6 +454,20 @@ public function providerTestDateTimestamp() { 'expected_offset' => 0, ), ), + ); + } + + /** + * Provides data for testDateTimestamp. + * + * @return array + * An array of arrays, each containing the arguments required for + * self::testDateTimestamp(). + * + * @see testDateTimestamp() + */ + public function providerTestDateTimestamp() { + return array( // Create date object from datetime string in UTC, and convert // it to a local date. array(