Commit e2a81db3 authored by catch's avatar catch

Issue #1844956 by msonnabaum, beejeebus: Fixed Optimize date formatting performance.

parent 18a03b1f
......@@ -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();
}
......
......@@ -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;
}
}
......@@ -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);
}
/**
......
......@@ -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;
}
}
......
......@@ -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);
}
}
......
......@@ -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()) {
......
......@@ -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);
}
......@@ -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.
}
}
}
......
......@@ -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.');
......
......@@ -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());
......
......@@ -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);
}
......
......@@ -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(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment