Skip to content

Commit

Permalink
Fix test
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinbonneaud committed Dec 15, 2021
1 parent f35fc1e commit 7c7ae93
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 136 deletions.
246 changes: 140 additions & 106 deletions lib/TimeZoneUtil.php
Expand Up @@ -33,109 +33,6 @@ class TimeZoneUtil
/** @var TimezoneFinder[] */
private $timezoneFinders = [];

private function __construct()
{
$this->addGuesser('lic', new GuessFromLicEntry());
$this->addGuesser('msTzId', new GuessFromMsTzId());
$this->addFinder('tzid', new FindFromTimezoneIdentifier());
$this->addFinder('tzmap', new FindFromTimezoneMap());
$this->addFinder('offset', new FindFromOffset());
}

private static function getInstance(): self
{
if (null === self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

private function addGuesser(string $key, TimezoneGuesser $guesser): void
{
$this->timezoneGuessers[$key] = $guesser;
}

private function addFinder(string $key, TimezoneFinder $finder): void
{
$this->timezoneFinders[$key] = $finder;
}

/**
* This method will try to find out the correct timezone for an iCalendar
* date-time value.
*
* You must pass the contents of the TZID parameter, as well as the full
* calendar.
*
* If the lookup fails, this method will return the default PHP timezone
* (as configured using date_default_timezone_set, or the date.timezone ini
* setting).
*
* Alternatively, if $failIfUncertain is set to true, it will throw an
* exception if we cannot accurately determine the timezone.
*/
private function findTimeZone(string $tzid, Component $vcalendar = null, bool $failIfUncertain = false): DateTimeZone
{
foreach ($this->timezoneFinders as $timezoneFinder) {
$timezone = $timezoneFinder->find($tzid, $failIfUncertain);
if (!$timezone instanceof DateTimeZone) {
continue;
}

return $timezone;
}

if ($vcalendar) {
// If that didn't work, we will scan VTIMEZONE objects
foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) {
if ((string) $vtimezone->TZID === $tzid) {
foreach ($this->timezoneGuessers as $timezoneGuesser) {
$timezone = $timezoneGuesser->guess($vtimezone, $failIfUncertain);
if (!$timezone instanceof DateTimeZone) {
continue;
}

return $timezone;
}
}
}
}

if ($failIfUncertain) {
throw new InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: '.$tzid);
}

// If we got all the way here, we default to whatever has been set as the PHP default timezone.
return new DateTimeZone(date_default_timezone_get());
}

public static function addTimezoneGuesser(string $key, TimezoneGuesser $guesser): void
{
self::getInstance()->addGuesser($key, $guesser);
}

public static function addTimezoneFinder(string $key, TimezoneFinder $finder): void
{
self::getInstance()->addFinder($key, $finder);
}

/**
* @param string $tzid
* @param false $failIfUncertain
*
* @return DateTimeZone
*/
public static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false)
{
return self::getInstance()->findTimeZone($tzid, $vcalendar, $failIfUncertain);
}

public static function clean(): void
{
self::$instance = null;
}

// Keeping things for backwards compatibility
/**
* @var array|null
Expand Down Expand Up @@ -301,11 +198,11 @@ public static function clean(): void
29 => 'Atlantic/Azores',
53 => 'Atlantic/Cape_Verde',
30 => 'America/Noronha',
8 => 'America/Sao_Paulo', // Best guess
8 => 'America/Sao_Paulo', // Best guess
32 => 'America/Argentina/Buenos_Aires',
60 => 'America/Godthab',
28 => 'America/St_Johns',
9 => 'America/Halifax',
9 => 'America/Halifax',
33 => 'America/Caracas',
65 => 'America/Santiago',
35 => 'America/Bogota',
Expand All @@ -325,6 +222,143 @@ public static function clean(): void
];

/**
<<<<<<< HEAD
* This method will try to find out the correct timezone for an iCalendar
* date-time value.
*
* You must pass the contents of the TZID parameter, as well as the full
* calendar.
*
* If the lookup fails, this method will return the default PHP timezone
* (as configured using date_default_timezone_set, or the date.timezone ini
* setting).
*
* Alternatively, if $failIfUncertain is set to true, it will throw an
* exception if we cannot accurately determine the timezone.
*
* @param string $tzid
* @param Sabre\VObject\Component $vcalendar
*
* @return \DateTimeZone
*/
public static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false)
{
// First we will just see if the tzid is a support timezone identifier.
//
// The only exception is if the timezone starts with (. This is to
// handle cases where certain microsoft products generate timezone
// identifiers that for instance look like:
//
// (GMT+01.00) Sarajevo/Warsaw/Zagreb
//
// Since PHP 5.5.10, the first bit will be used as the timezone and
// this method will return just GMT+01:00. This is wrong, because it
// doesn't take DST into account.
$originalTzid = $tzid;
if ($tzid && '(' !== $tzid[0]) {
// If the timezone is prefixed with a slash we remove the slash for lookup in the maps.
if ('/' === $tzid[0]) {
$tzid = substr($tzid, 1);
}
// PHP has a bug that logs PHP warnings even it shouldn't:
// https://bugs.php.net/bug.php?id=67881
//
// That's why we're checking if we'll be able to successfully instantiate
// \DateTimeZone() before doing so. Otherwise we could simply instantiate
// and catch the exception.
$tzIdentifiers = \DateTimeZone::listIdentifiers();

try {
if (
(in_array($tzid, $tzIdentifiers)) ||
(preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
(in_array($tzid, self::getIdentifiersBC()))
) {
return new \DateTimeZone($tzid);
}
} catch (\Exception $e) {
}
}

self::loadTzMaps();

// Next, we check if the tzid is somewhere in our tzid map.
if (isset(self::$map[$tzid])) {
return new \DateTimeZone(self::$map[$tzid]);
}

// If we removed the slash we add it back
$tzid = $originalTzid;

// Some Microsoft products prefix the offset first, so let's strip that off
// and see if it is our tzid map. We don't want to check for this first just
// in case there are overrides in our tzid map.
if (preg_match('/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/', $tzid, $matches)) {
$tzidAlternate = $matches[3];
if (isset(self::$map[$tzidAlternate])) {
return new \DateTimeZone(self::$map[$tzidAlternate]);
}
}

// Maybe the author was hyper-lazy and just included an offset. We
// support it, but we aren't happy about it.
if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
// Note that the path in the source will never be taken from PHP 5.5.10
// onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
// already gets returned early in this function. Once we drop support
// for versions under PHP 5.5.10, this bit can be taken out of the
// source.
// @codeCoverageIgnoreStart
return new \DateTimeZone('Etc/GMT'.$matches[1].ltrim(substr($matches[2], 0, 2), '0'));
// @codeCoverageIgnoreEnd
}

if ($vcalendar) {
// If that didn't work, we will scan VTIMEZONE objects
foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) {
if ((string) $vtimezone->TZID === $tzid) {
// Some clients add 'X-LIC-LOCATION' with the olson name.
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
$lic = (string) $vtimezone->{'X-LIC-LOCATION'};

// Libical generators may specify strings like
// "SystemV/EST5EDT". For those we must remove the
// SystemV part.
if ('SystemV/' === substr($lic, 0, 8)) {
$lic = substr($lic, 8);
}

return self::getTimeZone($lic, null, $failIfUncertain);
}
// Microsoft may add a magic number, which we also have an
// answer for.
if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
$cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();

// 2 can mean both Europe/Lisbon and Europe/Sarajevo.
if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) {
return new \DateTimeZone('Europe/Sarajevo');
}

if (isset(self::$microsoftExchangeMap[$cdoId])) {
return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
}
}
}
}
}

if ($failIfUncertain) {
throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: '.$tzid);
}

// If we got all the way here, we default to UTC.
return new \DateTimeZone(date_default_timezone_get());
}

/**
=======
>>>>>>> upstream/master
* This method will load in all the tz mapping information, if it's not yet
* done.
*
Expand Down Expand Up @@ -362,4 +396,4 @@ public static function getIdentifiersBC()
{
return include __DIR__.'/timezonedata/php-bc.php';
}
}
}
27 changes: 0 additions & 27 deletions tests/VObject/Recur/RRuleIteratorTest.php
Expand Up @@ -387,33 +387,6 @@ public function testMonthlyByDayUntilWithImpossibleNextOccurrence()
);
}

public function testMonthlyByDayUntil()
{
$this->parse(
'FREQ=MONTHLY;INTERVAL=1;BYDAY=WE;WKST=WE;UNTIL=20210317T000000Z',
'2021-02-10 00:00:00',
[
'2021-02-10 00:00:00',
'2021-02-17 00:00:00',
'2021-02-24 00:00:00',
'2021-03-03 00:00:00',
'2021-03-10 00:00:00',
'2021-03-17 00:00:00',
]
);
}

public function testMonthlyByDayUntilWithImpossibleNextOccurrence()
{
$this->parse(
'FREQ=MONTHLY;INTERVAL=1;BYDAY=2WE;BYMONTHDAY=2;WKST=WE;UNTIL=20210317T000000Z',
'2021-02-10 00:00:00',
[
'2021-02-10 00:00:00',
]
);
}

public function testMonthlyByDayByMonthDay()
{
$this->parse(
Expand Down
3 changes: 0 additions & 3 deletions tests/phpunit.xml
Expand Up @@ -6,9 +6,6 @@
convertWarningsToExceptions="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
failOnRisky="true"
failOnWarning="true"
enforceTimeLimit="true"
>
<testsuites>
<testsuite name="Sabre\VObject">
Expand Down

0 comments on commit 7c7ae93

Please sign in to comment.