From 77d2f16760948cfd1d838cf85794071fc7a01179 Mon Sep 17 00:00:00 2001 From: Jeroen De Dauw Date: Tue, 10 Apr 2012 15:37:03 +0200 Subject: [PATCH] duration formatter, makes time in sec easy to read The Language::formatDuration() method introduced by this patch let us easily render an amount of seconds for easier human reading. $ maintenance/eval.php > var_dump( $wgLang->formatDuration( 1000 ); string(25) "16 minutes and 40 seconds" Also ran rebuildLanguage.php on Siebrands request Change-Id: If287fb10e897d3d2374cf6eeae3bc5be00cdfc01 --- languages/Language.php | 73 ++++++++++++ languages/messages/MessagesEn.php | 50 +++++---- maintenance/language/messages.inc | 12 ++ tests/phpunit/languages/LanguageTest.php | 137 +++++++++++++++++++++++ 4 files changed, 252 insertions(+), 20 deletions(-) diff --git a/languages/Language.php b/languages/Language.php index 463ea19062..e6feb45edf 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -139,6 +139,22 @@ class Language { 'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12' ); + /** + * @since 1.20 + * @var array + */ + static public $durationIntervals = array( + 'millennia' => 31557600000, + 'centuries' => 3155760000, + 'decades' => 315576000, + 'years' => 31557600, // 86400 * 365.25 + 'weeks' => 604800, + 'days' => 86400, + 'hours' => 3600, + 'minutes' => 60, + 'seconds' => 1, + ); + /** * Get a cached language object for a given language code * @param $code String @@ -1908,6 +1924,63 @@ class Language { return $this->sprintfDate( $df, $ts ); } + /** + * Takes a number of seconds and turns it into a text using values such as hours and minutes. + * + * @since 1.20 + * + * @param integer $seconds The amount of seconds. + * @param array $chosenIntervals The intervals to enable. + * + * @return string + */ + public function formatDuration( $seconds, array $chosenIntervals = array() ) { + $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals ); + + $segments = array(); + + foreach ( $intervals as $intervalName => $intervalValue ) { + $message = new Message( 'duration-' . $intervalName, array( $intervalValue ) ); + $segments[] = $message->inLanguage( $this )->escaped(); + } + + return $this->listToText( $segments ); + } + + /** + * Takes a number of seconds and returns an array with a set of corresponding intervals. + * For example 65 will be turned into array( minutes => 1, seconds => 5 ). + * + * @since 1.20 + * + * @param integer $seconds The amount of seconds. + * @param array $chosenIntervals The intervals to enable. + * + * @return array + */ + public function getDurationIntervals( $seconds, array $chosenIntervals = array() ) { + if ( empty( $chosenIntervals ) ) { + $chosenIntervals = array( 'millennia', 'centuries', 'decades', 'years', 'days', 'hours', 'minutes', 'seconds' ); + } + + $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) ); + $sortedNames = array_keys( $intervals ); + $smallestInterval = array_pop( $sortedNames ); + + $segments = array(); + + foreach ( $intervals as $name => $length ) { + $value = floor( $seconds / $length ); + + if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) { + $seconds -= $value * $length; + $segments[$name] = $value; + } + } + + return $segments; + } + /** * Internal helper function for userDate(), userTime() and userTimeAndDate() * diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 6b07bc692f..1055554237 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -999,9 +999,6 @@ Please report this to an [[Special:ListUsers/sysop|administrator]], making note 'directorycreateerror' => 'Could not create directory "$1".', 'filenotfound' => 'Could not find file "$1".', 'fileexistserror' => 'Unable to write to file "$1": File exists.', -'filereadonlyerror' => 'Unable to modify the file "$1" because the file repository "$2" is in read-only mode. - -The administrator who locked it offered this explanation: "$3".', 'unexpected' => 'Unexpected value: "$1"="$2".', 'formerror' => 'Error: Could not submit form.', 'badarticleerror' => 'This action cannot be performed on this page.', @@ -1039,6 +1036,9 @@ $2', 'ns-specialprotected' => 'Special pages cannot be edited.', 'titleprotected' => 'This title has been protected from creation by [[User:$1|$1]]. The reason given is "\'\'$2\'\'".', +'filereadonlyerror' => 'Unable to the modify the file "$1" because the file repository "$2" is in read-only mode. + +The administrator who locked it offered this explanation: "$3".', # Virus scanner 'virus-badscanner' => "Bad configuration: Unknown virus scanner: ''$1''", @@ -2282,7 +2282,6 @@ If the problem persists, contact an [[Special:ListUsers/sysop|administrator]].', 'backend-fail-contenttype' => 'Could not determine the content type of the file to store at "$1".', 'backend-fail-batchsize' => 'Storage backend given a batch of $1 file {{PLURAL:$1|operation|operations}}; the limit is $2 {{PLURAL:$2|operation|operations}}.', -# File journal 'filejournal-fail-dbconnect' => 'Could not connect to the journal database for storage backend "$1".', 'filejournal-fail-dbquery' => 'Could not update the journal database for storage backend "$1".', @@ -2653,23 +2652,23 @@ You can narrow down the view by selecting a log type, the username (case-sensiti 'log-title-wildcard' => 'Search titles starting with this text', # Special:AllPages -'allpages' => 'All pages', -'allpages-summary' => '', # do not translate or duplicate this message to other languages -'alphaindexline' => '$1 to $2', -'nextpage' => 'Next page ($1)', -'prevpage' => 'Previous page ($1)', -'allpagesfrom' => 'Display pages starting at:', -'allpagesto' => 'Display pages ending at:', -'allarticles' => 'All pages', -'allinnamespace' => 'All pages ($1 namespace)', -'allnotinnamespace' => 'All pages (not in $1 namespace)', -'allpagesprev' => 'Previous', -'allpagesnext' => 'Next', -'allpagessubmit' => 'Go', -'allpagesprefix' => 'Display pages with prefix:', -'allpagesbadtitle' => 'The given page title was invalid or had an inter-language or inter-wiki prefix. +'allpages' => 'All pages', +'allpages-summary' => '', # do not translate or duplicate this message to other languages +'alphaindexline' => '$1 to $2', +'nextpage' => 'Next page ($1)', +'prevpage' => 'Previous page ($1)', +'allpagesfrom' => 'Display pages starting at:', +'allpagesto' => 'Display pages ending at:', +'allarticles' => 'All pages', +'allinnamespace' => 'All pages ($1 namespace)', +'allnotinnamespace' => 'All pages (not in $1 namespace)', +'allpagesprev' => 'Previous', +'allpagesnext' => 'Next', +'allpagessubmit' => 'Go', +'allpagesprefix' => 'Display pages with prefix:', +'allpagesbadtitle' => 'The given page title was invalid or had an inter-language or inter-wiki prefix. It may contain one or more characters which cannot be used in titles.', -'allpages-bad-ns' => '{{SITENAME}} does not have namespace "$1".', +'allpages-bad-ns' => '{{SITENAME}} does not have namespace "$1".', 'allpages-hide-redirects' => 'Hide redirects', # Special:Categories @@ -4832,4 +4831,15 @@ Otherwise, you can use the easy form below. Your comment will be added to the pa 'api-error-uploaddisabled' => 'Uploading is disabled on this wiki.', 'api-error-verification-error' => 'This file might be corrupt, or have the wrong extension.', +# Durations +'duration-seconds' => '$1 {{PLURAL:$1|second|seconds}}', +'duration-minutes' => '$1 {{PLURAL:$1|minute|minutes}}', +'duration-hours' => '$1 {{PLURAL:$1|hour|hours}}', +'duration-days' => '$1 {{PLURAL:$1|day|days}}', +'duration-weeks' => '$1 {{PLURAL:$1|week|weeks}}', +'duration-years' => '$1 {{PLURAL:$1|year|years}}', +'duration-decades' => '$1 {{PLURAL:$1|decade|decades}}', +'duration-centuries' => '$1 {{PLURAL:$1|century|centuries}}', +'duration-millennia' => '$1 {{PLURAL:$1|millennium|millennia}}', + ); diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index 4984495753..d4184eb970 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -3693,6 +3693,17 @@ $wgMessageStructure = array( 'api-error-uploaddisabled', 'api-error-verification-error', ), + 'duration' => array( + 'duration-seconds', + 'duration-minutes', + 'duration-hours', + 'duration-days', + 'duration-weeks', + 'duration-years', + 'duration-decades', + 'duration-centuries', + 'duration-millennia' + ), ); /** Comments for each block */ @@ -3931,4 +3942,5 @@ Variants for Chinese language", 'logging-irc' => 'For IRC, see bug 34508. Do not change', 'feedback' => 'Feedback', 'apierrors' => 'API errors', + 'duration' => 'Durations', ); diff --git a/tests/phpunit/languages/LanguageTest.php b/tests/phpunit/languages/LanguageTest.php index c089c31d18..42396708d3 100644 --- a/tests/phpunit/languages/LanguageTest.php +++ b/tests/phpunit/languages/LanguageTest.php @@ -654,4 +654,141 @@ class LanguageTest extends MediaWikiTestCase { ), ); } + + + + /** + * @dataProvider provideFormatDuration + */ + function testFormatDuration( $duration, $expected, $intervals = array() ) { + $this->assertEquals( + $expected, + $this->lang->formatDuration( $duration, $intervals ), + "formatDuration('$duration'): $expected" + ); + } + + function provideFormatDuration() { + return array( + array( + 0, + '0 seconds', + ), + array( + 1, + '1 second', + ), + array( + 2, + '2 seconds', + ), + array( + 60, + '1 minute', + ), + array( + 2 * 60, + '2 minutes', + ), + array( + 3600, + '1 hour', + ), + array( + 2 * 3600, + '2 hours', + ), + array( + 24 * 3600, + '1 day', + ), + array( + 2 * 86400, + '2 days', + ), + array( + 365.25 * 86400, // 365.25 * 86400 = 31557600 + '1 year', + ), + array( + 2 * 31557600, + '2 years', + ), + array( + 10 * 31557600, + '1 decade', + ), + array( + 20 * 31557600, + '2 decades', + ), + array( + 100 * 31557600, + '1 century', + ), + array( + 200 * 31557600, + '2 centuries', + ), + array( + 1000 * 31557600, + '1 millennium', + ), + array( + 2000 * 31557600, + '2 millennia', + ), + array( + 9001, + '2 hours, 30 minutes and 1 second' + ), + array( + 3601, + '1 hour and 1 second' + ), + array( + 31557600 + 2 * 86400 + 9000, + '1 year, 2 days, 2 hours and 30 minutes' + ), + array( + 42 * 1000 * 31557600 + 42, + '42 millennia and 42 seconds' + ), + array( + 60, + '60 seconds', + array( 'seconds' ), + ), + array( + 61, + '61 seconds', + array( 'seconds' ), + ), + array( + 1, + '1 second', + array( 'seconds' ), + ), + array( + 31557600 + 2 * 86400 + 9000, + '1 year, 2 days and 150 minutes', + array( 'years', 'days', 'minutes' ), + ), + array( + 42, + '0 days', + array( 'years', 'days' ), + ), + array( + 31557600 + 2 * 86400 + 9000, + '1 year, 2 days and 150 minutes', + array( 'minutes', 'days', 'years' ), + ), + array( + 42, + '0 days', + array( 'days', 'years' ), + ), + ); + } } -- 2.20.1