1<?php
2
3namespace Sabre\VObject;
4
5class TimeZoneUtilTest extends \PHPUnit_Framework_TestCase {
6
7    function setUp() {
8
9        // clearning the tz cache
10        TimezoneUtil::$map = null;
11
12    }
13
14    /**
15     * @dataProvider getMapping
16     */
17    function testCorrectTZ($timezoneName) {
18
19        try {
20            $tz = new \DateTimeZone($timezoneName);
21            $this->assertInstanceOf('DateTimeZone', $tz);
22        } catch (\Exception $e) {
23            if (strpos($e->getMessage(), "Unknown or bad timezone") !== false) {
24                $this->markTestSkipped($timezoneName . ' is not (yet) supported in this PHP version. Update pecl/timezonedb');
25            } else {
26                throw $e;
27            }
28
29        }
30
31    }
32
33    function getMapping() {
34
35        TimeZoneUtil::loadTzMaps();
36
37        // PHPUNit requires an array of arrays
38        return array_map(
39            function($value) {
40                return [$value];
41            },
42            TimeZoneUtil::$map
43        );
44
45    }
46
47    function testExchangeMap() {
48
49        $vobj = <<<HI
50BEGIN:VCALENDAR
51METHOD:REQUEST
52VERSION:2.0
53BEGIN:VTIMEZONE
54TZID:foo
55X-MICROSOFT-CDO-TZID:2
56BEGIN:STANDARD
57DTSTART:16010101T030000
58TZOFFSETFROM:+0200
59TZOFFSETTO:+0100
60RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
61END:STANDARD
62BEGIN:DAYLIGHT
63DTSTART:16010101T020000
64TZOFFSETFROM:+0100
65TZOFFSETTO:+0200
66RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
67END:DAYLIGHT
68END:VTIMEZONE
69BEGIN:VEVENT
70DTSTAMP:20120416T092149Z
71DTSTART;TZID="foo":20120418T1
72 00000
73SUMMARY:Begin Unterhaltsreinigung
74UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
75 0100000008FECD2E607780649BE5A4C9EE6418CBC
76 000
77END:VEVENT
78END:VCALENDAR
79HI;
80
81        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
82        $ex = new \DateTimeZone('Europe/Lisbon');
83
84        $this->assertEquals($ex->getName(), $tz->getName());
85
86    }
87
88    function testWetherMicrosoftIsStillInsane() {
89
90        $vobj = <<<HI
91BEGIN:VCALENDAR
92METHOD:REQUEST
93VERSION:2.0
94BEGIN:VTIMEZONE
95TZID:(GMT+01.00) Sarajevo/Warsaw/Zagreb
96X-MICROSOFT-CDO-TZID:2
97BEGIN:STANDARD
98DTSTART:16010101T030000
99TZOFFSETFROM:+0200
100TZOFFSETTO:+0100
101RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
102END:STANDARD
103END:VTIMEZONE
104END:VCALENDAR
105HI;
106
107        $tz = TimeZoneUtil::getTimeZone('(GMT+01.00) Sarajevo/Warsaw/Zagreb', Reader::read($vobj));
108        $ex = new \DateTimeZone('Europe/Sarajevo');
109
110        $this->assertEquals($ex->getName(), $tz->getName());
111
112    }
113
114    function testUnknownExchangeId() {
115
116        $vobj = <<<HI
117BEGIN:VCALENDAR
118METHOD:REQUEST
119VERSION:2.0
120BEGIN:VTIMEZONE
121TZID:foo
122X-MICROSOFT-CDO-TZID:2000
123BEGIN:STANDARD
124DTSTART:16010101T030000
125TZOFFSETFROM:+0200
126TZOFFSETTO:+0100
127RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
128END:STANDARD
129BEGIN:DAYLIGHT
130DTSTART:16010101T020000
131TZOFFSETFROM:+0100
132TZOFFSETTO:+0200
133RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
134END:DAYLIGHT
135END:VTIMEZONE
136BEGIN:VEVENT
137DTSTAMP:20120416T092149Z
138DTSTART;TZID="foo":20120418T1
139 00000
140SUMMARY:Begin Unterhaltsreinigung
141UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
142 0100000008FECD2E607780649BE5A4C9EE6418CBC
143DTEND;TZID="Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb":20120418T103
144 000
145END:VEVENT
146END:VCALENDAR
147HI;
148
149        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
150        $ex = new \DateTimeZone(date_default_timezone_get());
151        $this->assertEquals($ex->getName(), $tz->getName());
152
153    }
154
155    function testWindowsTimeZone() {
156
157        $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time');
158        $ex = new \DateTimeZone('America/New_York');
159        $this->assertEquals($ex->getName(), $tz->getName());
160
161    }
162
163    /**
164     * @dataProvider getPHPTimeZoneIdentifiers
165     */
166    function testTimeZoneIdentifiers($tzid) {
167
168        $tz = TimeZoneUtil::getTimeZone($tzid);
169        $ex = new \DateTimeZone($tzid);
170
171        $this->assertEquals($ex->getName(), $tz->getName());
172
173    }
174
175    /**
176     * @dataProvider getPHPTimeZoneBCIdentifiers
177     */
178    function testTimeZoneBCIdentifiers($tzid) {
179
180        $tz = TimeZoneUtil::getTimeZone($tzid);
181        $ex = new \DateTimeZone($tzid);
182
183        $this->assertEquals($ex->getName(), $tz->getName());
184
185    }
186
187    function getPHPTimeZoneIdentifiers() {
188
189        // PHPUNit requires an array of arrays
190        return array_map(
191            function($value) {
192                return [$value];
193            },
194            \DateTimeZone::listIdentifiers()
195        );
196
197    }
198
199    function getPHPTimeZoneBCIdentifiers() {
200
201        // PHPUNit requires an array of arrays
202        return array_map(
203            function($value) {
204                return [$value];
205            },
206            TimeZoneUtil::getIdentifiersBC()
207        );
208
209    }
210
211    function testTimezoneOffset() {
212
213        $tz = TimeZoneUtil::getTimeZone('GMT-0400', null, true);
214
215        if (version_compare(PHP_VERSION, '5.5.10', '>=') && !defined('HHVM_VERSION')) {
216            $ex = new \DateTimeZone('-04:00');
217        } else {
218            $ex = new \DateTimeZone('Etc/GMT-4');
219        }
220        $this->assertEquals($ex->getName(), $tz->getName());
221
222    }
223
224    /**
225     * @expectedException InvalidArgumentException
226     */
227    function testTimezoneFail() {
228
229        $tz = TimeZoneUtil::getTimeZone('FooBar', null, true);
230
231    }
232
233    function testFallBack() {
234
235        $vobj = <<<HI
236BEGIN:VCALENDAR
237METHOD:REQUEST
238VERSION:2.0
239BEGIN:VTIMEZONE
240TZID:foo
241BEGIN:STANDARD
242DTSTART:16010101T030000
243TZOFFSETFROM:+0200
244TZOFFSETTO:+0100
245RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
246END:STANDARD
247BEGIN:DAYLIGHT
248DTSTART:16010101T020000
249TZOFFSETFROM:+0100
250TZOFFSETTO:+0200
251RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
252END:DAYLIGHT
253END:VTIMEZONE
254BEGIN:VEVENT
255DTSTAMP:20120416T092149Z
256DTSTART;TZID="foo":20120418T1
257 00000
258SUMMARY:Begin Unterhaltsreinigung
259UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
260 0100000008FECD2E607780649BE5A4C9EE6418CBC
261 000
262END:VEVENT
263END:VCALENDAR
264HI;
265
266        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
267        $ex = new \DateTimeZone(date_default_timezone_get());
268        $this->assertEquals($ex->getName(), $tz->getName());
269
270    }
271
272    function testLjubljanaBug() {
273
274        $vobj = <<<HI
275BEGIN:VCALENDAR
276CALSCALE:GREGORIAN
277PRODID:-//Ximian//NONSGML Evolution Calendar//EN
278VERSION:2.0
279BEGIN:VTIMEZONE
280TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana
281X-LIC-LOCATION:Europe/Ljubljana
282BEGIN:STANDARD
283TZNAME:CET
284DTSTART:19701028T030000
285RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
286TZOFFSETFROM:+0200
287TZOFFSETTO:+0100
288END:STANDARD
289BEGIN:DAYLIGHT
290TZNAME:CEST
291DTSTART:19700325T020000
292RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
293TZOFFSETFROM:+0100
294TZOFFSETTO:+0200
295END:DAYLIGHT
296END:VTIMEZONE
297BEGIN:VEVENT
298UID:foo
299DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
300 20121003T080000
301DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
302 20121003T083000
303TRANSP:OPAQUE
304SEQUENCE:2
305SUMMARY:testing
306CREATED:20121002T172613Z
307LAST-MODIFIED:20121002T172613Z
308END:VEVENT
309END:VCALENDAR
310
311HI;
312
313
314        $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana', Reader::read($vobj));
315        $ex = new \DateTimeZone('Europe/Ljubljana');
316        $this->assertEquals($ex->getName(), $tz->getName());
317
318    }
319
320    function testWeirdSystemVLICs() {
321
322$vobj = <<<HI
323BEGIN:VCALENDAR
324CALSCALE:GREGORIAN
325PRODID:-//Ximian//NONSGML Evolution Calendar//EN
326VERSION:2.0
327BEGIN:VTIMEZONE
328TZID:/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT
329X-LIC-LOCATION:SystemV/EST5EDT
330BEGIN:STANDARD
331TZNAME:EST
332DTSTART:19701104T020000
333RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
334TZOFFSETFROM:-0400
335TZOFFSETTO:-0500
336END:STANDARD
337BEGIN:DAYLIGHT
338TZNAME:EDT
339DTSTART:19700311T020000
340RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
341TZOFFSETFROM:-0500
342TZOFFSETTO:-0400
343END:DAYLIGHT
344END:VTIMEZONE
345BEGIN:VEVENT
346UID:20121026T021107Z-6301-1000-1-0@chAir
347DTSTAMP:20120905T172126Z
348DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
349 20121026T153000
350DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
351 20121026T160000
352TRANSP:OPAQUE
353SEQUENCE:5
354SUMMARY:pick up Ibby
355CLASS:PUBLIC
356CREATED:20121026T021108Z
357LAST-MODIFIED:20121026T021118Z
358X-EVOLUTION-MOVE-CALENDAR:1
359END:VEVENT
360END:VCALENDAR
361HI;
362
363        $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT', Reader::read($vobj), true);
364        $ex = new \DateTimeZone('America/New_York');
365        $this->assertEquals($ex->getName(), $tz->getName());
366
367    }
368
369
370    function testPrefixedOffsetExchangeIdentifier()
371    {
372        $tz = TimeZoneUtil::getTimeZone('(UTC-05:00) Eastern Time (US & Canada)');
373        $ex = new \DateTimeZone('America/New_York');
374        $this->assertEquals($ex->getName(), $tz->getName());
375    }
376
377}
378