From 8e8ca30410ae803e97a995101fbb22c5ba33ad89 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Tue, 3 Sep 2024 16:08:18 +0200 Subject: [PATCH] Fix mutation coverage (#485) Fix mutation tests --- composer.lock | 198 +++++++++--------- config/infection.json.dist | 7 +- .../DateTimeConvenienceMethodsTrait.php | 9 +- src/Psl/DateTime/Duration.php | 29 ++- tests/unit/Collection/MutableSetTest.php | 12 +- tests/unit/Collection/SetTest.php | 10 + tests/unit/DateTime/DateTimeTest.php | 191 +++++++++++++++-- tests/unit/DateTime/DurationTest.php | 16 ++ .../InvalidArgumentExceptionTest.php | 102 +++++++++ tests/unit/DateTime/TimestampTest.php | 26 +++ tests/unit/DateTime/TimezoneTest.php | 24 +++ 11 files changed, 491 insertions(+), 133 deletions(-) create mode 100644 tests/unit/DateTime/Exception/InvalidArgumentExceptionTest.php diff --git a/composer.lock b/composer.lock index 8598a993..a41893b1 100644 --- a/composer.lock +++ b/composer.lock @@ -1131,16 +1131,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.58.0", + "version": "v3.58.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "55d3483c80c09f91d876aa4e2971ce349d07310c" + "reference": "04e9424025677a86914b9a4944dbbf4060bb0aff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/55d3483c80c09f91d876aa4e2971ce349d07310c", - "reference": "55d3483c80c09f91d876aa4e2971ce349d07310c", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/04e9424025677a86914b9a4944dbbf4060bb0aff", + "reference": "04e9424025677a86914b9a4944dbbf4060bb0aff", "shasum": "" }, "require": { @@ -1219,7 +1219,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.58.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.58.1" }, "funding": [ { @@ -1227,7 +1227,7 @@ "type": "github" } ], - "time": "2024-05-28T16:55:30+00:00" + "time": "2024-05-29T16:39:07+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1935,16 +1935,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -1952,11 +1952,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -1982,7 +1983,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -1990,7 +1991,7 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "netresearch/jsonmapper", @@ -2809,16 +2810,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.0", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { @@ -2850,9 +2851,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2024-05-06T12:04:23+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3835,28 +3836,28 @@ }, { "name": "react/dns", - "version": "v1.12.0", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "c134600642fa615b46b41237ef243daa65bb64ec" + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/c134600642fa615b46b41237ef243daa65bb64ec", - "reference": "c134600642fa615b46b41237ef243daa65bb64ec", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", "shasum": "" }, "require": { "php": ">=5.3.0", "react/cache": "^1.0 || ^0.6 || ^0.5", "react/event-loop": "^1.2", - "react/promise": "^3.0 || ^2.7 || ^1.2.1" + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4 || ^3 || ^2", - "react/promise-timer": "^1.9" + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, "type": "library", "autoload": { @@ -3899,7 +3900,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.12.0" + "source": "https://github.com/reactphp/dns/tree/v1.13.0" }, "funding": [ { @@ -3907,7 +3908,7 @@ "type": "open_collective" } ], - "time": "2023-11-29T12:41:06+00:00" + "time": "2024-06-13T14:18:03+00:00" }, { "name": "react/event-loop", @@ -4136,16 +4137,16 @@ }, { "name": "react/stream", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/reactphp/stream.git", - "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66" + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/6fbc9672905c7d5a885f2da2fc696f65840f4a66", - "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { @@ -4155,7 +4156,7 @@ }, "require-dev": { "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { @@ -4202,7 +4203,7 @@ ], "support": { "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.3.0" + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, "funding": [ { @@ -4210,7 +4211,7 @@ "type": "open_collective" } ], - "time": "2023-06-16T10:52:11+00:00" + "time": "2024-06-11T12:45:25+00:00" }, { "name": "roave/infection-static-analysis-plugin", @@ -5569,22 +5570,22 @@ }, { "name": "symfony/config", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "f66f908a975500aa4594258bf454dc66e3939eac" + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/f66f908a975500aa4594258bf454dc66e3939eac", - "reference": "f66f908a975500aa4594258bf454dc66e3939eac", + "url": "https://api.github.com/repos/symfony/config/zipball/2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { @@ -5624,7 +5625,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.0.7" + "source": "https://github.com/symfony/config/tree/v7.1.1" }, "funding": [ { @@ -5640,20 +5641,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/console", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c981e0e9380ce9f146416bde3150c79197ce9986" + "reference": "9b008f2d7b21c74ef4d0c3de6077a642bc55ece3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c981e0e9380ce9f146416bde3150c79197ce9986", - "reference": "c981e0e9380ce9f146416bde3150c79197ce9986", + "url": "https://api.github.com/repos/symfony/console/zipball/9b008f2d7b21c74ef4d0c3de6077a642bc55ece3", + "reference": "9b008f2d7b21c74ef4d0c3de6077a642bc55ece3", "shasum": "" }, "require": { @@ -5717,7 +5718,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.7" + "source": "https://github.com/symfony/console/tree/v7.1.1" }, "funding": [ { @@ -5733,7 +5734,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5804,16 +5805,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9" + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9", - "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", "shasum": "" }, "require": { @@ -5864,7 +5865,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" }, "funding": [ { @@ -5880,7 +5881,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5960,22 +5961,24 @@ }, { "name": "symfony/filesystem", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5" + "reference": "802e87002f919296c9f606457d9fa327a0b3d6b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/cc168be6fbdcdf3401f50ae863ee3818ed4338f5", - "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/802e87002f919296c9f606457d9fa327a0b3d6b2", + "reference": "802e87002f919296c9f606457d9fa327a0b3d6b2", "shasum": "" }, "require": { "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { "symfony/process": "^6.4|^7.0" }, "type": "library", @@ -6004,7 +6007,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.7" + "source": "https://github.com/symfony/filesystem/tree/v7.1.1" }, "funding": [ { @@ -6020,20 +6023,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/finder", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c" + "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4d58f0f4fe95a30d7b538d71197135483560b97c", - "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c", + "url": "https://api.github.com/repos/symfony/finder/zipball/fbb0ba67688b780efbc886c1a0a0948dcf7205d6", + "reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6", "shasum": "" }, "require": { @@ -6068,7 +6071,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.0.7" + "source": "https://github.com/symfony/finder/tree/v7.1.1" }, "funding": [ { @@ -6084,20 +6087,20 @@ "type": "tidelift" } ], - "time": "2024-04-28T11:44:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa" + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa", - "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", "shasum": "" }, "require": { @@ -6135,7 +6138,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.0.7" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" }, "funding": [ { @@ -6151,7 +6154,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6629,16 +6632,16 @@ }, { "name": "symfony/process", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0" + "reference": "febf90124323a093c7ee06fdb30e765ca3c20028" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3839e56b94dd1dbd13235d27504e66baf23faba0", - "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0", + "url": "https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028", + "reference": "febf90124323a093c7ee06fdb30e765ca3c20028", "shasum": "" }, "require": { @@ -6670,7 +6673,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.0.7" + "source": "https://github.com/symfony/process/tree/v7.1.1" }, "funding": [ { @@ -6686,7 +6689,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/service-contracts", @@ -6773,16 +6776,16 @@ }, { "name": "symfony/stopwatch", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84" + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84", - "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", + "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", "shasum": "" }, "require": { @@ -6815,7 +6818,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.0.7" + "source": "https://github.com/symfony/stopwatch/tree/v7.1.1" }, "funding": [ { @@ -6831,20 +6834,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/string", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63" + "reference": "60bc311c74e0af215101235aa6f471bcbc032df2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63", - "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63", + "url": "https://api.github.com/repos/symfony/string/zipball/60bc311c74e0af215101235aa6f471bcbc032df2", + "reference": "60bc311c74e0af215101235aa6f471bcbc032df2", "shasum": "" }, "require": { @@ -6858,6 +6861,7 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { + "symfony/emoji": "^7.1", "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", @@ -6901,7 +6905,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.7" + "source": "https://github.com/symfony/string/tree/v7.1.1" }, "funding": [ { @@ -6917,20 +6921,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-06-04T06:40:14+00:00" }, { "name": "symfony/yaml", - "version": "v7.0.7", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0d3916ae69ea28b59d94b60c4f2b50f4e25adb5c" + "reference": "fa34c77015aa6720469db7003567b9f772492bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0d3916ae69ea28b59d94b60c4f2b50f4e25adb5c", - "reference": "0d3916ae69ea28b59d94b60c4f2b50f4e25adb5c", + "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2", "shasum": "" }, "require": { @@ -6972,7 +6976,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.0.7" + "source": "https://github.com/symfony/yaml/tree/v7.1.1" }, "funding": [ { @@ -6988,7 +6992,7 @@ "type": "tidelift" } ], - "time": "2024-04-28T11:44:19+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "thecodingmachine/safe", diff --git a/config/infection.json.dist b/config/infection.json.dist index c2ee2166..1e6aeee1 100644 --- a/config/infection.json.dist +++ b/config/infection.json.dist @@ -32,7 +32,8 @@ }, "DecrementInteger": { "ignore": [ - "Psl\\DataStructure\\PriorityQueue::peek" + "Psl\\DataStructure\\PriorityQueue::peek", + "Psl\\DateTime\\TemporalConvenienceMethodsTrait::since" ] }, "FunctionCallRemoval": { @@ -42,7 +43,8 @@ }, "IncrementInteger": { "ignore": [ - "Psl\\DataStructure\\PriorityQueue::peek" + "Psl\\DataStructure\\PriorityQueue::peek", + "Psl\\DateTime\\TemporalConvenienceMethodsTrait::since" ] }, "LogicalNot": { @@ -58,6 +60,7 @@ }, "Throw_": { "ignore": [ + "Psl\\DateTime\\DateTime::__construct", "Psl\\File\\ReadHandle::__construct", "Psl\\File\\WriteHandle::__construct", "Psl\\File\\ReadWriteHandle::__construct", diff --git a/src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php b/src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php index e5662f5f..383b7e08 100644 --- a/src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php +++ b/src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php @@ -398,7 +398,7 @@ public function plusMonths(int $months): static return $this; } - if (0 > $months) { + if ($months < 1) { return $this->minusMonths(-$months); } @@ -406,7 +406,7 @@ public function plusMonths(int $months): static $months_left = $months - ($plus_years * MONTHS_PER_YEAR); $target_month = $this->getMonth() + $months_left; - if ($target_month > 12) { + if ($target_month > MONTHS_PER_YEAR) { $plus_years++; $target_month = $target_month - MONTHS_PER_YEAR; } @@ -438,7 +438,7 @@ public function minusMonths(int $months): static return $this; } - if (0 > $months) { + if ($months < 1) { return $this->plusMonths(-$months); } @@ -616,10 +616,9 @@ public function toString(null|DateStyle $date_style = null, null|TimeStyle $time $timestamp = $this->getTimestamp(); /** - * @psalm-suppress InvalidOperand * @psalm-suppress ImpureMethodCall */ return Internal\create_intl_date_formatter($date_style, $time_style, null, $timezone ?? $this->getTimezone(), $locale) - ->format($timestamp->getSeconds() + ($timestamp->getNanoseconds() / NANOSECONDS_PER_SECOND)); + ->format($timestamp->getSeconds()); } } diff --git a/src/Psl/DateTime/Duration.php b/src/Psl/DateTime/Duration.php index e5dd7270..5a5b54cd 100644 --- a/src/Psl/DateTime/Duration.php +++ b/src/Psl/DateTime/Duration.php @@ -674,27 +674,24 @@ public function toString(int $max_decimals = 3): string $sec_sign = $this->seconds < 0 || $this->nanoseconds < 0 ? '-' : ''; $sec = Math\abs($this->seconds); - /** @var list $values */ - $values = [ - [((string) $this->hours), 'hour(s)'], - [((string) $this->minutes), 'minute(s)'], - [$sec_sign . ((string) $sec) . $decimal_part, 'second(s)'], - ]; - // $end is the sizeof($values), use static value for better performance. - $end = 3; - while ($end > 0 && $values[$end - 1][0] === '0') { - --$end; + $containsHours = $this->hours !== 0; + $containsMinutes = $this->minutes !== 0; + $concatenatedSeconds = $sec_sign . ((string) $sec) . $decimal_part; + $containsSeconds = $concatenatedSeconds !== '0'; + + /** @var list $output */ + $output = []; + if ($containsHours) { + $output[] = ((string) $this->hours) . ' hour(s)'; } - $start = 0; - while ($start < $end && $values[$start][0] === '0') { - ++$start; + if ($containsMinutes || ($containsHours && $containsSeconds)) { + $output[] = ((string) $this->minutes) . ' minute(s)'; } - $output = []; - for ($i = $start; $i < $end; ++$i) { - $output[] = $values[$i][0] . ' ' . $values[$i][1]; + if ($containsSeconds) { + $output[] = $concatenatedSeconds . ' second(s)'; } return ([] === $output) ? '0 second(s)' : Str\join($output, ', '); diff --git a/tests/unit/Collection/MutableSetTest.php b/tests/unit/Collection/MutableSetTest.php index 415d2643..5f741f72 100644 --- a/tests/unit/Collection/MutableSetTest.php +++ b/tests/unit/Collection/MutableSetTest.php @@ -109,7 +109,7 @@ public function testArrayAccess(): void static::assertTrue(isset($set['foo'])); static::assertSame('foo', $set['foo']); - + unset($set['foo']); static::assertFalse(isset($set['foo'])); @@ -191,6 +191,16 @@ public function testOffsetGetThrowsForInvalidOffsetType(): void $set[false]; } + public function testFromArrayKeysConstructor() + { + $set = MutableSet::fromArrayKeys(['foo' => 1, 'bar' => 1, 'baz' => 1]); + + static::assertCount(3, $set); + static::assertTrue($set->contains('foo')); + static::assertTrue($set->contains('bar')); + static::assertTrue($set->contains('baz')); + } + /** * @template T of array-key * diff --git a/tests/unit/Collection/SetTest.php b/tests/unit/Collection/SetTest.php index fedc4f6e..1e07d008 100644 --- a/tests/unit/Collection/SetTest.php +++ b/tests/unit/Collection/SetTest.php @@ -26,4 +26,14 @@ protected function createFromList(array $items): Set { return Set::fromArray($items); } + + public function testFromArrayKeysConstructor() + { + $set = Set::fromArrayKeys(['foo' => 1, 'bar' => 1, 'baz' => 1]); + + static::assertCount(3, $set); + static::assertTrue($set->contains('foo')); + static::assertTrue($set->contains('bar')); + static::assertTrue($set->contains('baz')); + } } diff --git a/tests/unit/DateTime/DateTimeTest.php b/tests/unit/DateTime/DateTimeTest.php index b400d543..4e737106 100644 --- a/tests/unit/DateTime/DateTimeTest.php +++ b/tests/unit/DateTime/DateTimeTest.php @@ -5,14 +5,19 @@ namespace Psl\Tests\Unit\DateTime; use PHPUnit\Framework\TestCase; +use Psl\DateTime\DateStyle; use Psl\DateTime\DateTime; use Psl\DateTime\Exception\UnexpectedValueException; +use Psl\DateTime\FormatPattern; use Psl\DateTime\Meridiem; use Psl\DateTime\Month; +use Psl\DateTime\TimeStyle; use Psl\DateTime\Timezone; use Psl\DateTime\Weekday; use Psl\Json; +use Psl\Locale\Locale; +use function Psl\DateTime\Internal\create_intl_date_formatter; use function time; final class DateTimeTest extends TestCase @@ -36,6 +41,21 @@ public function testTodayAt(): void static::assertSame(14, $today->getHours()); static::assertSame(0, $today->getMinutes()); static::assertSame(0, $today->getSeconds()); + static::assertSame(0, $today->getNanoseconds()); + } + + public function testTodayAtDefaults(): void + { + $now = DateTime::now(); + $today = DateTime::todayAt(14, 0); + + static::assertSame($now->getDate(), $today->getDate()); + static::assertNotSame($now->getTime(), $today->getTime()); + static::assertSame(14, $today->getHours()); + static::assertSame(0, $today->getMinutes()); + static::assertSame(0, $today->getSeconds()); + static::assertSame(0, $today->getNanoseconds()); + static::assertSame(Timezone::default(), $today->getTimezone()); } public function testFromParts(): void @@ -44,6 +64,7 @@ public function testFromParts(): void static::assertSame(Timezone::UTC, $datetime->getTimezone()); static::assertSame(2024, $datetime->getYear()); + static::assertSame(24, $datetime->getYearShort()); static::assertSame(2, $datetime->getMonth()); static::assertSame(4, $datetime->getDay()); static::assertSame(Weekday::Sunday, $datetime->getWeekday()); @@ -51,24 +72,99 @@ public function testFromParts(): void static::assertSame(0, $datetime->getMinutes()); static::assertSame(0, $datetime->getSeconds()); static::assertSame(1, $datetime->getNanoseconds()); + static::assertSame([2024, 2, 4, 14, 0, 0, 1,], $datetime->getParts()); } - public function testFromPartsWithInvalidComponent(): void + public function testFromPartsWithDefaults(): void { + $datetime = DateTime::fromParts(Timezone::UTC, 2024, Month::February, 4,); + + static::assertSame(Timezone::UTC, $datetime->getTimezone()); + static::assertSame(2024, $datetime->getYear()); + static::assertSame(2, $datetime->getMonth()); + static::assertSame(4, $datetime->getDay()); + static::assertSame(Weekday::Sunday, $datetime->getWeekday()); + static::assertSame(0, $datetime->getHours()); + static::assertSame(0, $datetime->getMinutes()); + static::assertSame(0, $datetime->getSeconds()); + static::assertSame(0, $datetime->getNanoseconds()); + } + + + /** + * @dataProvider provideInvalidComponentParts + */ + public function testFromPartsWithInvalidComponent( + string $expectedMessage, + int $year, + int $month, + int $day, + int $hours, + int $minutes, + int $seconds, + int $nanoseconds + ): void { $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage('Unexpected hours value encountered. Provided "999", but the calendar expects "15". Ensure the hour falls within a 24-hour day.'); + $this->expectExceptionMessage($expectedMessage); - DateTime::fromParts(Timezone::UTC, 2024, Month::February, 4, 999, 0, 0, 1); + DateTime::fromParts(Timezone::UTC, $year, $month, $day, $hours, $minutes, $seconds, $nanoseconds); } - public function fromString(): void + public static function provideInvalidComponentParts(): array { - $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0); + return [ + ['Unexpected year value encountered. Provided "0", but the calendar expects "1". Check the year for accuracy and ensure it\'s within the supported range.', 0, 1, 1, 0, 0, 0, 0], + ['Unexpected month value encountered. Provided "0", but the calendar expects "12". Ensure the month is within the 1-12 range and matches the specific year context.', 2024, 0, 1, 0, 0, 0, 0], + ['Unexpected day value encountered. Provided "0", but the calendar expects "31". Ensure the day is valid for the given month and year, considering variations like leap years.', 2024, 1, 0, 0, 0, 0, 0], + ['Unexpected hours value encountered. Provided "-1", but the calendar expects "23". Ensure the hour falls within a 24-hour day.', 2024, 1, 1, -1, 0, 0, 0], + ['Unexpected minutes value encountered. Provided "-1", but the calendar expects "59". Check the minutes value for errors and ensure it\'s within the 0-59 range.', 2024, 1, 1, 0, -1, 0, 0], + ['Unexpected seconds value encountered. Provided "59", but the calendar expects "-1". Ensure the seconds are correct and within the 0-59 range.', 2024, 1, 1, 0, 0, -1, 0], + ]; + } + + public function testFromString(): void + { + $timezone = Timezone::EuropeBrussels; + $datetime = DateTime::fromParts($timezone, 2024, Month::February, 4, 14, 0, 0, 0); $string = $datetime->toString(); - $parsed = DateTime::fromString($string); + $parsed = DateTime::fromString($string, timezone: $timezone); static::assertEquals($datetime->getTimestamp(), $parsed->getTimestamp()); + static::assertSame($datetime->getTimezone(), $parsed->getTimezone()); + static::assertSame($string, $parsed->toString()); + } + + public function testToString(): void + { + $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0); + + static::assertSame('4 Feb 2024, 14:00:00', $datetime->toString()); + static::assertSame('04/02/2024, 14:00:00', $datetime->toString(date_style: DateStyle::Short)); + static::assertSame('4 Feb 2024, 14:00:00 Greenwich Mean Time', $datetime->toString(time_style: TimeStyle::Full)); + static::assertSame('4 Feb 2024, 15:00:00', $datetime->toString(timezone: TimeZone::EuropeBrussels)); + + // Formatting depends on version of intl - so compare with intl version instead of hardcoding a label: + static::assertSame( + create_intl_date_formatter(locale: Locale::DutchBelgium)->format($datetime->getTimestamp()->getSeconds()), + $datetime->toString(locale: Locale::DutchBelgium) + ); + } + + public function testFormat(): void + { + $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0); + + static::assertSame('4 Feb 2024, 14:00:00', $datetime->format()); + static::assertSame('02/04/2024', $datetime->format(pattern: FormatPattern::American)); + static::assertSame('02/04/2024', $datetime->format(pattern: FormatPattern::American->value)); + static::assertSame('4 Feb 2024, 15:00:00', $datetime->format(timezone: TimeZone::EuropeBrussels)); + + // Formatting depends on version of intl - so compare with intl version instead of hardcoding a label: + static::assertSame( + create_intl_date_formatter(locale: Locale::DutchBelgium)->format($datetime->getTimestamp()->getSeconds()), + $datetime->toString(locale: Locale::DutchBelgium) + ); } public function testParse(): void @@ -79,6 +175,18 @@ public function testParse(): void $parsed = DateTime::parse($string); static::assertEquals($datetime->getTimestamp(), $parsed->getTimestamp()); + static::assertSame($datetime->getTimezone(), $parsed->getTimezone()); + } + + public function testParseWithTimezone(): void + { + $datetime = DateTime::fromParts(Timezone::AmericaNewYork, 2024, Month::February, 4, 14, 0, 0, 0); + + $string = $datetime->format(); + $parsed = DateTime::parse($string, timezone: TimeZone::AmericaNewYork); + + static::assertEquals($datetime->getTimestamp(), $parsed->getTimestamp()); + static::assertSame($datetime->getTimezone(), $parsed->getTimezone()); } public function testWithDate(): void @@ -130,18 +238,32 @@ public function testGetEra() public function testGetCentury() { - $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0); + static::assertSame(20, DateTime::fromParts(Timezone::default(), 1999, Month::February, 4, 14)->getCentury()); + static::assertSame(21, DateTime::fromParts(Timezone::default(), 2000, Month::February, 4, 14)->getCentury()); + } - static::assertSame(21, $datetime->getCentury()); + public static function provideTwelveHours() + { + yield [0, 12, Meridiem::AnteMeridiem]; + yield [1, 1, Meridiem::AnteMeridiem]; + yield [2, 2, Meridiem::AnteMeridiem]; + yield [11, 11, Meridiem::AnteMeridiem]; + yield [12, 12, Meridiem::PostMeridiem]; + yield [13, 1, Meridiem::PostMeridiem]; + yield [14, 2, Meridiem::PostMeridiem]; + yield [23, 11, Meridiem::PostMeridiem]; } - public function testGetTwelveHours() + /** + * @dataProvider provideTwelveHours + */ + public function testGetTwelveHours(int $hour, $expectedTwelveHour, $expectedMeridiem) { - $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0); + $datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, $hour, 0, 0, 0); [$hours, $meridiem] = $datetime->getTwelveHours(); - static::assertSame(2, $hours); - static::assertSame(Meridiem::PostMeridiem, $meridiem); + static::assertSame($expectedTwelveHour, $hours); + static::assertSame($expectedMeridiem, $meridiem); } public function testGetIsoWeek(): void @@ -178,6 +300,12 @@ public function testPlusMethods(): void $new = $datetime->plusMonths(1); static::assertSame(3, $new->getMonth()); + $new = $datetime->plusMonths(0); + static::assertSame($datetime, $new); + + $new = $datetime->plusMonths(-1); + static::assertSame(1, $new->getMonth()); + $new = $datetime->plusDays(1); static::assertSame(5, $new->getDay()); @@ -242,6 +370,12 @@ public function testMinusMethods(): void $new = $datetime->minusMonths(1); static::assertSame(1, $new->getMonth()); + $new = $datetime->minusMonths(0); + static::assertSame($datetime, $new); + + $new = $datetime->minusMonths(-1); + static::assertSame(3, $new->getMonth()); + $new = $datetime->minusDays(1); static::assertSame(3, $new->getDay()); @@ -363,4 +497,37 @@ public function testJsonSerialize(): void Json\encode($datetime), ); } + + public function testWithTime() + { + $date = DateTime::todayAt(14, 0); + $new = $date->withTime(15, 0); + + static::assertSame(15, $new->getHours()); + static::assertSame(0, $new->getMinutes()); + static::assertSame(0, $new->getSeconds()); + static::assertSame(0, $new->getNanoseconds()); + } + + public function testTimezoneInfo() + { + $timeZone = Timezone::EuropeBrussels; + $date = DateTime::fromParts($timeZone, 2024, 01, 01); + + static::assertSame(!$timeZone->getDaylightSavingTimeOffset($date)->isZero(), $date->isDaylightSavingTime()); + static::assertEquals($timeZone->getOffset($date), $date->getTimezoneOffset()); + } + + public function testConvertTimeZone() + { + $date = DateTime::fromParts(Timezone::EuropeBrussels, 2024, 01, 01, 1); + $converted = $date->convertToTimezone($london = Timezone::EuropeLondon); + + static::assertSame($london, $converted->getTimezone()); + static::assertSame($date->getTimestamp(), $converted->getTimestamp()); + static::assertSame($date->getYear(), $converted->getYear()); + static::assertSame($date->getMonth(), $converted->getMonth()); + static::assertSame($date->getDay(), $converted->getDay()); + static::assertSame(0, $converted->getHours()); + } } diff --git a/tests/unit/DateTime/DurationTest.php b/tests/unit/DateTime/DurationTest.php index 0e76acc5..1274ef2c 100644 --- a/tests/unit/DateTime/DurationTest.php +++ b/tests/unit/DateTime/DurationTest.php @@ -27,6 +27,19 @@ public function testGetters(): void static::assertEquals([1, 2, 3, 4], $t->getParts()); } + public function testNamedConstructors() + { + static::assertSame(168.0, DateTime\Duration::weeks(1)->getTotalHours()); + static::assertSame(24.0, DateTime\Duration::days(1)->getTotalHours()); + static::assertSame(1.0, DateTime\Duration::hours(1)->getTotalHours()); + static::assertSame(1.0, DateTime\Duration::minutes(1)->getTotalMinutes()); + static::assertSame(1.0, DateTime\Duration::seconds(1)->getTotalSeconds()); + static::assertSame(1.0, DateTime\Duration::milliseconds(1)->getTotalMilliseconds()); + static::assertSame(1.0, DateTime\Duration::microseconds(1)->getTotalMicroseconds()); + static::assertSame(1, DateTime\Duration::nanoseconds(1)->getNanoseconds()); + static::assertSame(0.0, DateTime\Duration::zero(1)->getTotalSeconds()); + } + public function provideGetTotalHours(): array { return [ @@ -237,6 +250,9 @@ public function testPositiveNegative(int $h, int $m, int $s, int $ns, int $expec public static function provideCompare(): array { return [ + [DateTime\Duration::seconds(20), DateTime\Duration::seconds(10), Order::Greater], + [DateTime\Duration::seconds(10), DateTime\Duration::seconds(20), Order::Less], + [DateTime\Duration::seconds(10), DateTime\Duration::seconds(10), Order::Equal], [DateTime\Duration::hours(1), DateTime\Duration::minutes(42), Order::Greater], [DateTime\Duration::minutes(2), DateTime\Duration::seconds(120), Order::Equal], [DateTime\Duration::zero(), DateTime\Duration::nanoseconds(1), Order::Less], diff --git a/tests/unit/DateTime/Exception/InvalidArgumentExceptionTest.php b/tests/unit/DateTime/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 00000000..c30e321f --- /dev/null +++ b/tests/unit/DateTime/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,102 @@ +getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForMonth(): void + { + $exception = InvalidArgumentException::forMonth(13); + + static::assertSame( + 'The month \'13\' falls outside the acceptable range of \'1\' to \'12\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForDay(): void + { + $exception = InvalidArgumentException::forDay(32, 1, 2021); + + static::assertSame( + 'The day \'32\', for month \'1\' and year \'2021\', does not align with the expected range of \'1\' to \'31\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForHours(): void + { + $exception = InvalidArgumentException::forHours(24); + + static::assertSame( + 'The hour \'24\' exceeds the expected range of \'0\' to \'23\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForMinutes(): void + { + $exception = InvalidArgumentException::forMinutes(60); + + static::assertSame( + 'The minute \'60\' steps beyond the bounds of \'0\' to \'59\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForSeconds(): void + { + $exception = InvalidArgumentException::forSeconds(61); + + static::assertSame( + 'The seconds \'61\' stretch outside the acceptable range of \'0\' to \'59\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } + + public function testForNanoseconds(): void + { + $exception = InvalidArgumentException::forNanoseconds(1_000_000_000); + + static::assertSame( + 'The nanoseconds \'1000000000\' exceed the foreseen limit of \'0\' to \'999999999\'.', + $exception->getMessage() + ); + + $this->expectExceptionObject($exception); + throw $exception; + } +} diff --git a/tests/unit/DateTime/TimestampTest.php b/tests/unit/DateTime/TimestampTest.php index 716cedeb..4ab68b15 100644 --- a/tests/unit/DateTime/TimestampTest.php +++ b/tests/unit/DateTime/TimestampTest.php @@ -13,6 +13,7 @@ use Psl\DateTime\Exception\ParserException; use Psl\DateTime\Exception\UnderflowException; use Psl\DateTime\FormatPattern; +use Psl\DateTime\SecondsStyle; use Psl\DateTime\Timestamp; use Psl\DateTime\Timezone; use Psl\Locale\Locale; @@ -53,6 +54,17 @@ public function testMonotonicIsPrecise(): void static::assertGreaterThan(100.0, $difference->getTotalMilliseconds()); } + public function testSince() + { + $a = Timestamp::fromParts(20, 1); + $b = Timestamp::fromParts(30, 2); + + $duration = $b->since($a); + + static::assertSame(10, $duration->getSeconds()); + static::assertSame(1, $duration->getNanoseconds()); + } + public function testFromRowOverflow(): void { $this->expectException(OverflowException::class); @@ -189,6 +201,11 @@ public static function provideCompare(): array [Timestamp::fromParts(100), Timestamp::fromParts(42), Order::Greater], [Timestamp::fromParts(42), Timestamp::fromParts(42), Order::Equal], [Timestamp::fromParts(42), Timestamp::fromParts(100), Order::Less], + + // Nanoseconds + [Timestamp::fromParts(42, 100), Timestamp::fromParts(42, 42), Order::Greater], + [Timestamp::fromParts(42, 42), Timestamp::fromParts(42, 42), Order::Equal], + [Timestamp::fromParts(42, 42), Timestamp::fromParts(42, 100), Order::Less], ]; } /** @@ -403,4 +420,13 @@ public function testJsonSerialization(): void static::assertSame(1711917232, $serialized['seconds']); static::assertSame(12, $serialized['nanoseconds']); } + + public function testToRfc3999(): void + { + $timestamp = Timestamp::fromParts(1711917232, 12); + + static::assertSame('2024-03-31T20:33:52.12+00:00', $timestamp->toRfc3339()); + static::assertSame('2024-03-31T20:33:52+00:00', $timestamp->toRfc3339(seconds_style: SecondsStyle::Seconds)); + static::assertSame('2024-03-31T20:33:52.12Z', $timestamp->toRfc3339(use_z: true)); + } } diff --git a/tests/unit/DateTime/TimezoneTest.php b/tests/unit/DateTime/TimezoneTest.php index 991843ff..d3d1c7e7 100644 --- a/tests/unit/DateTime/TimezoneTest.php +++ b/tests/unit/DateTime/TimezoneTest.php @@ -5,6 +5,7 @@ namespace Psl\Tests\Unit\DateTime; use PHPUnit\Framework\TestCase; +use Psl\DateTime\DateTime; use Psl\DateTime\Timestamp; use Psl\DateTime\Timezone; @@ -31,6 +32,15 @@ public function testGetOffset(): void static::assertSame(-12600., Timezone::Minus0330->getOffset($temporal)->getTotalSeconds()); static::assertSame(3600., Timezone::Plus0100->getOffset($temporal)->getTotalSeconds()); static::assertSame(-3600., Timezone::Minus0100->getOffset($temporal)->getTotalSeconds()); + + // Local + $brussels = Timezone::EuropeBrussels; + date_default_timezone_set($brussels->value); + + $summer = DateTime::fromParts($brussels, 2024, 3, 31, 3); + + static::assertSame(2., $brussels->getOffset($summer)->getTotalHours()); + static::assertSame(1., $brussels->getOffset($summer, local: true)->getTotalHours()); } /** @@ -61,6 +71,20 @@ public function testHasTheSameRulesAs(): void static::assertFalse(Timezone::AmericaNewYork->hasTheSameRulesAs(Timezone::EuropeLondon)); } + public function testGetDaylightSavingTimeOffset(): void + { + $brussels = Timezone::EuropeBrussels; + date_default_timezone_set($brussels->value); + + $summer = DateTime::fromParts($brussels, 2024, 3, 31, 3); + $winter = DateTime::fromParts($brussels, 2024, 10, 27, 2); + + static::assertSame(0., $brussels->getDaylightSavingTimeOffset($winter)->getTotalHours()); + static::assertSame(1., $brussels->getDaylightSavingTimeOffset($winter, local: true)->getTotalHours()); + static::assertSame(1., $brussels->getDaylightSavingTimeOffset($summer)->getTotalHours()); + static::assertSame(0., $brussels->getDaylightSavingTimeOffset($summer, local: true)->getTotalHours()); + } + public static function provideRawOffsetData(): iterable { yield [Timezone::EuropeLondon, 0];