From 814f4b85dcdd58dc19615ccece024542564d9bc2 Mon Sep 17 00:00:00 2001 From: Valentin BONNEAUD Date: Wed, 15 Dec 2021 16:41:11 +0100 Subject: [PATCH] Merge upstream commits (#36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix Bad file descriptor (7.4) (#469) * travis: allow failure for phpstan for now (#470) * travis: add php 7.4snapshot build (#471) * reduce phpstan level back to 0, as level 1 is failing right now (#472) * reduce phpstan level back to 0, as level 1 is failing right now * travis: no longer allow failures for phpstan * Fixed typo in vobject CLI help (#477) * Release 4.2.1 (#479) * Release 4.2.1 * Update CHANGELOG.md * Prepare next iteration (#480) * Run phpstan on PHP >= 7.1 (#483) * Add TZ in iTip REPLY messages * Added phpstan for tests folder (#485) * Add PHONE-NUMBER value type (used for TEL in vCard 3.0) (#486) Signed-off-by: Christian Kraus * use latest phpstan 0.12.5 in CI * Add PHPstorm .idea to .gitignore * Fix various typos * Release 4.2.2 (#490) * Release 4.2.2 * Update CHANGELOG.md * Prepare next iteration * Update dependencies and code style tools * Remove unneeded 'bootstrap' line from phpstan.neon * Apply php-cs-fixer code style changes * run php-cs-fixer in CI * php-cs-fixer must be at least 2.16.1 for PHP 7.4 * Use phpunit8 where possible * Fixed phpstan level 1 errors * Make sure there is no logic change * Prevent setting foreach key beforehand * Refactored fqcn strings to ::class to allow checking with phpstan (#495) * Release 4.3.0 (#497) * Update CHANGELOG.md * Update Version.php * Added phpstan to dev dependencies * Reset bin-dir config * Added convenient development commands * Cleaned up .gitignore These entries should be in the developer's global .gitignore * cs-fixer: don't check only the lib folder * Decoupled cs-fixer command from chosen tool * Standardize CI * Use phpunit 9 where possible * Only upload coverage when it has been collected * Replace assertRegExp with assertMatchesRegularExpression in unit test * fix an incomplete phpdoc type annotation * Release 4.3.1 * Adjust boolean vars in .travis.yml to prepare for PHP8.0 * Run unit tests on PHP8 * Fixup calendar parameter to Broker parseEvent * Release 4.3.2 * Remove Pacific-New obsolete timezone * Do composer remove --no-update in Travis * Use min php-cs-fixer 2.16.7 * Release 4.3.3 * Add .gitattributes * Explicitly select PHP 8.0 in CI * Use latest php-cs-fixer 2.17.1 * Update windowszones timezone data to 2020-12-13 * Fix typos * Reassign modified date in yearly rrule * Add test * Code style * Add test for calendar expand * adjust unit test settings for time limits Some tests were testing a bug that caused an infinite loop. Annotate those tests with large, small annotations. Turn on enforceTimeLimit, failOnWarning and failOnRisky so that the annotations are enforced when unit tests are run. Add phpunit/php-invoker to the composer require-dev becaause this is required to make the enforceTimeLimit setting effective. * Release 4.3.4 * Make use of until parameter in nextMonthly function * CS FIX * CS FIX * Fix breaking tests * create testMonthlyByDayUntil * create testMonthlyByDayUntilWithImpossibleNextOccurrence * Fix setting properties with group assignment * Unit test for adding properties with group for a VCard * Adapt style * Release 4.3.5 * tests: migrate from Travis to gh-actions * Removed travis config * Minor edit to README * Run phpunit with coverage in CI * Fix deprecated usages and return types on PHP 8.1 * sync ci.yml to match other repos * Changes that should have happened for 4.3.6 * changelog and VERSION bump for 4.3.7 * EventIterator returns wrong endTime (#534) * Reordering of the attendees should not be a signitifcant change (sabre-io#540) * Reordering of vevent should not be a significant change (#542) * Prepare release 4.3.8 * Allow easier extension of the timezone guessing This will ease customization of timezone-guessing as it is now gets easier to extend that process with own implementations (as long as they implement the appropriate interface) This is espechially necessary when wanting to actually guess a timezone via the rules defined in the VTIMEZONE-entry (which is currently not done) * testEmptyTimeZone * Changelog for 4.4.0 * Fix Iterator method signatures This avoids warnings under PHP>=8.1 Real return types should be added before PHP 9 Signed-off-by: Côme Chilliet * Avoid passing null as separator for implode Signed-off-by: Côme Chilliet * Prepare release 4.4.1 * Rebase fix * Add PHP8.1 CI * Remove PHPUnit update * Fix PHP8.1 tests Co-authored-by: Remi Collet Co-authored-by: Markus Staab Co-authored-by: Dominik Co-authored-by: Jeroen van Oort Co-authored-by: Renaud BOYER Co-authored-by: Christian Kraus Co-authored-by: Thomas Müller Co-authored-by: Phil Davis Co-authored-by: Michael Stilkerich Co-authored-by: Stéphane Co-authored-by: Allon Moritz Co-authored-by: Jair Cueva Junior Co-authored-by: Parajuli Kiran Co-authored-by: Cédric Anne Co-authored-by: Holger Floerke Co-authored-by: Andreas Heigl Co-authored-by: Côme Chilliet --- .github/workflows/actions.yml | 2 +- .gitignore | 18 -- .php_cs.dist | 3 +- .travis.yml | 49 ----- lib/Parser/MimeDir.php | 2 +- lib/Property.php | 2 +- lib/Property/Binary.php | 4 +- lib/Property/FloatValue.php | 2 +- lib/Property/ICalendar/CalAddress.php | 4 +- lib/Property/ICalendar/Duration.php | 2 +- lib/Property/ICalendar/Period.php | 2 +- lib/Property/Time.php | 4 +- lib/Property/Uri.php | 4 +- lib/Property/UtcOffset.php | 4 +- lib/Property/VCard/DateAndOrTime.php | 4 +- lib/Property/VCard/TimeStamp.php | 4 +- lib/Recur/EventIterator.php | 9 + lib/Recur/RDateIterator.php | 9 + lib/Recur/RRuleIterator.php | 17 +- lib/TimeZoneUtil.php | 174 +++--------------- .../FindFromTimezoneIdentifier.php | 6 + lib/TimezoneGuesser/FindFromTimezoneMap.php | 3 +- tests/VObject/Recur/FastForwardBeforeTest.php | 53 ++++-- tests/VObject/Recur/FastForwardTest.php | 51 +++-- tests/VObject/Recur/FastForwardToEndTest.php | 20 +- tests/VObject/TimeZoneUtilTest.php | 5 +- 26 files changed, 185 insertions(+), 272 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index ac66d2129..d1c7c97b2 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0'] + php-versions: ['7.4', '8.0', '8.1'] runs-on: ${{ matrix.operating-system }} diff --git a/.gitignore b/.gitignore index 4b72e7ad6..82b7dad3f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,23 +5,5 @@ tests/cov/ tests/temp tests/.phpunit.result.cache -#vim -.*.swp - -#binaries -bin/phpunit -bin/phpcs -bin/php-cs-fixer -bin/sabre-cs-fixer -bin/hoa - -# Development stuff -.php_cs.cache -.idea - -# OS X -.DS_Store -======= - # Development stuff .php_cs.cache diff --git a/.php_cs.dist b/.php_cs.dist index 7c6cf6749..4aaf1d90f 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -6,8 +6,7 @@ $config->getFinder() ->in(__DIR__); $config->setRules([ '@PSR1' => true, - '@Symfony' => true, - 'phpdoc_summary' => false + '@Symfony' => true ]); return $config; diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4f50e97fd..000000000 --- a/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -language: php -php: - - 7.1 - - 7.2 - - 7.3 - - 7.4 - -env: - global: - - MEMCACHED_SERVER=127.0.0.1 - - RUN_PHPCSFIXER="TRUE" - - RUN_PHPUNIT="TRUE" - - RUN_PHPSTAN="FALSE" - matrix: - - PREFER_LOWEST="" REPORT_COVERAGE="TRUE" WITH_COVERAGE="--coverage-clover=coverage.xml" - - PREFER_LOWEST="--prefer-lowest" REPORT_COVERAGE="FALSE" WITH_COVERAGE="" - -matrix: - include: - - name: 'PHP8' - dist: focal - php: nightly - env: - - RUN_PHPCSFIXER="FALSE" - - REPORT_COVERAGE="FALSE" - - name: 'PHPStan' - php: 7.4 - env: - - RUN_PHPCSFIXER="FALSE" - - RUN_PHPUNIT="FALSE" - - RUN_PHPSTAN="TRUE" - - REPORT_COVERAGE="FALSE" - fast_finish: true - -before_script: - - if [ $RUN_PHPCSFIXER == "FALSE" ]; then composer remove --no-update --dev friendsofphp/php-cs-fixer; fi - - composer update $PREFER_LOWEST - -script: - - if [ $RUN_PHPCSFIXER == "TRUE" ]; then php vendor/bin/php-cs-fixer fix --dry-run --diff; fi - - if [ $RUN_PHPUNIT == "TRUE" ]; then php vendor/bin/phpunit --configuration tests/phpunit.xml $WITH_COVERAGE; fi - - if [ $RUN_PHPSTAN == "TRUE" ]; then composer phpstan; fi - -after_success: - - if [ $REPORT_COVERAGE == "TRUE" ]; then bash <(curl -s https://codecov.io/bash); fi - -cache: - directories: - - $HOME/.composer/cache diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index ecf1944fa..9256af6f6 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -518,7 +518,7 @@ protected function readProperty($line) * * Now for the parameters * - * If delimiter is not set (null) this method will just return a string. + * If delimiter is not set (empty string) this method will just return a string. * If it's a comma or a semi-colon the string will be split on those * characters, and always return an array. * diff --git a/lib/Property.php b/lib/Property.php index 6219c9b67..50cda9684 100644 --- a/lib/Property.php +++ b/lib/Property.php @@ -52,7 +52,7 @@ abstract class Property extends Node * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ public $delimiter = ';'; diff --git a/lib/Property/Binary.php b/lib/Property/Binary.php index ec6713fdd..1262dd054 100644 --- a/lib/Property/Binary.php +++ b/lib/Property/Binary.php @@ -24,9 +24,9 @@ class Binary extends Property * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Updates the current value. diff --git a/lib/Property/FloatValue.php b/lib/Property/FloatValue.php index 0d0346968..e780ae6c1 100644 --- a/lib/Property/FloatValue.php +++ b/lib/Property/FloatValue.php @@ -21,7 +21,7 @@ class FloatValue extends Property * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ public $delimiter = ';'; diff --git a/lib/Property/ICalendar/CalAddress.php b/lib/Property/ICalendar/CalAddress.php index 86be66c15..2dbbc6eaf 100644 --- a/lib/Property/ICalendar/CalAddress.php +++ b/lib/Property/ICalendar/CalAddress.php @@ -19,9 +19,9 @@ class CalAddress extends Text * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Property/ICalendar/Duration.php b/lib/Property/ICalendar/Duration.php index 87f008160..e18fe191e 100644 --- a/lib/Property/ICalendar/Duration.php +++ b/lib/Property/ICalendar/Duration.php @@ -22,7 +22,7 @@ class Duration extends Property * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ public $delimiter = ','; diff --git a/lib/Property/ICalendar/Period.php b/lib/Property/ICalendar/Period.php index eb3752770..ae8a78911 100644 --- a/lib/Property/ICalendar/Period.php +++ b/lib/Property/ICalendar/Period.php @@ -23,7 +23,7 @@ class Period extends Property * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ public $delimiter = ','; diff --git a/lib/Property/Time.php b/lib/Property/Time.php index 544b5ced3..1b81609aa 100644 --- a/lib/Property/Time.php +++ b/lib/Property/Time.php @@ -19,9 +19,9 @@ class Time extends Text * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Property/Uri.php b/lib/Property/Uri.php index 830cd3f18..1ad1fb199 100644 --- a/lib/Property/Uri.php +++ b/lib/Property/Uri.php @@ -20,9 +20,9 @@ class Uri extends Text * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Property/UtcOffset.php b/lib/Property/UtcOffset.php index 248ed40ea..04b88447f 100644 --- a/lib/Property/UtcOffset.php +++ b/lib/Property/UtcOffset.php @@ -17,9 +17,9 @@ class UtcOffset extends Text * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Property/VCard/DateAndOrTime.php b/lib/Property/VCard/DateAndOrTime.php index 09918b31a..7bf79c48c 100644 --- a/lib/Property/VCard/DateAndOrTime.php +++ b/lib/Property/VCard/DateAndOrTime.php @@ -24,9 +24,9 @@ class DateAndOrTime extends Property /** * Field separator. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Property/VCard/TimeStamp.php b/lib/Property/VCard/TimeStamp.php index fccf2d600..da6ea3d44 100644 --- a/lib/Property/VCard/TimeStamp.php +++ b/lib/Property/VCard/TimeStamp.php @@ -21,9 +21,9 @@ class TimeStamp extends Text * In case this is a multi-value property. This string will be used as a * delimiter. * - * @var string|null + * @var string */ - public $delimiter = null; + public $delimiter = ''; /** * Returns the type of value. diff --git a/lib/Recur/EventIterator.php b/lib/Recur/EventIterator.php index 310bebe41..61f05d7de 100644 --- a/lib/Recur/EventIterator.php +++ b/lib/Recur/EventIterator.php @@ -198,6 +198,7 @@ public function __construct($input, $uid = null, DateTimeZone $timeZone = null) * * @return DateTimeImmutable */ + #[\ReturnTypeWillChange] public function current() { if ($this->currentDate) { @@ -285,6 +286,7 @@ public function getEventObject() * * @return int */ + #[\ReturnTypeWillChange] public function key() { // The counter is always 1 ahead. @@ -297,6 +299,7 @@ public function key() * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { if ($this->counter > Settings::$maxRecurrences && -1 !== Settings::$maxRecurrences) { @@ -308,7 +311,10 @@ public function valid() /** * Sets the iterator back to the starting point. + * + * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->recurIterator->rewind(); @@ -331,7 +337,10 @@ public function rewind() /** * Advances the iterator with one step. + * + * @return void */ + #[\ReturnTypeWillChange] public function next() { $this->currentOverriddenEvent = null; diff --git a/lib/Recur/RDateIterator.php b/lib/Recur/RDateIterator.php index d117e152c..5d56657fa 100644 --- a/lib/Recur/RDateIterator.php +++ b/lib/Recur/RDateIterator.php @@ -35,6 +35,7 @@ public function __construct($rrule, DateTimeInterface $start) /* Implementation of the Iterator interface {{{ */ + #[\ReturnTypeWillChange] public function current() { if (!$this->valid()) { @@ -49,6 +50,7 @@ public function current() * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->counter; @@ -60,6 +62,7 @@ public function key() * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { return $this->counter <= count($this->dates); @@ -67,7 +70,10 @@ public function valid() /** * Resets the iterator. + * + * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->currentDate = clone $this->startDate; @@ -76,7 +82,10 @@ public function rewind() /** * Goes on to the next iteration. + * + * @return void */ + #[\ReturnTypeWillChange] public function next() { ++$this->counter; diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 3dd483429..a1b97ebee 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -37,6 +37,7 @@ public function __construct($rrule, DateTimeInterface $start) /* Implementation of the Iterator interface {{{ */ + #[\ReturnTypeWillChange] public function current() { if (!$this->valid()) { @@ -51,6 +52,7 @@ public function current() * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->counter; @@ -63,6 +65,7 @@ public function key() * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { if (null === $this->currentDate) { @@ -77,7 +80,10 @@ public function valid() /** * Resets the iterator. + * + * @return void */ + #[\ReturnTypeWillChange] public function rewind() { $this->currentDate = clone $this->startDate; @@ -88,8 +94,10 @@ public function rewind() * Goes on to the next iteration. * * @param int $amount + * @return void */ - public function next($amount = 1) + #[\ReturnTypeWillChange] + public function next(int $amount = 1) { // Otherwise, we find the next event in the normal RRULE // sequence. @@ -640,6 +648,9 @@ protected function nextMonthly($amount = 1) // This goes to 0 because we need to start counting at the // beginning. $currentDayOfMonth = 0; + + // For some reason the "until" parameter was not being used here, + // that's why the workaround of the 10000 year bug was needed at all $currentHourOfMonth = 0; $currentMinuteOfMonth = 0; $currentSecondOfMonth = 0; @@ -650,6 +661,10 @@ protected function nextMonthly($amount = 1) if ($this->until && $this->currentDate->getTimestamp() >= $this->until->getTimestamp()) { return; } + // let's stop it before the "until" parameter date + if ($this->until && $this->currentDate->getTimestamp() >= $this->until->getTimestamp()) { + return; + } // To prevent running this forever (better: until we hit the max date of DateTimeImmutable) we simply // stop at 9999-12-31. Looks like the year 10000 problem is not solved in php .... diff --git a/lib/TimeZoneUtil.php b/lib/TimeZoneUtil.php index 9e382c655..65f2e6c8c 100644 --- a/lib/TimeZoneUtil.php +++ b/lib/TimeZoneUtil.php @@ -33,14 +33,6 @@ class TimeZoneUtil /** @var TimezoneFinder[] */ private $timezoneFinders = []; - // Keeping things for backwards compatibility - /** - * @var array|null - * - * @deprecated - */ - public static $map = null; - private function __construct() { $this->addGuesser('lic', new GuessFromLicEntry()); @@ -128,11 +120,30 @@ public static function addTimezoneFinder(string $key, TimezoneFinder $finder): v 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 + * + * @deprecated + */ + public static $map = null; + /** * List of microsoft exchange timezone ids. * @@ -168,12 +179,12 @@ public static function clean(): void 48 => 'Asia/Kabul', 58 => 'Asia/Yekaterinburg', 47 => 'Asia/Karachi', - 23 => 'Asia/Kolkata', + 23 => 'Asia/Calcutta', 62 => 'Asia/Kathmandu', 46 => 'Asia/Almaty', 71 => 'Asia/Dhaka', 66 => 'Asia/Colombo', - 61 => 'Asia/Yangon', + 61 => 'Asia/Rangoon', 22 => 'Asia/Bangkok', 64 => 'Asia/Krasnoyarsk', 45 => 'Asia/Shanghai', @@ -198,11 +209,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', @@ -222,143 +233,6 @@ 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. * @@ -396,4 +270,4 @@ public static function getIdentifiersBC() { return include __DIR__.'/timezonedata/php-bc.php'; } -} +} \ No newline at end of file diff --git a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php index d8c6d5d3b..b5c58f715 100644 --- a/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php +++ b/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php @@ -31,6 +31,12 @@ public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone if ('(' === $tzid[0]) { return null; } + + // 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 // diff --git a/lib/TimezoneGuesser/FindFromTimezoneMap.php b/lib/TimezoneGuesser/FindFromTimezoneMap.php index b52ba6a19..0a6b608b4 100644 --- a/lib/TimezoneGuesser/FindFromTimezoneMap.php +++ b/lib/TimezoneGuesser/FindFromTimezoneMap.php @@ -59,7 +59,8 @@ private function getTzMaps() include __DIR__.'/../timezonedata/windowszones.php', include __DIR__.'/../timezonedata/lotuszones.php', include __DIR__.'/../timezonedata/exchangezones.php', - include __DIR__.'/../timezonedata/php-workaround.php' + include __DIR__.'/../timezonedata/php-workaround.php', + include __DIR__.'/../timezonedata/extrazones.php' ); } diff --git a/tests/VObject/Recur/FastForwardBeforeTest.php b/tests/VObject/Recur/FastForwardBeforeTest.php index ba71d23dd..3461bd6a5 100644 --- a/tests/VObject/Recur/FastForwardBeforeTest.php +++ b/tests/VObject/Recur/FastForwardBeforeTest.php @@ -282,39 +282,51 @@ public function testFastForwardBeforeMonthly31thDay() $this->fastForward($rrule, $ffDate); - $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + $expected = (new \DateTime('midnight', new DateTimeZone($timezone))) ->setDate(18000, 1, 31) ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // march $rrule->next(); - $expected += (29 + 31) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 3, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 5, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 7, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); - $expected += 31 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 8, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // october $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 10, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // december $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(18000, 12, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); } @@ -336,37 +348,52 @@ public function testFastForwardBeforeMonthlyAdvanced() $this->assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday - $expected += 8 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 1, 11) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday - $expected += 8 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 1, 19) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // thursday - $expected += 8 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 1, 27) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // monday march - $expected += (29 + 10) * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 3, 6) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday - $expected += 8 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 3, 14) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday (this month starts on wednesday so that's just the next day) - $expected += 1 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 3, 15) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // thursday $expected += 8 * 24 * 60 * 60; + $expected = (new \DateTime('1970-01-31 00:00:00', new DateTimeZone($timezone))) + ->setDate(8000, 3, 23) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); } diff --git a/tests/VObject/Recur/FastForwardTest.php b/tests/VObject/Recur/FastForwardTest.php index df9120561..7a305b4ad 100644 --- a/tests/VObject/Recur/FastForwardTest.php +++ b/tests/VObject/Recur/FastForwardTest.php @@ -261,32 +261,45 @@ public function testFastForwardMonthly31thDay() // march $rrule->next(); - $expected += (29 + 31) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 3, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // may $rrule->next(); $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 5, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // july $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 7, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // august $rrule->next(); - $expected += 31 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 8, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // october $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 10, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // december $rrule->next(); - $expected += (30 + 31) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(18000, 12, 31) + ->getTimestamp(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); } @@ -307,37 +320,51 @@ public function testFastForwardMonthlyAdvanced() $this->assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday - $expected += 8 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 1, 11) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday - $expected += 8 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 1, 19) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // thursday - $expected += 8 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 1, 27) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // monday march - $expected += (29 + 10) * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 3, 6) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // tuesday - $expected += 8 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 3, 14) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // wednesday (this month starts on wednesday so that's just the next day) - $expected += 1 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 3, 15) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); // thursday - $expected += 8 * 24 * 60 * 60; + $expected = (new DateTime('midnight', new DateTimeZone($timezone))) + ->setDate(8000, 3, 23) + ->getTimestamp(); $rrule->next(); $this->assertEquals($expected, $rrule->current()->getTimestamp()); } diff --git a/tests/VObject/Recur/FastForwardToEndTest.php b/tests/VObject/Recur/FastForwardToEndTest.php index 9f54d8386..992f2b9d8 100644 --- a/tests/VObject/Recur/FastForwardToEndTest.php +++ b/tests/VObject/Recur/FastForwardToEndTest.php @@ -186,9 +186,7 @@ public function testFastForwardToEndUntilMonthlyBasic() } /** - * FIXME fails in <=PHP 7.1. - * - * @requires PHP 7.2 + * @requires PHP < 8.1 */ public function testFastForwardToEndCountMonthly31thDay() { @@ -204,6 +202,22 @@ public function testFastForwardToEndCountMonthly31thDay() $this->assertEquals($expected, $rrule->current()->getTimestamp()); } + /** + * @requires PHP >= 8.1 + */ + public function testFastForwardToEndCountMonthly31thDayPHP81() + { + $startDate = new \DateTime('1970-01-31 00:00:00', new \DateTimeZone('America/New_York')); + $rrule = new RRuleIterator('FREQ=MONTHLY;COUNT=10000', $startDate); + + // We do not enforce the timing in case of a count rule as we cannot optimize it + $this->fastForwardToEnd($rrule, false); + + $expected = (new DateTime('midnight', new DateTimeZone('America/New_York'))) + ->setDate(3398, 7, 31); + $this->assertEquals($expected->getTimestamp(), $rrule->current()->getTimestamp()); + } + public function testFastForwardToEndUntilMonthly31thDay() { $startDate = new \DateTime('1970-01-31 00:00:00', new \DateTimeZone('America/New_York')); diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 33a1eff98..e6df79aed 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -8,8 +8,6 @@ class TimeZoneUtilTest extends TestCase { public function setUp(): void { - // clearning the tz cache - TimeZoneUtil::$map = null; TimeZoneUtil::clean(); } @@ -36,7 +34,8 @@ public function getMapping() include __DIR__.'/../../lib/timezonedata/windowszones.php', include __DIR__.'/../../lib/timezonedata/lotuszones.php', include __DIR__.'/../../lib/timezonedata/exchangezones.php', - include __DIR__.'/../../lib/timezonedata/php-workaround.php' + include __DIR__.'/../../lib/timezonedata/php-workaround.php', + include __DIR__.'/../../lib/timezonedata/extrazones.php' ); // PHPUNit requires an array of arrays