diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index ac66d212..d1c7c97b 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 4b72e7ad..82b7dad3 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 7c6cf674..4aaf1d90 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 4f50e97f..00000000 --- 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 ecf1944f..9256af6f 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 6219c9b6..50cda968 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 ec6713fd..1262dd05 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 0d034696..e780ae6c 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 86be66c1..2dbbc6ea 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 87f00816..e18fe191 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 eb375277..ae8a7891 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 544b5ced..1b81609a 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 830cd3f1..1ad1fb19 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 248ed40e..04b88447 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 09918b31..7bf79c48 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 fccf2d60..da6ea3d4 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 310bebe4..61f05d7d 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 d117e152..5d56657f 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 3dd48342..a1b97ebe 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 9e382c65..65f2e6c8 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 d8c6d5d3..b5c58f71 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 b52ba6a1..0a6b608b 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 ba71d23d..3461bd6a 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 df912056..7a305b4a 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 9f54d838..992f2b9d 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 33a1eff9..e6df79ae 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