1<?php
2/***********************************************
3* File      :   timezoneutil.php
4* Project   :   Z-Push
5* Descr     :   class to generate AS compatible timezone information
6*
7* Created   :   01.06.2012
8*
9* Copyright 2007 - 2016 Zarafa Deutschland GmbH
10*
11* This program is free software: you can redistribute it and/or modify
12* it under the terms of the GNU Affero General Public License, version 3,
13* as published by the Free Software Foundation.
14*
15* This program is distributed in the hope that it will be useful,
16* but WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18* GNU Affero General Public License for more details.
19*
20* You should have received a copy of the GNU Affero General Public License
21* along with this program.  If not, see <http://www.gnu.org/licenses/>.
22*
23* Consult LICENSE file for details
24************************************************/
25
26class TimezoneUtil {
27
28    /**
29     * list of MS and AS compatible timezones
30     *
31     * origin: http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx
32     * dots of tz identifiers were removed
33     *
34     * Updated at: 01.06.2012
35     */
36    private static $mstzones = array(
37                        "000" => array("Dateline Standard Time",                    "(GMT-12:00) International Date Line West"),
38                        "001" => array("Samoa Standard Time",                       "(GMT-11:00) Midway Island, Samoa"),
39                        "002" => array("Hawaiian Standard Time",                    "(GMT-10:00) Hawaii"),
40                        "003" => array("Alaskan Standard Time",                     "(GMT-09:00) Alaska"),
41                        "004" => array("Pacific Standard Time",                     "(GMT-08:00) Pacific Time (US and Canada); Tijuana"),
42                        "010" => array("Mountain Standard Time",                    "(GMT-07:00) Mountain Time (US and Canada)"),
43                        "013" => array("Mexico Standard Time 2",                    "(GMT-07:00) Chihuahua, La Paz, Mazatlan"),
44                        "015" => array("US Mountain Standard Time",                 "(GMT-07:00) Arizona"),
45                        "020" => array("Central Standard Time",                     "(GMT-06:00) Central Time (US and Canada"),
46                        "025" => array("Canada Central Standard Time",              "(GMT-06:00) Saskatchewan"),
47                        "030" => array("Mexico Standard Time",                      "(GMT-06:00) Guadalajara, Mexico City, Monterrey"),
48                        "033" => array("Central America Standard Time",             "(GMT-06:00) Central America"),
49                        "035" => array("Eastern Standard Time",                     "(GMT-05:00) Eastern Time (US and Canada)"),
50                        "040" => array("US Eastern Standard Time",                  "(GMT-05:00) Indiana (East)"),
51                        "045" => array("SA Pacific Standard Time",                  "(GMT-05:00) Bogota, Lima, Quito"),
52                        "uk1" => array("Venezuela Standard Time",                   "(GMT-04:30) Caracas"),                     // added
53                        "050" => array("Atlantic Standard Time",                    "(GMT-04:00) Atlantic Time (Canada)"),
54                        "055" => array("SA Western Standard Time",                  "(GMT-04:00) Caracas, La Paz"),
55                        "056" => array("Pacific SA Standard Time",                  "(GMT-04:00) Santiago"),
56                        "060" => array("Newfoundland and Labrador Standard Time",   "(GMT-03:30) Newfoundland and Labrador"),
57                        "065" => array("E South America Standard Time" ,            "(GMT-03:00) Brasilia"),
58                        "070" => array("SA Eastern Standard Time",                  "(GMT-03:00) Buenos Aires, Georgetown"),
59                        "073" => array("Greenland Standard Time",                   "(GMT-03:00) Greenland"),
60                        "075" => array("Mid-Atlantic Standard Time",                "(GMT-02:00) Mid-Atlantic"),
61                        "080" => array("Azores Standard Time",                      "(GMT-01:00) Azores"),
62                        "083" => array("Cape Verde Standard Time",                  "(GMT-01:00) Cape Verde Islands"),
63                        "085" => array("GMT Standard Time",                         "(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London"),
64                        "090" => array("Greenwich Standard Time",                   "(GMT) Casablanca, Monrovia"),
65                        "095" => array("Central Europe Standard Time",              "(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague"),
66                        "100" => array("Central European Standard Time",            "(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb"),
67                        "105" => array("Romance Standard Time",                     "(GMT+01:00) Brussels, Copenhagen, Madrid, Paris"),
68                        "110" => array("W Europe Standard Time",                    "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"),
69                        "111" => array("W. Europe Standard Time",                   "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"),
70                        "113" => array("W Central Africa Standard Time",            "(GMT+01:00) West Central Africa"),
71                        "115" => array("E Europe Standard Time",                    "(GMT+02:00) Bucharest"),
72                        "120" => array("Egypt Standard Time",                       "(GMT+02:00) Cairo"),
73                        "125" => array("FLE Standard Time",                         "(GMT+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius"),
74                        "130" => array("GTB Standard Time",                         "(GMT+02:00) Athens, Istanbul, Minsk"),
75                        "135" => array("Israel Standard Time",                      "(GMT+02:00) Jerusalem"),
76                        "140" => array("South Africa Standard Time",                "(GMT+02:00) Harare, Pretoria"),
77                        "145" => array("Russian Standard Time",                     "(GMT+03:00) Moscow, St. Petersburg, Volgograd"),
78                        "150" => array("Arab Standard Time",                        "(GMT+03:00) Kuwait, Riyadh"),
79                        "155" => array("E Africa Standard Time",                    "(GMT+03:00) Nairobi"),
80                        "158" => array("Arabic Standard Time",                      "(GMT+03:00) Baghdad"),
81                        "160" => array("Iran Standard Time",                        "(GMT+03:30) Tehran"),
82                        "165" => array("Arabian Standard Time",                     "(GMT+04:00) Abu Dhabi, Muscat"),
83                        "170" => array("Caucasus Standard Time",                    "(GMT+04:00) Baku, Tbilisi, Yerevan"),
84                        "175" => array("Transitional Islamic State of Afghanistan Standard Time","(GMT+04:30) Kabul"),
85                        "180" => array("Ekaterinburg Standard Time",                "(GMT+05:00) Ekaterinburg"),
86                        "185" => array("West Asia Standard Time",                   "(GMT+05:00) Islamabad, Karachi, Tashkent"),
87                        "190" => array("India Standard Time",                       "(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi"),
88                        "193" => array("Nepal Standard Time",                       "(GMT+05:45) Kathmandu"),
89                        "195" => array("Central Asia Standard Time",                "(GMT+06:00) Astana, Dhaka"),
90                        "200" => array("Sri Lanka Standard Time",                   "(GMT+06:00) Sri Jayawardenepura"),
91                        "201" => array("N Central Asia Standard Time",              "(GMT+06:00) Almaty, Novosibirsk"),
92                        "203" => array("Myanmar Standard Time",                     "(GMT+06:30) Yangon Rangoon"),
93                        "205" => array("SE Asia Standard Time",                     "(GMT+07:00) Bangkok, Hanoi, Jakarta"),
94                        "207" => array("North Asia Standard Time",                  "(GMT+07:00) Krasnoyarsk"),
95                        "210" => array("China Standard Time",                       "(GMT+08:00) Beijing, Chongqing, Hong Kong SAR, Urumqi"),
96                        "215" => array("Singapore Standard Time",                   "(GMT+08:00) Kuala Lumpur, Singapore"),
97                        "220" => array("Taipei Standard Time",                      "(GMT+08:00) Taipei"),
98                        "225" => array("W Australia Standard Time",                 "(GMT+08:00) Perth"),
99                        "227" => array("North Asia East Standard Time",             "(GMT+08:00) Irkutsk, Ulaanbaatar"),
100                        "230" => array("Korea Standard Time",                       "(GMT+09:00) Seoul"),
101                        "235" => array("Tokyo Standard Time",                       "(GMT+09:00) Osaka, Sapporo, Tokyo"),
102                        "240" => array("Yakutsk Standard Time",                     "(GMT+09:00) Yakutsk"),
103                        "245" => array("AUS Central Standard Time",                 "(GMT+09:30) Darwin"),
104                        "250" => array("Cen Australia Standard Time",               "(GMT+09:30) Adelaide"),
105                        "255" => array("AUS Eastern Standard Time",                 "(GMT+10:00) Canberra, Melbourne, Sydney"),
106                        "260" => array("E Australia Standard Time",                 "(GMT+10:00) Brisbane"),
107                        "265" => array("Tasmania Standard Time",                    "(GMT+10:00) Hobart"),
108                        "270" => array("Vladivostok Standard Time",                 "(GMT+10:00) Vladivostok"),
109                        "275" => array("West Pacific Standard Time",                "(GMT+10:00) Guam, Port Moresby"),
110                        "280" => array("Central Pacific Standard Time",             "(GMT+11:00) Magadan, Solomon Islands, New Caledonia"),
111                        "285" => array("Fiji Islands Standard Time",                "(GMT+12:00) Fiji Islands, Kamchatka, Marshall Islands"),
112                        "290" => array("New Zealand Standard Time",                 "(GMT+12:00) Auckland, Wellington"),
113                        "300" => array("Tonga Standard Time",                       "(GMT+13:00) Nuku'alofa"),
114                );
115
116    /**
117     * Python generated offset list
118     * dots in keys were removed
119     *
120     * Array indices
121     *  0 = lBias
122     *  1 = lStandardBias
123     *  2 = lDSTBias
124     *  3 = wStartYear
125     *  4 = wStartMonth
126     *  5 = wStartDOW
127     *  6 = wStartDay
128     *  7 = wStartHour
129     *  8 = wStartMinute
130     *  9 = wStartSecond
131     * 10 = wStartMilliseconds
132     * 11 = wEndYear
133     * 12 = wEndMonth
134     * 13 = wEndDOW
135     * 14 = wEndDay
136     * 15 = wEndHour
137     * 16 = wEndMinute
138     * 17 = wEndSecond
139     * 18 = wEndMilloseconds
140     *
141     * As the $tzoneoffsets and the $mstzones need to be resolved in both directions,
142     * some offsets are commented as they are not available in the $mstzones.
143     *
144     * Created at: 01.06.2012
145     */
146    private static $tzonesoffsets = array(
147                        "Transitional Islamic State of Afghanistan Standard Time"
148                                                                    => array(-270, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
149                        "Alaskan Standard Time"                     => array(540, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
150                        "Arab Standard Time"                        => array(-180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
151                        "Arabian Standard Time"                     => array(-240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
152                        "Arabic Standard Time"                      => array(-180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
153                        //"Argentina Standard Time"                   => array(180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
154                        "Atlantic Standard Time"                    => array(240, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
155                        "AUS Central Standard Time"                 => array(-570, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
156                        "AUS Eastern Standard Time"                 => array(-600, 0, -60,  0, 4, 0, 1, 3, 0, 0, 0,  0, 10, 0, 1, 2, 0, 0, 0),
157                        //"Azerbaijan Standard Time"                  => array(-240, 0, -60,  0, 10, 0, 5, 5, 0, 0, 0,  0, 3, 0, 5, 4, 0, 0, 0),
158                        "Azores Standard Time"                      => array(60, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
159                        //"Bangladesh Standard Time"                  => array(-360, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
160                        "Canada Central Standard Time"              => array(360, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
161                        "Cape Verde Standard Time"                  => array(60, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
162                        "Caucasus Standard Time"                    => array(-240, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
163                        "Cen Australia Standard Time"               => array(-570, 0, -60,  0, 4, 0, 1, 3, 0, 0, 0,  0, 10, 0, 1, 2, 0, 0, 0),
164                        "Central America Standard Time"             => array(360, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
165                        "Central Asia Standard Time"                => array(-360, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
166                        //"Central Brazilian Standard Time"           => array(240, 0, -60,  0, 2, 6, 4, 23, 59, 59, 999,  0, 10, 6, 3, 23, 59, 59, 999),
167                        "Central Europe Standard Time"              => array(-60, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
168                        "Central European Standard Time"            => array(-60, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
169                        "Central Pacific Standard Time"             => array(-660, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
170                        "Central Standard Time"                     => array(360, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
171                        "Mexico Standard Time"                      => array(360, 0, -60,  0, 10, 0, 5, 2, 0, 0, 0,  0, 4, 0, 1, 2, 0, 0, 0),
172                        "China Standard Time"                       => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
173                        "Dateline Standard Time"                    => array(720, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
174                        "E Africa Standard Time"                    => array(-180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
175                        "E Australia Standard Time"                 => array(-600, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
176                        "E Europe Standard Time"                    => array(-120, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
177                        "E South America Standard Time"             => array(180, 0, -60,  0, 2, 6, 4, 23, 59, 59, 999,  0, 10, 6, 3, 23, 59, 59, 999),
178                        "Eastern Standard Time"                     => array(300, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
179                        "Egypt Standard Time"                       => array(-120, 0, -60,  0, 9, 4, 5, 23, 59, 59, 999,  0, 4, 4, 5, 23, 59, 59, 999),
180                        "Ekaterinburg Standard Time"                => array(-300, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
181                        "Fiji Islands Standard Time"                => array(-720, 0, -60,  0, 3, 0, 5, 3, 0, 0, 0,  0, 10, 0, 4, 2, 0, 0, 0),
182                        "FLE Standard Time"                         => array(-120, 0, -60,  0, 10, 0, 5, 4, 0, 0, 0,  0, 3, 0, 5, 3, 0, 0, 0),
183                        //"Georgian Standard Time"                    => array(-240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
184                        "GMT Standard Time"                         => array(0, 0, -60,  0, 10, 0, 5, 2, 0, 0, 0,  0, 3, 0, 5, 1, 0, 0, 0),
185                        "Greenland Standard Time"                   => array(180, 0, -60,  0, 10, 6, 5, 23, 0, 0, 0,  0, 3, 6, 4, 22, 0, 0, 0),
186                        "Greenwich Standard Time"                   => array(0, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
187                        "GTB Standard Time"                         => array(-120, 0, -60,  0, 10, 0, 5, 4, 0, 0, 0,  0, 3, 0, 5, 3, 0, 0, 0),
188                        "Hawaiian Standard Time"                    => array(600, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
189                        "India Standard Time"                       => array(-330, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
190                        "Iran Standard Time"                        => array(-210, 0, -60,  0, 9, 1, 3, 23, 59, 59, 999,  0, 3, 6, 3, 23, 59, 59, 999),
191                        "Israel Standard Time"                      => array(-120, 0, -60,  0, 9, 0, 4, 2, 0, 0, 0,  0, 3, 5, 5, 2, 0, 0, 0),
192                        //"Jordan Standard Time"                      => array(-120, 0, -60,  0, 10, 5, 5, 1, 0, 0, 0,  0, 3, 4, 5, 23, 59, 59, 999),
193                        //"Kamchatka Standard Time"                   => array(-720, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
194                        "Korea Standard Time"                       => array(-540, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
195                        //"Magadan Standard Time"                     => array(-660, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
196                        //"Mauritius Standard Time"                   => array(-240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
197                        "Mid-Atlantic Standard Time"                => array(120, 0, -60,  0, 9, 0, 5, 2, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
198                        //"Middle East Standard Time"                 => array(-120, 0, -60,  0, 10, 6, 5, 23, 59, 59, 999,  0, 3, 6, 4, 23, 59, 59, 999),
199                        //"Montevideo Standard Time"                  => array(180, 0, -60,  0, 3, 0, 2, 2, 0, 0, 0,  0, 10, 0, 1, 2, 0, 0, 0),
200                        //"Morocco Standard Time"                     => array(0, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
201                        "Mountain Standard Time"                    => array(420, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
202                        "Mexico Standard Time 2"                    => array(420, 0, -60,  0, 10, 0, 5, 2, 0, 0, 0,  0, 4, 0, 1, 2, 0, 0, 0),
203                        "Myanmar Standard Time"                     => array(-390, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
204                        "N Central Asia Standard Time"              => array(-360, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
205                        //"Namibia Standard Time"                     => array(-60, 0, -60,  0, 4, 0, 1, 2, 0, 0, 0,  0, 9, 0, 1, 2, 0, 0, 0),
206                        "Nepal Standard Time"                       => array(-345, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
207                        "New Zealand Standard Time"                 => array(-720, 0, -60,  0, 4, 0, 1, 3, 0, 0, 0,  0, 9, 0, 5, 2, 0, 0, 0),
208                        "Newfoundland and Labrador Standard Time"   => array(210, 0, -60,  0, 11, 0, 1, 0, 1, 0, 0,  0, 3, 0, 2, 0, 1, 0, 0),
209                        "North Asia East Standard Time"             => array(-480, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
210                        "North Asia Standard Time"                  => array(-420, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
211                        "Pacific SA Standard Time"                  => array(240, 0, -60,  0, 3, 6, 2, 23, 59, 59, 999,  0, 10, 6, 2, 23, 59, 59, 999),
212                        "Pacific Standard Time"                     => array(480, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
213                        //"Pacific Standard Time (Mexico)"            => array(480, 0, -60,  0, 10, 0, 5, 2, 0, 0, 0,  0, 4, 0, 1, 2, 0, 0, 0),
214                        //"Pakistan Standard Time"                    => array(-300, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
215                        //"Paraguay Standard Time"                    => array(240, 0, -60,  0, 4, 6, 1, 23, 59, 59, 999,  0, 10, 6, 1, 23, 59, 59, 999),
216                        "Romance Standard Time"                     => array(-60, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
217                        "Russian Standard Time"                     => array(-180, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
218                        "SA Eastern Standard Time"                  => array(180, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
219                        "SA Pacific Standard Time"                  => array(300, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
220                        "SA Western Standard Time"                  => array(240, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
221                        "Samoa Standard Time"                       => array(660, 0, -60,  0, 3, 6, 5, 23, 59, 59, 999,  0, 9, 6, 5, 23, 59, 59, 999),
222                        "SE Asia Standard Time"                     => array(-420, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
223                        "Singapore Standard Time"                   => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
224                        "South Africa Standard Time"                => array(-120, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
225                        "Sri Lanka Standard Time"                   => array(-330, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
226                        //"Syria Standard Time"                       => array(-120, 0, -60,  0, 10, 4, 5, 23, 59, 59, 999,  0, 4, 4, 1, 23, 59, 59, 999),
227                        "Taipei Standard Time"                      => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
228                        "Tasmania Standard Time"                    => array(-600, 0, -60,  0, 4, 0, 1, 3, 0, 0, 0,  0, 10, 0, 1, 2, 0, 0, 0),
229                        "Tokyo Standard Time"                       => array(-540, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
230                        "Tonga Standard Time"                       => array(-780, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
231                        //"Ulaanbaatar Standard Time"                 => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
232                        "US Eastern Standard Time"                  => array(300, 0, -60,  0, 11, 0, 1, 2, 0, 0, 0,  0, 3, 0, 2, 2, 0, 0, 0),
233                        "US Mountain Standard Time"                 => array(420, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
234                        //"UTC"                                       => array(0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
235                        //"UTC+12"                                    => array(-720, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
236                        //"UTC-02"                                    => array(120, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
237                        //"UTC-11"                                    => array(660, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
238                        "Venezuela Standard Time"                   => array(270, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
239                        "Vladivostok Standard Time"                 => array(-600, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
240                        "W Australia Standard Time"                 => array(-480, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
241                        "W Central Africa Standard Time"            => array(-60, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
242                        "W Europe Standard Time"                    => array(-60, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
243                        "West Asia Standard Time"                   => array(-300, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
244                        "West Pacific Standard Time"                => array(-600, 0, -60,  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0),
245                        "Yakutsk Standard Time"                     => array(-540, 0, -60,  0, 10, 0, 5, 3, 0, 0, 0,  0, 3, 0, 5, 2, 0, 0, 0),
246                );
247
248    /**
249     * Generated list of PHP timezones in GMT timezones
250     *
251     * Created at: 01.06.2012
252     */
253    private static $phptimezones = array(
254            // -720 min
255            "Dateline Standard Time" => array(
256                        "Etc/GMT+12",
257                 ),
258
259            // -660 min
260            "Samoa Standard Time" => array(
261                        "Etc/GMT+11",
262                        "Pacific/Midway",
263                        "Pacific/Niue",
264                        "Pacific/Pago_Pago",
265                        "Pacific/Samoa",
266                        "US/Samoa",
267                 ),
268
269            // -600 min
270            "Hawaiian Standard Time" => array(
271                        "America/Adak",
272                        "America/Atka",
273                        "Etc/GMT+10",
274                        "HST",
275                        "Pacific/Honolulu",
276                        "Pacific/Johnston",
277                        "Pacific/Rarotonga",
278                        "Pacific/Tahiti",
279                        "US/Aleutian",
280                        "US/Hawaii",
281                 ),
282
283            // -570 min
284            "-570" => array(
285                        "Pacific/Marquesas",
286                 ),
287
288            // -540 min
289            "Alaskan Standard Time" => array(
290                        "America/Anchorage",
291                        "America/Juneau",
292                        "America/Nome",
293                        "America/Sitka",
294                        "America/Yakutat",
295                        "Etc/GMT+9",
296                        "Pacific/Gambier",
297                        "US/Alaska",
298                 ),
299
300            // -480 min
301            "Pacific Standard Time" => array(
302                        "America/Dawson",
303                        "America/Ensenada",
304                        "America/Los_Angeles",
305                        "America/Metlakatla",
306                        "America/Santa_Isabel",
307                        "America/Tijuana",
308                        "America/Vancouver",
309                        "America/Whitehorse",
310                        "Canada/Pacific",
311                        "Canada/Yukon",
312                        "Etc/GMT+8",
313                        "Mexico/BajaNorte",
314                        "Pacific/Pitcairn",
315                        "PST8PDT",
316                        "US/Pacific",
317                        "US/Pacific-New",
318                 ),
319
320            // -420 min
321            "US Mountain Standard Time" => array(
322                        "America/Boise",
323                        "America/Cambridge_Bay",
324                        "America/Chihuahua",
325                        "America/Creston",
326                        "America/Dawson_Creek",
327                        "America/Denver",
328                        "America/Edmonton",
329                        "America/Hermosillo",
330                        "America/Inuvik",
331                        "America/Mazatlan",
332                        "America/Ojinaga",
333                        "America/Phoenix",
334                        "America/Shiprock",
335                        "America/Yellowknife",
336                        "Canada/Mountain",
337                        "Etc/GMT+7",
338                        "Mexico/BajaSur",
339                        "MST",
340                        "MST7MDT",
341                        "Navajo",
342                        "US/Arizona",
343                        "US/Mountain",
344                 ),
345
346            // -360 min
347            "Central Standard Time" => array(
348                        "America/Chicago",
349                        "America/Indiana/Knox",
350                        "America/Indiana/Tell_City",
351                        "America/Knox_IN",
352                        "America/North_Dakota/Beulah",
353                        "America/North_Dakota/Center",
354                        "America/North_Dakota/New_Salem",
355                        "America/Rainy_River",
356                        "America/Rankin_Inlet",
357                        "America/Regina",
358                        "America/Resolute",
359                        "America/Swift_Current",
360                        "America/Tegucigalpa",
361                        "America/Winnipeg",
362                        "US/Central",
363                        "US/Indiana-Starke",
364                        "CST6CDT",
365                        "Etc/GMT+6",
366                 ),
367            "Canada Central Standard Time" => array(
368                        "Canada/Central",
369                        "Canada/East-Saskatchewan",
370                        "Canada/Saskatchewan",
371                 ),
372            "Mexico Standard Time" => array(
373                        "America/Mexico_City",
374                        "America/Monterrey",
375                        "Mexico/General",
376                 ),
377            "Central America Standard Time" => array(
378                        "America/Bahia_Banderas",
379                        "America/Belize",
380                        "America/Cancun",
381                        "America/Costa_Rica",
382                        "America/El_Salvador",
383                        "America/Guatemala",
384                        "America/Managua",
385                        "America/Matamoros",
386                        "America/Menominee",
387                        "America/Merida",
388                        "Chile/EasterIsland",
389                        "Pacific/Easter",
390                        "Pacific/Galapagos",
391                 ),
392
393            // -300 min
394            "US Eastern Standard Time" => array(
395                        "America/Detroit",
396                        "America/Fort_Wayne",
397                        "America/Grand_Turk",
398                        "America/Indiana/Indianapolis",
399                        "America/Indiana/Marengo",
400                        "America/Indiana/Petersburg",
401                        "America/Indiana/Vevay",
402                        "America/Indiana/Vincennes",
403                        "America/Indiana/Winamac",
404                        "America/Indianapolis",
405                        "America/Jamaica",
406                        "America/Kentucky/Louisville",
407                        "America/Kentucky/Monticello",
408                        "America/Louisville",
409                        "America/Montreal",
410                        "America/New_York",
411                        "America/Thunder_Bay",
412                        "America/Toronto",
413                        "Canada/Eastern",
414                        "Cuba",
415                        "EST",
416                        "EST5EDT",
417                        "Etc/GMT+5",
418                        "Jamaica",
419                        "US/East-Indiana",
420                        "US/Eastern",
421                        "US/Michigan",
422                 ),
423            "SA Pacific Standard Time" => array(
424                        "America/Atikokan",
425                        "America/Bogota",
426                        "America/Cayman",
427                        "America/Coral_Harbour",
428                        "America/Guayaquil",
429                        "America/Havana",
430                        "America/Iqaluit",
431                        "America/Lima",
432                        "America/Nassau",
433                        "America/Nipigon",
434                        "America/Panama",
435                        "America/Pangnirtung",
436                        "America/Port-au-Prince",
437                 ),
438
439            // -270 min
440            "Venezuela Standard Time" => array(
441                        "America/Caracas",
442                 ),
443            // -240 min
444            "Atlantic Standard Time" => array(
445                        "America/Barbados",
446                        "America/Blanc-Sablon",
447                        "America/Glace_Bay",
448                        "America/Goose_Bay",
449                        "America/Halifax",
450                        "America/Lower_Princes",
451                        "America/St_Barthelemy",
452                        "America/St_Kitts",
453                        "America/St_Lucia",
454                        "America/St_Thomas",
455                        "America/St_Vincent",
456                        "America/Thule",
457                        "America/Tortola",
458                        "America/Virgin",
459                        "Atlantic/Bermuda",
460                        "Canada/Atlantic",
461                        "Etc/GMT+4",
462                 ),
463            "SA Western Standard Time" => array(
464                        "America/Anguilla",
465                        "America/Antigua",
466                        "America/Aruba",
467                        "America/Asuncion",
468                        "America/Boa_Vista",
469                        "America/Campo_Grande",
470                        "America/Cuiaba",
471                        "America/Curacao",
472                        "America/Dominica",
473                        "America/Eirunepe",
474                        "America/Grenada",
475                        "America/Guadeloupe",
476                        "America/Guyana",
477                        "America/Kralendijk",
478                        "America/La_Paz",
479                        "America/Manaus",
480                        "America/Marigot",
481                        "America/Martinique",
482                        "America/Moncton",
483                        "America/Montserrat",
484                        "America/Port_of_Spain",
485                        "America/Porto_Acre",
486                        "America/Porto_Velho",
487                        "America/Puerto_Rico",
488                        "America/Rio_Branco",
489                        "Brazil/Acre",
490                        "Brazil/West",
491                 ),
492            "Pacific SA Standard Time" => array(
493                        "America/Santiago",
494                        "America/Santo_Domingo",
495                        "Antarctica/Palmer",
496                        "Chile/Continental",
497                 ),
498
499            // -210 min
500            "Newfoundland and Labrador Standard Time" => array(
501                        "America/St_Johns",
502                        "Canada/Newfoundland",
503                 ),
504
505            // -180 min
506            "E South America Standard Time" => array(
507                        "America/Araguaina",
508                        "America/Bahia",
509                        "America/Belem",
510                        "America/Fortaleza",
511                        "America/Maceio",
512                        "America/Recife",
513                        "America/Sao_Paulo",
514                        "Brazil/East",
515                        "Etc/GMT+3",
516                 ),
517            "SA Eastern Standard Time" => array(
518                        "America/Argentina/Buenos_Aires",
519                        "America/Argentina/Catamarca",
520                        "America/Argentina/ComodRivadavia",
521                        "America/Argentina/Cordoba",
522                        "America/Argentina/Jujuy",
523                        "America/Argentina/La_Rioja",
524                        "America/Argentina/Mendoza",
525                        "America/Argentina/Rio_Gallegos",
526                        "America/Argentina/Salta",
527                        "America/Argentina/San_Juan",
528                        "America/Argentina/San_Luis",
529                        "America/Argentina/Tucuman",
530                        "America/Argentina/Ushuaia",
531                        "America/Buenos_Aires",
532                        "America/Catamarca",
533                        "America/Cayenne",
534                        "America/Cordoba",
535                        "America/Godthab",
536                        "America/Jujuy",
537                        "America/Mendoza",
538                        "America/Miquelon",
539                        "America/Montevideo",
540                        "America/Paramaribo",
541                        "America/Rosario",
542                        "America/Santarem",
543                 ),
544            "Greenland Standard Time" => array(
545                        "Antarctica/Rothera",
546                        "Atlantic/Stanley",
547                 ),
548
549            // -120 min
550            "Mid-Atlantic Standard Time" => array(
551                        "America/Noronha",
552                        "Atlantic/South_Georgia",
553                        "Brazil/DeNoronha",
554                        "Etc/GMT+2",
555                 ),
556
557            // -60 min
558            "Azores Standard Time" => array(
559                        "Atlantic/Azores",
560                        "Etc/GMT+1",
561                 ),
562            "Cape Verde Standard Time" => array(
563                        "America/Scoresbysund",
564                        "Atlantic/Cape_Verde",
565                 ),
566
567            // 0 min
568            "GMT Standard Time" => array(
569                        "Eire",
570                        "Etc/GMT",
571                        "Etc/GMT+0",
572                        "Etc/GMT-0",
573                        "Etc/GMT0",
574                        "Etc/Greenwich",
575                        "Etc/UCT",
576                        "Etc/Universal",
577                        "Etc/UTC",
578                        "Etc/Zulu",
579                        "Europe/Belfast",
580                        "Europe/Dublin",
581                        "Europe/Guernsey",
582                        "Europe/Isle_of_Man",
583                        "Europe/Jersey",
584                        "Europe/Lisbon",
585                        "Europe/London",
586                        "Factory",
587                        "GB",
588                        "GB-Eire",
589                        "GMT",
590                        "GMT+0",
591                        "GMT-0",
592                        "GMT0",
593                        "Greenwich",
594                        "Iceland",
595                        "Portugal",
596                        "UCT",
597                        "Universal",
598                        "UTC",
599                 ),
600            "Greenwich Standard Time" => array(
601                        "Africa/Abidjan",
602                        "Africa/Accra",
603                        "Africa/Bamako",
604                        "Africa/Banjul",
605                        "Africa/Bissau",
606                        "Africa/Casablanca",
607                        "Africa/Conakry",
608                        "Africa/Dakar",
609                        "Africa/El_Aaiun",
610                        "Africa/Freetown",
611                        "Africa/Lome",
612                        "Africa/Monrovia",
613                        "Africa/Nouakchott",
614                        "Africa/Ouagadougou",
615                        "Africa/Sao_Tome",
616                        "Africa/Timbuktu",
617                        "America/Danmarkshavn",
618                        "Atlantic/Canary",
619                        "Atlantic/Faeroe",
620                        "Atlantic/Faroe",
621                        "Atlantic/Madeira",
622                        "Atlantic/Reykjavik",
623                        "Atlantic/St_Helena",
624                        "Zulu",
625                 ),
626
627            // +60 min
628            "Central Europe Standard Time" => array(
629                        "Europe/Belgrade",
630                        "Europe/Bratislava",
631                        "Europe/Budapest",
632                        "Europe/Ljubljana",
633                        "Europe/Prague",
634                        "Europe/Vaduz",
635                 ),
636            "Central European Standard Time" => array(
637                        "Europe/Sarajevo",
638                        "Europe/Skopje",
639                        "Europe/Warsaw",
640                        "Europe/Zagreb",
641                        "MET",
642                        "Poland",
643                 ),
644            "Romance Standard Time" => array(
645                        "Europe/Andorra",
646                        "Europe/Brussels",
647                        "Europe/Copenhagen",
648                        "Europe/Gibraltar",
649                        "Europe/Madrid",
650                        "Europe/Malta",
651                        "Europe/Monaco",
652                        "Europe/Paris",
653                        "Europe/Podgorica",
654                        "Europe/San_Marino",
655                        "Europe/Tirane",
656                 ),
657            "W Europe Standard Time" => array(
658                        "Europe/Amsterdam",
659                        "Europe/Berlin",
660                        "Europe/Luxembourg",
661                        "Europe/Vatican",
662                        "Europe/Rome",
663                        "Europe/Stockholm",
664                        "Arctic/Longyearbyen",
665                        "Europe/Vienna",
666                        "Europe/Zurich",
667                        "Europe/Oslo",
668                        "WET",
669                        "CET",
670                        "Etc/GMT-1",
671                 ),
672            "W Central Africa Standard Time" => array(
673                        "Africa/Algiers",
674                        "Africa/Bangui",
675                        "Africa/Brazzaville",
676                        "Africa/Ceuta",
677                        "Africa/Douala",
678                        "Africa/Kinshasa",
679                        "Africa/Lagos",
680                        "Africa/Libreville",
681                        "Africa/Luanda",
682                        "Africa/Malabo",
683                        "Africa/Ndjamena",
684                        "Africa/Niamey",
685                        "Africa/Porto-Novo",
686                        "Africa/Tunis",
687                        "Africa/Windhoek",
688                        "Atlantic/Jan_Mayen",
689                 ),
690
691            // +120 min
692            "E Europe Standard Time" => array(
693                        "Europe/Bucharest",
694                        "EET",
695                        "Etc/GMT-2",
696                        "Europe/Chisinau",
697                        "Europe/Mariehamn",
698                        "Europe/Nicosia",
699                        "Europe/Simferopol",
700                        "Europe/Tiraspol",
701                        "Europe/Uzhgorod",
702                        "Europe/Zaporozhye",
703                 ),
704            "Egypt Standard Time" => array(
705                        "Africa/Cairo",
706                        "Africa/Tripoli",
707                        "Egypt",
708                        "Libya",
709                 ),
710            "FLE Standard Time" => array(
711                        "Europe/Helsinki",
712                        "Europe/Kiev",
713                        "Europe/Riga",
714                        "Europe/Sofia",
715                        "Europe/Tallinn",
716                        "Europe/Vilnius",
717                 ),
718            "GTB Standard Time" => array(
719                        "Asia/Istanbul",
720                        "Europe/Athens",
721                        "Europe/Istanbul",
722                        "Turkey",
723                 ),
724            "Israel Standard Time" => array(
725                        "Asia/Amman",
726                        "Asia/Beirut",
727                        "Asia/Damascus",
728                        "Asia/Gaza",
729                        "Asia/Hebron",
730                        "Asia/Nicosia",
731                        "Asia/Tel_Aviv",
732                        "Asia/Jerusalem",
733                        "Israel",
734                 ),
735            "South Africa Standard Time" => array(
736                        "Africa/Blantyre",
737                        "Africa/Bujumbura",
738                        "Africa/Gaborone",
739                        "Africa/Harare",
740                        "Africa/Johannesburg",
741                        "Africa/Kigali",
742                        "Africa/Lubumbashi",
743                        "Africa/Lusaka",
744                        "Africa/Maputo",
745                        "Africa/Maseru",
746                        "Africa/Mbabane",
747                 ),
748
749            // +180 min
750            "Russian Standard Time" => array(
751                        "Antarctica/Syowa",
752                        "Europe/Kaliningrad",
753                        "Europe/Minsk",
754                        "Etc/GMT-3",
755                 ),
756            "Arab Standard Time" => array(
757                        "Asia/Qatar",
758                        "Asia/Kuwait",
759                        "Asia/Riyadh",
760                 ),
761            "E Africa Standard Time" => array(
762                        "Africa/Addis_Ababa",
763                        "Africa/Asmara",
764                        "Africa/Asmera",
765                        "Africa/Dar_es_Salaam",
766                        "Africa/Djibouti",
767                        "Africa/Juba",
768                        "Africa/Kampala",
769                        "Africa/Khartoum",
770                        "Africa/Mogadishu",
771                        "Africa/Nairobi",
772                 ),
773            "Arabic Standard Time" => array(
774                        "Asia/Aden",
775                        "Asia/Baghdad",
776                        "Asia/Bahrain",
777                        "Indian/Antananarivo",
778                        "Indian/Comoro",
779                        "Indian/Mayotte",
780                 ),
781
782            // +210 min
783            "Iran Standard Time" => array(
784                        "Asia/Tehran",
785                        "Iran",
786                 ),
787
788            // +240 min
789            "Arabian Standard Time" => array(
790                        "Asia/Dubai",
791                        "Asia/Muscat",
792                        "Indian/Mahe",
793                        "Indian/Mauritius",
794                        "Indian/Reunion",
795                 ),
796            "Caucasus Standard Time" => array(
797                        "Asia/Baku",
798                        "Asia/Tbilisi",
799                        "Asia/Yerevan",
800                        "Etc/GMT-4",
801                        "Europe/Moscow",
802                        "Europe/Samara",
803                        "Europe/Volgograd",
804                        "W-SU",
805                 ),
806
807            // +270 min
808            "Transitional Islamic State of Afghanistan Standard Time" => array(
809                        "Asia/Kabul",
810                 ),
811
812            // +300 min
813            "Ekaterinburg Standard Time" => array(
814                        "Antarctica/Mawson",
815                 ),
816            "West Asia Standard Time" => array(
817                        "Asia/Aqtau",
818                        "Asia/Aqtobe",
819                        "Asia/Ashgabat",
820                        "Asia/Ashkhabad",
821                        "Asia/Dushanbe",
822                        "Asia/Karachi",
823                        "Asia/Oral",
824                        "Asia/Samarkand",
825                        "Asia/Tashkent",
826                        "Etc/GMT-5",
827                        "Indian/Kerguelen",
828                        "Indian/Maldives",
829                 ),
830
831            // +330 min
832            "India Standard Time" => array(
833                        "Asia/Calcutta",
834                        "Asia/Colombo",
835                        "Asia/Kolkata",
836                 ),
837
838            // +345 min
839            "Nepal Standard Time" => array(
840                        "Asia/Kathmandu",
841                        "Asia/Katmandu",
842                 ),
843
844            // +360 min
845            "Central Asia Standard Time" => array(
846                        "Asia/Dacca",
847                        "Asia/Dhaka",
848                 ),
849            "Sri Lanka Standard Time" => array(
850                        "Indian/Chagos",
851                 ),
852            "N Central Asia Standard Time" => array(
853                        "Antarctica/Vostok",
854                        "Asia/Almaty",
855                        "Asia/Bishkek",
856                        "Asia/Qyzylorda",
857                        "Asia/Thimbu",
858                        "Asia/Thimphu",
859                        "Asia/Yekaterinburg",
860                        "Etc/GMT-6",
861                 ),
862
863            // +390 min
864            "Myanmar Standard Time" => array(
865                        "Asia/Rangoon",
866                        "Indian/Cocos",
867                 ),
868
869            // +420 min
870            "SE Asia Standard Time" => array(
871                        "Asia/Bangkok",
872                        "Asia/Ho_Chi_Minh",
873                        "Asia/Hovd",
874                        "Asia/Jakarta",
875                        "Asia/Phnom_Penh",
876                        "Asia/Saigon",
877                        "Indian/Christmas",
878                 ),
879            "North Asia Standard Time" => array(
880                        "Antarctica/Davis",
881                        "Asia/Novokuznetsk",
882                        "Asia/Novosibirsk",
883                        "Asia/Omsk",
884                        "Asia/Pontianak",
885                        "Asia/Vientiane",
886                        "Etc/GMT-7",
887                 ),
888
889            // +480 min
890            "China Standard Time" => array(
891                        "Asia/Brunei",
892                        "Asia/Choibalsan",
893                        "Asia/Chongqing",
894                        "Asia/Chungking",
895                        "Asia/Harbin",
896                        "Asia/Hong_Kong",
897                        "Asia/Shanghai",
898                        "Asia/Ujung_Pandang",
899                        "Asia/Urumqi",
900                        "Hongkong",
901                        "PRC",
902                        "ROC",
903                 ),
904            "Singapore Standard Time" => array(
905                        "Singapore",
906                        "Asia/Singapore",
907                        "Asia/Kuala_Lumpur",
908                 ),
909            "Taipei Standard Time" => array(
910                        "Asia/Taipei",
911                 ),
912            "W Australia Standard Time" => array(
913                        "Australia/Perth",
914                        "Australia/West",
915                 ),
916            "North Asia East Standard Time" => array(
917                        "Antarctica/Casey",
918                        "Asia/Kashgar",
919                        "Asia/Krasnoyarsk",
920                        "Asia/Kuching",
921                        "Asia/Macao",
922                        "Asia/Macau",
923                        "Asia/Makassar",
924                        "Asia/Manila",
925                        "Etc/GMT-8",
926                        "Asia/Ulaanbaatar",
927                        "Asia/Ulan_Bator",
928                 ),
929
930            // +525 min
931            "525" => array(
932                        "Australia/Eucla",
933                 ),
934
935            // +540 min
936            "Korea Standard Time" => array(
937                        "Asia/Seoul",
938                        "Asia/Pyongyang",
939                        "ROK",
940                 ),
941            "Tokyo Standard Time" => array(
942                        "Asia/Tokyo",
943                        "Japan",
944                        "Etc/GMT-9",
945                 ),
946            "Yakutsk Standard Time" => array(
947                        "Asia/Dili",
948                        "Asia/Irkutsk",
949                        "Asia/Jayapura",
950                        "Pacific/Palau",
951                 ),
952
953            // +570 min
954            "AUS Central Standard Time" => array(
955                        "Australia/Darwin",
956                        "Australia/North",
957                 ),
958             // DST
959            "Cen Australia Standard Time" => array(
960                        "Australia/Adelaide",
961                        "Australia/Broken_Hill",
962                        "Australia/South",
963                        "Australia/Yancowinna",
964                 ),
965
966            // +600 min
967            "AUS Eastern Standard Time" => array(
968                        "Australia/Canberra",
969                        "Australia/Melbourne",
970                        "Australia/Sydney",
971                        "Australia/Currie",
972                        "Australia/ACT",
973                        "Australia/NSW",
974                        "Australia/Victoria",
975                 ),
976            "E Australia Standard Time" => array(
977                        "Etc/GMT-10",
978                        "Australia/Brisbane",
979                        "Australia/Queensland",
980                        "Australia/Lindeman",
981                 ),
982            "Tasmania Standard Time" => array(
983                        "Australia/Hobart",
984                        "Australia/Tasmania",
985                 ),
986            "Vladivostok Standard Time" => array(
987                        "Antarctica/DumontDUrville",
988                 ),
989            "West Pacific Standard Time" => array(
990                        "Asia/Yakutsk",
991                        "Pacific/Chuuk",
992                        "Pacific/Guam",
993                        "Pacific/Port_Moresby",
994                        "Pacific/Saipan",
995                        "Pacific/Truk",
996                        "Pacific/Yap",
997                 ),
998
999            // +630 min
1000            "630" => array(
1001                        "Australia/LHI",
1002                        "Australia/Lord_Howe",
1003                 ),
1004
1005            // +660 min
1006            "Central Pacific Standard Time" => array(
1007                        "Antarctica/Macquarie",
1008                        "Asia/Sakhalin",
1009                        "Asia/Vladivostok",
1010                        "Etc/GMT-11",
1011                        "Pacific/Efate",
1012                        "Pacific/Guadalcanal",
1013                        "Pacific/Kosrae",
1014                        "Pacific/Noumea",
1015                        "Pacific/Pohnpei",
1016                        "Pacific/Ponape",
1017                 ),
1018
1019            // 690 min
1020            "690" => array(
1021                        "Pacific/Norfolk",
1022                 ),
1023
1024            // +720 min
1025            "Fiji Islands Standard Time" => array(
1026                        "Asia/Anadyr",
1027                        "Asia/Kamchatka",
1028                        "Asia/Magadan",
1029                        "Kwajalein",
1030                 ),
1031            "New Zealand Standard Time" => array(
1032                        "Antarctica/McMurdo",
1033                        "Antarctica/South_Pole",
1034                        "Etc/GMT-12",
1035                        "NZ",
1036                        "Pacific/Auckland",
1037                        "Pacific/Fiji",
1038                        "Pacific/Funafuti",
1039                        "Pacific/Kwajalein",
1040                        "Pacific/Majuro",
1041                        "Pacific/Nauru",
1042                        "Pacific/Tarawa",
1043                        "Pacific/Wake",
1044                        "Pacific/Wallis",
1045                 ),
1046
1047            // +765 min
1048            "765" => array(
1049                        "NZ-CHAT",
1050                        "Pacific/Chatham",
1051                 ),
1052
1053            // +780 min
1054            "Tonga Standard Time" => array(
1055                        "Etc/GMT-13",
1056                        "Pacific/Apia",
1057                        "Pacific/Enderbury",
1058                        "Pacific/Tongatapu",
1059                 ),
1060
1061            // +840 min
1062            "840" => array(
1063                        "Etc/GMT-14",
1064                        "Pacific/Fakaofo",
1065                        "Pacific/Kiritimati",
1066                 ),
1067            );
1068
1069    /**
1070     * Returns a full timezone array
1071     *
1072     * @param string    $phptimezone    (opt) a php timezone string.
1073     *                                  If omitted the env. default timezone is used.
1074     *
1075     * @access public
1076     * @return array
1077     */
1078    static public function GetFullTZ($phptimezone = false) {
1079        if ($phptimezone === false)
1080            $phptimezone = date_default_timezone_get();
1081
1082        ZLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::GetFullTZ() for ". $phptimezone);
1083
1084        $servertzname = self::guessTZNameFromPHPName($phptimezone);
1085        return self::GetFullTZFromTZName($servertzname);
1086    }
1087
1088    /**
1089     * Returns a full timezone array
1090     *
1091     * @param string   $tzname     a TZID value
1092     *
1093     * @access public
1094     * @return array
1095     */
1096    static public function GetFullTZFromTZName($tzname) {
1097        if (!array_key_exists($tzname, self::$tzonesoffsets)) {
1098            ZLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetFullTZFromTZName('%s'): Is a PHP TimeZone, converting", $tzname));
1099            $tzname = self::guessTZNameFromPHPName($tzname);
1100        }
1101
1102        $offset = self::$tzonesoffsets[$tzname];
1103
1104        $tz = array(
1105            "bias" => $offset[0],
1106            "tzname" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)),
1107            "dstendyear" => $offset[3],
1108            "dstendmonth" => $offset[4],
1109            "dstendday" => $offset[5],
1110            "dstendweek" => $offset[6],
1111            "dstendhour" => $offset[7],
1112            "dstendminute" => $offset[8],
1113            "dstendsecond" => $offset[9],
1114            "dstendmillis" => $offset[10],
1115            "stdbias" => $offset[1],
1116            "tznamedst" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)),
1117            "dststartyear" => $offset[11],
1118            "dststartmonth" => $offset[12],
1119            "dststartday" => $offset[13],
1120            "dststartweek" => $offset[14],
1121            "dststarthour" => $offset[15],
1122            "dststartminute" => $offset[16],
1123            "dststartsecond" => $offset[17],
1124            "dststartmillis" => $offset[18],
1125            "dstbias" => $offset[2]
1126        );
1127
1128        return $tz;
1129    }
1130
1131    /**
1132     * Sets the timezone name by matching data from the offset (bias etc)
1133     *
1134     * @param array     $offset         a z-push timezone array
1135     *
1136     * @access public
1137     * @return array
1138     */
1139    static public function FillTZNames($tz) {
1140        ZLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::FillTZNames() filling up bias ". $tz["bias"]);
1141        if (!isset($tz["bias"]))
1142            ZLog::Write(LOGLEVEL_WARN, "TimezoneUtil::FillTZNames() submitted TZ array does not have a bias");
1143        else {
1144            $tzname = self::guessTZNameFromOffset($tz);
1145            $tz['tzname'] = $tz['tznamedst'] = self::encodeTZName(self::getMSTZnameFromTZName($tzname));
1146        }
1147        return $tz;
1148    }
1149
1150    /**
1151     * Tries to find a timezone using the Bias and other offset parameters
1152     *
1153     * @param array     $offset         a z-push timezone array
1154     *
1155     * @access public
1156     * @return string
1157     */
1158    static private function guessTZNameFromOffset($offset) {
1159        // try to find a quite exact match
1160        foreach (self::$tzonesoffsets as $tzname => $tzoffset) {
1161            if ($offset["bias"] == $tzoffset[0] &&
1162                isset($offset["dstendmonth"])   && $offset["dstendmonth"] == $tzoffset[4] &&
1163                isset($offset["dstendday"])     && $offset["dstendday"] == $tzoffset[6] &&
1164                isset($offset["dststartmonth"]) && $offset["dststartmonth"] == $tzoffset[12] &&
1165                isset($offset["dststartday"])   && $offset["dststartday"] == $tzoffset[14])
1166                return $tzname;
1167        }
1168
1169        // try to find a bias match
1170        foreach (self::$tzonesoffsets as $tzname => $tzoffset) {
1171            if ($offset["bias"] == $tzoffset[0])
1172                return $tzname;
1173        }
1174
1175        // nothing found? return gmt
1176        ZLog::Write(LOGLEVEL_WARN, "TimezoneUtil::guessTZNameFromOffset() no timezone found for the data submitted. Returning 'GMT Standard Time'.");
1177        return "GMT Standard Time";
1178    }
1179
1180    /**
1181     * Tries to find a AS timezone for a php timezone.
1182     *
1183     * @param string    $phpname        a php timezone name
1184     *
1185     * @access public
1186     * @return string
1187     */
1188    static private function guessTZNameFromPHPName($phpname) {
1189        foreach (self::$phptimezones as $tzn => $phptzs) {
1190            if (in_array($phpname, $phptzs)) {
1191                if (!is_int($tzn)) {
1192                    return $tzn;
1193                }
1194                break;
1195            }
1196        }
1197        ZLog::Write(LOGLEVEL_ERROR, sprintf("TimezoneUtil::guessTZNameFromPHPName() no compatible timezone found for '%s'. Returning 'GMT Standard Time'. Please contact the Z-Push dev team.", $phpname));
1198        return self::$mstzones["085"][0];
1199    }
1200
1201    /**
1202     * Returns an AS compatible tz name
1203     *
1204     * @param string    $name       internal timezone name
1205     *
1206     * @access private
1207     * @return string
1208     */
1209    static private function getMSTZnameFromTZName($name) {
1210        // if $name is empty, get the timezone from system
1211        if (trim($name) == '') {
1212            $name = date_default_timezone_get();
1213            ZLog::Write(LOGLEVEL_INFO, sprintf("TimezoneUtil::getMSTZnameFromTZName(): empty timezone name sent. Got timezone from the system: '%s'", $name));
1214        }
1215
1216        foreach (self::$mstzones as $mskey => $msdefs) {
1217            if ($name == $msdefs[0])
1218                return $msdefs[1];
1219        }
1220
1221        // Not found? Then retrieve the correct TZName first and try again.
1222        // That's ugly and needs a proper fix. But for now this method can convert
1223        // - Europe/Berlin
1224        // - W Europe Standard Time
1225        // to "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"
1226        // which is more correct than the hardcoded default of (GMT+00:00...)
1227        $tzName = '';
1228        foreach (self::$phptimezones as $tzn => $phptzs) {
1229            if (in_array($name, $phptzs)) {
1230                $tzName = $tzn;
1231                break;
1232            }
1233        }
1234        if ($tzName != '') {
1235            foreach (self::$mstzones as $mskey => $msdefs) {
1236                if ($tzName == $msdefs[0])
1237                    return $msdefs[1];
1238            }
1239        }
1240
1241        ZLog::Write(LOGLEVEL_WARN, sprintf("TimezoneUtil::getMSTZnameFromTZName() no MS name found for '%s'. Returning '(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London'", $name));
1242        return self::$mstzones["085"][1];
1243    }
1244
1245    /**
1246     * Encodes the tz name to UTF-16 compatible with a syncblob
1247     *
1248     * @param string    $name       timezone name
1249     *
1250     * @access public
1251     * @return string
1252     */
1253    static private function encodeTZName($name) {
1254        return substr(iconv('UTF-8', 'UTF-16', $name),2,-1);
1255    }
1256
1257    /**
1258     * Test to check if $mstzones and $tzonesoffsets can be resolved
1259     * in both directions.
1260     *
1261     * @access public
1262     * @return
1263     */
1264    static public function TZtest() {
1265        foreach (self::$mstzones as $mskey => $msdefs) {
1266            if (!array_key_exists($msdefs[0], self::$tzonesoffsets))
1267                echo "key   '". $msdefs[0]. "'   not found in tzonesoffsets\n";
1268        }
1269
1270        foreach (self::$tzonesoffsets as $tzname => $offset) {
1271            $found = false;
1272            foreach (self::$mstzones as $mskey => $msdefs) {
1273                if ($tzname == $msdefs[0]) {
1274                    $found = true;
1275                    break;
1276                }
1277            }
1278            if (!$found)
1279                echo "key    '$tzname' NOT FOUND\n";
1280        }
1281    }
1282
1283    /**
1284     * Pack timezone info for Sync.
1285     *
1286     * @param array     $tz
1287     *
1288     * @access public
1289     * @return string
1290     */
1291    static public function GetSyncBlobFromTZ($tz) {
1292        // set the correct TZ name (done using the Bias)
1293        if (!isset($tz["tzname"]) || !$tz["tzname"] || !isset($tz["tznamedst"]) || !$tz["tznamedst"])
1294            $tz = TimezoneUtil::FillTZNames($tz);
1295
1296        $packed = pack("la64vvvvvvvv" . "la64vvvvvvvv" . "l",
1297                $tz["bias"], $tz["tzname"], 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"],
1298                $tz["stdbias"], $tz["tznamedst"], 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"],
1299                $tz["dstbias"]);
1300
1301        return $packed;
1302    }
1303
1304    /**
1305     * Generate date object from string and timezone.
1306     *
1307     * @param string $value
1308     * @param string $timezone
1309     *
1310     * @access public
1311     * @return int epoch
1312     */
1313    public static function MakeUTCDate($value, $timezone = null) {
1314        $tz = null;
1315        if ($timezone) {
1316            $tz = timezone_open($timezone);
1317        }
1318        if (!$tz) {
1319            //If there is no timezone set, we use the default timezone
1320            $tz = timezone_open(date_default_timezone_get());
1321        }
1322        //20110930T090000Z
1323        $date = date_create_from_format('Ymd\THis\Z', $value, timezone_open("UTC"));
1324        if (!$date) {
1325            //20110930T090000
1326            $date = date_create_from_format('Ymd\THis', $value, $tz);
1327        }
1328        if (!$date) {
1329            //20110930 (Append T000000Z to the date, so it starts at midnight)
1330            $date = date_create_from_format('Ymd\THis\Z', $value . "T000000Z", $tz);
1331        }
1332        return date_timestamp_get($date);
1333    }
1334
1335
1336    /**
1337     * Generate a tzid from various formats
1338     *
1339     * @param str $timezone
1340     *
1341     * @access public
1342     * @return timezone id
1343     */
1344    public static function ParseTimezone($timezone) {
1345        //(GMT+01.00) Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna
1346        if (preg_match('/GMT(\\+|\\-)0(\d)/', $timezone, $matches)) {
1347            return "Etc/GMT" . $matches[1] . $matches[2];
1348        }
1349        //(GMT+10.00) XXX / XXX / XXX / XXX
1350        if (preg_match('/GMT(\\+|\\-)1(\d)/', $timezone, $matches)) {
1351            return "Etc/GMT" . $matches[1] . "1" . $matches[2];
1352        }
1353        ///inverse.ca/20101018_1/Europe/Amsterdam or /inverse.ca/20101018_1/America/Argentina/Buenos_Aires
1354        if (preg_match('/\/[.[:word:]]+\/\w+\/(\w+)\/([\w\/]+)/', $timezone, $matches)) {
1355            return $matches[1] . "/" . $matches[2];
1356        }
1357        return self::getMSTZnameFromTZName(trim($timezone, '"'));
1358    }
1359
1360    /**
1361     * Returns a timezone supported by PHP for DateTimeZone constructor.
1362     * @see http://php.net/manual/en/timezones.php
1363     *
1364     * @param string $timezone
1365     *
1366     * @access public
1367     * @return string
1368     */
1369    public static function GetPhpSupportedTimezone($timezone) {
1370        if (in_array($timezone, DateTimeZone::listIdentifiers())) {
1371            ZLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetPhpSupportedTimezone(): '%s' is a PHP supported timezone", $timezone));
1372            return $timezone;
1373        }
1374        $dtz = date_default_timezone_get();
1375        ZLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetPhpSupportedTimezone(): '%s' is not a PHP supported timezone. Returning default timezone: '%s'", $timezone, $dtz));
1376        return $dtz;
1377    }
1378
1379    /**
1380     * Returns official timezone name from windows timezone name.
1381     * E.g. "W Europe Standard Time" for "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna".
1382     *
1383     * @param string $winTz Timezone name in windows
1384     *
1385     * @access public
1386     * @return string timezone name
1387     */
1388    public static function GetTZNameFromWinTZ($winTz = false) {
1389
1390        // Return "GMT Standard Time" per default
1391        if ($winTz === false) {
1392            return self::$mstzones['085'][0];
1393        }
1394
1395        foreach (self::$mstzones as $mskey => $msdefs) {
1396            if ($winTz == $msdefs[1]) {
1397                return $msdefs[0];
1398            }
1399        }
1400        return self::$mstzones['085'][0];
1401    }
1402}
1403