Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / htmlform / fields / HTMLTimezoneField.php
blobd9f217a74a7f808dd5e33a59f161d238b9a60a6f
1 <?php
3 namespace MediaWiki\HTMLForm\Field;
5 use DateTime;
6 use DateTimeZone;
7 use InvalidArgumentException;
8 use MediaWiki\Context\RequestContext;
9 use MediaWiki\MainConfigNames;
10 use MediaWiki\MediaWikiServices;
11 use MediaWiki\User\UserTimeCorrection;
12 use MediaWiki\Utils\MWTimestamp;
13 use Wikimedia\Message\ITextFormatter;
14 use Wikimedia\Message\MessageValue;
16 /**
17 * Dropdown widget that allows the user to select a timezone, either by choosing a geographic zone, by using the wiki
18 * default, or by manually specifying an offset. It also has an option to fill the value from the browser settings.
19 * The value of this field is in a format accepted by UserTimeCorrection.
21 class HTMLTimezoneField extends HTMLSelectOrOtherField {
22 private const FIELD_CLASS = 'mw-htmlform-timezone-field';
24 /** @var ITextFormatter */
25 private $msgFormatter;
27 /**
28 * @stable to call
29 * @inheritDoc
30 * Note that no options should be specified.
32 public function __construct( $params ) {
33 if ( isset( $params['options'] ) ) {
34 throw new InvalidArgumentException( "Options should not be provided to " . __CLASS__ );
36 $params['placeholder-message'] ??= 'timezone-useoffset-placeholder';
37 $params['options'] = [];
38 parent::__construct( $params );
39 $lang = $this->mParent ? $this->mParent->getLanguage() : RequestContext::getMain()->getLanguage();
40 $langCode = $lang->getCode();
41 $this->msgFormatter = MediaWikiServices::getInstance()->getMessageFormatterFactory()
42 ->getTextFormatter( $langCode );
43 $this->mOptions = $this->getTimezoneOptions();
46 /**
47 * @return array<string|string[]>
49 private function getTimezoneOptions(): array {
50 $opt = [];
52 $localTZoffset = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LocalTZoffset );
53 $timeZoneList = $this->getTimeZoneList();
55 $timestamp = MWTimestamp::getLocalInstance();
56 // Check that the LocalTZoffset is the same as the local time zone offset
57 if ( $localTZoffset === (int)$timestamp->format( 'Z' ) / 60 ) {
58 $timezoneName = $timestamp->getTimezone()->getName();
59 // Localize timezone
60 if ( isset( $timeZoneList[$timezoneName] ) ) {
61 $timezoneName = $timeZoneList[$timezoneName]['name'];
63 $server_tz_msg = $this->msgFormatter->format(
64 MessageValue::new( 'timezoneuseserverdefault', [ $timezoneName ] )
66 } else {
67 $tzstring = UserTimeCorrection::formatTimezoneOffset( $localTZoffset );
68 $server_tz_msg = $this->msgFormatter->format(
69 MessageValue::new( 'timezoneuseserverdefault', [ $tzstring ] )
72 $opt[$server_tz_msg] = "System|$localTZoffset";
73 $opt[$this->msgFormatter->format( MessageValue::new( 'timezoneuseoffset' ) )] = 'other';
74 $opt[$this->msgFormatter->format( MessageValue::new( 'guesstimezone' ) )] = 'guess';
76 foreach ( $timeZoneList as $timeZoneInfo ) {
77 $region = $timeZoneInfo['region'];
78 if ( !isset( $opt[$region] ) ) {
79 $opt[$region] = [];
81 $opt[$region][$timeZoneInfo['name']] = $timeZoneInfo['timecorrection'];
83 return $opt;
86 /**
87 * Get a list of all time zones
88 * @return string[][] A list of all time zones. The system name of the time zone is used as key and
89 * the value is an array which contains localized name, the timecorrection value used for
90 * preferences and the region
92 private function getTimeZoneList(): array {
93 $identifiers = DateTimeZone::listIdentifiers();
94 '@phan-var array|false $identifiers'; // See phan issue #3162
95 if ( $identifiers === false ) {
96 return [];
98 sort( $identifiers );
100 $tzRegions = [
101 'Africa' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-africa' ) ),
102 'America' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-america' ) ),
103 'Antarctica' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-antarctica' ) ),
104 'Arctic' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-arctic' ) ),
105 'Asia' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-asia' ) ),
106 'Atlantic' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-atlantic' ) ),
107 'Australia' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-australia' ) ),
108 'Europe' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-europe' ) ),
109 'Indian' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-indian' ) ),
110 'Pacific' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-pacific' ) ),
112 asort( $tzRegions );
114 $timeZoneList = [];
116 $now = new DateTime();
118 foreach ( $identifiers as $identifier ) {
119 $parts = explode( '/', $identifier, 2 );
121 // DateTimeZone::listIdentifiers() returns a number of
122 // backwards-compatibility entries. This filters them out of the
123 // list presented to the user.
124 if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
125 continue;
128 // Localize region
129 $parts[0] = $tzRegions[$parts[0]];
131 $dateTimeZone = new DateTimeZone( $identifier );
132 $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
134 $display = str_replace( '_', ' ', $parts[0] . '/' . $parts[1] );
135 $value = "ZoneInfo|$minDiff|$identifier";
137 $timeZoneList[$identifier] = [
138 'name' => $display,
139 'timecorrection' => $value,
140 'region' => $parts[0],
144 return $timeZoneList;
148 * @inheritDoc
150 public function validate( $value, $alldata ) {
151 $p = parent::validate( $value, $alldata );
152 if ( $p !== true ) {
153 return $p;
156 if ( !( new UserTimeCorrection( $value ) )->isValid() ) {
157 return $this->mParent->msg( 'timezone-invalid' )->escaped();
160 return true;
164 * @inheritDoc
166 protected function getFieldClasses(): array {
167 $classes = parent::getFieldClasses();
168 $classes[] = self::FIELD_CLASS;
169 return $classes;
173 /** @deprecated class alias since 1.42 */
174 class_alias( HTMLTimezoneField::class, 'HTMLTimezoneField' );