From e9e1b0a77731db65257bb15f3195a2be30deaed5 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Fri, 22 Mar 2013 12:41:03 -0400 Subject: [PATCH] (bug 33454) Add timezone support to Language::sprintfDate Add an optional timezone parameter to Language::sprintfDate, add format characters eIOPTZ, and correct crU. While we're at it, remove backwards-compatability code for 'N' and then merge the existing switch cases for cr and wNzWtLoU that are basically identical, since all those cases need to be changed anyway. Bug: 33454 Change-Id: Iea1f78428bc0d32d6395818311dbe4b94d776c42 --- languages/Language.php | 107 +++++++++------------- tests/phpunit/languages/LanguageTest.php | 111 ++++++++++++++++++++++- 2 files changed, 152 insertions(+), 66 deletions(-) diff --git a/languages/Language.php b/languages/Language.php index 8aaaec0765..b653f9975a 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -1018,8 +1018,8 @@ class Language { * internationalisation, a reduced set of format characters, and a better * escaping format. * - * Supported format characters are dDjlNwzWFmMntLoYyaAgGhHiscrU. See the - * PHP manual for definitions. There are a number of extensions, which + * Supported format characters are dDjlNwzWFmMntLoYyaAgGhHiscrUeIOPTZ. See + * the PHP manual for definitions. There are a number of extensions, which * start with "x": * * xn Do not translate digits of the next numeric format character @@ -1065,22 +1065,24 @@ class Language { * Backslash escaping is also supported. * * Input timestamp is assumed to be pre-normalized to the desired local - * time zone, if any. + * time zone, if any. Note that the format characters crUeIOPTZ will assume + * $ts is UTC if $zone is not given. * * @param $format String * @param $ts String: 14-character timestamp * YYYYMMDDHHMMSS * 01234567890123 + * @param $zone DateTimeZone: Timezone of $ts * @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai? * * @return string */ - function sprintfDate( $format, $ts ) { + function sprintfDate( $format, $ts, DateTimeZone $zone = null ) { $s = ''; $raw = false; $roman = false; $hebrewNum = false; - $unix = false; + $dateTimeObj = false; $rawToggle = false; $iranian = false; $hebrew = false; @@ -1126,8 +1128,12 @@ class Language { $num = substr( $ts, 6, 2 ); break; case 'D': - if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts ); - $s .= $this->getWeekdayAbbreviation( gmdate( 'w', $unix ) + 1 ); + if ( !$dateTimeObj ) { + $dateTimeObj = DateTime::createFromFormat( + 'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' ) + ); + } + $s .= $this->getWeekdayAbbreviation( $dateTimeObj->format( 'w' ) + 1 ); break; case 'j': $num = intval( substr( $ts, 6, 2 ) ); @@ -1151,35 +1157,12 @@ class Language { $num = $hebrew[2]; break; case 'l': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $s .= $this->getWeekdayName( gmdate( 'w', $unix ) + 1 ); - break; - case 'N': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $w = gmdate( 'w', $unix ); - $num = $w ? $w : 7; - break; - case 'w': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $num = gmdate( 'w', $unix ); - break; - case 'z': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $num = gmdate( 'z', $unix ); - break; - case 'W': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); + if ( !$dateTimeObj ) { + $dateTimeObj = DateTime::createFromFormat( + 'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' ) + ); } - $num = gmdate( 'W', $unix ); + $s .= $this->getWeekdayName( $dateTimeObj->format( 'w' ) + 1 ); break; case 'F': $s .= $this->getMonthName( substr( $ts, 4, 2 ) ); @@ -1229,30 +1212,12 @@ class Language { } $num = $hebrew[1]; break; - case 't': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $num = gmdate( 't', $unix ); - break; case 'xjt': if ( !$hebrew ) { $hebrew = self::tsToHebrew( $ts ); } $num = $hebrew[3]; break; - case 'L': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $num = gmdate( 'L', $unix ); - break; - case 'o': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $num = gmdate( 'o', $unix ); - break; case 'Y': $num = substr( $ts, 0, 4 ); break; @@ -1328,22 +1293,36 @@ class Language { $num = substr( $ts, 12, 2 ); break; case 'c': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); - } - $s .= gmdate( 'c', $unix ); - break; case 'r': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); + case 'e': + case 'O': + case 'P': + case 'T': + // Pass through string from $dateTimeObj->format() + if ( !$dateTimeObj ) { + $dateTimeObj = DateTime::createFromFormat( + 'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' ) + ); } - $s .= gmdate( 'r', $unix ); + $s .= $dateTimeObj->format( $code ); break; + case 'w': + case 'N': + case 'z': + case 'W': + case 't': + case 'L': + case 'o': case 'U': - if ( !$unix ) { - $unix = wfTimestamp( TS_UNIX, $ts ); + case 'I': + case 'Z': + // Pass through number from $dateTimeObj->format() + if ( !$dateTimeObj ) { + $dateTimeObj = DateTime::createFromFormat( + 'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' ) + ); } - $num = $unix; + $num = $dateTimeObj->format( $code ); break; case '\\': # Backslash escaping diff --git a/tests/phpunit/languages/LanguageTest.php b/tests/phpunit/languages/LanguageTest.php index f55684ff51..d5dbfb24db 100644 --- a/tests/phpunit/languages/LanguageTest.php +++ b/tests/phpunit/languages/LanguageTest.php @@ -511,10 +511,10 @@ class LanguageTest extends LanguageClassesTestCase { } /** - * bug 33454. sprintfDate should always use UTC. + * sprintfDate should always use UTC when no zone is given. * @dataProvider provideSprintfDateSamples */ - function testSprintfDateTZ( $format, $ts, $expected, $msg ) { + function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) { $oldTZ = date_default_timezone_get(); $res = date_default_timezone_set( 'Asia/Seoul' ); if ( !$res ) { @@ -530,42 +530,65 @@ class LanguageTest extends LanguageClassesTestCase { date_default_timezone_set( $oldTZ ); } + /** + * sprintfDate should use passed timezone + * @dataProvider provideSprintfDateSamples + */ + function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) { + $tz = new DateTimeZone( 'Asia/Seoul' ); + if ( !$tz ) { + $this->markTestSkipped( "Error getting Timezone" ); + } + + $this->assertEquals( + $expected, + $this->getLang()->sprintfDate( $format, $ts, $tz ), + "sprintfDate('$format', '$ts', 'Asia/Seoul'): $msg" + ); + } + public static function provideSprintfDateSamples() { return array( array( 'xiY', '20111212000000', '1390', // note because we're testing English locale we get Latin-standard digits + '1390', 'Iranian calendar full year' ), array( 'xiy', '20111212000000', '90', + '90', 'Iranian calendar short year' ), array( 'o', '20120101235000', '2011', + '2011', 'ISO 8601 (week) year' ), array( 'W', '20120101235000', '52', + '52', 'Week number' ), array( 'W', '20120102235000', '1', + '1', 'Week number' ), array( 'o-\\WW-N', '20091231235000', '2009-W53-4', + '2009-W53-4', 'leap week' ), // What follows is mostly copied from http://www.mediawiki.org/wiki/Help:Extension:ParserFunctions#.23time @@ -573,252 +596,336 @@ class LanguageTest extends LanguageClassesTestCase { 'Y', '20120102090705', '2012', + '2012', 'Full year' ), array( 'y', '20120102090705', '12', + '12', '2 digit year' ), array( 'L', '20120102090705', '1', + '1', 'Leap year' ), array( 'n', '20120102090705', '1', + '1', 'Month index, not zero pad' ), array( 'N', '20120102090705', '01', + '01', 'Month index. Zero pad' ), array( 'M', '20120102090705', 'Jan', + 'Jan', 'Month abbrev' ), array( 'F', '20120102090705', 'January', + 'January', 'Full month' ), array( 'xg', '20120102090705', 'January', + 'January', 'Genitive month name (same in EN)' ), array( 'j', '20120102090705', '2', + '2', 'Day of month (not zero pad)' ), array( 'd', '20120102090705', '02', + '02', 'Day of month (zero-pad)' ), array( 'z', '20120102090705', '1', + '1', 'Day of year (zero-indexed)' ), array( 'D', '20120102090705', 'Mon', + 'Mon', 'Day of week (abbrev)' ), array( 'l', '20120102090705', 'Monday', + 'Monday', 'Full day of week' ), array( 'N', '20120101090705', '7', + '7', 'Day of week (Mon=1, Sun=7)' ), array( 'w', '20120101090705', '0', + '0', 'Day of week (Sun=0, Sat=6)' ), array( 'N', '20120102090705', '1', + '1', 'Day of week' ), array( 'a', '20120102090705', 'am', + 'am', 'am vs pm' ), array( 'A', '20120102120000', 'PM', + 'PM', 'AM vs PM' ), array( 'a', '20120102000000', 'am', + 'am', 'AM vs PM' ), array( 'g', '20120102090705', '9', + '9', '12 hour, not Zero' ), array( 'h', '20120102090705', '09', + '09', '12 hour, zero padded' ), array( 'G', '20120102090705', '9', + '9', '24 hour, not zero' ), array( 'H', '20120102090705', '09', + '09', '24 hour, zero' ), array( 'H', '20120102110705', '11', + '11', '24 hour, zero' ), array( 'i', '20120102090705', '07', + '07', 'Minutes' ), array( 's', '20120102090705', '05', + '05', 'seconds' ), array( 'U', '20120102090705', '1325495225', + '1325462825', 'unix time' ), array( 't', '20120102090705', '31', + '31', 'Days in current month' ), array( 'c', '20120102090705', '2012-01-02T09:07:05+00:00', + '2012-01-02T09:07:05+09:00', 'ISO 8601 timestamp' ), array( 'r', '20120102090705', 'Mon, 02 Jan 2012 09:07:05 +0000', + 'Mon, 02 Jan 2012 09:07:05 +0900', 'RFC 5322' ), + array( + 'e', + '20120102090705', + 'UTC', + 'Asia/Seoul', + 'Timezone identifier' + ), + array( + 'I', + '19880602090705', + '0', + '1', + 'DST indicator' + ), + array( + 'O', + '20120102090705', + '+0000', + '+0900', + 'Timezone offset' + ), + array( + 'P', + '20120102090705', + '+00:00', + '+09:00', + 'Timezone offset with colon' + ), + array( + 'T', + '20120102090705', + 'UTC', + 'KST', + 'Timezone abbreviation' + ), + array( + 'Z', + '20120102090705', + '0', + '32400', + 'Timezone offset in seconds' + ), array( 'xmj xmF xmn xmY', '20120102090705', '7 Safar 2 1433', + '7 Safar 2 1433', 'Islamic' ), array( 'xij xiF xin xiY', '20120102090705', '12 Dey 10 1390', + '12 Dey 10 1390', 'Iranian' ), array( 'xjj xjF xjn xjY', '20120102090705', '7 Tevet 4 5772', + '7 Tevet 4 5772', 'Hebrew' ), array( 'xjt', '20120102090705', '29', + '29', 'Hebrew number of days in month' ), array( 'xjx', '20120102090705', 'Tevet', + 'Tevet', 'Hebrew genitive month name (No difference in EN)' ), array( 'xkY', '20120102090705', '2555', + '2555', 'Thai year' ), array( 'xoY', '20120102090705', '101', + '101', 'Minguo' ), array( 'xtY', '20120102090705', '平成24', + '平成24', 'nengo' ), array( 'xrxkYY', '20120102090705', 'MMDLV2012', + 'MMDLV2012', 'Roman numerals' ), array( 'xhxjYY', '20120102090705', 'ה\'תשע"ב2012', + 'ה\'תשע"ב2012', 'Hebrew numberals' ), array( 'xnY', '20120102090705', '2012', + '2012', 'Raw numerals (doesn\'t mean much in EN)' ), array( '[[Y "(yea"\\r)]] \\"xx\\"', '20120102090705', '[[2012 (year)]] "x"', + '[[2012 (year)]] "x"', 'Various escaping' ), -- 2.20.1