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