wiki.techinc.nl/tests/phpunit/includes/language/TimeAdjustTest.php
Máté Szabó 939a1576e0 Language: Don't generate local dates in the year 10000
MediaWiki only supports 14 character timestamps, and most date input
fields accordingly limit the accepted input range to fit within that
constraint, so the largest acceptable date is 9999-12-31 23:59:59.
However, if an user's own timezone preference is set to a timezone with
a higher offset than the server timezone, such dates may overflow into
the year 10000 and cause an error ("The timestamp XYZ should have 14
characters"). This very commonly happens when an admin decides to block
an user until 9999-12-31 instead of using the infinite expiry for some
reason, effectively breaking the block log for every user with a
timezone offset higher than the server offset.

Making MediaWiki support dates beyond the year 10000 would be a larger
undertaking, so for now, limit the impact of this problem by ensuring
that userAdjust() does not generate a local date that sprintfDate()
would not be able to handle.

Bug: T32148
Bug: T277809
Change-Id: I17ceee6c80dcc1559c6d66f1956ba1f0a4b519a3
2024-02-03 14:53:31 +01:00

97 lines
3.8 KiB
PHP

<?php
use MediaWiki\MainConfigNames;
class TimeAdjustTest extends MediaWikiLangTestCase {
private const LOCAL_TZ_OFFSET = 17;
/**
* Test offset usage for a given Language::userAdjust
* @dataProvider dataUserAdjust
* @covers Language::userAdjust
*/
public function testUserAdjust( string $date, $correction, string $expected ) {
$this->overrideConfigValue( MainConfigNames::LocalTZoffset, self::LOCAL_TZ_OFFSET );
$this->assertSame(
$expected,
$this->getServiceContainer()->getContentLanguage()->userAdjust( $date, $correction )
);
}
public static function dataUserAdjust() {
// Note: make sure to use dates in the past, especially with geographical time zones, to avoid any
// chance of tests failing due to a change to the time zone rules.
yield 'Literal int 0 (technically undocumented)' => [ '20221015120000', 0, '20221015120000' ];
yield 'Literal int 2 (technically undocumented)' => [ '20221015120000', 2, '20221015140000' ];
yield 'Literal int -2 (technically undocumented)' => [ '20221015120000', -2, '20221015100000' ];
yield 'Literal 0' => [ '20221015120000', '0', '20221015120000' ];
yield 'Literal 5' => [ '20221015120000', '5', '20221015170000' ];
yield 'Literal -5' => [ '20221015120000', '-5', '20221015070000' ];
$offsetsData = [
'+00:00' => [ '20221015120000', '20221015120000', 0 ],
'+02:00' => [ '20221015120000', '20221015140000', 2 * 60 ],
'+02:15' => [ '20221015120000', '20221015141500', 2.25 * 60 ],
'+14:00' => [ '20221015120000', '20221016020000', 14 * 60 ],
'-06:00' => [ '20221015120000', '20221015060000', -6 * 60 ],
'-06:45' => [ '20221015120000', '20221015051500', -6.75 * 60 ],
'-12:00' => [ '20221015120000', '20221015000000', -12 * 60 ],
];
foreach ( $offsetsData as $offset => [ $time, $expected, $minutesVal ] ) {
yield "Literal $offset" => [ $time, $offset, $expected ];
yield "Full format $offset" => [ $time, "Offset|$minutesVal", $expected ];
}
yield 'Literal +15:00, capped at +14' => [ '20221015120000', '+15:00', '20221016020000' ];
yield 'Full format +15:00, capped at +14' => [ '20221015120000', 'Offset|' . ( 15 * 60 ), '20221016020000' ];
yield 'Literal -13:00, capped at -12' => [ '20221015120000', '-13:00', '20221015000000' ];
yield 'Full format -13:00, capped at -12' => [ '20221015120000', 'Offset|' . ( -13 * 60 ), '20221015000000' ];
yield 'Geo: Europe/Rome when +2 and +2 is stored' => [
'20221015120000',
'ZoneInfo|120|Europe/Rome',
'20221015140000'
];
yield 'Geo: Europe/Rome when +2 and +1 is stored' => [
'20221015120000',
'ZoneInfo|60|Europe/Rome',
'20221015140000'
];
yield 'Geo: Europe/Rome when +1 and +2 is stored' => [
'20220320120000',
'ZoneInfo|120|Europe/Rome',
'20220320130000'
];
yield 'Geo: Europe/Rome when +1 and +1 is stored' => [
'20220320120000',
'ZoneInfo|60|Europe/Rome',
'20220320130000'
];
yield 'Invalid geographical zone, fall back to offset' => [
'20221015120000',
'ZoneInfo|42|Eriador/Hobbiton',
'20221015124200'
];
// These fall back to the local offset
yield 'System 0, fallback to local offset' => [ '20221015120000', 'System|0', '20221015121700' ];
yield 'System 120, fallback to local offset' => [ '20221015120000', 'System|120', '20221015121700' ];
yield 'System -60, fallback to local offset' => [ '20221015120000', 'System|-60', '20221015121700' ];
yield 'Garbage, fallback to local offset' => [ '20221015120000', 'WhatAmIEvenDoingHere', '20221015121700' ];
yield 'Empty string, fallback to local offset' => [ '20221015120000', '', '20221015121700' ];
yield 'T32148 - local date in year 10000' => [
'99991231235959',
'ZoneInfo|600|Asia/Vladivostok',
'99991231235959'
];
yield 'T32148 - date in year 10000 due to local offset' => [
'99991231235959',
'System|0',
'99991231235959'
];
}
}