From 1424b3875bcd5b98bb51d3192614e04c23a8119c Mon Sep 17 00:00:00 2001 From: giuseppe-arcuti <93533561+giuseppe-arcuti@users.noreply.github.com> Date: Wed, 15 Dec 2021 11:48:57 +0100 Subject: [PATCH] Merge upstream commits (#34) 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 Changelog * Merge github actions 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 --- .gitattributes | 7 + .github/workflows/actions.yml | 12 +- .gitignore | 4 + CHANGELOG.md | 12 +- README.md | 2 +- bin/bench_freebusygenerator.php | 2 +- bin/bench_manipulatevcard.php | 2 +- bin/fetch_windows_zones.php | 5 +- bin/generateicalendardata.php | 4 +- bin/mergeduplicates.php | 4 +- bin/rrulebench.php | 2 +- composer.json | 3 +- lib/Cli.php | 11 +- lib/Component.php | 5 +- lib/Component/VCalendar.php | 2 +- lib/Component/VCard.php | 3 +- lib/ElementList.php | 2 + lib/FreeBusyData.php | 8 +- lib/FreeBusyGenerator.php | 3 +- lib/ITip/Broker.php | 24 +++- lib/Node.php | 7 + lib/Parameter.php | 10 +- lib/Parser/MimeDir.php | 4 +- lib/Property.php | 5 + lib/Property/Boolean.php | 3 +- lib/Property/ICalendar/CalAddress.php | 3 +- lib/Property/ICalendar/DateTime.php | 3 +- lib/Property/IntegerValue.php | 3 +- lib/Property/VCard/LanguageTag.php | 3 +- lib/Recur/EventIterator.php | 10 +- lib/Recur/RRuleIterator.php | 12 +- lib/TimeZoneUtil.php | 129 +++++++++++++++++- lib/TimezoneGuesser/FindFromOffset.php | 31 +++++ .../FindFromTimezoneIdentifier.php | 71 ++++++++++ lib/TimezoneGuesser/FindFromTimezoneMap.php | 78 +++++++++++ lib/TimezoneGuesser/GuessFromLicEntry.php | 33 +++++ lib/TimezoneGuesser/GuessFromMsTzId.php | 119 ++++++++++++++++ lib/TimezoneGuesser/TimezoneFinder.php | 10 ++ lib/TimezoneGuesser/TimezoneGuesser.php | 11 ++ lib/timezonedata/windowszones.php | 17 ++- tests/VObject/Component/VAvailabilityTest.php | 14 +- tests/VObject/Component/VCalendarTest.php | 27 ++++ tests/VObject/Component/VCardTest.php | 4 +- tests/VObject/ComponentTest.php | 17 +++ tests/VObject/DateTimeParserTest.php | 40 +++--- tests/VObject/EmptyValueIssueTest.php | 4 +- .../VObject/ITip/BrokerAttendeeReplyTest.php | 2 +- tests/VObject/ITip/BrokerProcessReplyTest.php | 2 +- .../ITip/BrokerSignificantChangesTest.php | 108 +++++++++++++++ tests/VObject/Parser/XmlTest.php | 112 +++++++-------- tests/VObject/ReaderTest.php | 2 +- .../VObject/Recur/EventIterator/MainTest.php | 12 +- .../EventIterator/OverrideDurationTest.php | 52 +++++++ tests/VObject/Recur/RRuleIteratorTest.php | 54 +++++++- tests/VObject/TimeZoneUtilTest.php | 26 +++- 55 files changed, 979 insertions(+), 176 deletions(-) create mode 100644 .gitattributes create mode 100644 lib/TimezoneGuesser/FindFromOffset.php create mode 100644 lib/TimezoneGuesser/FindFromTimezoneIdentifier.php create mode 100644 lib/TimezoneGuesser/FindFromTimezoneMap.php create mode 100644 lib/TimezoneGuesser/GuessFromLicEntry.php create mode 100644 lib/TimezoneGuesser/GuessFromMsTzId.php create mode 100644 lib/TimezoneGuesser/TimezoneFinder.php create mode 100644 lib/TimezoneGuesser/TimezoneGuesser.php create mode 100644 tests/VObject/Recur/EventIterator/OverrideDurationTest.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..f95a950f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +/tests export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php_cs.dist export-ignore +/.travis.yml export-ignore +/CHANGELOG.md export-ignore +/phpstan.neon export-ignore diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 55ad1fb4b..7d143be0b 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -35,9 +35,17 @@ jobs: key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- - - name: Install dependencies + - name: Install composer dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader + - name: Code Analysis (PHP CS-Fixer) + if: matrix.code-analysis == 'yes' + run: php vendor/bin/php-cs-fixer fix --dry-run --diff + + - name: Code Analysis (PHPStan) + if: matrix.code-analysis == 'yes' + run: composer phpstan + - name: Test with phpunit run: vendor/bin/phpunit --configuration ./tests/phpunit.xml --coverage-text --coverage-clover=coverage.xml @@ -49,4 +57,4 @@ jobs: flags: tests name: codecov-umbrella yml: ./codecov.yml - fail_ci_if_error: true \ No newline at end of file + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index b952f0309..4b72e7ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ bin/hoa # OS X .DS_Store +======= + +# Development stuff +.php_cs.cache diff --git a/CHANGELOG.md b/CHANGELOG.md index 58921b307..b01f32d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ChangeLog ========= +4.7.0 (2021-12-15) +------------------ +* #34 Merge upstream changes from sabre-io/vobject:4.4.0 into protonlabs/vobject + 4.6.1 (2021-11-04) ------------------ * #29 Fix timezone name prefixed with / @@ -138,7 +142,7 @@ ChangeLog * #306: iTip REPLYs to the first instance of a recurring event was not handled correctly. * Slightly better error message during validation of `N` and `ADR` properties. -* #312: Correctly extracing timezone in the iTip broker, even when we don't +* #312: Correctly extracting timezone in the iTip broker, even when we don't have a master event. (@vkomrakov-sugar). * When validating a component's property that must appear once and which could automatically be repaired, make sure we report the change as 'repaired'. @@ -460,7 +464,7 @@ ChangeLog * #114: VTIMEZONE is retained when generating new REQUEST objects. * #114: Support for 'MAILTO:' style email addresses (in uppercase) in the iTip broker. This improves evolution support. -* #115: Using REQUEST-STATUS from REPLY messages and now propegating that into +* #115: Using REQUEST-STATUS from REPLY messages and now propagating that into SCHEDULE-STATUS. @@ -697,7 +701,7 @@ ChangeLog 3.0.0-alpha2 (2013-05-22) ------------------------- -* Fixed: vCard URL properties were referencing a non-existant class. +* Fixed: vCard URL properties were referencing a non-existent class. 3.0.0-alpha1 (2013-05-21) @@ -855,7 +859,7 @@ ChangeLog properties such as N, ADR, ORG and CATEGORIES. * Added: Splitter classes, that can split up large objects (such as exports) into individual objects (thanks @DominikTo and @armin-hackmann). -* Added: VFREEBUSY component, which allows easily checking wether timeslots are +* Added: VFREEBUSY component, which allows easily checking whether timeslots are available. * Added: The Reader class now has a 'FORGIVING' option, which allows it to parse properties with incorrect characters in the name (at this time, it just allows diff --git a/README.md b/README.md index 968bc32b9..b5b1cc96c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ sabre/vobject The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545) and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP. -The goal of the VObject library is to create a very complete library, with an easy to use API. +The goal of the VObject library is to create a very complete library, with an easy-to-use API. Installation diff --git a/bin/bench_freebusygenerator.php b/bin/bench_freebusygenerator.php index 1299c14fb..963623d18 100644 --- a/bin/bench_freebusygenerator.php +++ b/bin/bench_freebusygenerator.php @@ -11,7 +11,7 @@ echo "The process will be repeated 100 times to get accurate stats\n"; echo "\n"; echo 'Usage: '.$argv[0]." inputfile.ics\n"; - die(); + exit(); } list(, $inputFile) = $argv; diff --git a/bin/bench_manipulatevcard.php b/bin/bench_manipulatevcard.php index f229091db..df6d9f23d 100644 --- a/bin/bench_manipulatevcard.php +++ b/bin/bench_manipulatevcard.php @@ -10,7 +10,7 @@ echo 'system.'; echo "\n"; echo 'Usage: '.$argv[0]." inputfile.vcf\n"; - die(); + exit(); } list(, $inputFile) = $argv; diff --git a/bin/fetch_windows_zones.php b/bin/fetch_windows_zones.php index 9c4e51abd..2361dc309 100755 --- a/bin/fetch_windows_zones.php +++ b/bin/fetch_windows_zones.php @@ -1,13 +1,12 @@ #!/usr/bin/env php inputFormat = 'mimedir'; break; @@ -211,7 +206,7 @@ public function main(array $argv) } if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) { - throw new InvalidArgumentException('Uknown command: '.$positional[0]); + throw new InvalidArgumentException('Unknown command: '.$positional[0]); } } catch (InvalidArgumentException $e) { $this->showHelp(); @@ -458,8 +453,6 @@ protected function convert($vObj) * Colorizes a file. * * @param Component $vObj - * - * @return int */ protected function color($vObj) { diff --git a/lib/Component.php b/lib/Component.php index da45eb29f..f33b628a7 100644 --- a/lib/Component.php +++ b/lib/Component.php @@ -160,9 +160,9 @@ public function remove($item) return; } } - } - throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component'); + throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component'); + } } /** @@ -339,6 +339,7 @@ function ($a, $b) use ($sortScore, $tmp) { * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { $components = []; diff --git a/lib/Component/VCalendar.php b/lib/Component/VCalendar.php index 40e09a1c0..4db318135 100644 --- a/lib/Component/VCalendar.php +++ b/lib/Component/VCalendar.php @@ -309,7 +309,7 @@ public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTim foreach ($this->children() as $child) { if ($child instanceof Property && 'PRODID' !== $child->name) { - // We explictly want to ignore PRODID, because we want to + // We explicitly want to ignore PRODID, because we want to // overwrite it with our own. $newChildren[] = clone $child; } elseif ($child instanceof Component && 'VTIMEZONE' !== $child->name) { diff --git a/lib/Component/VCard.php b/lib/Component/VCard.php index 51321949f..eac789842 100644 --- a/lib/Component/VCard.php +++ b/lib/Component/VCard.php @@ -373,7 +373,7 @@ public function getValidationRules() /** * Returns a preferred field. * - * VCards can indicate wether a field such as ADR, TEL or EMAIL is + * VCards can indicate whether a field such as ADR, TEL or EMAIL is * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x * being a number between 1 and 100). * @@ -445,6 +445,7 @@ protected function getDefaults() * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { // A vcard does not have sub-components, so we're overriding this diff --git a/lib/ElementList.php b/lib/ElementList.php index 56058cbd5..860512649 100644 --- a/lib/ElementList.php +++ b/lib/ElementList.php @@ -25,6 +25,7 @@ class ElementList extends ArrayIterator * @param int $offset * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { throw new LogicException('You can not add new objects to an ElementList'); @@ -37,6 +38,7 @@ public function offsetSet($offset, $value) * * @param int $offset */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new LogicException('You can not remove objects from an ElementList'); diff --git a/lib/FreeBusyData.php b/lib/FreeBusyData.php index d05dfc799..4d9f441ce 100644 --- a/lib/FreeBusyData.php +++ b/lib/FreeBusyData.php @@ -84,7 +84,7 @@ public function add($start, $end, $type) 'type' => $type, ]; - $preceedingItem = $this->data[$insertStartIndex - 1]; + $precedingItem = $this->data[$insertStartIndex - 1]; if ($this->data[$insertStartIndex - 1]['start'] === $start) { // The old item starts at the exact same point as the new item. --$insertStartIndex; @@ -122,11 +122,11 @@ public function add($start, $end, $type) // between. if (-1 === $itemsToDelete) { $itemsToDelete = 0; - if ($newItem['end'] < $preceedingItem['end']) { + if ($newItem['end'] < $precedingItem['end']) { $newItems[] = [ 'start' => $newItem['end'] + 1, - 'end' => $preceedingItem['end'], - 'type' => $preceedingItem['type'], + 'end' => $precedingItem['end'], + 'type' => $precedingItem['type'], ]; } } diff --git a/lib/FreeBusyGenerator.php b/lib/FreeBusyGenerator.php index a1c24044c..81b8126d5 100644 --- a/lib/FreeBusyGenerator.php +++ b/lib/FreeBusyGenerator.php @@ -126,7 +126,7 @@ public function setVAvailability(Document $vcalendar) /** * Sets the input objects. * - * You must either specify a valendar object as a string, or as the parse + * You must either specify a vcalendar object as a string, or as the parse * Component. * It's also possible to specify multiple objects as an array. * @@ -362,7 +362,6 @@ protected function calculateBusy(FreeBusyData $fbData, array $objects) foreach ($object->getBaseComponents() as $component) { switch ($component->name) { case 'VEVENT': - $FBTYPE = 'BUSY'; if (isset($component->TRANSP) && ('TRANSPARENT' === strtoupper($component->TRANSP))) { break; diff --git a/lib/ITip/Broker.php b/lib/ITip/Broker.php index 4e0368e13..b66a59f54 100644 --- a/lib/ITip/Broker.php +++ b/lib/ITip/Broker.php @@ -547,9 +547,13 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, // properties changed in the event, or simply if there's a // difference in instances that the attendee is invited to. + $oldAttendeeInstances = array_keys($attendee['oldInstances']); + $newAttendeeInstances = array_keys($attendee['newInstances']); + $message->significantChange = 'REQUEST' === $attendee['forceSend'] || - array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) || + count($oldAttendeeInstances) != count($newAttendeeInstances) || + count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0 || $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash']; foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) { @@ -816,7 +820,10 @@ protected function parseEventInfo(VCalendar $calendar = null) $instances = []; $exdate = []; + $significantChangeEventProperties = []; + foreach ($calendar->VEVENT as $vevent) { + $eventSignificantChangeHash = ''; $rrule = []; if (is_null($uid)) { @@ -930,19 +937,26 @@ protected function parseEventInfo(VCalendar $calendar = null) if (isset($vevent->$prop)) { $propertyValues = $vevent->select($prop); - $significantChangeHash .= $prop.':'; + $eventSignificantChangeHash .= $prop.':'; if ('EXDATE' === $prop) { - $significantChangeHash .= implode(',', $exdate).';'; + $eventSignificantChangeHash .= implode(',', $exdate).';'; } elseif ('RRULE' === $prop) { - $significantChangeHash .= implode(',', $rrule).';'; + $eventSignificantChangeHash .= implode(',', $rrule).';'; } else { foreach ($propertyValues as $val) { - $significantChangeHash .= $val->getValue().';'; + $eventSignificantChangeHash .= $val->getValue().';'; } } } } + $significantChangeEventProperties[] = $eventSignificantChangeHash; + } + + asort($significantChangeEventProperties); + + foreach ($significantChangeEventProperties as $eventSignificantChangeHash) { + $significantChangeHash .= $eventSignificantChangeHash; } $significantChangeHash = md5($significantChangeHash); diff --git a/lib/Node.php b/lib/Node.php index 4c0c04f72..2041b2ac7 100644 --- a/lib/Node.php +++ b/lib/Node.php @@ -73,6 +73,7 @@ abstract public function serialize(); * * @return array */ + #[\ReturnTypeWillChange] abstract public function jsonSerialize(); /** @@ -102,6 +103,7 @@ public function destroy() * * @return ElementList */ + #[\ReturnTypeWillChange] public function getIterator() { if (!is_null($this->iterator)) { @@ -157,6 +159,7 @@ public function validate($options = 0) * * @return int */ + #[\ReturnTypeWillChange] public function count() { $it = $this->getIterator(); @@ -177,6 +180,7 @@ public function count() * * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { $iterator = $this->getIterator(); @@ -193,6 +197,7 @@ public function offsetExists($offset) * * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { $iterator = $this->getIterator(); @@ -208,6 +213,7 @@ public function offsetGet($offset) * @param int $offset * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $iterator = $this->getIterator(); @@ -228,6 +234,7 @@ public function offsetSet($offset, $value) * * @param int $offset */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { $iterator = $this->getIterator(); diff --git a/lib/Parameter.php b/lib/Parameter.php index e39d320a1..7e4d55743 100644 --- a/lib/Parameter.php +++ b/lib/Parameter.php @@ -95,13 +95,11 @@ public static function guessParameterNameByValue($value) case 'WORK': case 'HOME': case 'PREF': - // Delivery Label Type case 'DOM': case 'INTL': case 'POSTAL': case 'PARCEL': - // Telephone types case 'VOICE': case 'FAX': @@ -113,7 +111,6 @@ public static function guessParameterNameByValue($value) case 'CAR': case 'ISDN': case 'VIDEO': - // EMAIL types (lol) case 'AOL': case 'APPLELINK': @@ -127,7 +124,6 @@ public static function guessParameterNameByValue($value) case 'PRODIGY': case 'TLX': case 'X400': - // Photo / Logo format types case 'GIF': case 'CGM': @@ -143,12 +139,10 @@ public static function guessParameterNameByValue($value) case 'MPEG2': case 'AVI': case 'QTIME': - // Sound Digital Audio Type case 'WAVE': case 'PCM': case 'AIFF': - // Key types case 'X509': case 'PGP': @@ -299,7 +293,7 @@ function ($out, $item) { // https://tools.ietf.org/html/rfc6868 // // But we've found that iCal (7.0, shipped with OSX 10.9) - // severaly trips on + characters not being quoted, so we + // severely trips on + characters not being quoted, so we // added + as well. if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) { return $out.$item; @@ -327,6 +321,7 @@ function ($out, $item) { * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->value; @@ -360,6 +355,7 @@ public function __toString() * * @return ElementList */ + #[\ReturnTypeWillChange] public function getIterator() { if (!is_null($this->iterator)) { diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index 9b36df6a9..ecf1944fa 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -343,7 +343,7 @@ protected function readProperty($line) ) (?=[;:,]) /xi"; - //echo $regex, "\n"; die(); + //echo $regex, "\n"; exit(); preg_match_all($regex, $line, $matches, PREG_SET_ORDER); $property = [ @@ -439,7 +439,7 @@ protected function readProperty($line) $propObj->add(null, $namelessParameter); } - if ('QUOTED-PRINTABLE' === strtoupper($propObj['ENCODING'])) { + if (isset($propObj['ENCODING']) && 'QUOTED-PRINTABLE' === strtoupper($propObj['ENCODING'])) { $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue()); } else { $charset = $this->charset; diff --git a/lib/Property.php b/lib/Property.php index f9cf8e38e..6219c9b67 100644 --- a/lib/Property.php +++ b/lib/Property.php @@ -276,6 +276,7 @@ public function setJsonValue(array $value) * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { $parameters = []; @@ -387,6 +388,7 @@ public function __toString() * * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($name) { if (is_int($name)) { @@ -413,6 +415,7 @@ public function offsetExists($name) * * @return Node */ + #[\ReturnTypeWillChange] public function offsetGet($name) { if (is_int($name)) { @@ -433,6 +436,7 @@ public function offsetGet($name) * @param string $name * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($name, $value) { if (is_int($name)) { @@ -453,6 +457,7 @@ public function offsetSet($name, $value) * * @param string $name */ + #[\ReturnTypeWillChange] public function offsetUnset($name) { if (is_int($name)) { diff --git a/lib/Property/Boolean.php b/lib/Property/Boolean.php index 9fb2bce35..4bd6ffdfe 100644 --- a/lib/Property/Boolean.php +++ b/lib/Property/Boolean.php @@ -2,8 +2,7 @@ namespace Sabre\VObject\Property; -use - Sabre\VObject\Property; +use Sabre\VObject\Property; /** * Boolean property. diff --git a/lib/Property/ICalendar/CalAddress.php b/lib/Property/ICalendar/CalAddress.php index e89bb31f9..86be66c15 100644 --- a/lib/Property/ICalendar/CalAddress.php +++ b/lib/Property/ICalendar/CalAddress.php @@ -2,8 +2,7 @@ namespace Sabre\VObject\Property\ICalendar; -use - Sabre\VObject\Property\Text; +use Sabre\VObject\Property\Text; /** * CalAddress property. diff --git a/lib/Property/ICalendar/DateTime.php b/lib/Property/ICalendar/DateTime.php index 9342bc46e..cbafc16ba 100644 --- a/lib/Property/ICalendar/DateTime.php +++ b/lib/Property/ICalendar/DateTime.php @@ -184,7 +184,7 @@ public function setDateTime(DateTimeInterface $dt, $isFloating = false) * Sets the property as multiple date-time objects. * * The first value will be used as a reference for the timezones, and all - * the otehr values will be adjusted for that timezone + * the other values will be adjusted for that timezone * * @param DateTimeInterface[] $dt * @param bool isFloating If set to true, timezones will be ignored @@ -300,6 +300,7 @@ function ($item) { * @param string $name * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($name, $value) { parent::offsetSet($name, $value); diff --git a/lib/Property/IntegerValue.php b/lib/Property/IntegerValue.php index 6f709bfff..3ae775214 100644 --- a/lib/Property/IntegerValue.php +++ b/lib/Property/IntegerValue.php @@ -2,8 +2,7 @@ namespace Sabre\VObject\Property; -use - Sabre\VObject\Property; +use Sabre\VObject\Property; /** * Integer property. diff --git a/lib/Property/VCard/LanguageTag.php b/lib/Property/VCard/LanguageTag.php index 697273989..318ea0231 100644 --- a/lib/Property/VCard/LanguageTag.php +++ b/lib/Property/VCard/LanguageTag.php @@ -2,8 +2,7 @@ namespace Sabre\VObject\Property\VCard; -use - Sabre\VObject\Property; +use Sabre\VObject\Property; /** * LanguageTag property. diff --git a/lib/Recur/EventIterator.php b/lib/Recur/EventIterator.php index fd904b383..310bebe41 100644 --- a/lib/Recur/EventIterator.php +++ b/lib/Recur/EventIterator.php @@ -83,7 +83,7 @@ class EventIterator implements \Iterator * 2. You can pass an array of VEVENTs (all UIDS should match). * 3. You can pass a single VEVENT component. * - * Only the second method is recomended. The other 1 and 3 will be removed + * Only the second method is recommended. The other 1 and 3 will be removed * at some point in the future. * * The $uid parameter is only required for the first method. @@ -229,9 +229,13 @@ public function getDtEnd() if (!$this->valid()) { return; } - $end = clone $this->currentDate; + if ($this->currentOverriddenEvent && $this->currentOverriddenEvent->DTEND) { + return $this->currentOverriddenEvent->DTEND->getDateTime($this->timeZone); + } else { + $end = clone $this->currentDate; - return $end->modify('+'.$this->eventDuration.' seconds'); + return $end->modify('+'.$this->eventDuration.' seconds'); + } } /** diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 1607183b1..3dd483429 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -644,6 +644,13 @@ protected function nextMonthly($amount = 1) $currentMinuteOfMonth = 0; $currentSecondOfMonth = 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 + // 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 .... if ($this->currentDate->getTimestamp() > 253402300799) { @@ -715,7 +722,7 @@ protected function nextYearly($amount = 1) foreach ($this->byWeekNo as $byWeekNo) { foreach ($dayOffsets as $dayOffset) { $date = clone $this->currentDate; - $date->setISODate($currentYear, $byWeekNo, $dayOffset); + $date = $date->setISODate($currentYear, $byWeekNo, $dayOffset); if ($date > $this->currentDate) { $checkDates[] = $date; @@ -913,7 +920,6 @@ protected function parseRRule($rrule) break; case 'INTERVAL': - case 'COUNT': $val = (int) $value; if ($val < 1) { @@ -1092,7 +1098,7 @@ protected function getMonthlyOccurrences() foreach ($this->byMonthDay as $monthDay) { // Removing values that are out of range for this month if ($monthDay > $startDate->format('t') || - $monthDay < 0 - $startDate->format('t')) { + $monthDay < 0 - $startDate->format('t')) { continue; } if ($monthDay > 0) { diff --git a/lib/TimeZoneUtil.php b/lib/TimeZoneUtil.php index d1f6c344a..9e382c655 100644 --- a/lib/TimeZoneUtil.php +++ b/lib/TimeZoneUtil.php @@ -2,6 +2,16 @@ namespace Sabre\VObject; +use DateTimeZone; +use InvalidArgumentException; +use Sabre\VObject\TimezoneGuesser\FindFromOffset; +use Sabre\VObject\TimezoneGuesser\FindFromTimezoneIdentifier; +use Sabre\VObject\TimezoneGuesser\FindFromTimezoneMap; +use Sabre\VObject\TimezoneGuesser\GuessFromLicEntry; +use Sabre\VObject\TimezoneGuesser\GuessFromMsTzId; +use Sabre\VObject\TimezoneGuesser\TimezoneFinder; +use Sabre\VObject\TimezoneGuesser\TimezoneGuesser; + /** * Time zone name translation. * @@ -14,17 +24,125 @@ */ class TimeZoneUtil { + /** @var self */ + private static $instance = null; + + /** @var TimezoneGuesser[] */ + private $timezoneGuessers = []; + + /** @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()); + $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); + } + + public static function clean(): void + { + self::$instance = null; + } + /** * List of microsoft exchange timezone ids. * * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx + * + * @deprecated */ public static $microsoftExchangeMap = [ 0 => 'UTC', 31 => 'Africa/Casablanca', - // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo. // I'm not even kidding.. We handle this special case in the // getTimeZone method. @@ -104,6 +222,7 @@ class TimeZoneUtil ]; /** +<<<<<<< HEAD * This method will try to find out the correct timezone for an iCalendar * date-time value. * @@ -136,7 +255,7 @@ public static function getTimeZone($tzid, Component $vcalendar = null, $failIfUn // this method will return just GMT+01:00. This is wrong, because it // doesn't take DST into account. $originalTzid = $tzid; - if ('(' !== $tzid[0]) { + 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); @@ -238,8 +357,12 @@ public static function getTimeZone($tzid, Component $vcalendar = null, $failIfUn } /** +======= +>>>>>>> upstream/master * This method will load in all the tz mapping information, if it's not yet * done. + * + * @deprecated */ public static function loadTzMaps() { @@ -266,6 +389,8 @@ public static function loadTzMaps() * (See timezonedata/php-bc.php and timezonedata php-workaround.php) * * @return array + * + * @deprecated */ public static function getIdentifiersBC() { diff --git a/lib/TimezoneGuesser/FindFromOffset.php b/lib/TimezoneGuesser/FindFromOffset.php new file mode 100644 index 000000000..990ac9692 --- /dev/null +++ b/lib/TimezoneGuesser/FindFromOffset.php @@ -0,0 +1,31 @@ +getIdentifiersBC())) + ) { + return new DateTimeZone($tzid); + } + } catch (Exception $e) { + } + + return null; + } + + /** + * This method returns an array of timezone identifiers, that are supported + * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers(). + * + * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because: + * - It's not supported by some PHP versions as well as HHVM. + * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions. + * (See timezonedata/php-bc.php and timezonedata php-workaround.php) + * + * @return array + */ + private function getIdentifiersBC() + { + return include __DIR__.'/../timezonedata/php-bc.php'; + } +} diff --git a/lib/TimezoneGuesser/FindFromTimezoneMap.php b/lib/TimezoneGuesser/FindFromTimezoneMap.php new file mode 100644 index 000000000..b52ba6a19 --- /dev/null +++ b/lib/TimezoneGuesser/FindFromTimezoneMap.php @@ -0,0 +1,78 @@ +hasTzInMap($tzid)) { + return new DateTimeZone($this->getTzFromMap($tzid)); + } + + // 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. + foreach ($this->patterns as $pattern) { + if (!preg_match($pattern, $tzid, $matches)) { + continue; + } + $tzidAlternate = $matches[3]; + if ($this->hasTzInMap($tzidAlternate)) { + return new DateTimeZone($this->getTzFromMap($tzidAlternate)); + } + } + + return null; + } + + /** + * This method returns an array of timezone identifiers, that are supported + * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers(). + * + * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because: + * - It's not supported by some PHP versions as well as HHVM. + * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions. + * (See timezonedata/php-bc.php and timezonedata php-workaround.php) + * + * @return array + */ + private function getTzMaps() + { + if ([] === $this->map) { + $this->map = array_merge( + include __DIR__.'/../timezonedata/windowszones.php', + include __DIR__.'/../timezonedata/lotuszones.php', + include __DIR__.'/../timezonedata/exchangezones.php', + include __DIR__.'/../timezonedata/php-workaround.php' + ); + } + + return $this->map; + } + + private function getTzFromMap(string $tzid): string + { + return $this->getTzMaps()[$tzid]; + } + + private function hasTzInMap(string $tzid): bool + { + return isset($this->getTzMaps()[$tzid]); + } +} diff --git a/lib/TimezoneGuesser/GuessFromLicEntry.php b/lib/TimezoneGuesser/GuessFromLicEntry.php new file mode 100644 index 000000000..f340a3962 --- /dev/null +++ b/lib/TimezoneGuesser/GuessFromLicEntry.php @@ -0,0 +1,33 @@ +{'X-LIC-LOCATION'})) { + return null; + } + + $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 TimeZoneUtil::getTimeZone($lic, null, $failIfUncertain); + } +} diff --git a/lib/TimezoneGuesser/GuessFromMsTzId.php b/lib/TimezoneGuesser/GuessFromMsTzId.php new file mode 100644 index 000000000..b11ce1832 --- /dev/null +++ b/lib/TimezoneGuesser/GuessFromMsTzId.php @@ -0,0 +1,119 @@ + 'UTC', + 31 => 'Africa/Casablanca', + + // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo. + // I'm not even kidding.. We handle this special case in the + // getTimeZone method. + 2 => 'Europe/Lisbon', + 1 => 'Europe/London', + 4 => 'Europe/Berlin', + 6 => 'Europe/Prague', + 3 => 'Europe/Paris', + 69 => 'Africa/Luanda', // This was a best guess + 7 => 'Europe/Athens', + 5 => 'Europe/Bucharest', + 49 => 'Africa/Cairo', + 50 => 'Africa/Harare', + 59 => 'Europe/Helsinki', + 27 => 'Asia/Jerusalem', + 26 => 'Asia/Baghdad', + 74 => 'Asia/Kuwait', + 51 => 'Europe/Moscow', + 56 => 'Africa/Nairobi', + 25 => 'Asia/Tehran', + 24 => 'Asia/Muscat', // Best guess + 54 => 'Asia/Baku', + 48 => 'Asia/Kabul', + 58 => 'Asia/Yekaterinburg', + 47 => 'Asia/Karachi', + 23 => 'Asia/Calcutta', + 62 => 'Asia/Kathmandu', + 46 => 'Asia/Almaty', + 71 => 'Asia/Dhaka', + 66 => 'Asia/Colombo', + 61 => 'Asia/Rangoon', + 22 => 'Asia/Bangkok', + 64 => 'Asia/Krasnoyarsk', + 45 => 'Asia/Shanghai', + 63 => 'Asia/Irkutsk', + 21 => 'Asia/Singapore', + 73 => 'Australia/Perth', + 75 => 'Asia/Taipei', + 20 => 'Asia/Tokyo', + 72 => 'Asia/Seoul', + 70 => 'Asia/Yakutsk', + 19 => 'Australia/Adelaide', + 44 => 'Australia/Darwin', + 18 => 'Australia/Brisbane', + 76 => 'Australia/Sydney', + 43 => 'Pacific/Guam', + 42 => 'Australia/Hobart', + 68 => 'Asia/Vladivostok', + 41 => 'Asia/Magadan', + 17 => 'Pacific/Auckland', + 40 => 'Pacific/Fiji', + 67 => 'Pacific/Tongatapu', + 29 => 'Atlantic/Azores', + 53 => 'Atlantic/Cape_Verde', + 30 => 'America/Noronha', + 8 => 'America/Sao_Paulo', // Best guess + 32 => 'America/Argentina/Buenos_Aires', + 60 => 'America/Godthab', + 28 => 'America/St_Johns', + 9 => 'America/Halifax', + 33 => 'America/Caracas', + 65 => 'America/Santiago', + 35 => 'America/Bogota', + 10 => 'America/New_York', + 34 => 'America/Indiana/Indianapolis', + 55 => 'America/Guatemala', + 11 => 'America/Chicago', + 37 => 'America/Mexico_City', + 36 => 'America/Edmonton', + 38 => 'America/Phoenix', + 12 => 'America/Denver', // Best guess + 13 => 'America/Los_Angeles', // Best guess + 14 => 'America/Anchorage', + 15 => 'Pacific/Honolulu', + 16 => 'Pacific/Midway', + 39 => 'Pacific/Kwajalein', + ]; + + public function guess(VTimeZone $vtimezone, bool $throwIfUnsure = false): ?DateTimeZone + { + // Microsoft may add a magic number, which we also have an + // answer for. + if (!isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) { + return null; + } + $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]); + } + + return null; + } +} diff --git a/lib/TimezoneGuesser/TimezoneFinder.php b/lib/TimezoneGuesser/TimezoneFinder.php new file mode 100644 index 000000000..5aa880a1c --- /dev/null +++ b/lib/TimezoneGuesser/TimezoneFinder.php @@ -0,0 +1,10 @@ + 'Australia/Darwin', 'AUS Eastern Standard Time' => 'Australia/Sydney', 'Afghanistan Standard Time' => 'Asia/Kabul', @@ -74,6 +74,7 @@ 'Line Islands Standard Time' => 'Pacific/Kiritimati', 'Lord Howe Standard Time' => 'Australia/Lord_Howe', 'Magadan Standard Time' => 'Asia/Magadan', + 'Magallanes Standard Time' => 'America/Punta_Arenas', 'Marquesas Standard Time' => 'Pacific/Marquesas', 'Mauritius Standard Time' => 'Indian/Mauritius', 'Middle East Standard Time' => 'Asia/Beirut', @@ -91,11 +92,13 @@ 'North Asia East Standard Time' => 'Asia/Irkutsk', 'North Asia Standard Time' => 'Asia/Krasnoyarsk', 'North Korea Standard Time' => 'Asia/Pyongyang', + 'Omsk Standard Time' => 'Asia/Omsk', 'Pacific SA Standard Time' => 'America/Santiago', 'Pacific Standard Time' => 'America/Los_Angeles', 'Pacific Standard Time (Mexico)' => 'America/Tijuana', 'Pakistan Standard Time' => 'Asia/Karachi', 'Paraguay Standard Time' => 'America/Asuncion', + 'Qyzylorda Standard Time' => 'Asia/Qyzylorda', 'Romance Standard Time' => 'Europe/Paris', 'Russia Time Zone 10' => 'Asia/Srednekolymsk', 'Russia Time Zone 11' => 'Asia/Kamchatka', @@ -108,9 +111,12 @@ 'Saint Pierre Standard Time' => 'America/Miquelon', 'Sakhalin Standard Time' => 'Asia/Sakhalin', 'Samoa Standard Time' => 'Pacific/Apia', + 'Sao Tome Standard Time' => 'Africa/Sao_Tome', + 'Saratov Standard Time' => 'Europe/Saratov', 'Singapore Standard Time' => 'Asia/Singapore', 'South Africa Standard Time' => 'Africa/Johannesburg', 'Sri Lanka Standard Time' => 'Asia/Colombo', + 'Sudan Standard Time' => 'Africa/Khartoum', 'Syria Standard Time' => 'Asia/Damascus', 'Taipei Standard Time' => 'Asia/Taipei', 'Tasmania Standard Time' => 'Australia/Hobart', @@ -125,6 +131,7 @@ 'US Mountain Standard Time' => 'America/Phoenix', 'UTC' => 'Etc/GMT', 'UTC+12' => 'Etc/GMT-12', + 'UTC+13' => 'Etc/GMT-13', 'UTC-02' => 'Etc/GMT+2', 'UTC-08' => 'Etc/GMT+8', 'UTC-09' => 'Etc/GMT+9', @@ -132,6 +139,7 @@ 'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar', 'Venezuela Standard Time' => 'America/Caracas', 'Vladivostok Standard Time' => 'Asia/Vladivostok', + 'Volgograd Standard Time' => 'Europe/Volgograd', 'W. Australia Standard Time' => 'Australia/Perth', 'W. Central Africa Standard Time' => 'Africa/Lagos', 'W. Europe Standard Time' => 'Europe/Berlin', @@ -140,4 +148,5 @@ 'West Bank Standard Time' => 'Asia/Hebron', 'West Pacific Standard Time' => 'Pacific/Port_Moresby', 'Yakutsk Standard Time' => 'Asia/Yakutsk', + 'Yukon Standard Time' => 'America/Whitehorse', ]; diff --git a/tests/VObject/Component/VAvailabilityTest.php b/tests/VObject/Component/VAvailabilityTest.php index 2fd9c0dde..edd06b02b 100644 --- a/tests/VObject/Component/VAvailabilityTest.php +++ b/tests/VObject/Component/VAvailabilityTest.php @@ -122,7 +122,7 @@ public function testIsInTimeRangeOutside() ); } - public function testRFCxxxSection3_1_availabilityprop_required() + public function testRFCxxxSection3Part1AvailabilitypropRequired() { // UID and DTSTAMP are present. $this->assertIsValid(Reader::read( @@ -177,7 +177,7 @@ public function testRFCxxxSection3_1_availabilityprop_required() )); } - public function testRFCxxxSection3_1_availabilityprop_optional_once() + public function testRFCxxxSection3Part1AvailabilitypropOptionalOnce() { $properties = [ 'BUSYTYPE:BUSY', @@ -205,7 +205,7 @@ public function testRFCxxxSection3_1_availabilityprop_optional_once() } } - public function testRFCxxxSection3_1_availabilityprop_dtend_duration() + public function testRFCxxxSection3Part1AvailabilitypropDtendDuration() { // Only DTEND. $this->assertIsValid(Reader::read($this->template([ @@ -239,7 +239,7 @@ public function testAvailableSubComponent() $this->assertInstanceOf(Available::class, $document->VAVAILABILITY->AVAILABLE); } - public function testRFCxxxSection3_1_availableprop_required() + public function testRFCxxxSection3Part1AvailablepropRequired() { // UID, DTSTAMP and DTSTART are present. $this->assertIsValid(Reader::read( @@ -331,7 +331,7 @@ public function testRFCxxxSection3_1_availableprop_required() )); } - public function testRFCxxxSection3_1_available_dtend_duration() + public function testRFCxxxSection3Part1AvailableDtendDuration() { // Only DTEND. $this->assertIsValid(Reader::read($this->templateAvailable([ @@ -350,7 +350,7 @@ public function testRFCxxxSection3_1_available_dtend_duration() ]))); } - public function testRFCxxxSection3_1_available_optional_once() + public function testRFCxxxSection3Part1AvailableOptionalOnce() { $properties = [ 'CREATED:20111005T135125Z', @@ -373,7 +373,7 @@ public function testRFCxxxSection3_1_available_optional_once() } } - public function testRFCxxxSection3_2() + public function testRFCxxxSection3Part2() { $this->assertEquals( 'BUSY', diff --git a/tests/VObject/Component/VCalendarTest.php b/tests/VObject/Component/VCalendarTest.php index c2f0ce978..d34e12d2b 100644 --- a/tests/VObject/Component/VCalendarTest.php +++ b/tests/VObject/Component/VCalendarTest.php @@ -350,6 +350,33 @@ public function testBrokenEventExpand() ); } + /** + * This test used to induce an infinite loop. + * The "medium" annotation means that phpunit will fail the + * test if it takes longer than a default of 10 seconds. + * + * @medium + */ + public function testEventExpandYearly() + { + $input = 'BEGIN:VCALENDAR +BEGIN:VEVENT +UID:1a093f1012086078fdd3d9df5ff4d7d0 +DTSTART;TZID=UTC:20210203T130000 +DTEND;TZID=UTC:20210203T140000 +RRULE:FREQ=YEARLY;COUNT=7;WKST=MO;BYDAY=MO;BYWEEKNO=13,15,50 +END:VEVENT +END:VCALENDAR +'; + $vcal = VObject\Reader::read($input); + $events = $vcal->expand( + new \DateTime('2021-01-01'), + new \DateTime('2023-01-01') + ); + + $this->assertCount(7, $events->VEVENT); + } + public function testGetDocumentType() { $vcard = new VCalendar(); diff --git a/tests/VObject/Component/VCardTest.php b/tests/VObject/Component/VCardTest.php index 3124fec84..d8e6110b6 100644 --- a/tests/VObject/Component/VCardTest.php +++ b/tests/VObject/Component/VCardTest.php @@ -135,8 +135,8 @@ public function testGetByType() $vcard = VObject\Reader::read($vcard); $this->assertEquals('1@example.org', $vcard->getByType('EMAIL', 'home')->getValue()); $this->assertEquals('2@example.org', $vcard->getByType('EMAIL', 'work')->getValue()); - $this->assertNull($vcard->getByType('EMAIL', 'non-existant')); - $this->assertNull($vcard->getByType('ADR', 'non-existant')); + $this->assertNull($vcard->getByType('EMAIL', 'non-existent')); + $this->assertNull($vcard->getByType('ADR', 'non-existent')); } public function testPreferredNoPref() diff --git a/tests/VObject/ComponentTest.php b/tests/VObject/ComponentTest.php index f56d55531..cf3e196dc 100644 --- a/tests/VObject/ComponentTest.php +++ b/tests/VObject/ComponentTest.php @@ -72,6 +72,23 @@ public function testMagicGetGroups() $this->assertEquals(null, $email3[0]->group); } + public function testAddGroupProperties() + { + $comp = new VCard([ + 'VERSION' => '3.0', + 'item2.X-ABLabel' => 'item2-Foo', + ]); + + $comp->{'ITEM1.X-ABLabel'} = 'ITEM1-Foo'; + + foreach (['item2', 'ITEM1'] as $group) { + $prop = $comp->{"$group.X-ABLabel"}; + $this->assertInstanceOf(Property::class, $prop); + $this->assertSame("$group-Foo", (string) $prop); + $this->assertSame($group, $prop->group); + } + } + public function testMagicIsset() { $comp = new VCalendar(); diff --git a/tests/VObject/DateTimeParserTest.php b/tests/VObject/DateTimeParserTest.php index b20a43217..ede81e321 100644 --- a/tests/VObject/DateTimeParserTest.php +++ b/tests/VObject/DateTimeParserTest.php @@ -408,7 +408,7 @@ public function vcardDates() ]; } - public function testDateAndOrTime_DateWithYearMonthDay() + public function testDateAndOrTimeDateWithYearMonthDay() { $this->assertDateAndOrTimeEqualsTo( '20150128', @@ -420,7 +420,7 @@ public function testDateAndOrTime_DateWithYearMonthDay() ); } - public function testDateAndOrTime_DateWithYearMonth() + public function testDateAndOrTimeDateWithYearMonth() { $this->assertDateAndOrTimeEqualsTo( '2015-01', @@ -431,7 +431,7 @@ public function testDateAndOrTime_DateWithYearMonth() ); } - public function testDateAndOrTime_DateWithMonth() + public function testDateAndOrTimeDateWithMonth() { $this->assertDateAndOrTimeEqualsTo( '--01', @@ -441,7 +441,7 @@ public function testDateAndOrTime_DateWithMonth() ); } - public function testDateAndOrTime_DateWithMonthDay() + public function testDateAndOrTimeDateWithMonthDay() { $this->assertDateAndOrTimeEqualsTo( '--0128', @@ -452,7 +452,7 @@ public function testDateAndOrTime_DateWithMonthDay() ); } - public function testDateAndOrTime_DateWithDay() + public function testDateAndOrTimeDateWithDay() { $this->assertDateAndOrTimeEqualsTo( '---28', @@ -462,7 +462,7 @@ public function testDateAndOrTime_DateWithDay() ); } - public function testDateAndOrTime_TimeWithHour() + public function testDateAndOrTimeTimeWithHour() { $this->assertDateAndOrTimeEqualsTo( '13', @@ -472,7 +472,7 @@ public function testDateAndOrTime_TimeWithHour() ); } - public function testDateAndOrTime_TimeWithHourMinute() + public function testDateAndOrTimeTimeWithHourMinute() { $this->assertDateAndOrTimeEqualsTo( '1353', @@ -483,7 +483,7 @@ public function testDateAndOrTime_TimeWithHourMinute() ); } - public function testDateAndOrTime_TimeWithHourSecond() + public function testDateAndOrTimeTimeWithHourSecond() { $this->assertDateAndOrTimeEqualsTo( '135301', @@ -495,7 +495,7 @@ public function testDateAndOrTime_TimeWithHourSecond() ); } - public function testDateAndOrTime_TimeWithMinute() + public function testDateAndOrTimeTimeWithMinute() { $this->assertDateAndOrTimeEqualsTo( '-53', @@ -505,7 +505,7 @@ public function testDateAndOrTime_TimeWithMinute() ); } - public function testDateAndOrTime_TimeWithMinuteSecond() + public function testDateAndOrTimeTimeWithMinuteSecond() { $this->assertDateAndOrTimeEqualsTo( '-5301', @@ -516,7 +516,7 @@ public function testDateAndOrTime_TimeWithMinuteSecond() ); } - public function testDateAndOrTime_TimeWithSecond() + public function testDateAndOrTimeTimeWithSecond() { $this->assertTrue(true); @@ -526,7 +526,7 @@ public function testDateAndOrTime_TimeWithSecond() */ } - public function testDateAndOrTime_TimeWithSecondZ() + public function testDateAndOrTimeTimeWithSecondZ() { $this->assertDateAndOrTimeEqualsTo( '--01Z', @@ -537,7 +537,7 @@ public function testDateAndOrTime_TimeWithSecondZ() ); } - public function testDateAndOrTime_TimeWithSecondTZ() + public function testDateAndOrTimeTimeWithSecondTZ() { $this->assertDateAndOrTimeEqualsTo( '--01+1234', @@ -548,7 +548,7 @@ public function testDateAndOrTime_TimeWithSecondTZ() ); } - public function testDateAndOrTime_DateTimeWithYearMonthDayHour() + public function testDateAndOrTimeDateTimeWithYearMonthDayHour() { $this->assertDateAndOrTimeEqualsTo( '20150128T13', @@ -561,7 +561,7 @@ public function testDateAndOrTime_DateTimeWithYearMonthDayHour() ); } - public function testDateAndOrTime_DateTimeWithMonthDayHour() + public function testDateAndOrTimeDateTimeWithMonthDayHour() { $this->assertDateAndOrTimeEqualsTo( '--0128T13', @@ -573,7 +573,7 @@ public function testDateAndOrTime_DateTimeWithMonthDayHour() ); } - public function testDateAndOrTime_DateTimeWithDayHour() + public function testDateAndOrTimeDateTimeWithDayHour() { $this->assertDateAndOrTimeEqualsTo( '---28T13', @@ -584,7 +584,7 @@ public function testDateAndOrTime_DateTimeWithDayHour() ); } - public function testDateAndOrTime_DateTimeWithDayHourMinute() + public function testDateAndOrTimeDateTimeWithDayHourMinute() { $this->assertDateAndOrTimeEqualsTo( '---28T1353', @@ -596,7 +596,7 @@ public function testDateAndOrTime_DateTimeWithDayHourMinute() ); } - public function testDateAndOrTime_DateTimeWithDayHourMinuteSecond() + public function testDateAndOrTimeDateTimeWithDayHourMinuteSecond() { $this->assertDateAndOrTimeEqualsTo( '---28T135301', @@ -609,7 +609,7 @@ public function testDateAndOrTime_DateTimeWithDayHourMinuteSecond() ); } - public function testDateAndOrTime_DateTimeWithDayHourZ() + public function testDateAndOrTimeDateTimeWithDayHourZ() { $this->assertDateAndOrTimeEqualsTo( '---28T13Z', @@ -621,7 +621,7 @@ public function testDateAndOrTime_DateTimeWithDayHourZ() ); } - public function testDateAndOrTime_DateTimeWithDayHourTZ() + public function testDateAndOrTimeDateTimeWithDayHourTZ() { $this->assertDateAndOrTimeEqualsTo( '---28T13+1234', diff --git a/tests/VObject/EmptyValueIssueTest.php b/tests/VObject/EmptyValueIssueTest.php index 91a4d84f6..0798d9c4a 100644 --- a/tests/VObject/EmptyValueIssueTest.php +++ b/tests/VObject/EmptyValueIssueTest.php @@ -17,7 +17,7 @@ public function testDecodeValue() BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT -DESCRIPTION:This is a descpription\\nwith a linebreak and a \\; \\, and : +DESCRIPTION:This is a description\\nwith a linebreak and a \\; \\, and : END:VEVENT END:VCALENDAR ICS; @@ -25,6 +25,6 @@ public function testDecodeValue() $vobj = Reader::read($input); // Before this bug was fixed, getValue() would return nothing. - $this->assertEquals("This is a descpription\nwith a linebreak and a ; , and :", $vobj->VEVENT->DESCRIPTION->getValue()); + $this->assertEquals("This is a description\nwith a linebreak and a ; , and :", $vobj->VEVENT->DESCRIPTION->getValue()); } } diff --git a/tests/VObject/ITip/BrokerAttendeeReplyTest.php b/tests/VObject/ITip/BrokerAttendeeReplyTest.php index 71008c6ae..284075adf 100644 --- a/tests/VObject/ITip/BrokerAttendeeReplyTest.php +++ b/tests/VObject/ITip/BrokerAttendeeReplyTest.php @@ -907,7 +907,7 @@ public function testDeclinedCancelledEvent() * Except in this case, there was already an overridden event, and the * overridden event was marked as cancelled by the attendee. * - * For any other attendence status, the new status would have been + * For any other attendance status, the new status would have been * declined, but for this, no message should we sent. */ public function testDontCreateReplyWhenEventWasDeclined() diff --git a/tests/VObject/ITip/BrokerProcessReplyTest.php b/tests/VObject/ITip/BrokerProcessReplyTest.php index 1cb685096..bbfd6c419 100644 --- a/tests/VObject/ITip/BrokerProcessReplyTest.php +++ b/tests/VObject/ITip/BrokerProcessReplyTest.php @@ -373,7 +373,7 @@ public function testReplyNewExceptionTz() $result = $this->process($itip, $old, $expected); } - public function testReplyPartyCrashCreateExcepton() + public function testReplyPartyCrashCreateException() { // IN this test there's a recurring event that has an exception. The // exception is missing the attendee. diff --git a/tests/VObject/ITip/BrokerSignificantChangesTest.php b/tests/VObject/ITip/BrokerSignificantChangesTest.php index a225cb98c..a20d55025 100644 --- a/tests/VObject/ITip/BrokerSignificantChangesTest.php +++ b/tests/VObject/ITip/BrokerSignificantChangesTest.php @@ -105,4 +105,112 @@ public function testSignificantChangesRRuleOrderNoChange() $this->parse($old, $new, $expected, 'mailto:martin@fruux.com'); } + + /** + * Check significant changes detection (no change). + * Reordering of the attendees should not be a significant change (#540) + * https://github.com/sabre-io/vobject/issues/540. + */ + public function testSignificantChangesAttendeesOrderNoChange() + { + $old = << false]; + $expected[] = ['significantChange' => false]; + + $this->parse($old, $new, $expected, 'mailto:martin@fruux.com'); + } + + /** + * Check significant changes detection (no change). + * Reordering of vevent in a recurring event with exceptions should + * not be a significant change + * https://github.com/sabre-io/vobject/issues/542. + */ + public function testSignificantChangesVeventOrderNoChange() + { + $vevent1 = << false]]; + + $this->parse($old, $new, $expected, 'mailto:martin@fruux.com'); + } } diff --git a/tests/VObject/Parser/XmlTest.php b/tests/VObject/Parser/XmlTest.php index e520185ba..46ee30ce2 100644 --- a/tests/VObject/Parser/XmlTest.php +++ b/tests/VObject/Parser/XmlTest.php @@ -262,7 +262,7 @@ public function testRFC6321Example2() /** * iCalendar Stream. */ - public function testRFC6321Section3_2() + public function testRFC6321Section3Part2() { $this->assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( @@ -466,7 +466,7 @@ public function testRFC6321Section3_4_1_3() /** * Values, Binary. */ - public function testRFC6321Section3_6_1() + public function testRFC6321Section3Part6Part1() { $this->assertXMLEqualsToMimeDir( <<assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( << with a positive and a non-negative numbers. - $this->testRFC6321Section3_4_1_2(); + $this->testRFC6321Section3Part4Part1Part2(); } /** * Values, Integer. */ - public function testRFC6321Section3_6_8() + public function testRFC6321Section3Part6Part8() { $this->assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( @@ -1259,7 +1259,7 @@ public function testRFC6351Section5Group() /** * Extensibility. */ - public function testRFC6351Section5_1_NoNamespace() + public function testRFC6351Section5Part1NoNamespace() { $this->assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<assertXMLReflexivelyEqualsToMimeDir( <<createComponent('VEVENT'); @@ -1318,12 +1318,12 @@ public function testOverridenEventNoValuesExpected() $summaries = []; // The reported problem was specifically related to the VCALENDAR - // expansion. In this parcitular case, we had to forward to the 28th of + // expansion. In this particular case, we had to forward to the 28th of // january. $it->fastForward(new DateTimeImmutable('2012-01-28 23:00:00')); - // We stop the loop when it hits the 6th of februari. Normally this - // iterator would hit 24, 25 (overriden from 31) and 7 feb but because + // We stop the loop when it hits the 6th of February. Normally this + // iterator would hit 24, 25 (overridden from 31) and 7 feb but because // we 'filter' from the 28th till the 6th, we should get 0 results. while ($it->valid() && $it->getDTStart() < new DateTimeImmutable('2012-02-06 23:00:00')) { $dates[] = $it->getDTStart(); diff --git a/tests/VObject/Recur/EventIterator/OverrideDurationTest.php b/tests/VObject/Recur/EventIterator/OverrideDurationTest.php new file mode 100644 index 000000000..f25ef13d9 --- /dev/null +++ b/tests/VObject/Recur/EventIterator/OverrideDurationTest.php @@ -0,0 +1,52 @@ +getComponents()); + + $this->assertEquals($eventIterator->current()->format('Y-m-d H:i:s'), '2021-05-17 09:00:00', 'recur event start time'); + $this->assertEquals($eventIterator->getDtEnd()->format('Y-m-d H:i:s'), '2021-05-17 10:00:00', 'recur event end time'); + + $eventIterator->next(); + $this->assertEquals($eventIterator->current()->format('Y-m-d H:i:s'), '2021-05-18 09:00:00', 'recur event start time'); + $this->assertEquals($eventIterator->getDtEnd()->format('Y-m-d H:i:s'), '2021-05-18 10:00:00', 'recur event end time'); + + $eventIterator->next(); + $this->assertEquals($eventIterator->current()->format('Y-m-d H:i:s'), '2021-05-19 09:00:00', 'overridden event start time'); + $this->assertEquals($eventIterator->getDtEnd()->format('Y-m-d H:i:s'), '2021-05-19 12:00:00', 'overridden event end time'); + + $eventIterator->next(); + $this->assertEquals($eventIterator->current()->format('Y-m-d H:i:s'), '2021-05-20 09:00:00', 'recur event start time'); + $this->assertEquals($eventIterator->getDtEnd()->format('Y-m-d H:i:s'), '2021-05-20 10:00:00', 'recur event end time'); + } +} diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index 5f56e6849..cd4c3f8d4 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -278,7 +278,7 @@ public function testMonthly() ); } - public function testMonlthyEndOfMonth() + public function testMonthlyEndOfMonth() { $this->parse( 'FREQ=MONTHLY;INTERVAL=2;COUNT=12', @@ -358,6 +358,35 @@ public function testMonthlyByDay() ); } + 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', + ], + 'monthly', null, 1, new DateTime('2021-03-17') + ); + } + + 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', + ], + 'monthly', null, 1, new DateTime('2021-03-17') + ); + } + public function testMonthlyByDayByMonthDay() { $this->parse( @@ -689,6 +718,20 @@ public function testYearlyByYearDayInvalid0() ); } + public function testYearlyByDayByWeekNo() + { + $this->parse( + 'FREQ=YEARLY;COUNT=3;BYDAY=MO;BYWEEKNO=13,15,50', + '2021-01-01 00:00:00', + [ + '2021-01-01 00:00:00', + '2021-03-29 00:00:00', + '2021-04-12 00:00:00', + ], + 'yearly', 3, 1 + ); + } + public function testFastForward() { // The idea is that we're fast-forwarding too far in the future, so @@ -728,7 +771,7 @@ public function testFifthTuesdayProblem() * This bug came from a Fruux customer. This would result in a never-ending * request. */ - public function testFastFowardTooFar() + public function testFastForwardTooFar() { $this->parse( 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1', @@ -1014,6 +1057,13 @@ public function testMinusFifthThursday() ); } + /** + * This test can take some seconds to complete. + * The "large" annotation means phpunit will let it run for + * up to 60 seconds by default. + * + * @large + */ public function testNeverEnding() { $this->parse( diff --git a/tests/VObject/TimeZoneUtilTest.php b/tests/VObject/TimeZoneUtilTest.php index 8b6103f68..33a1eff98 100644 --- a/tests/VObject/TimeZoneUtilTest.php +++ b/tests/VObject/TimeZoneUtilTest.php @@ -10,6 +10,7 @@ public function setUp(): void { // clearning the tz cache TimeZoneUtil::$map = null; + TimeZoneUtil::clean(); } /** @@ -31,22 +32,28 @@ public function testCorrectTZ($timezoneName) public function getMapping() { - TimeZoneUtil::loadTzMaps(); + $map = array_merge( + 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' + ); // PHPUNit requires an array of arrays return array_map( function ($value) { return [$value]; }, - TimeZoneUtil::$map + $map ); } /** * @dataProvider getMapping */ - public function testSlashTZ($timezonename) { - $slashTimezone = '/' . $timezonename; + public function testSlashTZ($timezonename) + { + $slashTimezone = '/'.$timezonename; $expected = TimeZoneUtil::getTimeZone($timezonename)->getName(); $actual = TimeZoneUtil::getTimeZone($slashTimezone)->getName(); $this->assertEquals($expected, $actual); @@ -92,7 +99,7 @@ public function testExchangeMap() $this->assertEquals($ex->getName(), $tz->getName()); } - public function testWetherMicrosoftIsStillInsane() + public function testWhetherMicrosoftIsStillInsane() { $vobj = <<assertEquals($ex->getName(), $tz->getName()); } + public function testEmptyTimeZone() + { + $tz = TimeZoneUtil::getTimeZone(''); + $ex = new \DateTimeZone('UTC'); + $this->assertEquals($ex->getName(), $tz->getName()); + } + public function testWindowsTimeZone() { $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time'); @@ -204,7 +218,7 @@ public function getPHPTimeZoneBCIdentifiers() function ($value) { return [$value]; }, - TimeZoneUtil::getIdentifiersBC() + include __DIR__.'/../../lib/timezonedata/php-bc.php' ); }