Issues (1513)

lib/utils/timezoneutil.php (3 issues)

1
<?php
2
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
6
 * SPDX-FileCopyrightText: Copyright 2020-2025 grommunio GmbH
7
 *
8
 * Class to generate AS compatible timezone information
9
 */
10
11
class TimezoneUtil {
12
	/**
13
	 * list of MS and AS compatible timezones.
14
	 *
15
	 * origin: http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx
16
	 * dots of tz identifiers were removed
17
	 *
18
	 * Updated at: 01.06.2012
19
	 */
20
	private static $mstzones = [
21
		"000" => ["Dateline Standard Time", "(GMT-12:00) International Date Line West"],
22
		"001" => ["Samoa Standard Time", "(GMT-11:00) Midway Island, Samoa"],
23
		"002" => ["Hawaiian Standard Time", "(GMT-10:00) Hawaii"],
24
		"003" => ["Alaskan Standard Time", "(GMT-09:00) Alaska"],
25
		"004" => ["Pacific Standard Time", "(GMT-08:00) Pacific Time (US and Canada); Tijuana"],
26
		"010" => ["Mountain Standard Time", "(GMT-07:00) Mountain Time (US and Canada)"],
27
		"013" => ["Mexico Standard Time 2", "(GMT-07:00) Chihuahua, La Paz, Mazatlan"],
28
		"015" => ["US Mountain Standard Time", "(GMT-07:00) Arizona"],
29
		"020" => ["Central Standard Time", "(GMT-06:00) Central Time (US and Canada"],
30
		"025" => ["Canada Central Standard Time", "(GMT-06:00) Saskatchewan"],
31
		"030" => ["Mexico Standard Time", "(GMT-06:00) Guadalajara, Mexico City, Monterrey"],
32
		"033" => ["Central America Standard Time", "(GMT-06:00) Central America"],
33
		"035" => ["Eastern Standard Time", "(GMT-05:00) Eastern Time (US and Canada)"],
34
		"040" => ["US Eastern Standard Time", "(GMT-05:00) Indiana (East)"],
35
		"045" => ["SA Pacific Standard Time", "(GMT-05:00) Bogota, Lima, Quito"],
36
		"uk1" => ["Venezuela Standard Time", "(GMT-04:30) Caracas"],                     // added
37
		"050" => ["Atlantic Standard Time", "(GMT-04:00) Atlantic Time (Canada)"],
38
		"055" => ["SA Western Standard Time", "(GMT-04:00) Caracas, La Paz"],
39
		"056" => ["Pacific SA Standard Time", "(GMT-04:00) Santiago"],
40
		"060" => ["Newfoundland and Labrador Standard Time", "(GMT-03:30) Newfoundland and Labrador"],
41
		"065" => ["E South America Standard Time", "(GMT-03:00) Brasilia"],
42
		"070" => ["SA Eastern Standard Time", "(GMT-03:00) Buenos Aires, Georgetown"],
43
		"073" => ["Greenland Standard Time", "(GMT-03:00) Greenland"],
44
		"075" => ["Mid-Atlantic Standard Time", "(GMT-02:00) Mid-Atlantic"],
45
		"080" => ["Azores Standard Time", "(GMT-01:00) Azores"],
46
		"083" => ["Cape Verde Standard Time", "(GMT-01:00) Cape Verde Islands"],
47
		"085" => ["GMT Standard Time", "(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London"],
48
		"090" => ["Greenwich Standard Time", "(GMT) Casablanca, Monrovia"],
49
		"095" => ["Central Europe Standard Time", "(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague"],
50
		"100" => ["Central European Standard Time", "(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb"],
51
		"105" => ["Romance Standard Time", "(GMT+01:00) Brussels, Copenhagen, Madrid, Paris"],
52
		"110" => ["W Europe Standard Time", "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"],
53
		"111" => ["W. Europe Standard Time", "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"],
54
		"113" => ["W Central Africa Standard Time", "(GMT+01:00) West Central Africa"],
55
		"115" => ["E Europe Standard Time", "(GMT+02:00) Bucharest"],
56
		"120" => ["Egypt Standard Time", "(GMT+02:00) Cairo"],
57
		"125" => ["FLE Standard Time", "(GMT+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius"],
58
		"130" => ["GTB Standard Time", "(GMT+02:00) Athens, Istanbul, Minsk"],
59
		"135" => ["Israel Standard Time", "(GMT+02:00) Jerusalem"],
60
		"140" => ["South Africa Standard Time", "(GMT+02:00) Harare, Pretoria"],
61
		"145" => ["Russian Standard Time", "(GMT+03:00) Moscow, St. Petersburg, Volgograd"],
62
		"150" => ["Arab Standard Time", "(GMT+03:00) Kuwait, Riyadh"],
63
		"155" => ["E Africa Standard Time", "(GMT+03:00) Nairobi"],
64
		"158" => ["Arabic Standard Time", "(GMT+03:00) Baghdad"],
65
		"160" => ["Iran Standard Time", "(GMT+03:30) Tehran"],
66
		"165" => ["Arabian Standard Time", "(GMT+04:00) Abu Dhabi, Muscat"],
67
		"170" => ["Caucasus Standard Time", "(GMT+04:00) Baku, Tbilisi, Yerevan"],
68
		"175" => ["Transitional Islamic State of Afghanistan Standard Time", "(GMT+04:30) Kabul"],
69
		"180" => ["Ekaterinburg Standard Time", "(GMT+05:00) Ekaterinburg"],
70
		"185" => ["West Asia Standard Time", "(GMT+05:00) Islamabad, Karachi, Tashkent"],
71
		"190" => ["India Standard Time", "(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi"],
72
		"193" => ["Nepal Standard Time", "(GMT+05:45) Kathmandu"],
73
		"195" => ["Central Asia Standard Time", "(GMT+06:00) Astana, Dhaka"],
74
		"200" => ["Sri Lanka Standard Time", "(GMT+06:00) Sri Jayawardenepura"],
75
		"201" => ["N Central Asia Standard Time", "(GMT+06:00) Almaty, Novosibirsk"],
76
		"203" => ["Myanmar Standard Time", "(GMT+06:30) Yangon Rangoon"],
77
		"205" => ["SE Asia Standard Time", "(GMT+07:00) Bangkok, Hanoi, Jakarta"],
78
		"207" => ["North Asia Standard Time", "(GMT+07:00) Krasnoyarsk"],
79
		"210" => ["China Standard Time", "(GMT+08:00) Beijing, Chongqing, Hong Kong SAR, Urumqi"],
80
		"215" => ["Singapore Standard Time", "(GMT+08:00) Kuala Lumpur, Singapore"],
81
		"220" => ["Taipei Standard Time", "(GMT+08:00) Taipei"],
82
		"225" => ["W Australia Standard Time", "(GMT+08:00) Perth"],
83
		"227" => ["North Asia East Standard Time", "(GMT+08:00) Irkutsk, Ulaanbaatar"],
84
		"230" => ["Korea Standard Time", "(GMT+09:00) Seoul"],
85
		"235" => ["Tokyo Standard Time", "(GMT+09:00) Osaka, Sapporo, Tokyo"],
86
		"240" => ["Yakutsk Standard Time", "(GMT+09:00) Yakutsk"],
87
		"245" => ["AUS Central Standard Time", "(GMT+09:30) Darwin"],
88
		"250" => ["Cen Australia Standard Time", "(GMT+09:30) Adelaide"],
89
		"255" => ["AUS Eastern Standard Time", "(GMT+10:00) Canberra, Melbourne, Sydney"],
90
		"260" => ["E Australia Standard Time", "(GMT+10:00) Brisbane"],
91
		"265" => ["Tasmania Standard Time", "(GMT+10:00) Hobart"],
92
		"270" => ["Vladivostok Standard Time", "(GMT+10:00) Vladivostok"],
93
		"275" => ["West Pacific Standard Time", "(GMT+10:00) Guam, Port Moresby"],
94
		"280" => ["Central Pacific Standard Time", "(GMT+11:00) Magadan, Solomon Islands, New Caledonia"],
95
		"285" => ["Fiji Islands Standard Time", "(GMT+12:00) Fiji Islands, Kamchatka, Marshall Islands"],
96
		"290" => ["New Zealand Standard Time", "(GMT+12:00) Auckland, Wellington"],
97
		"300" => ["Tonga Standard Time", "(GMT+13:00) Nuku'alofa"],
98
	];
99
100
	/**
101
	 * Python generated offset list
102
	 * dots in keys were removed.
103
	 *
104
	 * Array indices
105
	 *  0 = lBias
106
	 *  1 = lStandardBias
107
	 *  2 = lDSTBias
108
	 *  3 = wStartYear
109
	 *  4 = wStartMonth
110
	 *  5 = wStartDOW
111
	 *  6 = wStartDay
112
	 *  7 = wStartHour
113
	 *  8 = wStartMinute
114
	 *  9 = wStartSecond
115
	 * 10 = wStartMilliseconds
116
	 * 11 = wEndYear
117
	 * 12 = wEndMonth
118
	 * 13 = wEndDOW
119
	 * 14 = wEndDay
120
	 * 15 = wEndHour
121
	 * 16 = wEndMinute
122
	 * 17 = wEndSecond
123
	 * 18 = wEndMilloseconds
124
	 *
125
	 * As the $tzoneoffsets and the $mstzones need to be resolved in both directions,
126
	 * some offsets are commented as they are not available in the $mstzones.
127
	 *
128
	 * Created at: 01.06.2012
129
	 */
130
	private static $tzonesoffsets = [
131
		"Transitional Islamic State of Afghanistan Standard Time" => [-270, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
132
		"Alaskan Standard Time" => [540, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
133
		"Arab Standard Time" => [-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
134
		"Arabian Standard Time" => [-240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
135
		"Arabic Standard Time" => [-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
136
		// "Argentina Standard Time"                   => array(180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
137
		"Atlantic Standard Time" => [240, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
138
		"AUS Central Standard Time" => [-570, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
139
		"AUS Eastern Standard Time" => [-600, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0],
140
		// "Azerbaijan Standard Time"                  => array(-240, 0, -60,  0, 10, 0, 5, 5, 0, 0, 0,  0, 3, 0, 5, 4, 0, 0, 0),
141
		"Azores Standard Time" => [60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
142
		// "Bangladesh Standard Time"                  => array(-360, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
143
		"Canada Central Standard Time" => [360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
144
		"Cape Verde Standard Time" => [60, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
145
		"Caucasus Standard Time" => [-240, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
146
		"Cen Australia Standard Time" => [-570, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0],
147
		"Central America Standard Time" => [360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
148
		"Central Asia Standard Time" => [-360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
149
		// "Central Brazilian Standard Time"           => array(240, 0, -60,  0, 2, 6, 4, 23, 59, 59, 999,  0, 10, 6, 3, 23, 59, 59, 999),
150
		"Central Europe Standard Time" => [-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
151
		"Central European Standard Time" => [-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
152
		"Central Pacific Standard Time" => [-660, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
153
		"Central Standard Time" => [360, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
154
		"Mexico Standard Time" => [360, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 4, 0, 1, 2, 0, 0, 0],
155
		"China Standard Time" => [-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
156
		"Dateline Standard Time" => [720, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
157
		"E Africa Standard Time" => [-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
158
		"E Australia Standard Time" => [-600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
159
		"E Europe Standard Time" => [-120, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
160
		"E South America Standard Time" => [180, 0, -60, 0, 2, 6, 4, 23, 59, 59, 999, 0, 10, 6, 3, 23, 59, 59, 999],
161
		"Eastern Standard Time" => [300, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
162
		"Egypt Standard Time" => [-120, 0, -60, 0, 9, 4, 5, 23, 59, 59, 999, 0, 4, 4, 5, 23, 59, 59, 999],
163
		"Ekaterinburg Standard Time" => [-300, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
164
		"Fiji Islands Standard Time" => [-720, 0, -60, 0, 3, 0, 5, 3, 0, 0, 0, 0, 10, 0, 4, 2, 0, 0, 0],
165
		"FLE Standard Time" => [-120, 0, -60, 0, 10, 0, 5, 4, 0, 0, 0, 0, 3, 0, 5, 3, 0, 0, 0],
166
		// "Georgian Standard Time"                    => array(-240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
167
		"GMT Standard Time" => [0, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 3, 0, 5, 1, 0, 0, 0],
168
		"Greenland Standard Time" => [180, 0, -60, 0, 10, 6, 5, 23, 0, 0, 0, 0, 3, 6, 4, 22, 0, 0, 0],
169
		"Greenwich Standard Time" => [0, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
170
		"GTB Standard Time" => [-120, 0, -60, 0, 10, 0, 5, 4, 0, 0, 0, 0, 3, 0, 5, 3, 0, 0, 0],
171
		"Hawaiian Standard Time" => [600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
172
		"India Standard Time" => [-330, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
173
		"Iran Standard Time" => [-210, 0, -60, 0, 9, 1, 3, 23, 59, 59, 999, 0, 3, 6, 3, 23, 59, 59, 999],
174
		"Israel Standard Time" => [-120, 0, -60, 0, 9, 0, 4, 2, 0, 0, 0, 0, 3, 5, 5, 2, 0, 0, 0],
175
		// "Jordan Standard Time"                      => array(-120, 0, -60,  0, 10, 5, 5, 1, 0, 0, 0,  0, 3, 4, 5, 23, 59, 59, 999),
176
		// "Kamchatka Standard Time"                   => array(-720, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
177
		"Korea Standard Time" => [-540, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
178
		// "Magadan Standard Time"                     => array(-660, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
179
		// "Mauritius Standard Time"                   => array(-240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
180
		"Mid-Atlantic Standard Time" => [120, 0, -60, 0, 9, 0, 5, 2, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
181
		// "Middle East Standard Time"                 => array(-120, 0, -60,  0, 10, 6, 5, 23, 59, 59, 999,  0, 3, 6, 4, 23, 59, 59, 999),
182
		// "Montevideo Standard Time"                  => array(180, 0, -60,  0, 3, 0, 2, 2, 0, 0, 0,  0, 10, 0, 1, 2, 0, 0, 0),
183
		// "Morocco Standard Time"                     => array(0, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
184
		"Mountain Standard Time" => [420, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
185
		"Mexico Standard Time 2" => [420, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 4, 0, 1, 2, 0, 0, 0],
186
		"Myanmar Standard Time" => [-390, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
187
		"N Central Asia Standard Time" => [-360, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
188
		// "Namibia Standard Time"                     => array(-60, 0, -60,  0, 4, 0, 1, 2, 0, 0, 0,  0, 9, 0, 1, 2, 0, 0, 0),
189
		"Nepal Standard Time" => [-345, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
190
		"New Zealand Standard Time" => [-720, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 9, 0, 5, 2, 0, 0, 0],
191
		"Newfoundland and Labrador Standard Time" => [210, 0, -60, 0, 11, 0, 1, 0, 1, 0, 0, 0, 3, 0, 2, 0, 1, 0, 0],
192
		"North Asia East Standard Time" => [-480, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
193
		"North Asia Standard Time" => [-420, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
194
		"Pacific SA Standard Time" => [240, 0, -60, 0, 3, 6, 2, 23, 59, 59, 999, 0, 10, 6, 2, 23, 59, 59, 999],
195
		"Pacific Standard Time" => [480, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
196
		// "Pacific Standard Time (Mexico)"            => array(480, 0, -60,  0, 10, 0, 5, 2, 0, 0, 0,  0, 4, 0, 1, 2, 0, 0, 0),
197
		// "Pakistan Standard Time"                    => array(-300, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
198
		// "Paraguay Standard Time"                    => array(240, 0, -60,  0, 4, 6, 1, 23, 59, 59, 999,  0, 10, 6, 1, 23, 59, 59, 999),
199
		"Romance Standard Time" => [-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
200
		"Russian Standard Time" => [-180, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
201
		"SA Eastern Standard Time" => [180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
202
		"SA Pacific Standard Time" => [300, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
203
		"SA Western Standard Time" => [240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
204
		"Samoa Standard Time" => [660, 0, -60, 0, 3, 6, 5, 23, 59, 59, 999, 0, 9, 6, 5, 23, 59, 59, 999],
205
		"SE Asia Standard Time" => [-420, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
206
		"Singapore Standard Time" => [-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
207
		"South Africa Standard Time" => [-120, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
208
		"Sri Lanka Standard Time" => [-330, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
209
		// "Syria Standard Time"                       => array(-120, 0, -60,  0, 10, 4, 5, 23, 59, 59, 999,  0, 4, 4, 1, 23, 59, 59, 999),
210
		"Taipei Standard Time" => [-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
211
		"Tasmania Standard Time" => [-600, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0],
212
		"Tokyo Standard Time" => [-540, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
213
		"Tonga Standard Time" => [-780, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
214
		// "Ulaanbaatar Standard Time"                 => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
215
		"US Eastern Standard Time" => [300, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0],
216
		"US Mountain Standard Time" => [420, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
217
		// "UTC"                                       => array(0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
218
		// "UTC+12"                                    => array(-720, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
219
		// "UTC-02"                                    => array(120, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
220
		// "UTC-11"                                    => array(660, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
221
		"Venezuela Standard Time" => [270, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
222
		"Vladivostok Standard Time" => [-600, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
223
		"W Australia Standard Time" => [-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
224
		"W Central Africa Standard Time" => [-60, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
225
		"W Europe Standard Time" => [-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
226
		"West Asia Standard Time" => [-300, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
227
		"West Pacific Standard Time" => [-600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
228
		"Yakutsk Standard Time" => [-540, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0],
229
	];
230
231
	/**
232
	 * Generated list of PHP timezones in GMT timezones.
233
	 *
234
	 * Created at: 01.06.2012
235
	 */
236
	private static $phptimezones = [
237
		// -720 min
238
		"Dateline Standard Time" => [
239
			"Etc/GMT+12",
240
		],
241
242
		// -660 min
243
		"Samoa Standard Time" => [
244
			"Etc/GMT+11",
245
			"Pacific/Midway",
246
			"Pacific/Niue",
247
			"Pacific/Pago_Pago",
248
			"Pacific/Samoa",
249
			"US/Samoa",
250
		],
251
252
		// -600 min
253
		"Hawaiian Standard Time" => [
254
			"America/Adak",
255
			"America/Atka",
256
			"Etc/GMT+10",
257
			"HST",
258
			"Pacific/Honolulu",
259
			"Pacific/Johnston",
260
			"Pacific/Rarotonga",
261
			"Pacific/Tahiti",
262
			"US/Aleutian",
263
			"US/Hawaii",
264
		],
265
266
		// -570 min
267
		"-570" => [
268
			"Pacific/Marquesas",
269
		],
270
271
		// -540 min
272
		"Alaskan Standard Time" => [
273
			"America/Anchorage",
274
			"America/Juneau",
275
			"America/Nome",
276
			"America/Sitka",
277
			"America/Yakutat",
278
			"Etc/GMT+9",
279
			"Pacific/Gambier",
280
			"US/Alaska",
281
		],
282
283
		// -480 min
284
		"Pacific Standard Time" => [
285
			"America/Dawson",
286
			"America/Ensenada",
287
			"America/Los_Angeles",
288
			"America/Metlakatla",
289
			"America/Santa_Isabel",
290
			"America/Tijuana",
291
			"America/Vancouver",
292
			"America/Whitehorse",
293
			"Canada/Pacific",
294
			"Canada/Yukon",
295
			"Etc/GMT+8",
296
			"Mexico/BajaNorte",
297
			"Pacific/Pitcairn",
298
			"PST8PDT",
299
			"US/Pacific",
300
			"US/Pacific-New",
301
		],
302
303
		// -420 min
304
		"US Mountain Standard Time" => [
305
			"America/Boise",
306
			"America/Cambridge_Bay",
307
			"America/Chihuahua",
308
			"America/Creston",
309
			"America/Dawson_Creek",
310
			"America/Denver",
311
			"America/Edmonton",
312
			"America/Hermosillo",
313
			"America/Inuvik",
314
			"America/Mazatlan",
315
			"America/Ojinaga",
316
			"America/Phoenix",
317
			"America/Shiprock",
318
			"America/Yellowknife",
319
			"Canada/Mountain",
320
			"Etc/GMT+7",
321
			"Mexico/BajaSur",
322
			"MST",
323
			"MST7MDT",
324
			"Navajo",
325
			"US/Arizona",
326
			"US/Mountain",
327
		],
328
329
		// -360 min
330
		"Central Standard Time" => [
331
			"America/Chicago",
332
			"America/Indiana/Knox",
333
			"America/Indiana/Tell_City",
334
			"America/Knox_IN",
335
			"America/North_Dakota/Beulah",
336
			"America/North_Dakota/Center",
337
			"America/North_Dakota/New_Salem",
338
			"America/Rainy_River",
339
			"America/Rankin_Inlet",
340
			"America/Regina",
341
			"America/Resolute",
342
			"America/Swift_Current",
343
			"America/Tegucigalpa",
344
			"America/Winnipeg",
345
			"US/Central",
346
			"US/Indiana-Starke",
347
			"CST6CDT",
348
			"Etc/GMT+6",
349
		],
350
		"Canada Central Standard Time" => [
351
			"Canada/Central",
352
			"Canada/East-Saskatchewan",
353
			"Canada/Saskatchewan",
354
		],
355
		"Mexico Standard Time" => [
356
			"America/Mexico_City",
357
			"America/Monterrey",
358
			"Mexico/General",
359
		],
360
		"Central America Standard Time" => [
361
			"America/Bahia_Banderas",
362
			"America/Belize",
363
			"America/Cancun",
364
			"America/Costa_Rica",
365
			"America/El_Salvador",
366
			"America/Guatemala",
367
			"America/Managua",
368
			"America/Matamoros",
369
			"America/Menominee",
370
			"America/Merida",
371
			"Chile/EasterIsland",
372
			"Pacific/Easter",
373
			"Pacific/Galapagos",
374
		],
375
376
		// -300 min
377
		"US Eastern Standard Time" => [
378
			"America/Detroit",
379
			"America/Fort_Wayne",
380
			"America/Grand_Turk",
381
			"America/Indiana/Indianapolis",
382
			"America/Indiana/Marengo",
383
			"America/Indiana/Petersburg",
384
			"America/Indiana/Vevay",
385
			"America/Indiana/Vincennes",
386
			"America/Indiana/Winamac",
387
			"America/Indianapolis",
388
			"America/Jamaica",
389
			"America/Kentucky/Louisville",
390
			"America/Kentucky/Monticello",
391
			"America/Louisville",
392
			"America/Montreal",
393
			"America/New_York",
394
			"America/Thunder_Bay",
395
			"America/Toronto",
396
			"Canada/Eastern",
397
			"Cuba",
398
			"EST",
399
			"EST5EDT",
400
			"Etc/GMT+5",
401
			"Jamaica",
402
			"US/East-Indiana",
403
			"US/Eastern",
404
			"US/Michigan",
405
		],
406
		"SA Pacific Standard Time" => [
407
			"America/Atikokan",
408
			"America/Bogota",
409
			"America/Cayman",
410
			"America/Coral_Harbour",
411
			"America/Guayaquil",
412
			"America/Havana",
413
			"America/Iqaluit",
414
			"America/Lima",
415
			"America/Nassau",
416
			"America/Nipigon",
417
			"America/Panama",
418
			"America/Pangnirtung",
419
			"America/Port-au-Prince",
420
		],
421
422
		// -270 min
423
		"Venezuela Standard Time" => [
424
			"America/Caracas",
425
		],
426
		// -240 min
427
		"Atlantic Standard Time" => [
428
			"America/Barbados",
429
			"America/Blanc-Sablon",
430
			"America/Glace_Bay",
431
			"America/Goose_Bay",
432
			"America/Halifax",
433
			"America/Lower_Princes",
434
			"America/St_Barthelemy",
435
			"America/St_Kitts",
436
			"America/St_Lucia",
437
			"America/St_Thomas",
438
			"America/St_Vincent",
439
			"America/Thule",
440
			"America/Tortola",
441
			"America/Virgin",
442
			"Atlantic/Bermuda",
443
			"Canada/Atlantic",
444
			"Etc/GMT+4",
445
		],
446
		"SA Western Standard Time" => [
447
			"America/Anguilla",
448
			"America/Antigua",
449
			"America/Aruba",
450
			"America/Asuncion",
451
			"America/Boa_Vista",
452
			"America/Campo_Grande",
453
			"America/Cuiaba",
454
			"America/Curacao",
455
			"America/Dominica",
456
			"America/Eirunepe",
457
			"America/Grenada",
458
			"America/Guadeloupe",
459
			"America/Guyana",
460
			"America/Kralendijk",
461
			"America/La_Paz",
462
			"America/Manaus",
463
			"America/Marigot",
464
			"America/Martinique",
465
			"America/Moncton",
466
			"America/Montserrat",
467
			"America/Port_of_Spain",
468
			"America/Porto_Acre",
469
			"America/Porto_Velho",
470
			"America/Puerto_Rico",
471
			"America/Rio_Branco",
472
			"Brazil/Acre",
473
			"Brazil/West",
474
		],
475
		"Pacific SA Standard Time" => [
476
			"America/Santiago",
477
			"America/Santo_Domingo",
478
			"Antarctica/Palmer",
479
			"Chile/Continental",
480
		],
481
482
		// -210 min
483
		"Newfoundland and Labrador Standard Time" => [
484
			"America/St_Johns",
485
			"Canada/Newfoundland",
486
		],
487
488
		// -180 min
489
		"E South America Standard Time" => [
490
			"America/Araguaina",
491
			"America/Bahia",
492
			"America/Belem",
493
			"America/Fortaleza",
494
			"America/Maceio",
495
			"America/Recife",
496
			"America/Sao_Paulo",
497
			"Brazil/East",
498
			"Etc/GMT+3",
499
		],
500
		"SA Eastern Standard Time" => [
501
			"America/Argentina/Buenos_Aires",
502
			"America/Argentina/Catamarca",
503
			"America/Argentina/ComodRivadavia",
504
			"America/Argentina/Cordoba",
505
			"America/Argentina/Jujuy",
506
			"America/Argentina/La_Rioja",
507
			"America/Argentina/Mendoza",
508
			"America/Argentina/Rio_Gallegos",
509
			"America/Argentina/Salta",
510
			"America/Argentina/San_Juan",
511
			"America/Argentina/San_Luis",
512
			"America/Argentina/Tucuman",
513
			"America/Argentina/Ushuaia",
514
			"America/Buenos_Aires",
515
			"America/Catamarca",
516
			"America/Cayenne",
517
			"America/Cordoba",
518
			"America/Godthab",
519
			"America/Jujuy",
520
			"America/Mendoza",
521
			"America/Miquelon",
522
			"America/Montevideo",
523
			"America/Paramaribo",
524
			"America/Rosario",
525
			"America/Santarem",
526
		],
527
		"Greenland Standard Time" => [
528
			"Antarctica/Rothera",
529
			"Atlantic/Stanley",
530
		],
531
532
		// -120 min
533
		"Mid-Atlantic Standard Time" => [
534
			"America/Noronha",
535
			"Atlantic/South_Georgia",
536
			"Brazil/DeNoronha",
537
			"Etc/GMT+2",
538
		],
539
540
		// -60 min
541
		"Azores Standard Time" => [
542
			"Atlantic/Azores",
543
			"Etc/GMT+1",
544
		],
545
		"Cape Verde Standard Time" => [
546
			"America/Scoresbysund",
547
			"Atlantic/Cape_Verde",
548
		],
549
550
		// 0 min
551
		"GMT Standard Time" => [
552
			"Eire",
553
			"Etc/GMT",
554
			"Etc/GMT+0",
555
			"Etc/GMT-0",
556
			"Etc/GMT0",
557
			"Etc/Greenwich",
558
			"Etc/UCT",
559
			"Etc/Universal",
560
			"Etc/UTC",
561
			"Etc/Zulu",
562
			"Europe/Belfast",
563
			"Europe/Dublin",
564
			"Europe/Guernsey",
565
			"Europe/Isle_of_Man",
566
			"Europe/Jersey",
567
			"Europe/Lisbon",
568
			"Europe/London",
569
			"Factory",
570
			"GB",
571
			"GB-Eire",
572
			"GMT",
573
			"GMT+0",
574
			"GMT-0",
575
			"GMT0",
576
			"Greenwich",
577
			"Iceland",
578
			"Portugal",
579
			"UCT",
580
			"Universal",
581
			"UTC",
582
		],
583
		"Greenwich Standard Time" => [
584
			"Africa/Abidjan",
585
			"Africa/Accra",
586
			"Africa/Bamako",
587
			"Africa/Banjul",
588
			"Africa/Bissau",
589
			"Africa/Casablanca",
590
			"Africa/Conakry",
591
			"Africa/Dakar",
592
			"Africa/El_Aaiun",
593
			"Africa/Freetown",
594
			"Africa/Lome",
595
			"Africa/Monrovia",
596
			"Africa/Nouakchott",
597
			"Africa/Ouagadougou",
598
			"Africa/Sao_Tome",
599
			"Africa/Timbuktu",
600
			"America/Danmarkshavn",
601
			"Atlantic/Canary",
602
			"Atlantic/Faeroe",
603
			"Atlantic/Faroe",
604
			"Atlantic/Madeira",
605
			"Atlantic/Reykjavik",
606
			"Atlantic/St_Helena",
607
			"Zulu",
608
		],
609
610
		// +60 min
611
		"Central Europe Standard Time" => [
612
			"Europe/Belgrade",
613
			"Europe/Bratislava",
614
			"Europe/Budapest",
615
			"Europe/Ljubljana",
616
			"Europe/Prague",
617
			"Europe/Vaduz",
618
		],
619
		"Central European Standard Time" => [
620
			"Europe/Sarajevo",
621
			"Europe/Skopje",
622
			"Europe/Warsaw",
623
			"Europe/Zagreb",
624
			"MET",
625
			"Poland",
626
		],
627
		"Romance Standard Time" => [
628
			"Europe/Andorra",
629
			"Europe/Brussels",
630
			"Europe/Copenhagen",
631
			"Europe/Gibraltar",
632
			"Europe/Madrid",
633
			"Europe/Malta",
634
			"Europe/Monaco",
635
			"Europe/Paris",
636
			"Europe/Podgorica",
637
			"Europe/San_Marino",
638
			"Europe/Tirane",
639
		],
640
		"W Europe Standard Time" => [
641
			"Europe/Amsterdam",
642
			"Europe/Berlin",
643
			"Europe/Luxembourg",
644
			"Europe/Vatican",
645
			"Europe/Rome",
646
			"Europe/Stockholm",
647
			"Arctic/Longyearbyen",
648
			"Europe/Vienna",
649
			"Europe/Zurich",
650
			"Europe/Oslo",
651
			"WET",
652
			"CET",
653
			"Etc/GMT-1",
654
		],
655
		"W Central Africa Standard Time" => [
656
			"Africa/Algiers",
657
			"Africa/Bangui",
658
			"Africa/Brazzaville",
659
			"Africa/Ceuta",
660
			"Africa/Douala",
661
			"Africa/Kinshasa",
662
			"Africa/Lagos",
663
			"Africa/Libreville",
664
			"Africa/Luanda",
665
			"Africa/Malabo",
666
			"Africa/Ndjamena",
667
			"Africa/Niamey",
668
			"Africa/Porto-Novo",
669
			"Africa/Tunis",
670
			"Africa/Windhoek",
671
			"Atlantic/Jan_Mayen",
672
		],
673
674
		// +120 min
675
		"E Europe Standard Time" => [
676
			"Europe/Bucharest",
677
			"EET",
678
			"Etc/GMT-2",
679
			"Europe/Chisinau",
680
			"Europe/Mariehamn",
681
			"Europe/Nicosia",
682
			"Europe/Simferopol",
683
			"Europe/Tiraspol",
684
			"Europe/Uzhgorod",
685
			"Europe/Zaporozhye",
686
		],
687
		"Egypt Standard Time" => [
688
			"Africa/Cairo",
689
			"Africa/Tripoli",
690
			"Egypt",
691
			"Libya",
692
		],
693
		"FLE Standard Time" => [
694
			"Europe/Helsinki",
695
			"Europe/Kiev",
696
			"Europe/Riga",
697
			"Europe/Sofia",
698
			"Europe/Tallinn",
699
			"Europe/Vilnius",
700
		],
701
		"GTB Standard Time" => [
702
			"Asia/Istanbul",
703
			"Europe/Athens",
704
			"Europe/Istanbul",
705
			"Turkey",
706
		],
707
		"Israel Standard Time" => [
708
			"Asia/Amman",
709
			"Asia/Beirut",
710
			"Asia/Damascus",
711
			"Asia/Gaza",
712
			"Asia/Hebron",
713
			"Asia/Nicosia",
714
			"Asia/Tel_Aviv",
715
			"Asia/Jerusalem",
716
			"Israel",
717
		],
718
		"South Africa Standard Time" => [
719
			"Africa/Blantyre",
720
			"Africa/Bujumbura",
721
			"Africa/Gaborone",
722
			"Africa/Harare",
723
			"Africa/Johannesburg",
724
			"Africa/Kigali",
725
			"Africa/Lubumbashi",
726
			"Africa/Lusaka",
727
			"Africa/Maputo",
728
			"Africa/Maseru",
729
			"Africa/Mbabane",
730
		],
731
732
		// +180 min
733
		"Russian Standard Time" => [
734
			"Antarctica/Syowa",
735
			"Europe/Kaliningrad",
736
			"Europe/Minsk",
737
			"Etc/GMT-3",
738
		],
739
		"Arab Standard Time" => [
740
			"Asia/Qatar",
741
			"Asia/Kuwait",
742
			"Asia/Riyadh",
743
		],
744
		"E Africa Standard Time" => [
745
			"Africa/Addis_Ababa",
746
			"Africa/Asmara",
747
			"Africa/Asmera",
748
			"Africa/Dar_es_Salaam",
749
			"Africa/Djibouti",
750
			"Africa/Juba",
751
			"Africa/Kampala",
752
			"Africa/Khartoum",
753
			"Africa/Mogadishu",
754
			"Africa/Nairobi",
755
		],
756
		"Arabic Standard Time" => [
757
			"Asia/Aden",
758
			"Asia/Baghdad",
759
			"Asia/Bahrain",
760
			"Indian/Antananarivo",
761
			"Indian/Comoro",
762
			"Indian/Mayotte",
763
		],
764
765
		// +210 min
766
		"Iran Standard Time" => [
767
			"Asia/Tehran",
768
			"Iran",
769
		],
770
771
		// +240 min
772
		"Arabian Standard Time" => [
773
			"Asia/Dubai",
774
			"Asia/Muscat",
775
			"Indian/Mahe",
776
			"Indian/Mauritius",
777
			"Indian/Reunion",
778
		],
779
		"Caucasus Standard Time" => [
780
			"Asia/Baku",
781
			"Asia/Tbilisi",
782
			"Asia/Yerevan",
783
			"Etc/GMT-4",
784
			"Europe/Moscow",
785
			"Europe/Samara",
786
			"Europe/Volgograd",
787
			"W-SU",
788
		],
789
790
		// +270 min
791
		"Transitional Islamic State of Afghanistan Standard Time" => [
792
			"Asia/Kabul",
793
		],
794
795
		// +300 min
796
		"Ekaterinburg Standard Time" => [
797
			"Antarctica/Mawson",
798
		],
799
		"West Asia Standard Time" => [
800
			"Asia/Aqtau",
801
			"Asia/Aqtobe",
802
			"Asia/Ashgabat",
803
			"Asia/Ashkhabad",
804
			"Asia/Dushanbe",
805
			"Asia/Karachi",
806
			"Asia/Oral",
807
			"Asia/Samarkand",
808
			"Asia/Tashkent",
809
			"Etc/GMT-5",
810
			"Indian/Kerguelen",
811
			"Indian/Maldives",
812
		],
813
814
		// +330 min
815
		"India Standard Time" => [
816
			"Asia/Calcutta",
817
			"Asia/Colombo",
818
			"Asia/Kolkata",
819
		],
820
821
		// +345 min
822
		"Nepal Standard Time" => [
823
			"Asia/Kathmandu",
824
			"Asia/Katmandu",
825
		],
826
827
		// +360 min
828
		"Central Asia Standard Time" => [
829
			"Asia/Dacca",
830
			"Asia/Dhaka",
831
		],
832
		"Sri Lanka Standard Time" => [
833
			"Indian/Chagos",
834
		],
835
		"N Central Asia Standard Time" => [
836
			"Antarctica/Vostok",
837
			"Asia/Almaty",
838
			"Asia/Bishkek",
839
			"Asia/Qyzylorda",
840
			"Asia/Thimbu",
841
			"Asia/Thimphu",
842
			"Asia/Yekaterinburg",
843
			"Etc/GMT-6",
844
		],
845
846
		// +390 min
847
		"Myanmar Standard Time" => [
848
			"Asia/Rangoon",
849
			"Indian/Cocos",
850
		],
851
852
		// +420 min
853
		"SE Asia Standard Time" => [
854
			"Asia/Bangkok",
855
			"Asia/Ho_Chi_Minh",
856
			"Asia/Hovd",
857
			"Asia/Jakarta",
858
			"Asia/Phnom_Penh",
859
			"Asia/Saigon",
860
			"Indian/Christmas",
861
		],
862
		"North Asia Standard Time" => [
863
			"Antarctica/Davis",
864
			"Asia/Novokuznetsk",
865
			"Asia/Novosibirsk",
866
			"Asia/Omsk",
867
			"Asia/Pontianak",
868
			"Asia/Vientiane",
869
			"Etc/GMT-7",
870
		],
871
872
		// +480 min
873
		"China Standard Time" => [
874
			"Asia/Brunei",
875
			"Asia/Choibalsan",
876
			"Asia/Chongqing",
877
			"Asia/Chungking",
878
			"Asia/Harbin",
879
			"Asia/Hong_Kong",
880
			"Asia/Shanghai",
881
			"Asia/Ujung_Pandang",
882
			"Asia/Urumqi",
883
			"Hongkong",
884
			"PRC",
885
			"ROC",
886
		],
887
		"Singapore Standard Time" => [
888
			"Singapore",
889
			"Asia/Singapore",
890
			"Asia/Kuala_Lumpur",
891
		],
892
		"Taipei Standard Time" => [
893
			"Asia/Taipei",
894
		],
895
		"W Australia Standard Time" => [
896
			"Australia/Perth",
897
			"Australia/West",
898
		],
899
		"North Asia East Standard Time" => [
900
			"Antarctica/Casey",
901
			"Asia/Kashgar",
902
			"Asia/Krasnoyarsk",
903
			"Asia/Kuching",
904
			"Asia/Macao",
905
			"Asia/Macau",
906
			"Asia/Makassar",
907
			"Asia/Manila",
908
			"Etc/GMT-8",
909
			"Asia/Ulaanbaatar",
910
			"Asia/Ulan_Bator",
911
		],
912
913
		// +525 min
914
		"525" => [
915
			"Australia/Eucla",
916
		],
917
918
		// +540 min
919
		"Korea Standard Time" => [
920
			"Asia/Seoul",
921
			"Asia/Pyongyang",
922
			"ROK",
923
		],
924
		"Tokyo Standard Time" => [
925
			"Asia/Tokyo",
926
			"Japan",
927
			"Etc/GMT-9",
928
		],
929
		"Yakutsk Standard Time" => [
930
			"Asia/Dili",
931
			"Asia/Irkutsk",
932
			"Asia/Jayapura",
933
			"Pacific/Palau",
934
		],
935
936
		// +570 min
937
		"AUS Central Standard Time" => [
938
			"Australia/Darwin",
939
			"Australia/North",
940
		],
941
		// DST
942
		"Cen Australia Standard Time" => [
943
			"Australia/Adelaide",
944
			"Australia/Broken_Hill",
945
			"Australia/South",
946
			"Australia/Yancowinna",
947
		],
948
949
		// +600 min
950
		"AUS Eastern Standard Time" => [
951
			"Australia/Canberra",
952
			"Australia/Melbourne",
953
			"Australia/Sydney",
954
			"Australia/Currie",
955
			"Australia/ACT",
956
			"Australia/NSW",
957
			"Australia/Victoria",
958
		],
959
		"E Australia Standard Time" => [
960
			"Etc/GMT-10",
961
			"Australia/Brisbane",
962
			"Australia/Queensland",
963
			"Australia/Lindeman",
964
		],
965
		"Tasmania Standard Time" => [
966
			"Australia/Hobart",
967
			"Australia/Tasmania",
968
		],
969
		"Vladivostok Standard Time" => [
970
			"Antarctica/DumontDUrville",
971
		],
972
		"West Pacific Standard Time" => [
973
			"Asia/Yakutsk",
974
			"Pacific/Chuuk",
975
			"Pacific/Guam",
976
			"Pacific/Port_Moresby",
977
			"Pacific/Saipan",
978
			"Pacific/Truk",
979
			"Pacific/Yap",
980
		],
981
982
		// +630 min
983
		"630" => [
984
			"Australia/LHI",
985
			"Australia/Lord_Howe",
986
		],
987
988
		// +660 min
989
		"Central Pacific Standard Time" => [
990
			"Antarctica/Macquarie",
991
			"Asia/Sakhalin",
992
			"Asia/Vladivostok",
993
			"Etc/GMT-11",
994
			"Pacific/Efate",
995
			"Pacific/Guadalcanal",
996
			"Pacific/Kosrae",
997
			"Pacific/Noumea",
998
			"Pacific/Pohnpei",
999
			"Pacific/Ponape",
1000
		],
1001
1002
		// 690 min
1003
		"690" => [
1004
			"Pacific/Norfolk",
1005
		],
1006
1007
		// +720 min
1008
		"Fiji Islands Standard Time" => [
1009
			"Asia/Anadyr",
1010
			"Asia/Kamchatka",
1011
			"Asia/Magadan",
1012
			"Kwajalein",
1013
		],
1014
		"New Zealand Standard Time" => [
1015
			"Antarctica/McMurdo",
1016
			"Antarctica/South_Pole",
1017
			"Etc/GMT-12",
1018
			"NZ",
1019
			"Pacific/Auckland",
1020
			"Pacific/Fiji",
1021
			"Pacific/Funafuti",
1022
			"Pacific/Kwajalein",
1023
			"Pacific/Majuro",
1024
			"Pacific/Nauru",
1025
			"Pacific/Tarawa",
1026
			"Pacific/Wake",
1027
			"Pacific/Wallis",
1028
		],
1029
1030
		// +765 min
1031
		"765" => [
1032
			"NZ-CHAT",
1033
			"Pacific/Chatham",
1034
		],
1035
1036
		// +780 min
1037
		"Tonga Standard Time" => [
1038
			"Etc/GMT-13",
1039
			"Pacific/Apia",
1040
			"Pacific/Enderbury",
1041
			"Pacific/Tongatapu",
1042
		],
1043
1044
		// +840 min
1045
		"840" => [
1046
			"Etc/GMT-14",
1047
			"Pacific/Fakaofo",
1048
			"Pacific/Kiritimati",
1049
		],
1050
	];
1051
1052
	/**
1053
	 * Returns the binary timezone definition.
1054
	 *
1055
	 * @param string $phptimezone (opt) a php timezone string.
1056
	 *                            If omitted the env. default timezone is used.
1057
	 *
1058
	 * @return array|bool
1059
	 */
1060
	public static function GetBinaryTZ($phptimezone = false) {
1061
		if ($phptimezone === false) {
1062
			$phptimezone = date_default_timezone_get();
1063
		}
1064
		SLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetBinaryTZ() for %s", $phptimezone));
1065
1066
		try {
1067
			return mapi_ianatz_to_tzdef($phptimezone);
0 ignored issues
show
The function mapi_ianatz_to_tzdef was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1067
			return /** @scrutinizer ignore-call */ mapi_ianatz_to_tzdef($phptimezone);
Loading history...
1068
		}
1069
		catch (Exception $e) {
1070
			SLog::Write(LOGLEVEL_WARN, sprintf("TimezoneUtil::GetBinaryTZ() mapi_ianatz_to_tzdef() for '%s' failed!", $phptimezone));
1071
		}
1072
1073
		return false;
1074
	}
1075
1076
	/**
1077
	 * Returns a full timezone array.
1078
	 *
1079
	 * @param string $phptimezone (opt) a php timezone string.
1080
	 *                            If omitted the env. default timezone is used.
1081
	 *
1082
	 * @return array
1083
	 */
1084
	public static function GetFullTZ($phptimezone = false) {
1085
		if ($phptimezone === false) {
1086
			$phptimezone = date_default_timezone_get();
1087
		}
1088
1089
		SLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::GetFullTZ() for " . $phptimezone);
1090
1091
		$servertzname = self::guessTZNameFromPHPName($phptimezone);
1092
1093
		return self::GetFullTZFromTZName($servertzname);
1094
	}
1095
1096
	/**
1097
	 * Returns a full timezone array.
1098
	 *
1099
	 * @param string $tzname a TZID value
1100
	 *
1101
	 * @return array
1102
	 */
1103
	public static function GetFullTZFromTZName($tzname) {
1104
		if (!array_key_exists($tzname, self::$tzonesoffsets)) {
1105
			SLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetFullTZFromTZName('%s'): Is a PHP TimeZone, converting", $tzname));
1106
			$tzname = self::guessTZNameFromPHPName($tzname);
1107
		}
1108
1109
		$offset = self::$tzonesoffsets[$tzname];
1110
1111
		return [
1112
			"bias" => $offset[0],
1113
			"tzname" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)),
1114
			"dstendyear" => $offset[3],
1115
			"dstendmonth" => $offset[4],
1116
			"dstendday" => $offset[5],
1117
			"dstendweek" => $offset[6],
1118
			"dstendhour" => $offset[7],
1119
			"dstendminute" => $offset[8],
1120
			"dstendsecond" => $offset[9],
1121
			"dstendmillis" => $offset[10],
1122
			"stdbias" => $offset[1],
1123
			"tznamedst" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)),
1124
			"dststartyear" => $offset[11],
1125
			"dststartmonth" => $offset[12],
1126
			"dststartday" => $offset[13],
1127
			"dststartweek" => $offset[14],
1128
			"dststarthour" => $offset[15],
1129
			"dststartminute" => $offset[16],
1130
			"dststartsecond" => $offset[17],
1131
			"dststartmillis" => $offset[18],
1132
			"dstbias" => $offset[2],
1133
		];
1134
	}
1135
1136
	/**
1137
	 * Sets the timezone name by matching data from the offset (bias etc).
1138
	 *
1139
	 * @param mixed $tz
1140
	 *
1141
	 * @return array
1142
	 */
1143
	public static function FillTZNames($tz) {
1144
		SLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::FillTZNames() filling up bias " . $tz["bias"]);
1145
		if (!isset($tz["bias"])) {
1146
			SLog::Write(LOGLEVEL_WARN, "TimezoneUtil::FillTZNames() submitted TZ array does not have a bias");
1147
		}
1148
		else {
1149
			$tzname = self::guessTZNameFromOffset($tz);
1150
			$tz['tzname'] = $tz['tznamedst'] = self::encodeTZName(self::getMSTZnameFromTZName($tzname));
1151
		}
1152
1153
		return $tz;
1154
	}
1155
1156
	/**
1157
	 * Tries to find a timezone using the Bias and other offset parameters.
1158
	 *
1159
	 * @param array $offset a grommunio-sync timezone array
1160
	 *
1161
	 * @return string
1162
	 */
1163
	private static function guessTZNameFromOffset($offset) {
1164
		// try to find a quite exact match
1165
		foreach (self::$tzonesoffsets as $tzname => $tzoffset) {
1166
			if ($offset["bias"] == $tzoffset[0] &&
1167
				isset($offset["dstendmonth"]) && $offset["dstendmonth"] == $tzoffset[4] &&
1168
				isset($offset["dstendday"]) && $offset["dstendday"] == $tzoffset[6] &&
1169
				isset($offset["dststartmonth"]) && $offset["dststartmonth"] == $tzoffset[12] &&
1170
				isset($offset["dststartday"]) && $offset["dststartday"] == $tzoffset[14]) {
1171
				return $tzname;
1172
			}
1173
		}
1174
1175
		// try to find a bias match
1176
		foreach (self::$tzonesoffsets as $tzname => $tzoffset) {
1177
			if ($offset["bias"] == $tzoffset[0]) {
1178
				return $tzname;
1179
			}
1180
		}
1181
1182
		// nothing found? return gmt
1183
		SLog::Write(LOGLEVEL_WARN, "TimezoneUtil::guessTZNameFromOffset() no timezone found for the data submitted. Returning 'GMT Standard Time'.");
1184
1185
		return "GMT Standard Time";
1186
	}
1187
1188
	/**
1189
	 * Tries to find a AS timezone for a php timezone.
1190
	 *
1191
	 * @param string $phpname a php timezone name
1192
	 *
1193
	 * @return string
1194
	 */
1195
	private static function guessTZNameFromPHPName($phpname) {
1196
		foreach (self::$phptimezones as $tzn => $phptzs) {
1197
			if (in_array($phpname, $phptzs)) {
1198
				if (!is_int($tzn)) {
1199
					return $tzn;
1200
				}
1201
1202
				break;
1203
			}
1204
		}
1205
		SLog::Write(LOGLEVEL_ERROR, sprintf("TimezoneUtil::guessTZNameFromPHPName() no compatible timezone found for '%s'. Returning 'GMT Standard Time'. Please contact the grommunio dev team.", $phpname));
1206
1207
		return self::$mstzones["085"][0];
1208
	}
1209
1210
	/**
1211
	 * Returns an AS compatible tz name.
1212
	 *
1213
	 * @param string $name internal timezone name
1214
	 *
1215
	 * @return string
1216
	 */
1217
	private static function getMSTZnameFromTZName($name) {
1218
		// if $name is empty, get the timezone from system
1219
		if (trim($name) == '') {
1220
			$name = date_default_timezone_get();
1221
			SLog::Write(LOGLEVEL_INFO, sprintf("TimezoneUtil::getMSTZnameFromTZName(): empty timezone name sent. Got timezone from the system: '%s'", $name));
1222
		}
1223
1224
		foreach (self::$mstzones as $mskey => $msdefs) {
1225
			if ($name == $msdefs[0]) {
1226
				return $msdefs[1];
1227
			}
1228
		}
1229
1230
		// Not found? Then retrieve the correct TZName first and try again.
1231
		// That's ugly and needs a proper fix. But for now this method can convert
1232
		// - Europe/Berlin
1233
		// - W Europe Standard Time
1234
		// to "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"
1235
		// which is more correct than the hardcoded default of (GMT+00:00...)
1236
		$tzName = '';
1237
		foreach (self::$phptimezones as $tzn => $phptzs) {
1238
			if (in_array($name, $phptzs)) {
1239
				$tzName = $tzn;
1240
1241
				break;
1242
			}
1243
		}
1244
		if ($tzName != '') {
1245
			foreach (self::$mstzones as $mskey => $msdefs) {
1246
				if ($tzName == $msdefs[0]) {
1247
					return $msdefs[1];
1248
				}
1249
			}
1250
		}
1251
		if (strpos($name, '.') !== false) {
1252
			return self::getMSTZnameFromTZName(str_replace('.', '', $name));
1253
		}
1254
		SLog::Write(LOGLEVEL_WARN, sprintf("TimezoneUtil::getMSTZnameFromTZName() no MS name found for '%s'. Returning '(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London'", $name));
1255
1256
		return self::$mstzones["085"][1];
1257
	}
1258
1259
	/**
1260
	 * Encodes the tz name to UTF-16 compatible with a syncblob.
1261
	 *
1262
	 * @param string $name timezone name
1263
	 *
1264
	 * @return string
1265
	 */
1266
	private static function encodeTZName($name) {
1267
		return substr(iconv('UTF-8', 'UTF-16', $name), 2, -1);
1268
	}
1269
1270
	/**
1271
	 * Pack timezone info for Sync.
1272
	 *
1273
	 * @param array $tz
1274
	 *
1275
	 * @return string
1276
	 */
1277
	public static function GetSyncBlobFromTZ($tz) {
1278
		// set the correct TZ name (done using the Bias)
1279
		if (!isset($tz["tzname"]) || !isset($tz["tznamedst"])) {
1280
			$tz = TimezoneUtil::FillTZNames($tz);
1281
		}
1282
1283
		return pack(
1284
			"la64vvvvvvvvla64vvvvvvvvl",
1285
			$tz["bias"],
1286
			$tz["tzname"],
1287
			0,
1288
			$tz["dstendmonth"],
1289
			$tz["dstendday"],
1290
			$tz["dstendweek"],
1291
			$tz["dstendhour"],
1292
			$tz["dstendminute"],
1293
			$tz["dstendsecond"],
1294
			$tz["dstendmillis"],
1295
			$tz["stdbias"],
1296
			$tz["tznamedst"],
1297
			0,
1298
			$tz["dststartmonth"],
1299
			$tz["dststartday"],
1300
			$tz["dststartweek"],
1301
			$tz["dststarthour"],
1302
			$tz["dststartminute"],
1303
			$tz["dststartsecond"],
1304
			$tz["dststartmillis"],
1305
			$tz["dstbias"]
1306
		);
1307
	}
1308
1309
	/**
1310
	 * Returns official timezone name from windows timezone name.
1311
	 * E.g. "W Europe Standard Time" for "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna".
1312
	 *
1313
	 * @param string $winTz Timezone name in windows
1314
	 *
1315
	 * @return string timezone name
1316
	 */
1317
	public static function GetTZNameFromWinTZ($winTz = false) {
1318
		// Return "GMT Standard Time" per default
1319
		if ($winTz === false) {
1320
			return self::$mstzones['085'][0];
1321
		}
1322
1323
		foreach (self::$mstzones as $mskey => $msdefs) {
1324
			if ($winTz == $msdefs[1]) {
1325
				return $msdefs[0];
1326
			}
1327
		}
1328
1329
		return self::$mstzones['085'][0];
1330
	}
1331
1332
	/**
1333
	 * Returns an GMT timezone array.
1334
	 *
1335
	 * @return array
1336
	 */
1337
	public static function GetGMTTz() {
1338
		return [
1339
			"bias" => 0,
1340
			"tzname" => "",
1341
			"dstendyear" => 0,
1342
			"dstendmonth" => 10,
1343
			"dstendday" => 0,
1344
			"dstendweek" => 5,
1345
			"dstendhour" => 2,
1346
			"dstendminute" => 0,
1347
			"dstendsecond" => 0,
1348
			"dstendmillis" => 0,
1349
			"stdbias" => 0,
1350
			"tznamedst" => "",
1351
			"dststartyear" => 0,
1352
			"dststartmonth" => 3,
1353
			"dststartday" => 0,
1354
			"dststartweek" => 5,
1355
			"dststarthour" => 1,
1356
			"dststartminute" => 0,
1357
			"dststartsecond" => 0,
1358
			"dststartmillis" => 0,
1359
			"dstbias" => -60,
1360
		];
1361
	}
1362
1363
	/**
1364
	 * Creates an array that has split up all the components of a timezone
1365
	 * definition binary.
1366
	 *
1367
	 * Timezone definition structure:
1368
	 *
1369
	 * Major ver : UINT, 1 byte,  2 hex characters
1370
	 * Minor ver : UINT, 1 byte,  2 hex characters
1371
	 * cbHeader  : UINT, 2 bytes, 4 hex characters
1372
	 * Reserved  : UINT, 2 bytes, 4 hex characters
1373
	 * cchKeyName: UINT, 2 bytes, 4 hex characters
1374
	 * KeyName   : CHAR, variable length (defined by cckeyname value)
1375
	 * cRules    : UINT, 2 bytes, 4 hex characters
1376
	 * rules     : STRUCT, variable length (defined by cRules value):
1377
	 *   Major ver     : UINT, 1 byte,  2 hex characters
1378
	 *   Minor ver     : UINT, 1 byte,  2 hex characters
1379
	 *   Reserved      : UINT, 2 bytes, 4 hex characters
1380
	 *   TZRule flags  : UINT, 2 bytes, 4 hex characters
1381
	 *   wYear         : UINT, 2 bytes, 4 hex characters
1382
	 *   X             : TCHAR[14]
1383
	 *   lBias         : LONG, 4 bytes, 8 hex characters
1384
	 *   lStandardBias : LONG, 4 bytes, 8 hex characters
1385
	 *   lDaylightBias : LONG, 4 bytes, 8 hex characters
1386
	 *   stStandardDate: STRUCT
1387
	 *   stDaylightDate: STRUCT
1388
	 *
1389
	 * stStandardDate/stDaylightDate:
1390
	 *   wYear        : UINT, 2 bytes, 4 hex characters
1391
	 *   wMonth       : UINT, 2 bytes, 4 hex characters
1392
	 *   wDayOfWeek   : UINT, 2 bytes, 4 hex characters
1393
	 *   wDay         : UINT, 2 bytes, 4 hex characters
1394
	 *   wHour        : UINT, 2 bytes, 4 hex characters
1395
	 *   wMinute      : UINT, 2 bytes, 4 hex characters
1396
	 *   wSecond      : UINT, 2 bytes, 4 hex characters
1397
	 *   wMilliseconds: UINT, 2 bytes, 4 hex characters
1398
	 *
1399
	 * @param string $tzdef Timezone definition binary
1400
	 *
1401
	 * @return array timezone definition array
1402
	 */
1403
	public static function CreateTimezoneDefinitionObject($tzdef) {
1404
		if (!$tzdef) {
1405
			return [];
1406
		}
1407
1408
		$offset = 0;
1409
1410
		$res = unpack("Cmajorver/Cminorver/vcbheader/vreserved/vcchkeyname", substr($tzdef, $offset, 8));
1411
		$offset += 8;
1412
1413
		$cchKeyName = $res['cchkeyname'] * 2;
1414
		$data = unpack("a{$cchKeyName}keyname/vcrules", substr($tzdef, $offset, $cchKeyName + 2));
1415
		$res['keyname'] = $data['keyname'];
1416
		$res['crules'] = $data['crules'];
1417
		$offset += $cchKeyName + 2;
1418
1419
		for ($i = 0; $i < $res['crules']; ++$i) {
1420
			$rule = [];
0 ignored issues
show
The assignment to $rule is dead and can be removed.
Loading history...
1421
			$rule = unpack(
1422
				"Cmajorver/Cminorver/vreserved/vtzruleflags/vwyear/a14x/lbias/lstdbias/ldstbias/",
1423
				substr($tzdef, $offset, 34)
1424
			);
1425
			$offset += 34;
1426
1427
			$rule['stStandardDate'] = unpack(
1428
				"vyear/vmonth/vdayofweek/vday/vhour/vminute/vsecond/vmiliseconds/",
1429
				substr($tzdef, $offset, 16)
1430
			);
1431
			$offset += 16;
1432
1433
			$rule['stDaylightDate'] = unpack(
1434
				"vyear/vmonth/vdayofweek/vday/vhour/vminute/vsecond/vmiliseconds/",
1435
				substr($tzdef, $offset, 16)
1436
			);
1437
			$offset += 16;
1438
1439
			$res['rules'][] = $rule;
1440
		}
1441
1442
		return $res;
1443
	}
1444
1445
	/**
1446
	 * Converts timezone definition to timezonetag prop
1447
	 * ("PT_BINARY:PSETID_Appointment:0x8233" aka
1448
	 * "PT_BINARY:PSETID_Appointment:" . PidLidTimeZoneStruct)
1449
	 * compatible structure.
1450
	 *
1451
	 * @param array $tzdef timezone definition array
1452
	 *
1453
	 * @return array
1454
	 */
1455
	public static function GetTzFromTimezoneDef($tzdef) {
1456
		$tz = [
1457
			'tzname' => '',
1458
			'tznamedst' => '',
1459
		];
1460
1461
		foreach ($tzdef['rules'] as $rule) {
1462
			if ($rule['tzruleflags'] & TZRULE_FLAG_EFFECTIVE_TZREG) {
0 ignored issues
show
The constant TZRULE_FLAG_EFFECTIVE_TZREG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1463
				$tz['bias'] = $rule['bias'];
1464
				$tz['dstendyear'] = $rule['stStandardDate']['year'];
1465
				$tz['dstendmonth'] = $rule['stStandardDate']['month'];
1466
				$tz['dstendday'] = $rule['stStandardDate']['dayofweek'];
1467
				$tz['dstendweek'] = $rule['stStandardDate']['day'];
1468
				$tz['dstendhour'] = $rule['stStandardDate']['hour'];
1469
				$tz['dstendminute'] = $rule['stStandardDate']['minute'];
1470
				$tz['dstendsecond'] = $rule['stStandardDate']['second'];
1471
				$tz['dstendmillis'] = $rule['stStandardDate']['miliseconds'];
1472
				$tz['stdbias'] = $rule['stdbias'];
1473
				$tz['dststartyear'] = $rule['stDaylightDate']['year'];
1474
				$tz['dststartmonth'] = $rule['stDaylightDate']['month'];
1475
				$tz['dststartday'] = $rule['stDaylightDate']['dayofweek'];
1476
				$tz['dststartweek'] = $rule['stDaylightDate']['day'];
1477
				$tz['dststarthour'] = $rule['stDaylightDate']['hour'];
1478
				$tz['dststartminute'] = $rule['stDaylightDate']['minute'];
1479
				$tz['dststartsecond'] = $rule['stDaylightDate']['second'];
1480
				$tz['dststartmillis'] = $rule['stDaylightDate']['miliseconds'];
1481
				$tz['dstbias'] = $rule['dstbias'];
1482
			}
1483
		}
1484
1485
		// Fallback if there isn't effective timezone
1486
		if (!isset($tz['bias'])) {
1487
			$tz = self::GetGMTTz();
1488
		}
1489
1490
		// Make the structure compatible with class.recurrence.php
1491
		$tz['timezone'] = $tz['bias'];
1492
		$tz['timezonedst'] = $tz['dstbias'];
1493
1494
		return $tz;
1495
	}
1496
}
1497