r44915 - Code Review

From MediaWiki.org

Jump to: navigation, search
Repository:MediaWiki
Revision:r44914 | r44915 (on ViewVC) | r44916 >
Date:21:55, 22 December 2008
Author:brion
Status:ok (Comments)
Tags:
Comment:* (bug 505) Time zones can now be specified by location in user preferences,
avoiding the need to manually update for DST. Patch by Brad Jorsch.
Modified paths:

Diff [purge]

Index: trunk/phase3/skins/common/prefs.js
===================================================================
--- trunk/phase3/skins/common/prefs.js	(revision 44914)
+++ trunk/phase3/skins/common/prefs.js	(revision 44915)
@@ -95,6 +95,7 @@
 	if (tzb) {
 		tzb.style.display = 'inline';
 	}
+	updateTimezoneSelection(false);
 }
 
 // in [-]HH:MM format...
@@ -113,7 +114,51 @@
 
 function guessTimezone(box) {
 	document.getElementsByName("wpHourDiff")[0].value = fetchTimezone();
+	updateTimezoneSelection(true);
 }
 
+function updateTimezoneSelection(force_offset) {
+	var wpTimeZone = document.getElementsByName("wpTimeZone")[0];
+	var wpHourDiff = document.getElementsByName("wpHourDiff")[0];
+	var wpLocalTime = document.getElementById("wpLocalTime");
+	var wpServerTime = document.getElementsByName("wpServerTime")[0];
+	var minDiff = 0;
+
+	if (force_offset) wpTimeZone.selectedIndex = 1;
+	if (wpTimeZone.selectedIndex == 1) {
+		wpHourDiff.disabled = false;
+		var diffArr = wpHourDiff.value.split(':');
+		if (diffArr.length == 1) {
+			minDiff = parseInt(diffArr[0], 10) * 60;
+		} else {
+			minDiff = Math.abs(parseInt(diffArr[0], 10))*60 + parseInt(diffArr[1], 10);
+			if (parseInt(diffArr[0], 10) < 0) minDiff = -minDiff;
+		}
+	} else {
+		wpHourDiff.disabled = true;
+		var diffArr = wpTimeZone.options[wpTimeZone.selectedIndex].value.split('|');
+		minDiff = parseInt(diffArr[1], 10);
+	}
+	if (isNaN(minDiff)) minDiff = 0;
+	var localTime = parseInt(wpServerTime.value, 10) + minDiff;
+	while (localTime < 0) localTime += 1440;
+	while (localTime >= 1440) localTime -= 1440;
+
+	var hour = String(Math.floor(localTime/60));
+	if (hour.length<2) hour = '0'+hour;
+	var min = String(localTime%60);
+	if (min.length<2) min = '0'+min;
+	changeText(wpLocalTime, hour+':'+min);
+
+	if (wpTimeZone.selectedIndex != 1) {
+		hour = String(Math.abs(Math.floor(minDiff/60)));
+		if (hour.length<2) hour = '0'+hour;
+		if (minDiff < 0) hour = '-'+hour;
+		min = String(minDiff%60);
+		if (min.length<2) min = '0'+min;
+		wpHourDiff.value = hour+':'+min;
+	}
+}
+
 hookEvent("load", unhidetzbutton);
 hookEvent("load", tabbedprefs);
Index: trunk/phase3/includes/DefaultSettings.php
===================================================================
--- trunk/phase3/includes/DefaultSettings.php	(revision 44914)
+++ trunk/phase3/includes/DefaultSettings.php	(revision 44915)
@@ -1444,7 +1444,7 @@
  * to ensure that client-side caches don't keep obsolete copies of global
  * styles.
  */
-$wgStyleVersion = '191';
+$wgStyleVersion = '192';
 
 
 # Server-side caching:
Index: trunk/phase3/includes/specials/SpecialPreferences.php
===================================================================
--- trunk/phase3/includes/specials/SpecialPreferences.php	(revision 44914)
+++ trunk/phase3/includes/specials/SpecialPreferences.php	(revision 44915)
@@ -24,7 +24,7 @@
 	var $mQuickbar, $mStubs;
 	var $mRows, $mCols, $mSkin, $mMath, $mDate, $mUserEmail, $mEmailFlag, $mNick;
 	var $mUserLanguage, $mUserVariant;
-	var $mSearch, $mRecent, $mRecentDays, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
+	var $mSearch, $mRecent, $mRecentDays, $mTimeZone, $mHourDiff, $mSearchLines, $mSearchChars, $mAction;
 	var $mReset, $mPosted, $mToggles, $mSearchNs, $mRealName, $mImageSize;
 	var $mUnderline, $mWatchlistEdits;
 
@@ -51,6 +51,7 @@
 		$this->mSearch = $request->getVal( 'wpSearch' );
 		$this->mRecent = $request->getVal( 'wpRecent' );
 		$this->mRecentDays = $request->getVal( 'wpRecentDays' );
+		$this->mTimeZone = $request->getVal( 'wpTimeZone' );
 		$this->mHourDiff = $request->getVal( 'wpHourDiff' );
 		$this->mSearchLines = $request->getVal( 'wpSearchLines' );
 		$this->mSearchChars = $request->getVal( 'wpSearchChars' );
@@ -170,34 +171,37 @@
 
 	/**
 	 * Used to validate the user inputed timezone before saving it as
-	 * 'timecorrection', will return '00:00' if fed bogus data.
-	 * Note: It's not a 100% correct implementation timezone-wise, it will
-	 * accept stuff like '14:30',
+	 * 'timecorrection', will return 'System' if fed bogus data.
 	 * @access private
-	 * @param string $s the user input
+	 * @param string $tz the user input Zoneinfo timezone
+	 * @param string $s  the user input offset string
 	 * @return string
 	 */
-	function validateTimeZone( $s ) {
-		if ( $s !== '' ) {
-			if ( strpos( $s, ':' ) ) {
-				# HH:MM
-				$array = explode( ':' , $s );
-				$hour = intval( $array[0] );
-				$minute = intval( $array[1] );
-			} else {
-				$minute = intval( $s * 60 );
-				$hour = intval( $minute / 60 );
-				$minute = abs( $minute ) % 60;
-			}
-			# Max is +14:00 and min is -12:00, see:
-			# http://en.wikipedia.org/wiki/Timezone
-			$hour = min( $hour, 14 );
-			$hour = max( $hour, -12 );
-			$minute = min( $minute, 59 );
-			$minute = max( $minute, 0 );
-			$s = sprintf( "%02d:%02d", $hour, $minute );
+	function validateTimeZone( $tz, $s ) {
+		$data = explode( '|', $tz, 3 );
+		switch ( $data[0] ) {
+			case 'ZoneInfo':
+			case 'System':
+				return $tz;
+			case 'Offset':
+			default:
+				$data = explode( ':', $s, 2 );
+				$minDiff = 0;
+				if( count( $data ) == 2 ) {
+					$data[0] = intval( $data[0] );
+					$data[1] = intval( $data[1] );
+					$minDiff = abs( $data[0] ) * 60 + $data[1];
+					if ( $data[0] < 0 ) $minDiff = -$minDiff;
+				} else {
+					$minDiff = intval( $data[0] ) * 60;
+				}
+
+				# Max is +14:00 and min is -12:00, see:
+				# http://en.wikipedia.org/wiki/Timezone
+				$minDiff = min( $minDiff, 840 );  # 14:00
+				$minDiff = max( $minDiff, -720 ); # -12:00
+				return 'Offset|'.$minDiff;
 		}
-		return $s;
 	}
 
 	/**
@@ -259,7 +263,7 @@
 		$wgUser->setOption( 'rows', $this->validateInt( $this->mRows, 4, 1000 ) );
 		$wgUser->setOption( 'cols', $this->validateInt( $this->mCols, 4, 1000 ) );
 		$wgUser->setOption( 'stubthreshold', $this->validateIntOrNull( $this->mStubs ) );
-		$wgUser->setOption( 'timecorrection', $this->validateTimeZone( $this->mHourDiff, -12, 14 ) );
+		$wgUser->setOption( 'timecorrection', $this->validateTimeZone( $this->mTimeZone, $this->mHourDiff ) );
 		$wgUser->setOption( 'imagesize', $this->mImageSize );
 		$wgUser->setOption( 'thumbsize', $this->mThumbSize );
 		$wgUser->setOption( 'underline', $this->validateInt($this->mUnderline, 0, 2) );
@@ -344,7 +348,7 @@
 	 * @access private
 	 */
 	function resetPrefs() {
-		global $wgUser, $wgLang, $wgContLang, $wgContLanguageCode, $wgAllowRealName;
+		global $wgUser, $wgLang, $wgContLang, $wgContLanguageCode, $wgAllowRealName, $wgLocalTZoffset;
 
 		$this->mUserEmail = $wgUser->getEmail();
 		$this->mUserEmailAuthenticationtimestamp = $wgUser->getEmailAuthenticationtimestamp();
@@ -364,7 +368,47 @@
 		$this->mRows = $wgUser->getOption( 'rows' );
 		$this->mCols = $wgUser->getOption( 'cols' );
 		$this->mStubs = $wgUser->getOption( 'stubthreshold' );
-		$this->mHourDiff = $wgUser->getOption( 'timecorrection' );
+
+		$tz = $wgUser->getOption( 'timecorrection' );
+		$data = explode( '|', $tz, 3 );
+		$minDiff = null;
+		switch ( $data[0] ) {
+			case 'ZoneInfo':
+				$this->mTimeZone = $tz;
+				# Check if the specified TZ exists, and change to 'Offset' if 
+				# not.
+				if ( !function_exists('timezone_open') || @timezone_open( $data[2] ) === false ) {
+					$this->mTimeZone = 'Offset';
+					$minDiff = intval( $data[1] );
+				}
+				break;
+			case '':
+			case 'System':
+				$this->mTimeZone = 'System|'.$wgLocalTZoffset;
+				break;
+			case 'Offset':
+				$this->mTimeZone = 'Offset';
+				$minDiff = intval( $data[1] );
+				break;
+			default:
+				$this->mTimeZone = 'Offset';
+				$data = explode( ':', $tz, 2 );
+				if( count( $data ) == 2 ) {
+					$data[0] = intval( $data[0] );
+					$data[1] = intval( $data[1] );
+					$minDiff = abs( $data[0] ) * 60 + $data[1];
+					if ( $data[0] < 0 ) $minDiff = -$minDiff;
+				} else {
+					$minDiff = intval( $data[0] ) * 60;
+				}
+				break;
+		}
+		if ( is_null( $minDiff ) ) {
+			$this->mHourDiff = '';
+		} else {
+			$this->mHourDiff = sprintf( '%+03d:%02d', floor($minDiff/60), abs($minDiff)%60 );
+		}
+
 		$this->mSearch = $wgUser->getOption( 'searchlimit' );
 		$this->mSearchLines = $wgUser->getOption( 'contextlines' );
 		$this->mSearchChars = $wgUser->getOption( 'contextchars' );
@@ -490,7 +534,7 @@
 		global $wgRCShowWatchingUsers, $wgEnotifRevealEditorAddress;
 		global $wgEnableEmail, $wgEnableUserEmail, $wgEmailAuthentication;
 		global $wgContLanguageCode, $wgDefaultSkin, $wgCookieExpiration;
-		global $wgEmailConfirmToEdit, $wgEnableMWSuggest;
+		global $wgEmailConfirmToEdit, $wgEnableMWSuggest, $wgLocalTZoffset;
 
 		$wgOut->setPageTitle( wfMsg( 'preferences' ) );
 		$wgOut->setArticleRelated( false );
@@ -908,18 +952,61 @@
 			$wgOut->addHTML( Xml::closeElement( 'fieldset' ) . "\n" );
 		}
 
-		$nowlocal = $wgLang->time( $now = wfTimestampNow(), true );
-		$nowserver = $wgLang->time( $now, false );
+		$nowlocal = Xml::openElement( 'span', array( 'id' => 'wpLocalTime' ) ) .
+			$wgLang->time( $now = wfTimestampNow(), true ) .
+			Xml::closeElement( 'span' );
+		$nowserver = $wgLang->time( $now, false ) .
+			Xml::hidden( 'wpServerTime', substr( $now, 8, 2 ) * 60 + substr( $now, 10, 2 ) );
 
 		$wgOut->addHTML(
 			Xml::openElement( 'fieldset' ) .
 			Xml::element( 'legend', null, wfMsg( 'timezonelegend' ) ) .
 			Xml::openElement( 'table' ) .
 		 	$this->addRow( wfMsg( 'servertime' ), $nowserver ) .
-			$this->addRow( wfMsg( 'localtime' ), $nowlocal ) .
+			$this->addRow( wfMsg( 'localtime' ), $nowlocal )
+		);
+		$opt = Xml::openElement( 'select', array(
+			'name' => 'wpTimeZone',
+			'id' => 'wpTimeZone',
+			'onchange' => 'javascript:updateTimezoneSelection(false)' ) );
+		$opt .= Xml::option( wfMsg( 'timezoneuseserverdefault' ), "System|$wgLocalTZoffset", $this->mTimeZone === "System|$wgLocalTZoffset" );
+		$opt .= Xml::option( wfMsg( 'timezoneuseoffset' ), 'Offset', $this->mTimeZone === 'Offset' );
+		if ( function_exists( 'timezone_identifiers_list' ) ) {
+			$optgroup = '';
+			$tzs = timezone_identifiers_list();
+			sort( $tzs );
+			$selZone = explode( '|', $this->mTimeZone, 3);
+			$selZone = ( $selZone[0] == 'ZoneInfo' ) ? $selZone[2] : null;
+			$now = date_create( 'now' );
+			foreach ( $tzs as $tz ) {
+				$z = explode( '/', $tz, 2 );
+				# timezone_identifiers_list() returns a number of
+				# backwards-compatibility entries. This filters them out of the 
+				# list presented to the user.
+				if ( count( $z ) != 2 || !in_array( $z[0], array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific' ) ) ) continue;
+				if ( $optgroup != $z[0] ) {
+					if ( $optgroup !== '' ) $opt .= Xml::closeElement( 'optgroup' );
+					$optgroup = $z[0];
+					$opt .= Xml::openElement( 'optgroup', array( 'label' => $z[0] ) );
+				}
+				$minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
+				$opt .= Xml::option( str_replace( '_', ' ', $tz ), "ZoneInfo|$minDiff|$tz", $selZone === $tz, array( 'label' => $z[1] ) );
+			}
+			if ( $optgroup !== '' ) $opt .= Xml::closeElement( 'optgroup' );
+		}
+		$opt .= Xml::closeElement( 'select' );
+		$wgOut->addHTML(
 			$this->addRow(
+				Xml::label( wfMsg( 'timezoneselect' ), 'wpTimeZone' ),
+				$opt )
+		);
+		$wgOut->addHTML(
+			$this->addRow(
 				Xml::label( wfMsg( 'timezoneoffset' ), 'wpHourDiff'  ),
-				Xml::input( 'wpHourDiff', 6, $this->mHourDiff, array( 'id' => 'wpHourDiff' ) ) ) .
+				Xml::input( 'wpHourDiff', 6, $this->mHourDiff, array(
+					'id' => 'wpHourDiff',
+					'onfocus' => 'javascript:updateTimezoneSelection(true)',
+					'onblur' => 'javascript:updateTimezoneSelection(false)' ) ) ) .
 			"<tr>
 				<td></td>
 				<td class='mw-submit'>" .
Index: trunk/phase3/languages/messages/MessagesEn.php
===================================================================
--- trunk/phase3/languages/messages/MessagesEn.php	(revision 44914)
+++ trunk/phase3/languages/messages/MessagesEn.php	(revision 44915)
@@ -1566,6 +1566,9 @@
 'timezonelegend'            => 'Time zone',
 'timezonetext'              => '¹The number of hours your local time differs from server time (UTC).',
 'localtime'                 => 'Local time',
+'timezoneselect'            => 'Timezone',
+'timezoneuseserverdefault'  => 'Use server default',
+'timezoneuseoffset'         => 'Other (specify offset)',
 'timezoneoffset'            => 'Offset¹',
 'servertime'                => 'Server time',
 'guesstimezone'             => 'Fill in from browser',
Index: trunk/phase3/languages/Language.php
===================================================================
--- trunk/phase3/languages/Language.php	(revision 44914)
+++ trunk/phase3/languages/Language.php	(revision 44915)
@@ -479,39 +479,50 @@
 	function userAdjust( $ts, $tz = false )	{
 		global $wgUser, $wgLocalTZoffset;
 
-		if (!$tz) {
+		if ( $tz === false ) {
 			$tz = $wgUser->getOption( 'timecorrection' );
 		}
 
-		# minutes and hours differences:
+		$data = explode( '|', $tz, 3 );
+
+		if ( $data[0] == 'ZoneInfo' ) {
+			if ( function_exists( 'timezone_open' ) && @timezone_open( $data[2] ) !== false ) {
+				$date = date_create( $ts, timezone_open( 'UTC' ) );
+				date_timezone_set( $date, timezone_open( $data[2] ) );
+				$date = date_format( $date, 'YmdHis' );
+				return $date;
+			}
+			# Unrecognized timezone, default to 'Offset' with the stored offset.
+			$data[0] = 'Offset';
+		}
+
 		$minDiff = 0;
-		$hrDiff  = 0;
-
-		if ( $tz === '' ) {
+		if ( $data[0] == 'System' || $tz == '' ) {
 			# Global offset in minutes.
-			if( isset($wgLocalTZoffset) ) {
-				if( $wgLocalTZoffset >= 0 ) {
-					$hrDiff = floor($wgLocalTZoffset / 60);
-				} else {
-					$hrDiff = ceil($wgLocalTZoffset / 60);
-				}
-				$minDiff = $wgLocalTZoffset % 60;
+			if( isset($wgLocalTZoffset) ) $minDiff = $wgLocalTZoffset;
+		} else if ( $data[0] == 'Offset' ) {
+			$minDiff = intval( $data[1] );
+		} else {
+			$data = explode( ':', $tz );
+			if( count( $data ) == 2 ) {
+				$data[0] = intval( $data[0] );
+				$data[1] = intval( $data[1] );
+				$minDiff = abs( $data[0] ) * 60 + $data[1];
+				if ( $data[0] < 0 ) $minDiff = -$minDiff;
+			} else {
+				$minDiff = intval( $data[0] ) * 60;
 			}
-		} elseif ( strpos( $tz, ':' ) !== false ) {
-			$tzArray = explode( ':', $tz );
-			$hrDiff = intval($tzArray[0]);
-			$minDiff = intval($hrDiff < 0 ? -$tzArray[1] : $tzArray[1]);
-		} else {
-			$hrDiff = intval( $tz );
 		}
 
 		# No difference ? Return time unchanged
-		if ( 0 == $hrDiff && 0 == $minDiff ) { return $ts; }
+		if ( 0 == $minDiff ) return $ts;
 
 		wfSuppressWarnings(); // E_STRICT system time bitching
-		# Generate an adjusted date
+		# Generate an adjusted date; take advantage of the fact that mktime
+		# will normalize out-of-range values so we don't have to split $minDiff 
+		# into hours and minutes.
 		$t = mktime( (
-		  (int)substr( $ts, 8, 2) ) + $hrDiff, # Hours
+		  (int)substr( $ts, 8, 2) ), # Hours
 		  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
 		  (int)substr( $ts, 12, 2 ), # Seconds
 		  (int)substr( $ts, 4, 2 ), # Month
Index: trunk/phase3/RELEASE-NOTES
===================================================================
--- trunk/phase3/RELEASE-NOTES	(revision 44914)
+++ trunk/phase3/RELEASE-NOTES	(revision 44915)
@@ -239,7 +239,10 @@
 * Added 'UserCryptPassword' and 'UserComparePasswords' hooks to allow extensions to implement
   their own password hashing methods.
 * (bug 16760) Add CSS-class to action links of Special:Log
+* (bug 505) Time zones can now be specified by location in user preferences,
+  avoiding the need to manually update for DST. Patch by Brad Jorsch.
 
+
 === Bug fixes in 1.14 ===
 
 * (bug 14907) DatabasePostgres::fieldType now defined.

Follow-up revisions

RevisionCommit summaryAuthorDate
r44916Follow up r44915: Add new messages to messages.incraymond22:10, 22 December 2008

Comments

#Comment by Aaron Schulz (Talk | contribs)   23:17, 22 December 2008

Seems to work. Might have some duplication.

Also, the timezone_identifiers_list() filter stuff could perhaps be split off a bit.

#Comment by Kwj2772 (Talk | contribs)   06:19, 25 December 2008

Timezone name can't be localized now. It is a problem.

#Comment by Nikerabbit (Talk | contribs)   13:21, 25 December 2008

That is also a thing that we could pull out of cldr data instead of duplicating the effort.

Status & tagging log

Views
Toolbox