Added some basic timezone support; DST support is still to come.
authorDan
Fri, 08 Feb 2008 23:20:20 -0500
changeset 406 7468a663315f
parent 405 adb7f8de8ce1
child 407 35d94240a197
Added some basic timezone support; DST support is still to come.
includes/common.php
includes/functions.php
includes/sessions.php
install/schemas/mysql_stage2.sql
install/schemas/postgresql_stage2.sql
install/schemas/upgrade/migration/1.0-1.1-mysql.sql
language/english/core.json
language/english/user.json
plugins/SpecialUserPrefs.php
--- a/includes/common.php	Fri Feb 08 12:16:43 2008 -0500
+++ b/includes/common.php	Fri Feb 08 23:20:20 2008 -0500
@@ -139,6 +139,10 @@
 // Language object
 global $lang;
 
+// Timezone offset
+global $timezone;
+$timezone = 0;
+
 // Because Enano sends out complete URLs in several occasions, we need to know what hostname the user is requesting the page from.
 // In future versions we may include a fallback "safety" host to use, but that's too much to worry about now
 if ( !isset($_SERVER['HTTP_HOST']) )
--- a/includes/functions.php	Fri Feb 08 12:16:43 2008 -0500
+++ b/includes/functions.php	Fri Feb 08 23:20:20 2008 -0500
@@ -251,8 +251,29 @@
 {
   if ( !is_int($timestamp) && !is_double($timestamp) && strval(intval($timestamp)) !== $timestamp )
     $timestamp = time();
-  // FIXME: Offset $timestamp by user's timezone
-  return gmdate($string, $timestamp);
+  
+  /*
+  // List of valid characters for date()
+  $date_chars = 'dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZFcrU';
+  // Split them into an array
+  $date_chars = enano_str_split($date_chars);
+  // Emulate date() formatting by replacing date characters with their
+  // percentage-signed counterparts, but not escaped characters which
+  // shouldn't be parsed.
+  foreach ( $date_chars as $char )
+  {
+    $string = str_replace($char, "%$char", $string);
+    $string = str_replace("\\%$char", $char, $string);
+  }
+  */
+  
+  // perform timestamp offset
+  global $timezone;
+  // it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
+  $timestamp = $timestamp + ( $timezone * 60 );
+  
+  // Let PHP do the work for us =)
+  return date($string, $timestamp);
 }
 
 /**
--- a/includes/sessions.php	Fri Feb 08 12:16:43 2008 -0500
+++ b/includes/sessions.php	Fri Feb 08 23:20:20 2008 -0500
@@ -370,6 +370,7 @@
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
     global $lang;
+    global $timezone;
     if($this->started) return;
     $this->started = true;
     $user = false;
@@ -391,6 +392,7 @@
         {
           $language = intval(getConfig('default_language'));
           $lang = new Language($language);
+          @setlocale(LC_ALL, $lang->lang_code);
           
           $this->logout();
           $a = getConfig('account_activation');
@@ -490,12 +492,14 @@
           }
         }
         $user = true;
+        $GLOBALS['timezone'] = $userdata['user_timezone'];
         
         // Set language
         if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
         {
           $lang_id = intval($userdata['user_lang']);
           $lang = new Language($lang_id);
+          @setlocale(LC_ALL, $lang->lang_code);
         }
         
         if(isset($_REQUEST['auth']) && !$this->sid_super)
@@ -1168,6 +1172,7 @@
     {
       $language = ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) ) ? $_GET['lang'] : intval(getConfig('default_language'));
       $lang = new Language($language);
+      @setlocale(LC_ALL, $lang->lang_code);
     }
   }
   
@@ -1201,7 +1206,7 @@
     // using a normal call to $db->sql_query to avoid failing on errors here
     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,' . "\n"
                              . '    u.reg_time,u.account_active,u.activation_key,u.user_lang,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,' . "\n"
-                             . '    x.* FROM '.table_prefix.'session_keys AS k' . "\n"
+                             . '    u.user_timezone, x.* FROM '.table_prefix.'session_keys AS k' . "\n"
                              . '  LEFT JOIN '.table_prefix.'users AS u' . "\n"
                              . '    ON ( u.user_id=k.user_id )' . "\n"
                              . '  LEFT JOIN '.table_prefix.'users_extra AS x' . "\n"
@@ -1214,7 +1219,7 @@
     
     if ( !$query )
     {
-      $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms FROM '.table_prefix.'session_keys AS k
+      $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
                              LEFT JOIN '.table_prefix.'users AS u
                                ON ( u.user_id=k.user_id )
                              LEFT JOIN '.table_prefix.'privmsgs AS p
@@ -1282,6 +1287,7 @@
     // Leave the rest to PHP's automatic garbage collector ;-)
     
     $row['password'] = md5($real_pass);
+    $row['user_timezone'] = intval($row['user_timezone']) - 1440;
     
     profiler_log("SessionManager: finished session check");
     
@@ -1299,7 +1305,7 @@
     global $db, $session, $paths, $template, $plugins; // Common objects
     $key = $db->escape($key);
     
-    $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level FROM '.table_prefix.'session_keys AS k
+    $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level,1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
                            LEFT JOIN '.table_prefix.'users AS u
                              ON u.user_id=k.user_id
                            WHERE k.session_key=\''.$key.'\';');
@@ -1344,6 +1350,8 @@
       return false;
     }
     
+    $row['user_timezone'] = intval($row['user_timezone']) - 1440;
+    
     return $row;
   }
    
--- a/install/schemas/mysql_stage2.sql	Fri Feb 08 12:16:43 2008 -0500
+++ b/install/schemas/mysql_stage2.sql	Fri Feb 08 23:20:20 2008 -0500
@@ -106,6 +106,7 @@
   avatar_type ENUM('jpg', 'png', 'gif') NOT NULL DEFAULT 'png',
   user_registration_ip varchar(39),
   user_rank int(12) UNSIGNED NOT NULL DEFAULT 1,
+  user_timezone int(12) UNSIGNED NOT NULL DEFAULT 0,
   PRIMARY KEY  (user_id)
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
 
--- a/install/schemas/postgresql_stage2.sql	Fri Feb 08 12:16:43 2008 -0500
+++ b/install/schemas/postgresql_stage2.sql	Fri Feb 08 23:20:20 2008 -0500
@@ -105,6 +105,7 @@
   avatar_type varchar(3) NOT NULL,
   user_registration_ip varchar(39),
   user_rank int NOT NULL DEFAULT 1,
+  user_timezone int NOT NULL DEFAULT 0,
   CHECK (avatar_type IN ('jpg', 'png', 'gif')),
   PRIMARY KEY  (user_id)
 );
--- a/install/schemas/upgrade/migration/1.0-1.1-mysql.sql	Fri Feb 08 12:16:43 2008 -0500
+++ b/install/schemas/upgrade/migration/1.0-1.1-mysql.sql	Fri Feb 08 23:20:20 2008 -0500
@@ -14,6 +14,7 @@
 ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_has_avatar tinyint(1) NOT NULL;
 ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN avatar_type ENUM('jpg', 'png', 'gif') NOT NULL;
 ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_registration_ip varchar(39);
+ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_timezone int(12) NOT NULL DEFAULT 0;
 ALTER TABLE {{TABLE_PREFIX}}comments ADD COLUMN ip_address varchar(39);
 
 CREATE TABLE {{TABLE_PREFIX}}lockout(
--- a/language/english/core.json	Fri Feb 08 12:16:43 2008 -0500
+++ b/language/english/core.json	Fri Feb 08 23:20:20 2008 -0500
@@ -17,7 +17,7 @@
 
 var enano_lang = {
   categories: [
-    'meta', 'page', 'comment', 'onpage', 'etc', 'editor', 'history', 'catedit', 'tags', 'delvote', 'ajax', 'sidebar', 'perm', 'plugin', 'paginate', 'upload',
+    'meta', 'page', 'comment', 'onpage', 'etc', 'editor', 'history', 'catedit', 'tags', 'delvote', 'ajax', 'sidebar', 'perm', 'plugin', 'paginate', 'upload', 'tz',
   ],
   strings: {
     meta: {
@@ -37,6 +37,7 @@
       plugin: 'Plugin names and descriptions',
       paginate: 'Pagination widget',
       upload: 'File upload interface',
+      tz: 'Time zones',
       plural: 's',
       enano_about_th: 'About the Enano Content Management System',
       enano_about_poweredby: '<p>This website is powered by <a href="http://enanocms.org/">Enano</a>, the lightweight and open source CMS that everyone can use. Enano is copyright &copy; 2006-2007 Dan Fuhry. For legal information, along with a list of libraries that Enano uses, please see <a href="http://enanocms.org/Legal_information">Legal Information</a>.</p><p>The developers and maintainers of Enano strongly believe that software should not only be free to use, but free to be modified, distributed, and used to create derivative works. For more information about Free Software, check out the <a href="http://en.wikipedia.org/wiki/Free_Software" onclick="window.open(this.href); return false;">Wikipedia page</a> or the <a href="http://www.fsf.org/" onclick="window.open(this.href); return false;">Free Software Foundation\'s</a> homepage.</p>',
@@ -550,6 +551,90 @@
       err_not_found_title: 'File not found',
       err_not_found_body: 'The file "%filename%" cannot be found.',
     },
+    tz: {
+      // Thanks to phpBB for this timezone data.
+      // Do not add or remove from this list - contact the core team to have changes made to this list.
+      hrs_n12: 'UTC - 12 hours',
+      hrs_n11: 'UTC - 11 hours',
+      hrs_n10: 'UTC - 10 hours',
+      hrs_n9p5: 'UTC - 9:30 hours',
+      hrs_n9: 'UTC - 9 hours',
+      hrs_n8: 'UTC - 8 hours',
+      hrs_n7: 'UTC - 7 hours',
+      hrs_n6: 'UTC - 6 hours',
+      hrs_n5: 'UTC - 5 hours',
+      hrs_n4: 'UTC - 4 hours',
+      hrs_n3p5: 'UTC - 3:30 hours',
+      hrs_n3: 'UTC - 3 hours',
+      hrs_n2: 'UTC - 2 hours',
+      hrs_n1: 'UTC - 1 hour',
+      hrs_0: 'UTC',
+      hrs_1: 'UTC + 1 hour',
+      hrs_2: 'UTC + 2 hours',
+      hrs_3: 'UTC + 3 hours',
+      hrs_3p5: 'UTC + 3:30 hours',
+      hrs_4: 'UTC + 4 hours',
+      hrs_4p5: 'UTC + 4:30 hours',
+      hrs_5: 'UTC + 5 hours',
+      hrs_5p5: 'UTC + 5:30 hours',
+      hrs_5p75: 'UTC + 5:45 hours',
+      hrs_6: 'UTC + 6 hours',
+      hrs_6p5: 'UTC + 6:30 hours',
+      hrs_7: 'UTC + 7 hours',
+      hrs_8: 'UTC + 8 hours',
+      hrs_8p75: 'UTC + 8:45 hours',
+      hrs_9: 'UTC + 9 hours',
+      hrs_9p5: 'UTC + 9:30 hours',
+      hrs_10: 'UTC + 10 hours',
+      hrs_10p5: 'UTC + 10:30 hours',
+      hrs_11: 'UTC + 11 hours',
+      hrs_11p5: 'UTC + 11:30 hours',
+      hrs_12: 'UTC + 12 hours',
+      hrs_12p75: 'UTC + 12:45 hours',
+      hrs_13: 'UTC + 13 hours',
+      hrs_14: 'UTC + 14 hours',
+      title_n12: '[UTC - 12] Baker Island Time',
+      title_n11: '[UTC - 11] Niue Time, Samoa Standard Time',
+      title_n10: '[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time',
+      title_n9p5: '[UTC - 9:30] Marquesas Islands Time',
+      title_n9: '[UTC - 9] Alaska Standard Time, Gambier Island Time',
+      title_n8: '[UTC - 8] Pacific Standard Time',
+      title_n7: '[UTC - 7] Mountain Standard Time',
+      title_n6: '[UTC - 6] Central Standard Time',
+      title_n5: '[UTC - 5] Eastern Standard Time',
+      title_n4: '[UTC - 4] Atlantic Standard Time',
+      title_n3p5: '[UTC - 3:30] Newfoundland Standard Time',
+      title_n3: '[UTC - 3] Amazon Standard Time, Central Greenland Time',
+      title_n2: '[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time',
+      title_n1: '[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time',
+      title_0: '[UTC] Western European Time, Greenwich Mean Time',
+      title_1: '[UTC + 1] Central European Time, West African Time',
+      title_2: '[UTC + 2] Eastern European Time, Central African Time',
+      title_3: '[UTC + 3] Moscow Standard Time, Eastern African Time',
+      title_3p5: '[UTC + 3:30] Iran Standard Time',
+      title_4: '[UTC + 4] Gulf Standard Time, Samara Standard Time',
+      title_4p5: '[UTC + 4:30] Afghanistan Time',
+      title_5: '[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time',
+      title_5p5: '[UTC + 5:30] Indian Standard Time, Sri Lanka Time',
+      title_5p75: '[UTC + 5:45] Nepal Time',
+      title_6: '[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time',
+      title_6p5: '[UTC + 6:30] Cocos Islands Time, Myanmar Time',
+      title_7: '[UTC + 7] Indochina Time, Krasnoyarsk Standard Time',
+      title_8: '[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time',
+      title_8p75: '[UTC + 8:45] Southeastern Western Australia Standard Time',
+      title_9: '[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time',
+      title_9p5: '[UTC + 9:30] Australian Central Standard Time',
+      title_10: '[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time',
+      title_10p5: '[UTC + 10:30] Lord Howe Standard Time',
+      title_11: '[UTC + 11] Solomon Island Time, Magadan Standard Time',
+      title_11p5: '[UTC + 11:30] Norfolk Island Time',
+      title_12: '[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time',
+      title_12p75: '[UTC + 12:45] Chatham Islands Time',
+      title_13: '[UTC + 13] Tonga Time, Phoenix Islands Time',
+      title_14: '[UTC + 14] Line Island Time',
+      // This is a JSON string that lists all the timezones that are defined here.
+      list: '{"n12":-12,"n11":-11,"n10":-10,"n9p5":-9.5,"n9":-9,"n8":-8,"n7":-7,"n6":-6,"n5":-5,"n4":-4,"n3p5":-3.5,"n3":-3,"n2":-2,"n1":-1,"0":0,"1":1,"2":2,"3":3,"3p5":3.5,"4":4,"4p5":4.5,"5":5,"5p5":5.5,"5p75":5.75,"6":6,"6p5":6.5,"7":7,"8":8,"8p75":8.75,"9":9,"9p5":9.5,"10":10,"10p5":10.5,"11":11,"11p5":11.5,"12":12,"12p75":12.75,"13":13,"14":14}',
+    },
     etc: {
       redirect_title: 'Redirecting...',
       redirect_body: 'Please wait while you are redirected.',
--- a/language/english/user.json	Fri Feb 08 12:16:43 2008 -0500
+++ b/language/english/user.json	Fri Feb 08 23:20:20 2008 -0500
@@ -278,6 +278,8 @@
       publicinfo_field_changetheme_title: 'Change theme:',
       publicinfo_field_changetheme_hint: 'If you don\'t like the look of the site, need a visual break, or are just curious, we might have some different themes for you to try out!',
       publicinfo_field_changetheme: 'Change my theme...',
+      publicinfo_field_timezone: 'Time zone:',
+      publicinfo_field_timezone_hint: 'Select the time zone you live in and when Daylight Savings Time occurs, if at all.',
       publicinfo_th_im: 'Instant messenger contact information',
       publicinfo_field_aim: 'AIM handle:',
       publicinfo_field_wlm: '<acronym title="Windows&trade; Live Messenger">WLM</acronym> handle:<br /><small>If you don\'t specify the domain (@whatever.com), "@hotmail.com" will be assumed.</small>',
--- a/plugins/SpecialUserPrefs.php	Fri Feb 08 12:16:43 2008 -0500
+++ b/plugins/SpecialUserPrefs.php	Fri Feb 08 23:20:20 2008 -0500
@@ -152,6 +152,7 @@
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
   global $lang;
+  global $timezone;
   
   // We need a login to continue
   if ( !$session->user_logged_in )
@@ -483,6 +484,9 @@
         $real_name = htmlspecialchars($_POST['real_name']);
         $real_name = $db->escape($real_name);
         
+        $timezone = intval($_POST['timezone']);
+        $tz_local = $timezone + 1440;
+        
         $imaddr_aim = htmlspecialchars($_POST['imaddr_aim']);
         $imaddr_aim = $db->escape($imaddr_aim);
         
@@ -536,7 +540,7 @@
         $session->user_extra['user_hobbies'] = $hobbies;
         $session->user_extra['email_public'] = intval($email_public);
         
-        $q = $db->sql_query('UPDATE '.table_prefix."users SET real_name='$real_name' WHERE user_id=$session->user_id;");
+        $q = $db->sql_query('UPDATE '.table_prefix."users SET real_name='$real_name', user_timezone = $tz_local WHERE user_id=$session->user_id;");
         if ( !$q )
           $db->_die();
         
@@ -592,6 +596,26 @@
       
       $lang_box .= '</select>';
       
+      $tz_select = '<select name="timezone">';
+      $tz_list = $lang->get('tz_list');
+      try
+      {
+        $tz_list = enano_json_decode($tz_list);
+      }
+      catch(Exception $e)
+      {
+        die("Caught exception decoding timezone data: <pre>$e</pre>");
+      }
+      foreach ( $tz_list as $key => $i )
+      {
+        $i = ($i * 60);
+        $title = $lang->get("tz_title_{$key}");
+        $hrs = $lang->get("tz_hrs_{$key}");
+        $selected = ( $i == $timezone ) ? ' selected="selected"' : '';
+        $tz_select .= "<option value=\"$i\"$selected>$title</option>";
+      }
+      $tz_select .= '</select>';
+      
       echo '<form action="'.makeUrl($paths->fullpage).'" method="post">';
       ?>
       <div class="tblholder">
@@ -615,6 +639,10 @@
             <td class="row1"><?php echo $lang->get('usercp_publicinfo_field_changetheme_hint'); ?> <a href="<?php echo makeUrlNS('Special', 'ChangeStyle/' . $paths->page); ?>" onclick="ajaxChangeStyle(); return false;"><?php echo $lang->get('usercp_publicinfo_field_changetheme'); ?></a></td>
           </tr>
           <tr>
+            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_timezone'); ?><br /><small><?php echo $lang->get('usercp_publicinfo_field_timezone_hint'); ?></small></td>
+            <td class="row1"><?php echo $tz_select; ?></td>
+          </tr>
+          <tr>
             <th class="subhead" colspan="2">
               <?php echo $lang->get('usercp_publicinfo_th_im'); ?>
             </th>