? sites/all/modules ? sites/default/files ? sites/default/settings.pgsql.php ? sites/default/settings.php Index: install.php =================================================================== RCS file: /cvs/drupal/drupal/install.php,v retrieving revision 1.117 diff -u -p -r1.117 install.php --- install.php 14 Apr 2008 17:48:33 -0000 1.117 +++ install.php 21 Apr 2008 19:18:23 -0000 @@ -1047,7 +1047,7 @@ function install_configure_form(&$form_s $form['server_settings']['date_default_timezone'] = array( '#type' => 'select', '#title' => st('Default time zone'), - '#default_value' => 0, + '#default_value' => date_default_timezone_get(), '#options' => _system_zonelist(), '#description' => st('By default, dates in this site will be displayed in the chosen time zone.'), '#weight' => 5, Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.764 diff -u -p -r1.764 common.inc --- includes/common.inc 14 Apr 2008 17:48:33 -0000 1.764 +++ includes/common.inc 21 Apr 2008 19:18:26 -0000 @@ -1141,7 +1141,7 @@ function format_interval($timestamp, $gr * before a character to avoid interpreting the character as part of a date * format. * @param $timezone - * Time zone offset in seconds; if omitted, the user's time zone is used. + * Time zone identifier; if omitted, the user's time zone is used. * @param $langcode * Optional language code to translate to a language other than what is used * to display the page. @@ -1149,17 +1149,21 @@ function format_interval($timestamp, $gr * A translated date string in the requested format. */ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) { - if (!isset($timezone)) { + static $timezones = array(); + if (!$timezone) { global $user; - if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) { + if (variable_get('configurable_timezones', 1) && $user->uid && $user->timezone) { $timezone = $user->timezone; } else { - $timezone = variable_get('date_default_timezone', 0); + $timezone = variable_get('date_default_timezone', 'UTC'); } } - - $timestamp += $timezone; + // Store DateTimeZone objects in an array rather than repeatedly + // contructing identical objects over the life of a request. + if (!isset($timezones[$timezone])) { + $timezones[$timezone] = timezone_open($timezone); + } switch ($type) { case 'small': @@ -1178,28 +1182,27 @@ function format_date($timestamp, $type = $max = strlen($format); $date = ''; + // Create a DateTime object from the timestamp. + $date_time = date_create('@' . $timestamp); + // Set the time zone for the DateTime object. + date_timezone_set($date_time, $timezones[$timezone]); + for ($i = 0; $i < $max; $i++) { $c = $format[$i]; - if (strpos('AaDlM', $c) !== FALSE) { - $date .= t(gmdate($c, $timestamp), array(), $langcode); + if (strpos('AaeDlMT', $c) !== FALSE) { + $date .= t(date_format($date_time, $c), array(), $langcode); } else if ($c == 'F') { // Special treatment for long month names: May is both an abbreviation // and a full month name in English, but other languages have // different abbreviations. - $date .= trim(t('!long-month-name ' . gmdate($c, $timestamp), array('!long-month-name' => ''), $langcode)); + $date .= trim(t('!long-month-name ' . date_format($date_time, $c), array('!long-month-name' => ''), $langcode)); } - else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) { - $date .= gmdate($c, $timestamp); + else if (strpos('BcdGgHhIijLmNnOoPSstUuWwYyZz', $c) !== FALSE) { + $date .= date_format($date_time, $c); } else if ($c == 'r') { - $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode); - } - else if ($c == 'O') { - $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60); - } - else if ($c == 'Z') { - $date .= $timezone; + $date .= format_date($timestamp, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode); } else if ($c == '\\') { $date .= $format[++$i]; Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.71 diff -u -p -r1.71 system.admin.inc --- modules/system/system.admin.inc 16 Apr 2008 11:35:52 -0000 1.71 +++ modules/system/system.admin.inc 21 Apr 2008 19:18:30 -0000 @@ -1546,7 +1546,7 @@ function system_date_time_settings() { $form['locale']['date_default_timezone'] = array( '#type' => 'select', '#title' => t('Default time zone'), - '#default_value' => variable_get('date_default_timezone', 0), + '#default_value' => variable_get('date_default_timezone', date_default_timezone_get()), '#options' => $zones, '#description' => t('Select the default site time zone.') ); Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.250 diff -u -p -r1.250 system.install --- modules/system/system.install 16 Apr 2008 11:35:52 -0000 1.250 +++ modules/system/system.install 21 Apr 2008 19:18:33 -0000 @@ -2861,6 +2861,29 @@ function system_update_7005() { } /** + * Convert default time zone offset to default time zone name. + */ +function system_update_7006() { + $ret = array(); + // If the contributed date module set a default time zone name, use + // this setting as the default time zone. + if ($timezone = variable_get('date_default_timezone_name', NULL)) { + variable_set('date_default_timezone', $timezone); + } + // If the previous default time zone was a non-zero offset, guess the + // site's intended time zone based on that offset and the server's + // daylight saving time status. + elseif ($timezone = variable_get('date_default_timezone', 0)) { + variable_set('date_default_timezone', timezone_name_from_abbr('', intval($timezone), intval(date('I')))); + } + // Otherwise, set the default time zone to UTC. + else { + variable_set('date_default_timezone', 'UTC'); + } + return $ret; +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.598 diff -u -p -r1.598 system.module --- modules/system/system.module 16 Apr 2008 11:35:52 -0000 1.598 +++ modules/system/system.module 21 Apr 2008 19:18:34 -0000 @@ -709,7 +709,7 @@ function system_user($type, $edit, &$use $form['timezone']['timezone'] = array( '#type' => 'select', '#title' => t('Time zone'), - '#default_value' => strlen($edit['timezone']) ? $edit['timezone'] : variable_get('date_default_timezone', 0), + '#default_value' => $edit['timezone'] ? $edit['timezone'] : variable_get('date_default_timezone', 'UTC'), '#options' => $zones, '#description' => t('Select your current local time. Dates and times throughout this site will be displayed using this time zone.'), ); @@ -2014,11 +2014,15 @@ function system_block_ip_action() { */ function _system_zonelist() { $timestamp = time(); - $zonelist = array(-11, -10, -9.5, -9, -8, -7, -6, -5, -4, -3.5, -3, -2, -1, 0, 1, 2, 3, 3.5, 4, 5, 5.5, 5.75, 6, 6.5, 7, 8, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 14); + $zonelist = timezone_identifiers_list(); $zones = array(); - foreach ($zonelist as $offset) { - $zone = $offset * 3600; - $zones[$zone] = format_date($timestamp, 'custom', variable_get('date_format_long', 'l, F j, Y - H:i') . ' O', $zone); + foreach ($zonelist as $zone) { + // Because many time zones exist in PHP only for backward + // compatibility reasons and should not be used, the list is + // filtered by a regular expression. + if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) { + $zones[$zone] = t('@zone: @date', array('@zone' => t($zone), '@date' => format_date($timestamp, 'custom', variable_get('date_format_long', 'l, F j, Y - H:i') . ' O', $zone))); + } } return $zones; } Index: modules/system/system.test =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.test,v retrieving revision 1.1 diff -u -p -r1.1 system.test --- modules/system/system.test 20 Apr 2008 18:23:31 -0000 1.1 +++ modules/system/system.test 21 Apr 2008 19:18:34 -0000 @@ -126,3 +126,63 @@ class EnableDisableCoreTestCase extends } } } + +class DateTimeTestCase extends DrupalWebTestCase { + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('Date and time functionality'), + 'description' => t('Configure date and time settings. Test date formatting and time zone handling, including daylight saving time.'), + 'group' => t('System'), + ); + } + + function testDateTimeZone() { + $admin_user = $this->drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($admin_user); + + // Setup date/time settings. + $edit = array(); + $edit['date_default_timezone'] = 'Pacific/Honolulu'; + $edit['configurable_timezones'] = 0; + $edit['date_first_day'] = 0; + $edit['date_format_short'] = 'm/d/Y - H:i'; + $edit['date_format_medium'] = 'custom'; + $edit['date_format_medium_custom'] = 'Y-m-d H:i:s O'; + $edit['date_format_long'] = 'l, F j, Y - H:i'; + + $this->drupalPost('admin/settings/date-time', $edit, t('Save configuration')); + $this->assertText('The configuration options have been saved.', 'Date and time settings saved (Honolulu time).'); + + // Create some nodes with different authored-on dates. + $date1 = '2007-01-31 21:00:00 -1000'; + $date2 = '2007-07-31 21:00:00 -1000'; + $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article')); + $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article')); + + $this->drupalGet("node/$node1->nid"); + $this->assertText('2007-01-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.'); + $this->drupalGet("node/$node2->nid"); + $this->assertText('2007-07-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.'); + + // Setup date/time settings. + $edit = array(); + $edit['date_default_timezone'] = 'America/Los_Angeles'; + $edit['configurable_timezones'] = 0; + $edit['date_first_day'] = 0; + $edit['date_format_short'] = 'm/d/Y - H:i'; + $edit['date_format_medium'] = 'custom'; + $edit['date_format_medium_custom'] = 'Y-m-d H:i:s O'; + $edit['date_format_long'] = 'l, F j, Y - H:i'; + + $this->drupalPost('admin/settings/date-time', $edit, t('Save configuration')); + $this->assertText('The configuration options have been saved.', 'Date and time settings saved (Los Angeles time).'); + + $this->drupalGet("node/$node1->nid"); + $this->assertText('2007-01-31 23:00:00 -0800', 'Date should be two hours ahead, with GMT offset of -8 hours.'); + $this->drupalGet("node/$node2->nid"); + $this->assertText('2007-08-01 00:00:00 -0700', 'Date should be three hours ahead, with GMT offset of -7 hours.'); + } +} Index: modules/user/user.install =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.install,v retrieving revision 1.11 diff -u -p -r1.11 user.install --- modules/user/user.install 16 Apr 2008 11:26:29 -0000 1.11 +++ modules/user/user.install 21 Apr 2008 19:18:35 -0000 @@ -170,7 +170,7 @@ function user_schema() { ), 'timezone' => array( 'type' => 'varchar', - 'length' => 8, + 'length' => 32, 'not null' => FALSE, 'description' => t("User's timezone."), ), @@ -304,6 +304,33 @@ function user_update_7001() { } /** + * Convert user time zones from time zone offsets to time zone names. + */ +function user_update_7002() { + $ret = array(); + db_change_field($ret, 'users', 'timezone', 'timezone', array('type' => 'varchar', 'length' => 32, 'not null' => FALSE, 'description' => t("User's timezone."))); + // If the contributed date module has created a users.timezone_name + // column, use this data to set each user's time zone. + if (db_column_exists('users', 'timezone_name')) { + $ret[] = update_sql("UPDATE {users} SET timezone = timezone_name"); + } + else { + $result = db_query("SELECT timezone FROM {users} WHERE LENGTH(timezone) > 0 GROUP BY timezone"); + while ($user_offset = db_fetch_object($result)) { + // It is impossible to determine the time zone correctly from the + // offset alone. We can throw a little extra data at the problem + // by also providing the server's daylight saving time status; + // this, however, may not match the user's daylight saving time + // status. + $timezone = timezone_name_from_abbr('', intval($user_offset->timezone), intval(date('I'))); + db_query("UPDATE {users} SET timezone = '%s' WHERE timezone = '%s'", $timezone, $user_offset->timezone); + $ret[] = array('success' => TRUE, 'query' => check_plain("UPDATE {users} SET timezone = '$timezone' WHERE timezone = '$user_offset->timezone'")); + } + } + return $ret; +} + +/** * @} End of "defgroup user-updates-6.x-to-7.x" * The next series of updates should start at 8000. */ Index: modules/user/user.js =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.js,v retrieving revision 1.6 diff -u -p -r1.6 user.js --- modules/user/user.js 12 Sep 2007 18:29:32 -0000 1.6 +++ modules/user/user.js 21 Apr 2008 19:18:35 -0000 @@ -168,11 +168,50 @@ Drupal.evaluatePasswordStrength = functi }; /** - * Set the client's system timezone as default values of form fields. + * Set the client's system time zone as default values of form fields. */ Drupal.setDefaultTimezone = function() { - var offset = new Date().getTimezoneOffset() * -60; - $("#edit-date-default-timezone, #edit-user-register-timezone").val(offset); + // In some client environments, date strings include a time zone + // abbreviation which can be interpreted by PHP. + var matches = Date().match(/\(([A-Z]{3,5})\)/); + var abbreviation = matches ? matches[1] : 0; + + // For all other client environments, the abbreviation is set to "0" + // and the current offset from UTC and daylight saving time status are + // used to guess the time zone. + var dateNow = new Date(); + var offsetNow = dateNow.getTimezoneOffset() * -60; + + // Use January 1 and July 1 as test dates for determining daylight + // saving time status by comparing their offsets. + var dateJan = new Date(dateNow.getFullYear(), 0, 1, 12, 0, 0, 0); + var dateJul = new Date(dateNow.getFullYear(), 6, 1, 12, 0, 0, 0); + var offsetJan = dateJan.getTimezoneOffset() * -60; + var offsetJul = dateJul.getTimezoneOffset() * -60; + + // If the offset from UTC is identical on January 1 and July 1, + // assume daylight saving time is not used in this time zone. + if (offsetJan == offsetJul) { + var isDaylightSavingTime = ''; + } + // If the maximum annual offset is equivalent to the current offset, + // assume daylight saving time is in effect. + else if (Math.max(offsetJan, offsetJul) == offsetNow) { + var isDaylightSavingTime = 1; + } + // Otherwise, assume daylight saving time is not in effect. + else { + var isDaylightSavingTime = 0; + } + + // Submit request to the user/timezone callback and set the form field + // to the response time zone. + var path = 'user/timezone/' + abbreviation + '/' + offsetNow + '/' + isDaylightSavingTime; + $.getJSON(Drupal.settings.basePath, { q: path }, function (data) { + if (data) { + $("#edit-date-default-timezone, #edit-user-register-timezone").val(data); + } + }); }; /** Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.903 diff -u -p -r1.903 user.module --- modules/user/user.module 19 Apr 2008 17:10:50 -0000 1.903 +++ modules/user/user.module 21 Apr 2008 19:18:38 -0000 @@ -852,6 +852,14 @@ function user_menu() { 'file' => 'user.pages.inc', ); + $items['user/timezone'] = array( + 'title' => 'User timezone', + 'page callback' => 'user_timezone', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + 'file' => 'user.pages.inc', + ); + // Registration and login pages. $items['user'] = array( 'title' => 'User account', Index: modules/user/user.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v retrieving revision 1.13 diff -u -p -r1.13 user.pages.inc --- modules/user/user.pages.inc 14 Apr 2008 17:48:43 -0000 1.13 +++ modules/user/user.pages.inc 21 Apr 2008 19:18:38 -0000 @@ -22,6 +22,17 @@ function user_autocomplete($string = '') } /** + * Menu callback; Retrieve a JSON object containing a suggested time + * zone name. + */ +function user_timezone($abbreviation = '', $offset = -1, $is_daylight_saving_time = NULL) { + // An abbreviation of "0" passed in the callback arguments should be + // interpreted as the empty string. + $abbreviation = $abbreviation ? $abbreviation : ''; + drupal_json(timezone_name_from_abbr($abbreviation, intval($offset), $is_daylight_saving_time)); +} + +/** * Form builder; Request a password reset. * * @ingroup forms Index: modules/user/user.test =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.test,v retrieving revision 1.1 diff -u -p -r1.1 user.test --- modules/user/user.test 20 Apr 2008 18:23:33 -0000 1.1 +++ modules/user/user.test 21 Apr 2008 19:18:38 -0000 @@ -203,3 +203,66 @@ class UserDeleteTestCase extends DrupalW $this->assertFalse(user_load($edit), t('User is not found in the database')); } } + + +class DateTimeZoneTestCase extends DrupalWebTestCase { + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('User time zone'), + 'description' => t('Set a user time zone and verify that dates are displayed in local time.'), + 'group' => t('User'), + ); + } + + function testDateTimeZone() { + $admin_user = $this->drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($admin_user); + + // Setup date/time settings. + $edit = array(); + $edit['date_default_timezone'] = 'America/Los_Angeles'; + $edit['configurable_timezones'] = 1; + $edit['date_first_day'] = 0; + $edit['date_format_short'] = 'm/d/Y - H:i'; + $edit['date_format_medium'] = 'custom'; + $edit['date_format_medium_custom'] = 'Y-m-d H:i T'; + $edit['date_format_long'] = 'l, F j, Y - H:i'; + + $this->drupalPost('admin/settings/date-time', $edit, t('Save configuration')); + $this->assertText('The configuration options have been saved.', 'Date and time settings saved (Los Angeles time).'); + + $web_user = $this->drupalCreateUser(array('access content', 'create article content')); + $this->drupalLogin($web_user); + + // Create some nodes with different authored-on dates. + $date1 = '2007-03-09 21:00:00 -0800'; + $date2 = '2007-03-11 01:00:00 -0800'; + $date3 = '2007-03-20 21:00:00 -0700'; + $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article')); + $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article')); + $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article')); + + $this->drupalGet("node/$node1->nid"); + $this->assertText('2007-03-09 21:00 PST', 'Date should be PST.'); + $this->drupalGet("node/$node2->nid"); + $this->assertText('2007-03-11 01:00 PST', 'Date should be PST.'); + $this->drupalGet("node/$node3->nid"); + $this->assertText('2007-03-20 21:00 PDT', 'Date should be PDT.'); + + $edit = array(); + $edit['mail'] = $web_user->mail; + $edit['timezone'] = 'America/Santiago'; + $this->drupalPost("user/$web_user->uid/edit", $edit, t('Save')); + $this->assertText(t('The changes have been saved.'), t('Time zone changed to Santiago time.')); + + $this->drupalGet("node/$node1->nid"); + $this->assertText('2007-03-10 02:00 CLST', 'Date should be Chile Summer Time; five hours ahead of Pacific Standard Time.'); + $this->drupalGet("node/$node2->nid"); + $this->assertText('2007-03-11 05:00 CLT', 'Date should be Chile Time; four hours ahead of Pacific Standard Time'); + $this->drupalGet("node/$node3->nid"); + $this->assertText('2007-03-21 00:00 CLT', 'Date should be Chile Time; three hours ahead of Pacific Daylight Time.'); + } +}