ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
TimeZoneUtil.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Sabre\VObject;
4 
15 class TimeZoneUtil {
16 
17  static $map = null;
18 
25  0 => 'UTC',
26  31 => 'Africa/Casablanca',
27 
28  // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
29  // I'm not even kidding.. We handle this special case in the
30  // getTimeZone method.
31  2 => 'Europe/Lisbon',
32  1 => 'Europe/London',
33  4 => 'Europe/Berlin',
34  6 => 'Europe/Prague',
35  3 => 'Europe/Paris',
36  69 => 'Africa/Luanda', // This was a best guess
37  7 => 'Europe/Athens',
38  5 => 'Europe/Bucharest',
39  49 => 'Africa/Cairo',
40  50 => 'Africa/Harare',
41  59 => 'Europe/Helsinki',
42  27 => 'Asia/Jerusalem',
43  26 => 'Asia/Baghdad',
44  74 => 'Asia/Kuwait',
45  51 => 'Europe/Moscow',
46  56 => 'Africa/Nairobi',
47  25 => 'Asia/Tehran',
48  24 => 'Asia/Muscat', // Best guess
49  54 => 'Asia/Baku',
50  48 => 'Asia/Kabul',
51  58 => 'Asia/Yekaterinburg',
52  47 => 'Asia/Karachi',
53  23 => 'Asia/Calcutta',
54  62 => 'Asia/Kathmandu',
55  46 => 'Asia/Almaty',
56  71 => 'Asia/Dhaka',
57  66 => 'Asia/Colombo',
58  61 => 'Asia/Rangoon',
59  22 => 'Asia/Bangkok',
60  64 => 'Asia/Krasnoyarsk',
61  45 => 'Asia/Shanghai',
62  63 => 'Asia/Irkutsk',
63  21 => 'Asia/Singapore',
64  73 => 'Australia/Perth',
65  75 => 'Asia/Taipei',
66  20 => 'Asia/Tokyo',
67  72 => 'Asia/Seoul',
68  70 => 'Asia/Yakutsk',
69  19 => 'Australia/Adelaide',
70  44 => 'Australia/Darwin',
71  18 => 'Australia/Brisbane',
72  76 => 'Australia/Sydney',
73  43 => 'Pacific/Guam',
74  42 => 'Australia/Hobart',
75  68 => 'Asia/Vladivostok',
76  41 => 'Asia/Magadan',
77  17 => 'Pacific/Auckland',
78  40 => 'Pacific/Fiji',
79  67 => 'Pacific/Tongatapu',
80  29 => 'Atlantic/Azores',
81  53 => 'Atlantic/Cape_Verde',
82  30 => 'America/Noronha',
83  8 => 'America/Sao_Paulo', // Best guess
84  32 => 'America/Argentina/Buenos_Aires',
85  60 => 'America/Godthab',
86  28 => 'America/St_Johns',
87  9 => 'America/Halifax',
88  33 => 'America/Caracas',
89  65 => 'America/Santiago',
90  35 => 'America/Bogota',
91  10 => 'America/New_York',
92  34 => 'America/Indiana/Indianapolis',
93  55 => 'America/Guatemala',
94  11 => 'America/Chicago',
95  37 => 'America/Mexico_City',
96  36 => 'America/Edmonton',
97  38 => 'America/Phoenix',
98  12 => 'America/Denver', // Best guess
99  13 => 'America/Los_Angeles', // Best guess
100  14 => 'America/Anchorage',
101  15 => 'Pacific/Honolulu',
102  16 => 'Pacific/Midway',
103  39 => 'Pacific/Kwajalein',
104  ];
105 
125  static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
126 
127  // First we will just see if the tzid is a support timezone identifier.
128  //
129  // The only exception is if the timezone starts with (. This is to
130  // handle cases where certain microsoft products generate timezone
131  // identifiers that for instance look like:
132  //
133  // (GMT+01.00) Sarajevo/Warsaw/Zagreb
134  //
135  // Since PHP 5.5.10, the first bit will be used as the timezone and
136  // this method will return just GMT+01:00. This is wrong, because it
137  // doesn't take DST into account.
138  if ($tzid[0] !== '(') {
139 
140  // PHP has a bug that logs PHP warnings even it shouldn't:
141  // https://bugs.php.net/bug.php?id=67881
142  //
143  // That's why we're checking if we'll be able to successfull instantiate
144  // \DateTimeZone() before doing so. Otherwise we could simply instantiate
145  // and catch the exception.
146  $tzIdentifiers = \DateTimeZone::listIdentifiers();
147 
148  try {
149  if (
150  (in_array($tzid, $tzIdentifiers)) ||
151  (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
152  (in_array($tzid, self::getIdentifiersBC()))
153  ) {
154  return new \DateTimeZone($tzid);
155  }
156  } catch (\Exception $e) {
157  }
158 
159  }
160 
161  self::loadTzMaps();
162 
163  // Next, we check if the tzid is somewhere in our tzid map.
164  if (isset(self::$map[$tzid])) {
165  return new \DateTimeZone(self::$map[$tzid]);
166  }
167 
168  // Some Microsoft products prefix the offset first, so let's strip that off
169  // and see if it is our tzid map. We don't want to check for this first just
170  // in case there are overrides in our tzid map.
171  if (preg_match('/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/', $tzid, $matches)) {
172  $tzidAlternate = $matches[3];
173  if (isset(self::$map[$tzidAlternate])) {
174  return new \DateTimeZone(self::$map[$tzidAlternate]);
175  }
176  }
177 
178  // Maybe the author was hyper-lazy and just included an offset. We
179  // support it, but we aren't happy about it.
180  if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
181 
182  // Note that the path in the source will never be taken from PHP 5.5.10
183  // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
184  // already gets returned early in this function. Once we drop support
185  // for versions under PHP 5.5.10, this bit can be taken out of the
186  // source.
187  // @codeCoverageIgnoreStart
188  return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2], 0, 2), '0'));
189  // @codeCoverageIgnoreEnd
190  }
191 
192  if ($vcalendar) {
193 
194  // If that didn't work, we will scan VTIMEZONE objects
195  foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) {
196 
197  if ((string)$vtimezone->TZID === $tzid) {
198 
199  // Some clients add 'X-LIC-LOCATION' with the olson name.
200  if (isset($vtimezone->{'X-LIC-LOCATION'})) {
201 
202  $lic = (string)$vtimezone->{'X-LIC-LOCATION'};
203 
204  // Libical generators may specify strings like
205  // "SystemV/EST5EDT". For those we must remove the
206  // SystemV part.
207  if (substr($lic, 0, 8) === 'SystemV/') {
208  $lic = substr($lic, 8);
209  }
210 
211  return self::getTimeZone($lic, null, $failIfUncertain);
212 
213  }
214  // Microsoft may add a magic number, which we also have an
215  // answer for.
216  if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
217  $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
218 
219  // 2 can mean both Europe/Lisbon and Europe/Sarajevo.
220  if ($cdoId === 2 && strpos((string)$vtimezone->TZID, 'Sarajevo') !== false) {
221  return new \DateTimeZone('Europe/Sarajevo');
222  }
223 
224  if (isset(self::$microsoftExchangeMap[$cdoId])) {
225  return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
226  }
227  }
228 
229  }
230 
231  }
232 
233  }
234 
235  if ($failIfUncertain) {
236  throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
237  }
238 
239  // If we got all the way here, we default to UTC.
240  return new \DateTimeZone(date_default_timezone_get());
241 
242  }
243 
248  static function loadTzMaps() {
249 
250  if (!is_null(self::$map)) return;
251 
252  self::$map = array_merge(
253  include __DIR__ . '/timezonedata/windowszones.php',
254  include __DIR__ . '/timezonedata/lotuszones.php',
255  include __DIR__ . '/timezonedata/exchangezones.php',
256  include __DIR__ . '/timezonedata/php-workaround.php'
257  );
258 
259  }
260 
272  static function getIdentifiersBC() {
273  return include __DIR__ . '/timezonedata/php-bc.php';
274  }
275 
276 }
static getIdentifiersBC()
This method returns an array of timezone identifiers, that are supported by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
static static getTimeZone($tzid, Component $vcalendar=null, $failIfUncertain=false)
This method will try to find out the correct timezone for an iCalendar date-time value.
static loadTzMaps()
This method will load in all the tz mapping information, if it&#39;s not yet done.
Time zone name translation.
static $microsoftExchangeMap
List of microsoft exchange timezone ids.