@@ -221,6 +221,76 @@ public function testTimezoneOffset(): void
221221 self ::assertEquals ($ ex ->getName (), $ tz ->getName ());
222222 }
223223
224+ /**
225+ * @dataProvider getValidUtcOffsets
226+ */
227+ public function testValidUtcOffset (string $ tzid , string $ expectedOffset ): void
228+ {
229+ $ tz = TimeZoneUtil::getTimeZone ($ tzid , null , true );
230+
231+ self ::assertSame ($ expectedOffset , $ tz ->getName ());
232+ }
233+
234+ public static function getValidUtcOffsets (): array
235+ {
236+ return [
237+ ['UTC-04:00 ' , '-04:00 ' ],
238+ ['UTC-0400 ' , '-04:00 ' ],
239+ ['UTC+05:30 ' , '+05:30 ' ],
240+ ['UTC+0545 ' , '+05:45 ' ],
241+ ['utc-03:30 ' , '-03:30 ' ],
242+ ['UTC+00:00 ' , '+00:00 ' ],
243+ ['UTC-00:00 ' , '+00:00 ' ],
244+ ['UTC+23:59 ' , '+23:59 ' ],
245+ ];
246+ }
247+
248+ /**
249+ * @dataProvider getInvalidUtcOffsets
250+ */
251+ public function testInvalidUtcOffset (string $ tzid ): void
252+ {
253+ self ::assertSame (date_default_timezone_get (), TimeZoneUtil::getTimeZone ($ tzid )->getName ());
254+
255+ $ this ->expectException (\InvalidArgumentException::class);
256+ TimeZoneUtil::getTimeZone ($ tzid , null , true );
257+ }
258+
259+ public static function getInvalidUtcOffsets (): array
260+ {
261+ return [
262+ ['UTC-24:00 ' ],
263+ ['UTC+04:60 ' ],
264+ ['UTC+4:00 ' ],
265+ ['UTC+04 ' ],
266+ ['UTC04:00 ' ],
267+ ['UTC+ab:cd ' ],
268+ ['UTC+04:00extra ' ],
269+ ["UTC+04:00 \n" ],
270+ ['UTC+04::00 ' ],
271+ ['UTC+ ' ],
272+ ];
273+ }
274+
275+ public function testParseUtcOffsetDateTime (): void
276+ {
277+ $ calendar = Reader::read (<<<ICS
278+ BEGIN:VCALENDAR
279+ VERSION:2.0
280+ BEGIN:VEVENT
281+ UID:fixed-offset
282+ DTSTART;TZID="UTC-04:00":20260728T111500
283+ END:VEVENT
284+ END:VCALENDAR
285+ ICS
286+ );
287+
288+ $ start = $ calendar ->VEVENT ->DTSTART ->getDateTime ();
289+
290+ self ::assertSame ('2026-07-28 11:15:00 -04:00 ' , $ start ->format ('Y-m-d H:i:s P ' ));
291+ self ::assertSame ((new \DateTimeImmutable ('2026-07-28T15:15:00Z ' ))->getTimestamp (), $ start ->getTimestamp ());
292+ }
293+
224294 public function testTimezoneFail (): void
225295 {
226296 $ this ->expectException (\InvalidArgumentException::class);
0 commit comments