* (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.
This commit is contained in:
parent
79d267d212
commit
c672ce6032
6 changed files with 204 additions and 55 deletions
|
|
@ -239,6 +239,9 @@ The following extensions are migrated into MediaWiki 1.14:
|
|||
* 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 ===
|
||||
|
||||
|
|
|
|||
|
|
@ -1444,7 +1444,7 @@ $wgCacheEpoch = '20030516000000';
|
|||
* to ensure that client-side caches don't keep obsolete copies of global
|
||||
* styles.
|
||||
*/
|
||||
$wgStyleVersion = '191';
|
||||
$wgStyleVersion = '192';
|
||||
|
||||
|
||||
# Server-side caching:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class PreferencesForm {
|
|||
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 @@ class PreferencesForm {
|
|||
$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 @@ class PreferencesForm {
|
|||
|
||||
/**
|
||||
* 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 @@ class PreferencesForm {
|
|||
$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 @@ class PreferencesForm {
|
|||
* @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 @@ class PreferencesForm {
|
|||
$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 @@ class PreferencesForm {
|
|||
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 @@ class PreferencesForm {
|
|||
$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'>" .
|
||||
|
|
|
|||
|
|
@ -479,39 +479,50 @@ class Language {
|
|||
function userAdjust( $ts, $tz = false ) {
|
||||
global $wgUser, $wgLocalTZoffset;
|
||||
|
||||
if (!$tz) {
|
||||
if ( $tz === false ) {
|
||||
$tz = $wgUser->getOption( 'timecorrection' );
|
||||
}
|
||||
|
||||
# minutes and hours differences:
|
||||
$minDiff = 0;
|
||||
$hrDiff = 0;
|
||||
$data = explode( '|', $tz, 3 );
|
||||
|
||||
if ( $tz === '' ) {
|
||||
# Global offset in minutes.
|
||||
if( isset($wgLocalTZoffset) ) {
|
||||
if( $wgLocalTZoffset >= 0 ) {
|
||||
$hrDiff = floor($wgLocalTZoffset / 60);
|
||||
} else {
|
||||
$hrDiff = ceil($wgLocalTZoffset / 60);
|
||||
}
|
||||
$minDiff = $wgLocalTZoffset % 60;
|
||||
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;
|
||||
}
|
||||
} elseif ( strpos( $tz, ':' ) !== false ) {
|
||||
$tzArray = explode( ':', $tz );
|
||||
$hrDiff = intval($tzArray[0]);
|
||||
$minDiff = intval($hrDiff < 0 ? -$tzArray[1] : $tzArray[1]);
|
||||
# Unrecognized timezone, default to 'Offset' with the stored offset.
|
||||
$data[0] = 'Offset';
|
||||
}
|
||||
|
||||
$minDiff = 0;
|
||||
if ( $data[0] == 'System' || $tz == '' ) {
|
||||
# Global offset in minutes.
|
||||
if( isset($wgLocalTZoffset) ) $minDiff = $wgLocalTZoffset;
|
||||
} else if ( $data[0] == 'Offset' ) {
|
||||
$minDiff = intval( $data[1] );
|
||||
} else {
|
||||
$hrDiff = intval( $tz );
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -1566,6 +1566,9 @@ please see math/README to configure.',
|
|||
'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',
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ function unhidetzbutton() {
|
|||
if (tzb) {
|
||||
tzb.style.display = 'inline';
|
||||
}
|
||||
updateTimezoneSelection(false);
|
||||
}
|
||||
|
||||
// in [-]HH:MM format...
|
||||
|
|
@ -113,6 +114,50 @@ function fetchTimezone() {
|
|||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue