[
  {
    "path": ".gitattributes",
    "content": "*.php text eol=lf\n*.phpt text eol=lf\n/composer.lock export-ignore\n/tools/ export-ignore\n/.github/ export-ignore\n/docs export-ignore\n/tests export-ignore\n/.phive export-ignore\n/benchmark export-ignore\n/.gitattributes export-ignore\n/.gitignore export-ignore\n/.php_cs export-ignore\n/phpstan.neon export-ignore\n/phpunit.xml export-ignore\n/psalm.xml export-ignore\n/infection.json export-ignore\n/phpbench.json export-ignore"
  },
  {
    "path": ".github/workflows/readonly.yaml",
    "content": "name: Readonly\n\non:\n  pull_request_target:\n    types: [opened]\n\njobs:\n  run:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: superbrothers/close-pull-request@v3\n        with:\n          comment: |\n            Hi, thank you for your contribution.\n            Unfortunately, this repository is read-only. It's a split from our main monorepo repository.\n            In order to proceed with this PR please open it against https://github.com/aeon-php/aeon repository.\n            Thank you.\n"
  },
  {
    "path": ".gitignore",
    "content": "vendor\n*.cache\nvar"
  },
  {
    "path": "README.md",
    "content": "# Aeon\n\n[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg)](https://php.net/)\n[![Latest Stable Version](https://poser.pugx.org/aeon-php/calendar/v)](https://packagist.org/packages/aeon-php/calendar)\n[![Latest Unstable Version](https://poser.pugx.org/aeon-php/calendar/v/unstable)](https://packagist.org/packages/aeon-php/calendar)\n[![License](https://poser.pugx.org/aeon-php/calendar/license)](https://packagist.org/packages/aeon-php/calendar)\n![Tests](https://github.com/aeon-php/calendar/workflows/Tests/badge.svg?branch=1.x)\n[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Faeon-php%2Fcalendar%2F1.x)](https://dashboard.stryker-mutator.io/reports/github.com/aeon-php/calendar/1.x)\n\nTime Management Framework for PHP\n\n> The word aeon /ˈiːɒn/, also spelled eon (in American English), originally meant \"life\", \"vital force\" or \"being\", \n> \"generation\" or \"a period of time\", though it tended to be translated as \"age\" in the sense of \"ages\", \"forever\", \n> \"timeless\" or \"for eternity\".\n\n[Source: Wikipedia](https://en.wikipedia.org/wiki/Aeon) \n\nAeon is a set of libraries that makes easier to work with PHP Date & Time in elegant Object Oriented way.\n\n* [Documentation & Examples](https://aeon-php.org/docs/calendar/)\n* [Contributing & Development](https://github.com/aeon-php/.github/blob/master/CONTRIBUTING.md)\n* [Community](https://github.com/orgs/aeon-php/discussions)\n\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/DateTimeFormatBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\n\n/**\n * @revs(50)\n *\n * @iterations(10)\n *\n * @outputTimeUnit(\"milliseconds\")\n *\n * @BeforeMethods({\"init\"})\n */\nfinal class DateTimeFormatBench\n{\n    private DateTime $aeonDateTime;\n\n    private \\DateTimeImmutable $dateTime;\n\n    public function init() : void\n    {\n        $this->aeonDateTime = DateTime::fromString('2020-01-01 00:00:00');\n        $this->dateTime = new \\DateTimeImmutable('2020-01-01 00:00:00');\n    }\n\n    public function bench_aeon_format() : void\n    {\n        $this->aeonDateTime->format('Y-m-d H:i:s.u P');\n    }\n\n    public function bench_php_format() : void\n    {\n        $this->dateTime->format('Y-m-d H:i:s.u P');\n    }\n}\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/DateTimeInitBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Time;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse Aeon\\Calendar\\Gregorian\\Year;\n\n/**\n * @revs(50)\n *\n * @iterations(10)\n *\n * @outputTimeUnit(\"milliseconds\")\n */\nfinal class DateTimeInitBench\n{\n    public function bench_datetime_immutable_constructor() : void\n    {\n        new \\DateTimeImmutable('2020-01-01 00:00:00.00000 UTC');\n    }\n\n    public function bench_datetime_constructor() : void\n    {\n        new \\DateTime('2020-01-01 00:00:00.00000 UTC');\n    }\n\n    public function bench_aeon_datetime_from_datetime_immutable() : void\n    {\n        DateTime::fromDateTime(new \\DateTimeImmutable('2020-01-01 00:00:00.00000 UTC'));\n    }\n\n    public function bench_aeon_datetime_create() : void\n    {\n        DateTime::create(2020, 01, 01, 00, 00, 00, 0, 'UTC');\n    }\n\n    public function bench_aeon_datetime_constructor() : void\n    {\n        new DateTime(\n            new Day(new Month(new Year(2020), 01), 01),\n            new Time(00, 00, 00, 0),\n            TimeZone::fromString('UTC')\n        );\n    }\n\n    public function bench_aeon_datetime_from_string() : void\n    {\n        DateTime::fromString('2020-01-01 00:00:00.00000 UTC');\n    }\n}\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/DateTimeUnixTimestampBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\n\n/**\n * @revs(50)\n *\n * @iterations(10)\n *\n * @outputTimeUnit(\"milliseconds\")\n */\nfinal class DateTimeUnixTimestampBench\n{\n    private DateTime $aeonDateTime;\n\n    private \\DateTimeImmutable $dateTime;\n\n    public function __construct()\n    {\n        $this->aeonDateTime = DateTime::fromString('2020-01-01 00:00:00');\n        $this->dateTime = new \\DateTimeImmutable('2020-01-01 00:00:00');\n    }\n\n    public function bench_aeon_unix_timestamp() : void\n    {\n        $this->aeonDateTime->timestampUNIX();\n    }\n\n    public function bench_php_unix_timestamp() : void\n    {\n        $this->dateTime->getTimestamp();\n    }\n}\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/DayIterationBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\GregorianCalendar;\nuse Aeon\\Calendar\\Gregorian\\Interval;\n\n/**\n * @revs(50)\n *\n * @iterations(10)*\n *\n * @outputTimeUnit(\"milliseconds\")\n */\nfinal class DayIterationBench\n{\n    public function bench_aeon_iteration_over_last_half_of_the_year() : void\n    {\n        $end = GregorianCalendar::UTC()->currentDay();\n        $start = $end->subMonths(6);\n\n        $days = [];\n\n        foreach ($start->until($end, Interval::rightOpen())->all() as $nextDay) {\n            $days[] = $nextDay->format('Y-m-d');\n        }\n    }\n\n    public function bench_php_iteration_over_last_half_of_the_year() : void\n    {\n        \\date_default_timezone_set('UTC');\n\n        $end = new \\DateTime('today');\n        $start = new \\DateTime('-6 months');\n\n        $interval = \\DateInterval::createFromDateString('1 day');\n        $period = new \\DatePeriod($start, $interval, $end);\n\n        $days = [];\n\n        foreach ($period as $nextDay) {\n            $days[] = $nextDay->format('Y-m-d');\n        }\n    }\n}\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/TimeUnitInitBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @revs(50)\n *\n * @iterations(10)\n *\n * @outputTimeUnit(\"milliseconds\")\n */\nfinal class TimeUnitInitBench\n{\n    public function bench_time_unit_positive() : void\n    {\n        TimeUnit::positive(100, 50000);\n    }\n\n    public function bench_time_unit_negative() : void\n    {\n        TimeUnit::positive(100, 50000);\n    }\n}\n"
  },
  {
    "path": "benchmark/Aeon/Calendar/Benchmark/Gregorian/TimeZoneInitBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Benchmark\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\n\n/**\n * @revs(50)\n *\n * @iterations(10)\n *\n * @outputTimeUnit(\"milliseconds\")\n */\nfinal class TimeZoneInitBench\n{\n    public function bench_time_zone_init_by_static_name() : void\n    {\n        TimeZone::europeWarsaw();\n    }\n\n    public function bench_time_zone_init_by_abbreviation() : void\n    {\n        TimeZone::abbreviation('CEST');\n    }\n\n    public function bench_time_zone_init_by_id() : void\n    {\n        TimeZone::id('Europe/Warsaw');\n    }\n\n    public function bench_time_zone_init_by_offset() : void\n    {\n        TimeZone::offset('+01:00');\n    }\n\n    public function bench_time_zone_init_by_string() : void\n    {\n        TimeZone::fromString('Europe/Warsaw');\n    }\n\n    public function bench_php_datetime_zone_constructor() : void\n    {\n        new \\DateTimeZone('Europe/Warsaw');\n    }\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"aeon-php/calendar\",\n    \"type\": \"library\",\n    \"description\": \"PHP type safe, immutable calendar library\",\n    \"keywords\": [\n        \"calendar\",\n        \"immutable\",\n        \"date\",\n        \"time\",\n        \"datetime\"\n    ],\n    \"require\": {\n        \"php\": \"~8.3.0 || ~8.4.0 || ~8.5.0\"\n    },\n    \"require-dev\": {\n        \"ext-bcmath\": \"*\",\n        \"ext-pcov\": \"*\"\n    },\n    \"suggest\": {\n        \"ext-bcmath\": \"Compare time units with high precision\"\n    },\n    \"config\": {\n        \"optimize-autoloader\": true,\n        \"sort-packages\": true\n    },\n    \"license\": \"MIT\",\n    \"autoload\": {\n        \"psr-4\": {\n            \"Aeon\\\\\": [\n                \"src/Aeon\"\n            ]\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Aeon\\\\Calendar\\\\Tests\\\\\": \"tests/Aeon/Calendar/Tests/\",\n            \"Aeon\\\\Calculator\\\\Tests\\\\\": \"tests/Aeon/Calculator/Tests/\",\n            \"Aeon\\\\Calendar\\\\Benchmark\\\\\": \"benchmark/Aeon/Calendar/Benchmark/\"\n        }\n    },\n    \"prefer-stable\": true\n}\n"
  },
  {
    "path": "phpbench.json",
    "content": "{\n  \"runner.bootstrap\": \"./vendor/autoload.php\",\n  \"runner.path\": \"benchmark\",\n  \"runner.retry_threshold\": 5,\n  \"runner.progress\": \"dots\",\n  \"report.generators\": {\n    \"aeon\": {\n      \"extends\": \"default\",\n      \"break\": [\"benchmark\"]\n    }\n  }\n}"
  },
  {
    "path": "src/Aeon/Calculator/BCMathCalculator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator;\n\nuse Aeon\\Calculator\\Exception\\InvalidTypeException;\n\n/**\n * @psalm-immutable\n */\nfinal class BCMathCalculator implements Calculator\n{\n    private int $precision;\n\n    public function __construct(int $precision)\n    {\n        $this->precision = $precision;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function supported() : bool\n    {\n        return \\extension_loaded('bcmath');\n    }\n\n    public function precision() : int\n    {\n        return $this->precision;\n    }\n\n    /**\n     * @psalm-suppress InvalidNullableReturnType\n     * @psalm-suppress NullableReturnStatement\n     */\n    public function divide(string $value, string $divisor) : string\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($divisor)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        if (\\floatval($divisor) === \\floatval('0')) {\n            throw new \\LogicException(\"Divisor can't be 0\");\n        }\n\n        return \\bcdiv($value, $divisor, $this->precision);\n    }\n\n    /**\n     * @psalm-suppress InvalidNullableReturnType\n     * @psalm-suppress NullableReturnStatement\n     */\n    public function modulo(string $value, string $divisor) : string\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($divisor)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        if (\\floatval($divisor) === \\floatval('0')) {\n            throw new \\LogicException(\"Divisor can't be 0\");\n        }\n\n        return \\bcmod($value, $divisor, $this->precision);\n    }\n\n    public function multiply(string $value, string $multiplier) : string\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($multiplier)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bcmul($value, $multiplier, $this->precision);\n    }\n\n    public function add(string $value, string $nextValue) : string\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bcadd($value, $nextValue, $this->precision);\n    }\n\n    public function sub(string $value, string $nextValue) : string\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bcsub($value, $nextValue, $this->precision);\n    }\n\n    public function isGreaterThan(string $value, string $nextValue) : bool\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bccomp($value, $nextValue, $this->precision) === 1;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isGreaterThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isGreaterThanEq(string $value, string $nextValue) : bool\n    {\n        return $this->isGreaterThanOrEqualTo($value, $nextValue);\n    }\n\n    public function isGreaterThanOrEqualTo(string $value, string $nextValue) : bool\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\in_array(\\bccomp($value, $nextValue, $this->precision), [0, 1], true);\n    }\n\n    public function isLessThan(string $value, string $nextValue) : bool\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bccomp($value, $nextValue, $this->precision) === -1;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isLessThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isLessThanEq(string $value, string $nextValue) : bool\n    {\n        return $this->isLessThanOrEqualTo($value, $nextValue);\n    }\n\n    public function isLessThanOrEqualTo(string $value, string $nextValue) : bool\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\in_array(\\bccomp($value, $nextValue, $this->precision), [-1, 0], true);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(string $value, string $nextValue) : bool\n    {\n        return $this->isEqualTo($value, $nextValue);\n    }\n\n    public function isEqualTo(string $value, string $nextValue) : bool\n    {\n        if (!\\is_numeric($value) || !\\is_numeric($nextValue)) {\n            throw new InvalidTypeException('Expected values to be numeric string');\n        }\n\n        return \\bccomp($value, $nextValue, $this->precision) === 0;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calculator/Calculator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator;\n\n/**\n * @psalm-immutable\n */\ninterface Calculator\n{\n    public function precision() : int;\n\n    public function modulo(string $value, string $divisor) : string;\n\n    public function divide(string $value, string $divisor) : string;\n\n    public function multiply(string $value, string $multiplier) : string;\n\n    public function add(string $value, string $nextValue) : string;\n\n    public function sub(string $value, string $nextValue) : string;\n\n    public function isGreaterThan(string $value, string $nextValue) : bool;\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isGreaterThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isGreaterThanEq(string $value, string $nextValue) : bool;\n\n    public function isGreaterThanOrEqualTo(string $value, string $nextValue) : bool;\n\n    public function isLessThan(string $value, string $nextValue) : bool;\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isLessThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isLessThanEq(string $value, string $nextValue) : bool;\n\n    public function isLessThanOrEqualTo(string $value, string $nextValue) : bool;\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(string $value, string $nextValue) : bool;\n\n    public function isEqualTo(string $value, string $nextValue) : bool;\n}\n"
  },
  {
    "path": "src/Aeon/Calculator/Exception/Exception.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator\\Exception;\n\nclass Exception extends \\Exception\n{\n}\n"
  },
  {
    "path": "src/Aeon/Calculator/Exception/InvalidTypeException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator\\Exception;\n\nfinal class InvalidTypeException extends Exception\n{\n}\n"
  },
  {
    "path": "src/Aeon/Calculator/PHPCalculator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator;\n\n/**\n * @psalm-immutable\n */\nfinal class PHPCalculator implements Calculator\n{\n    private int $precision;\n\n    public function __construct(int $precision)\n    {\n        $this->precision = $precision;\n    }\n\n    public function precision() : int\n    {\n        return $this->precision;\n    }\n\n    public function divide(string $value, string $divisor) : string\n    {\n        return \\number_format(\\floatval($value) / \\floatval($divisor), $this->precision, '.', '');\n    }\n\n    public function modulo(string $value, string $divisor) : string\n    {\n        return \\number_format(\\fmod(\\floatval($value), \\floatval($divisor)), $this->precision, '.', '');\n    }\n\n    public function multiply(string $value, string $multiplier) : string\n    {\n        return \\number_format(\\floatval($value) * \\floatval($multiplier), $this->precision, '.', '');\n    }\n\n    public function add(string $value, string $nextValue) : string\n    {\n        return \\number_format(\\floatval($value) + \\floatval($nextValue), $this->precision, '.', '');\n    }\n\n    public function sub(string $value, string $nextValue) : string\n    {\n        return \\number_format(\\floatval($value) - \\floatval($nextValue), $this->precision, '.', '');\n    }\n\n    public function isGreaterThan(string $value, string $nextValue) : bool\n    {\n        return \\floatval(\\number_format(\\floatval($value), $this->precision, '.', '')) >\n            \\floatval(\\number_format(\\floatval($nextValue), $this->precision, '.', ''));\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isGreaterThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isGreaterThanEq(string $value, string $nextValue) : bool\n    {\n        return $this->isGreaterThanOrEqualTo($value, $nextValue);\n    }\n\n    public function isGreaterThanOrEqualTo(string $value, string $nextValue) : bool\n    {\n        return \\floatval(\\number_format(\\floatval($value), $this->precision, '.', '')) >=\n            \\floatval(\\number_format(\\floatval($nextValue), $this->precision, '.', ''));\n    }\n\n    public function isLessThan(string $value, string $nextValue) : bool\n    {\n        return \\floatval(\\number_format(\\floatval($value), $this->precision, '.', '')) <\n            \\floatval(\\number_format(\\floatval($nextValue), $this->precision, '.', ''));\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isLessThanOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isLessThanEq(string $value, string $nextValue) : bool\n    {\n        return $this->isLessThanOrEqualTo($value, $nextValue);\n    }\n\n    public function isLessThanOrEqualTo(string $value, string $nextValue) : bool\n    {\n        return \\floatval(\\number_format(\\floatval($value), $this->precision, '.', '')) <=\n            \\floatval(\\number_format(\\floatval($nextValue), $this->precision, '.', ''));\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(string $value, string $nextValue) : bool\n    {\n        return $this->isEqualTo($value, $nextValue);\n    }\n\n    public function isEqualTo(string $value, string $nextValue) : bool\n    {\n        return \\floatval(\\number_format(\\floatval($value), $this->precision, '.', '')) ==\n            \\floatval(\\number_format(\\floatval($nextValue), $this->precision, '.', ''));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calculator/PreciseCalculator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator;\n\nfinal class PreciseCalculator\n{\n    private static ?Calculator $instance = null;\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureStaticProperty\n     */\n    public static function initialize(int $precision) : Calculator\n    {\n        if (self::$instance instanceof Calculator && self::$instance->precision() === $precision) {\n            return self::$instance;\n        }\n\n        if (BCMathCalculator::supported()) {\n            self::$instance = new BCMathCalculator($precision);\n\n            return self::$instance;\n        }\n\n        // @codeCoverageIgnoreStart\n        self::$instance = new PHPCalculator($precision);\n\n        return self::$instance;\n        // @codeCoverageIgnoreEnd\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Exception/Exception.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Exception;\n\nclass Exception extends \\Exception\n{\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Exception/InvalidArgumentException.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Exception;\n\nfinal class InvalidArgumentException extends Exception\n{\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Calendar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n */\ninterface Calendar\n{\n    public function timeZone() : TimeZone;\n\n    public function currentYear() : Year;\n\n    public function currentMonth() : Month;\n\n    public function currentDay() : Day;\n\n    public function yesterday() : DateTime;\n\n    public function tomorrow() : DateTime;\n\n    public function now() : DateTime;\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/DateTime.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\nuse Aeon\\Calendar\\Unit;\n\n/**\n * @psalm-immutable\n */\nfinal class DateTime\n{\n    private Day $day;\n\n    private Time $time;\n\n    private TimeZone $timeZone;\n\n    public function __construct(Day $day, Time $time, TimeZone $timeZone)\n    {\n        $this->day = $day;\n        $this->time = $time;\n        $this->timeZone = $timeZone;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function create(int $year, int $month, int $day, int $hour, int $minute, int $second, int $microsecond = 0, string $timezone = 'UTC') : self\n    {\n        return new self(\n            new Day(\n                new Month(\n                    new Year($year),\n                    $month\n                ),\n                $day\n            ),\n            new Time($hour, $minute, $second, $microsecond),\n            TimeZone::fromString($timezone)\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     * @psalm-suppress ImpureStaticProperty\n     * @psalm-suppress PropertyTypeCoercion\n     * @psalm-suppress ImpurePropertyAssignment\n     * @psalm-suppress InaccessibleProperty\n     */\n    public static function fromDateTime(\\DateTimeInterface $dateTime) : self\n    {\n        $day = Day::fromDateTime($dateTime);\n        $time = Time::fromDateTime($dateTime);\n        $timeZone = TimeZone::fromDateTimeZone($dateTime->getTimezone());\n\n        return new self(\n            $day,\n            $time,\n            $timeZone,\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureFunctionCall\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromString(string $date) : self\n    {\n        $currentPHPTimeZone = \\date_default_timezone_get();\n        \\date_default_timezone_set('UTC');\n\n        try {\n            $dateTime = self::fromDateTime(new \\DateTimeImmutable($date));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"Value \\\"{$date}\\\" is not valid date time format.\");\n        }\n\n        if ($currentPHPTimeZone !== 'UTC') {\n            \\date_default_timezone_set($currentPHPTimeZone);\n        }\n\n        return $dateTime;\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureFunctionCall\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromTimestampUnix(int $timestamp) : self\n    {\n        $currentPHPTimeZone = \\date_default_timezone_get();\n\n        \\date_default_timezone_set('UTC');\n\n        $dateTime = self::fromDateTime((new \\DateTimeImmutable)->setTimestamp($timestamp));\n\n        if ($currentPHPTimeZone !== 'UTC') {\n            \\date_default_timezone_set($currentPHPTimeZone);\n        }\n\n        return $dateTime;\n    }\n\n    public function __toString() : string\n    {\n        return $this->toISO8601();\n    }\n\n    /**\n     * @return array{datetime: string, day: Day, time: Time, timeZone: TimeZone}\n     */\n    public function __debugInfo() : array\n    {\n        return [\n            'datetime' => $this->toISO8601(),\n            'day' => $this->day,\n            'time' => $this->time,\n            'timeZone' => $this->timeZone,\n        ];\n    }\n\n    /**\n     * @return array{day: Day, time: Time, timeZone: TimeZone}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'day' => $this->day,\n            'time' => $this->time,\n            'timeZone' => $this->timeZone,\n        ];\n    }\n\n    /**\n     * @param array{day: Day, time: Time, timeZone: TimeZone} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->day = $data['day'];\n        $this->time = $data['time'];\n        $this->timeZone = $data['timeZone'];\n    }\n\n    public function year() : Year\n    {\n        return $this->month()->year();\n    }\n\n    public function month() : Month\n    {\n        return $this->day->month();\n    }\n\n    public function day() : Day\n    {\n        return $this->day;\n    }\n\n    public function time() : Time\n    {\n        return $this->time;\n    }\n\n    public function setTime(Time $time) : self\n    {\n        return new self(\n            $this->day,\n            $time,\n            $this->timeZone\n        );\n    }\n\n    public function setTimeIn(Time $time, TimeZone $timeZone) : self\n    {\n        return (new self(\n            $this->day,\n            $time,\n            $this->timeZone\n        ))->toTimeZone($timeZone)\n            ->setTime($time);\n    }\n\n    public function setDay(Day $day) : self\n    {\n        return new self(\n            $day,\n            $this->time,\n            $this->timeZone\n        );\n    }\n\n    public function toDateTimeImmutable() : \\DateTimeImmutable\n    {\n        return new \\DateTimeImmutable(\n            \\sprintf(\n                '%d-%02d-%02d %02d:%02d:%02d.%06d',\n                $this->day->year()->number(),\n                $this->day->month()->number(),\n                $this->day->number(),\n                $this->time->hour(),\n                $this->time->minute(),\n                $this->time->second(),\n                $this->time->microsecond(),\n            ),\n            $this->timeZone->toDateTimeZone()\n        );\n    }\n\n    public function toAtomicTime() : self\n    {\n        return $this->add(LeapSeconds::load()->until($this)->offsetTAI());\n    }\n\n    public function toGPSTime() : self\n    {\n        return $this->add(LeapSeconds::load()\n            ->since(TimeEpoch::GPS()->date())\n            ->until($this)->count());\n    }\n\n    public function format(string $format) : string\n    {\n        return $this->toDateTimeImmutable()->format($format);\n    }\n\n    public function timeZone() : TimeZone\n    {\n        return $this->timeZone;\n    }\n\n    public function timeZoneAbbreviation() : TimeZone\n    {\n        if ($this->timeZone->isOffset()) {\n            throw new Exception(\"TimeZone offset {$this->timeZone->name()} can't be converted into abbreviation.\");\n        }\n\n        return TimeZone::fromString($this->toDateTimeImmutable()->format('T'));\n    }\n\n    public function toTimeZone(TimeZone $dateTimeZone) : self\n    {\n        return self::fromDateTime($this->toDateTimeImmutable()->setTimezone($dateTimeZone->toDateTimeZone()));\n    }\n\n    public function toISO8601(bool $extended = true) : string\n    {\n        return $extended\n            ? $this->toDateTimeImmutable()->format('Y-m-d\\TH:i:sP')\n            : $this->toDateTimeImmutable()->format('Ymd\\THisO');\n    }\n\n    public function isDaylightSaving() : bool\n    {\n        return (bool) $this->toDateTimeImmutable()->format('I');\n    }\n\n    public function isDaylight() : bool\n    {\n        return !$this->isDaylightSaving();\n    }\n\n    public function timestamp(TimeEpoch $timeEpoch) : TimeUnit\n    {\n        if ($this->isBefore($timeEpoch->date())) {\n            throw new Exception('Given epoch started at ' . $timeEpoch->date()->toISO8601() . ' which was after ' . $this->toISO8601());\n        }\n\n        switch ($timeEpoch->type()) {\n            case TimeEpoch::UTC:\n                return $this->timestampUNIX()\n                    ->sub(TimeEpoch::UNIX()->distanceTo($timeEpoch))\n                    ->add(LeapSeconds::load()->offsetTAI());\n            case TimeEpoch::GPS:\n                return $this->timestampUNIX()\n                    ->sub(TimeEpoch::UNIX()->distanceTo($timeEpoch))\n                    ->add(\n                        LeapSeconds::load()->offsetTAI()\n                            ->sub(LeapSeconds::load()->until($timeEpoch->date())->offsetTAI())\n                    );\n            case TimeEpoch::TAI:\n                return $timeEpoch->date()->until($this)->distance()\n                    ->add($timeEpoch->date()->until($this)->leapSeconds()->offsetTAI());\n\n            default:\n                return $this->timestampUNIX();\n        }\n    }\n\n    /**\n     * Number of seconds elapsed since Unix epoch started at 1970-01-01 (1970-01-01T00:00:00Z)\n     * Not including leap seconds.\n     */\n    public function timestampUNIX() : TimeUnit\n    {\n        $timestamp = $this->toDateTimeImmutable()->getTimestamp();\n\n        if ($timestamp >= 0) {\n            return TimeUnit::positive($timestamp, $this->time->microsecond());\n        }\n\n        return TimeUnit::negative(\\abs($timestamp), $this->time->microsecond());\n    }\n\n    public function modify(string $modifier) : self\n    {\n        $dateTimeParts = \\date_parse($modifier);\n\n        if (!\\is_array($dateTimeParts)) {\n            throw new InvalidArgumentException('Value \"{modify}\" is not valid relative time format.');\n        }\n\n        if ($dateTimeParts['error_count'] > 0) {\n            throw new InvalidArgumentException(\"Value \\\"{$modifier}\\\" is not valid relative time format.\");\n        }\n\n        if (!isset($dateTimeParts['relative']) || !\\is_array($dateTimeParts['relative'])) {\n            throw new InvalidArgumentException(\"Value \\\"{$modifier}\\\" is not valid relative time format.\");\n        }\n\n        $dateTime = $this;\n\n        if (\n            !\\is_int($dateTimeParts['relative']['year']) ||\n            !\\is_int($dateTimeParts['relative']['month']) ||\n            !\\is_int($dateTimeParts['relative']['day']) ||\n            !\\is_int($dateTimeParts['relative']['hour']) ||\n            !\\is_int($dateTimeParts['relative']['minute']) ||\n            !\\is_int($dateTimeParts['relative']['second'])\n        ) {\n            throw new InvalidArgumentException(\"Value \\\"{$modifier}\\\" is not valid relative time format.\");\n        }\n\n        if ($dateTimeParts['relative']['year'] !== 0) {\n            $dateTime = $dateTime->add(RelativeTimeUnit::years($dateTimeParts['relative']['year']));\n        }\n\n        if ($dateTimeParts['relative']['month'] !== 0) {\n            $dateTime = $dateTime->add(RelativeTimeUnit::months($dateTimeParts['relative']['month']));\n        }\n\n        if (\\array_key_exists('weekday', $dateTimeParts['relative'])) {\n            if ($dateTimeParts['relative']['weekday'] !== 0) {\n                $dateTimeWeekDay = $dateTime->day()->weekDay();\n\n                if ($dateTime->day()->weekDay()->number() < (int) $dateTimeParts['relative']['weekday']) {\n                    $dateTime = $dateTime->add(TimeUnit::days((int) $dateTimeParts['relative']['weekday'] - $dateTimeWeekDay->number()));\n                }\n            }\n        }\n\n        return $dateTime->add(\n            TimeUnit::days($dateTimeParts['relative']['day'])\n                ->add(TimeUnit::hours($dateTimeParts['relative']['hour']))\n                ->add(TimeUnit::minutes($dateTimeParts['relative']['minute']))\n                ->add(TimeUnit::seconds($dateTimeParts['relative']['second']))\n        );\n    }\n\n    public function addHour() : self\n    {\n        return $this->add(TimeUnit::hour());\n    }\n\n    public function subHour() : self\n    {\n        return $this->sub(TimeUnit::hour());\n    }\n\n    public function addHours(int $hours) : self\n    {\n        return $this->add(TimeUnit::hours($hours));\n    }\n\n    public function subHours(int $hours) : self\n    {\n        return $this->sub(TimeUnit::hours($hours));\n    }\n\n    public function addMinute() : self\n    {\n        return $this->add(TimeUnit::minute());\n    }\n\n    public function subMinute() : self\n    {\n        return $this->sub(TimeUnit::minute());\n    }\n\n    public function addMinutes(int $minutes) : self\n    {\n        return $this->add(TimeUnit::minutes($minutes));\n    }\n\n    public function subMinutes(int $minutes) : self\n    {\n        return $this->sub(TimeUnit::minutes($minutes));\n    }\n\n    public function addSecond() : self\n    {\n        return $this->add(TimeUnit::second());\n    }\n\n    public function subSecond() : self\n    {\n        return $this->sub(TimeUnit::second());\n    }\n\n    public function addSeconds(int $seconds) : self\n    {\n        return $this->add(TimeUnit::seconds($seconds));\n    }\n\n    public function subSeconds(int $seconds) : self\n    {\n        return $this->sub(TimeUnit::seconds($seconds));\n    }\n\n    public function addDay() : self\n    {\n        return $this->add(TimeUnit::day());\n    }\n\n    public function subDay() : self\n    {\n        return $this->sub(TimeUnit::day());\n    }\n\n    public function addDays(int $days) : self\n    {\n        return $this->add(TimeUnit::days($days));\n    }\n\n    public function subDays(int $days) : self\n    {\n        return $this->sub(TimeUnit::days($days));\n    }\n\n    public function addMonth() : self\n    {\n        return $this->add(RelativeTimeUnit::month());\n    }\n\n    public function subMonth() : self\n    {\n        return $this->sub(RelativeTimeUnit::month());\n    }\n\n    public function addMonths(int $months) : self\n    {\n        return $this->add(RelativeTimeUnit::months($months));\n    }\n\n    public function subMonths(int $months) : self\n    {\n        return $this->sub(RelativeTimeUnit::months($months));\n    }\n\n    public function addYear() : self\n    {\n        return $this->add(RelativeTimeUnit::year());\n    }\n\n    public function subYear() : self\n    {\n        return $this->sub(RelativeTimeUnit::year());\n    }\n\n    public function addYears(int $years) : self\n    {\n        return $this->add(RelativeTimeUnit::years($years));\n    }\n\n    public function subYears(int $years) : self\n    {\n        return $this->sub(RelativeTimeUnit::years($years));\n    }\n\n    public function midnight() : self\n    {\n        return new self(\n            $this->day,\n            new Time(0, 0, 0, 0),\n            $this->timeZone\n        );\n    }\n\n    public function noon() : self\n    {\n        return new self(\n            $this->day,\n            new Time(12, 0, 0, 0),\n            $this->timeZone\n        );\n    }\n\n    public function endOfDay() : self\n    {\n        return new self(\n            $this->day,\n            new Time(23, 59, 59, 999999),\n            $this->timeZone\n        );\n    }\n\n    public function yesterday() : self\n    {\n        return $this->sub(TimeUnit::day())->midnight();\n    }\n\n    public function tomorrow() : self\n    {\n        return $this->add(TimeUnit::day())->midnight();\n    }\n\n    /**\n     * When adding RelativeTimeUnit::month() this method will try to change month and adjust\n     * day of the month.\n     * Examples:\n     *  - 2021-02-28 + RelativeTimeUnit::month() = 2021-03-28.\n     */\n    public function add(Unit $timeUnit) : self\n    {\n        if ($timeUnit instanceof RelativeTimeUnit && $timeUnit->inMonths()) {\n            $years = $timeUnit->toPositive()->inYears();\n            $months = $timeUnit->inCalendarMonths();\n\n            $newMonth = $timeUnit->isNegative()\n                ? $this->month()->sub($years, $months)\n                : $this->month()->add($years, $months);\n\n            if ($newMonth->lastDay()->number() < $this->day->number()) {\n                return new self(new Day($newMonth, $newMonth->lastDay()->number()), $this->time, $this->timeZone);\n            }\n\n            return new self(new Day($newMonth, $this->day->number()), $this->time, $this->timeZone);\n        }\n\n        return self::fromDateTime($this->toDateTimeImmutable()->add($timeUnit->toDateInterval()));\n    }\n\n    /**\n     * When subtracting RelativeTimeUnit::month() this method will try to change month and adjust\n     * day of the month.\n     * Examples:\n     *  - 2021-03-31 - RelativeTimeUnit::month() = 2021-02-28.\n     */\n    public function sub(Unit $timeUnit) : self\n    {\n        return $this->add($timeUnit->invert());\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $dateTime) : bool\n    {\n        return $this->isEqualTo($dateTime);\n    }\n\n    public function isEqualTo(self $dateTime) : bool\n    {\n        return $this->toDateTimeImmutable() == $dateTime->toDateTimeImmutable();\n    }\n\n    public function isAfter(self $dateTime) : bool\n    {\n        return $this->toDateTimeImmutable() > $dateTime->toDateTimeImmutable();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfterOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isAfterOrEqual(self $dateTime) : bool\n    {\n        return $this->isAfterOrEqualTo($dateTime);\n    }\n\n    public function isAfterOrEqualTo(self $dateTime) : bool\n    {\n        return $this->toDateTimeImmutable() >= $dateTime->toDateTimeImmutable();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBeforeOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isBeforeOrEqual(self $dateTime) : bool\n    {\n        return $this->isBeforeOrEqualTo($dateTime);\n    }\n\n    public function isBeforeOrEqualTo(self $dateTime) : bool\n    {\n        return $this->toDateTimeImmutable() <= $dateTime->toDateTimeImmutable();\n    }\n\n    public function isBefore(self $dateTime) : bool\n    {\n        return $this->toDateTimeImmutable() < $dateTime->toDateTimeImmutable();\n    }\n\n    public function until(self $dateTime) : TimePeriod\n    {\n        return new TimePeriod($this, $dateTime);\n    }\n\n    public function since(self $dateTime) : TimePeriod\n    {\n        return new TimePeriod($dateTime, $this);\n    }\n\n    public function distance(self $dateTime) : TimeUnit\n    {\n        return $this->until($dateTime)->distance();\n    }\n\n    public function distanceSince(self $dateTime) : TimeUnit\n    {\n        return $this->since($dateTime)->distance();\n    }\n\n    public function distanceUntil(self $dateTime) : TimeUnit\n    {\n        return $this->until($dateTime)->distance();\n    }\n\n    public function iterate(self $pointInTime, TimeUnit $by) : TimePeriods\n    {\n        return $pointInTime->isBefore($this)\n            ? $this->since($pointInTime)->iterateBackward($by, Interval::closed())\n            : $this->until($pointInTime)->iterate($by, Interval::closed());\n    }\n\n    public function isAmbiguous() : bool\n    {\n        $tz = $this->timeZone;\n\n        if ($tz->timeOffset($this)->isUTC()) {\n            return false;\n        }\n\n        /**\n         * @var array<int, array{ts: int, time: string, offset: int, isdst: bool, abbr: string}> $transitions\n         */\n        $transitions = $tz->toDateTimeZone()->getTransitions(\n            $this->timestampUNIX()->sub(TimeUnit::hours(1)->add(TimeUnit::minute()))->inSeconds(),\n            $this->timestampUNIX()->add(TimeUnit::hours(1))->inSeconds(),\n        );\n\n        if (\\count($transitions) === 1) {\n            return false;\n        }\n\n        if ($transitions[1]['offset'] - $transitions[0]['offset'] === 3600) {\n            return false;\n        }\n\n        return true;\n    }\n\n    public function quarter() : Quarter\n    {\n        return $this->year()->quarter((int) \\ceil($this->month()->number() / 3));\n    }\n\n    public function compareTo(self $dateTime) : int\n    {\n        if ($this->isEqualTo($dateTime)) {\n            return 0;\n        }\n\n        return $this->isBefore($dateTime)\n            ? -1\n            : 1;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/DateTimeIntervalIterator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Unit;\n\nfinal class DateTimeIntervalIterator extends \\FilterIterator\n{\n    private Interval $interval;\n\n    public function __construct(DateTime $start, DateTime $end, Unit $timeUnit, Interval $interval)\n    {\n        $this->interval = $interval;\n        parent::__construct(new DateTimeIterator($start, $end, $timeUnit));\n    }\n\n    public function start() : DateTime\n    {\n        return $this->getInnerIterator()->start();\n    }\n\n    public function end() : DateTime\n    {\n        return $this->getInnerIterator()->end();\n    }\n\n    public function unit() : Unit\n    {\n        return $this->getInnerIterator()->unit();\n    }\n\n    public function isForward() : bool\n    {\n        return $this->getInnerIterator()->isForward();\n    }\n\n    public function interval() : Interval\n    {\n        return $this->interval;\n    }\n\n    public function hasNext() : bool\n    {\n        $dateTimeIterator = clone $this->getInnerIterator();\n\n        $dateTimeIterator->next();\n\n        if ($dateTimeIterator->isForward()) {\n            if ($this->interval->isRightOpen() && !$dateTimeIterator->hasNext()) {\n                return false;\n            }\n\n            if ($dateTimeIterator->current()->isAfter($dateTimeIterator->end())) {\n                return false;\n            }\n        } else {\n            if ($this->interval->isLeftOpen() && !$dateTimeIterator->hasNext()) {\n                return false;\n            }\n\n            if ($dateTimeIterator->current()->isBefore($dateTimeIterator->end())) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public function accept() : bool\n    {\n        $dateTimeIterator = $this->getInnerIterator();\n\n        if ($dateTimeIterator->isForward()) {\n            if (($this->interval->isRightOpen() || $this->interval->isOpen()) && !$dateTimeIterator->hasNext()) {\n                return false;\n            }\n\n            if (($this->interval->isLeftOpen() || $this->interval->isOpen()) && $dateTimeIterator->isFirst()) {\n                return false;\n            }\n        } else {\n            if (($this->interval->isRightOpen() || $this->interval->isOpen()) && $dateTimeIterator->isFirst()) {\n                return false;\n            }\n\n            if (($this->interval->isLeftOpen() || $this->interval->isOpen()) && !$dateTimeIterator->hasNext()) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public function key() : int\n    {\n        $dateTimeIterator = $this->getInnerIterator();\n\n        if ($dateTimeIterator->isForward()) {\n            if ($this->interval->isLeftOpen() || $this->interval->isOpen()) {\n                return $dateTimeIterator->key() - 1;\n            }\n        } else {\n            if ($this->interval->isRightOpen() || $this->interval->isOpen()) {\n                return $dateTimeIterator->key() - 1;\n            }\n        }\n\n        return $dateTimeIterator->key();\n    }\n\n    /**\n     * @psalm-suppress MixedReturnStatement\n     */\n    public function current() : ?DateTime\n    {\n        /**\n         * @phpstan-ignore-next-line\n         */\n        return parent::current();\n    }\n\n    /**\n     * @psalm-suppress InvalidReturnStatement\n     * @psalm-suppress InvalidReturnType\n     */\n    public function getInnerIterator() : DateTimeIterator\n    {\n        /** @phpstan-ignore-next-line */\n        return parent::getInnerIterator();\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/DateTimeIterator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Unit;\n\n/**\n * @implements \\Iterator<DateTime>\n */\nfinal class DateTimeIterator implements \\Iterator\n{\n    private DateTime $currentDate;\n\n    private DateTime $start;\n\n    private DateTime $end;\n\n    private Unit $timeUnit;\n\n    private int $key;\n\n    private bool $forward;\n\n    private bool $empty;\n\n    /**\n     * @throws InvalidArgumentException\n     */\n    public function __construct(DateTime $start, DateTime $end, Unit $timeUnit)\n    {\n        $this->start = $start;\n        $this->end = $end;\n        $this->timeUnit = $timeUnit;\n        $this->currentDate = $start;\n        $this->key = 0;\n        $this->forward = $start->isBeforeOrEqualTo($end);\n        $this->empty = $start->isEqualTo($end);\n\n        if ($this->forward && $timeUnit->isNegative()) {\n            throw new InvalidArgumentException(\"Forward DateTimeIterator {$start->format('Y-m-d H:i:sP')}...{$end->format('Y-m-d H:i:sP')} requires positive TimeUnit\");\n        }\n\n        if (!$this->forward && !$timeUnit->isNegative()) {\n            throw new InvalidArgumentException(\"Backward DateTimeIterator {$start->format('Y-m-d H:i:sP')}...{$end->format('Y-m-d H:i:sP')} requires negative TimeUnit\");\n        }\n    }\n\n    public function isForward() : bool\n    {\n        return $this->forward;\n    }\n\n    public function unit() : Unit\n    {\n        return $this->timeUnit;\n    }\n\n    public function start() : DateTime\n    {\n        return $this->start;\n    }\n\n    public function end() : DateTime\n    {\n        return $this->end;\n    }\n\n    public function current() : DateTime\n    {\n        return $this->currentDate;\n    }\n\n    public function next() : void\n    {\n        $this->currentDate = $this->currentDate->add($this->timeUnit);\n        $this->key++;\n    }\n\n    public function hasNext() : bool\n    {\n        if ($this->forward) {\n            return !$this->current()->add($this->timeUnit)->isAfter($this->end);\n        }\n\n        return !$this->current()->add($this->timeUnit)->isBefore($this->end);\n    }\n\n    public function key() : int\n    {\n        return $this->key;\n    }\n\n    public function isFirst() : bool\n    {\n        return $this->key() === 0;\n    }\n\n    public function valid() : bool\n    {\n        if ($this->empty) {\n            return false;\n        }\n\n        if ($this->forward) {\n            return $this->currentDate->isBeforeOrEqualTo($this->end);\n        }\n\n        return $this->currentDate->isAfterOrEqualTo($this->end);\n    }\n\n    public function rewind() : void\n    {\n        $this->currentDate = $this->start;\n        $this->key = 0;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Day/WeekDay.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian\\Day;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\n\n/**\n * @psalm-immutable\n */\nfinal class WeekDay\n{\n    private const NAMES = [\n        1 => 'Monday',\n        2 => 'Tuesday',\n        3 => 'Wednesday',\n        4 => 'Thursday',\n        5 => 'Friday',\n        6 => 'Saturday',\n        7 => 'Sunday',\n    ];\n\n    private const NAMES_SHORT = [\n        1 => 'Mon',\n        2 => 'Tue',\n        3 => 'Wed',\n        4 => 'Thu',\n        5 => 'Fri',\n        6 => 'Sat',\n        7 => 'Sun',\n    ];\n\n    private int $number;\n\n    public function __construct(int $number)\n    {\n        if ($number <= 0 || $number > 7) {\n            throw new InvalidArgumentException('Day number must be greater or equal 1 and less or equal than 7');\n        }\n\n        $this->number = $number;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function monday() : self\n    {\n        return new self(1);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function tuesday() : self\n    {\n        return new self(2);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function wednesday() : self\n    {\n        return new self(3);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function thursday() : self\n    {\n        return new self(4);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function friday() : self\n    {\n        return new self(5);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function saturday() : self\n    {\n        return new self(6);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function sunday() : self\n    {\n        return new self(7);\n    }\n\n    public function number() : int\n    {\n        return $this->number;\n    }\n\n    public function name() : string\n    {\n        return self::NAMES[$this->number];\n    }\n\n    public function shortName() : string\n    {\n        return self::NAMES_SHORT[$this->number];\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $weekDay) : bool\n    {\n        return $this->isEqualTo($weekDay);\n    }\n\n    public function isEqualTo(self $weekDay) : bool\n    {\n        return $this->number() === $weekDay->number();\n    }\n\n    public function isWeekend() : bool\n    {\n        return \\in_array($this->number(), [6, 7], true);\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Day.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Day\\WeekDay;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class Day\n{\n    private Month $month;\n\n    private int $number;\n\n    public function __construct(Month $month, int $number)\n    {\n        if ($number <= 0 || $number > $month->numberOfDays()) {\n            throw new InvalidArgumentException('Day number must be greater or equal 1 and less or equal than ' . $month->numberOfDays());\n        }\n\n        $this->number = $number;\n        $this->month = $month;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function create(int $year, int $month, int $day) : self\n    {\n        return new self(\n            new Month(\n                new Year($year),\n                $month\n            ),\n            $day\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress PossiblyNullArrayAccess\n     * @psalm-suppress PossiblyNullArgument\n     * @psalm-suppress ImpureMethodCall\n     * @psalm-suppress PossiblyInvalidArgument\n     */\n    public static function fromDateTime(\\DateTimeInterface $dateTime) : self\n    {\n        /**\n         * @phpstan-ignore-next-line\n         */\n        [$year, $month, $day] = \\sscanf($dateTime->format('Y-m-d'), '%d-%d-%d');\n\n        /** @phpstan-ignore-next-line */\n        return new self(new Month(new Year($year), $month), $day, );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromString(string $date) : self\n    {\n        try {\n            return self::fromDateTime(new \\DateTimeImmutable($date));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"Value \\\"{$date}\\\" is not valid day format.\");\n        }\n    }\n\n    /**\n     * @return array{year: int, month:int, day: int}\n     */\n    public function __debugInfo() : array\n    {\n        return [\n            'year' => $this->month->year()->number(),\n            'month' => $this->month->number(),\n            'day' => $this->number,\n        ];\n    }\n\n    /**\n     * @return array{month: Month, number: int}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'month' => $this->month,\n            'number' => $this->number,\n        ];\n    }\n\n    /**\n     * @param array{month: Month, number: int} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->month = $data['month'];\n        $this->number = $data['number'];\n    }\n\n    public function toString() : string\n    {\n        return $this->format('Y-m-d');\n    }\n\n    public function timeBetween(self $day) : TimeUnit\n    {\n        return TimeUnit::seconds(\\abs(($this->toDateTimeImmutable()->getTimestamp() - $day->toDateTimeImmutable()->getTimestamp())));\n    }\n\n    /** @deprecated Use `add` instead. Will be removed with 2.0 */\n    public function plus(int $years, int $months, int $days) : self\n    {\n        return $this->add($years, $months, $days);\n    }\n\n    public function add(int $years, int $months, int $days) : self\n    {\n        $dateTime = $this->midnight(TimeZone::UTC());\n\n        if ($years !== 0) {\n            $dateTime = $dateTime->add(RelativeTimeUnit::years($years));\n        }\n\n        if ($months !== 0) {\n            $dateTime = $dateTime->add(RelativeTimeUnit::months($months));\n        }\n\n        if ($days !== 0) {\n            $dateTime = $dateTime->add(TimeUnit::days($days));\n        }\n\n        return $dateTime->day();\n    }\n\n    /** @deprecated Use `sub` instead. Will be removed with 2.0 */\n    public function minus(int $years, int $months, int $days) : self\n    {\n        return $this->sub($years, $months, $days);\n    }\n\n    public function sub(int $years, int $months, int $days) : self\n    {\n        $dateTime = $this->midnight(TimeZone::UTC());\n\n        if ($years !== 0) {\n            $dateTime = $dateTime->sub(RelativeTimeUnit::years($years));\n        }\n\n        if ($months !== 0) {\n            $dateTime = $dateTime->sub(RelativeTimeUnit::months($months));\n        }\n\n        if ($days !== 0) {\n            $dateTime = $dateTime->sub(TimeUnit::days($days));\n        }\n\n        return $dateTime->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `addDays` instead. Will be removed with 2.0\n     */\n    public function plusDays(int $days) : self\n    {\n        return $this->addDays($days);\n    }\n\n    public function addDays(int $days) : self\n    {\n        return $this->midnight(TimeZone::UTC())->add(TimeUnit::days($days))->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `subDays` instead. Will be removed with 2.0\n     */\n    public function minusDays(int $days) : self\n    {\n        return $this->subDays($days);\n    }\n\n    public function subDays(int $days) : self\n    {\n        return $this->midnight(TimeZone::UTC())->sub(TimeUnit::days($days))->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `addMonths` instead. Will be removed with 2.0\n     */\n    public function plusMonths(int $months) : self\n    {\n        return $this->addMonths($months);\n    }\n\n    public function addMonths(int $months) : self\n    {\n        return $this->midnight(TimeZone::UTC())->add(RelativeTimeUnit::months($months))->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `subMonths` instead. Will be removed with 2.0\n     */\n    public function minusMonths(int $months) : self\n    {\n        return $this->subMonths($months);\n    }\n\n    public function subMonths(int $months) : self\n    {\n        return $this->midnight(TimeZone::UTC())->sub(RelativeTimeUnit::months($months))->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `addYears` instead. Will be removed with 2.0\n     */\n    public function plusYears(int $years) : self\n    {\n        return $this->addYears($years);\n    }\n\n    public function addYears(int $years) : self\n    {\n        return $this->midnight(TimeZone::UTC())->add(RelativeTimeUnit::years($years))->day();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `subYears` instead. Will be removed with 2.0\n     */\n    public function minusYears(int $years) : self\n    {\n        return $this->subYears($years);\n    }\n\n    public function subYears(int $years) : self\n    {\n        return $this->midnight(TimeZone::UTC())->sub(RelativeTimeUnit::years($years))->day();\n    }\n\n    public function previous() : self\n    {\n        return $this->midnight(TimeZone::UTC())->sub(TimeUnit::day())->day();\n    }\n\n    public function next() : self\n    {\n        return $this->midnight(TimeZone::UTC())->add(TimeUnit::day())->day();\n    }\n\n    public function midnight(TimeZone $timeZone) : DateTime\n    {\n        return new DateTime($this, new Time(0, 0, 0, 0), $timeZone);\n    }\n\n    public function noon(TimeZone $timeZone) : DateTime\n    {\n        return new DateTime($this, new Time(12, 0, 0, 0), $timeZone);\n    }\n\n    public function endOfDay(TimeZone $timeZone) : DateTime\n    {\n        return new DateTime($this, new Time(23, 59, 59, 999999), $timeZone);\n    }\n\n    public function setTime(Time $time, TimeZone $timeZone) : DateTime\n    {\n        return new DateTime(\n            $this,\n            $time,\n            $timeZone\n        );\n    }\n\n    public function month() : Month\n    {\n        return $this->month;\n    }\n\n    public function year() : Year\n    {\n        return $this->month->year();\n    }\n\n    public function number() : int\n    {\n        return $this->number;\n    }\n\n    public function weekDay() : WeekDay\n    {\n        return new WeekDay((int) $this->toDateTimeImmutable()->format('N'));\n    }\n\n    /**\n     * Week of year starting from 1.\n     */\n    public function weekOfYear() : int\n    {\n        return (int) $this->toDateTimeImmutable()->format('W');\n    }\n\n    /**\n     * Week of year starting from 1.\n     */\n    public function weekOfMonth() : int\n    {\n        return $this->weekOfYear() - $this->month->days()->first()->weekOfYear() + 1;\n    }\n\n    /**\n     * Day of year starting from 1.\n     */\n    public function dayOfYear() : int\n    {\n        return \\intval($this->toDateTimeImmutable()->format('z')) + 1;\n    }\n\n    public function isWeekend() : bool\n    {\n        return $this->weekDay()->isWeekend();\n    }\n\n    public function toDateTimeImmutable() : \\DateTimeImmutable\n    {\n        return new \\DateTimeImmutable(\\sprintf(\n            '%d-%d-%d 00:00:00.000000 UTC',\n            $this->month->year()->number(),\n            $this->month->number(),\n            $this->number\n        ));\n    }\n\n    public function format(string $format) : string\n    {\n        return $this->toDateTimeImmutable()->format($format);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $day) : bool\n    {\n        return $this->isEqualTo($day);\n    }\n\n    public function isEqualTo(self $day) : bool\n    {\n        return $this->number === $day->number()\n            && $this->month->isEqualTo($day->month());\n    }\n\n    public function isBefore(self $day) : bool\n    {\n        if ($this->month->isBefore($day->month())) {\n            return true;\n        }\n\n        if ($this->month->isAfter($day->month())) {\n            return false;\n        }\n\n        return $this->number < $day->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBeforeOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isBeforeOrEqual(self $day) : bool\n    {\n        return $this->isBeforeOrEqualTo($day);\n    }\n\n    public function isBeforeOrEqualTo(self $day) : bool\n    {\n        if ($this->month->isBefore($day->month())) {\n            return true;\n        }\n\n        if ($this->month->isAfter($day->month())) {\n            return false;\n        }\n\n        return $this->number <= $day->number();\n    }\n\n    public function isAfter(self $day) : bool\n    {\n        if ($this->month->isAfter($day->month())) {\n            return true;\n        }\n\n        if ($this->month->isBefore($day->month())) {\n            return false;\n        }\n\n        return $this->number > $day->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfterOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isAfterOrEqual(self $day) : bool\n    {\n        return $this->isAfterOrEqualTo($day);\n    }\n\n    public function isAfterOrEqualTo(self $day) : bool\n    {\n        if ($this->month->isAfter($day->month())) {\n            return true;\n        }\n\n        if ($this->month->isBefore($day->month())) {\n            return false;\n        }\n\n        return $this->number >= $day->number();\n    }\n\n    public function iterate(self $destination, Interval $interval) : Days\n    {\n        return $this->isAfter($destination)\n            ? $this->since($destination, $interval)\n            : $this->until($destination, $interval);\n    }\n\n    public function until(self $day, Interval $interval) : Days\n    {\n        if ($this->isAfter($day)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%d %s %d is after %d %s %d',\n                    $this->number,\n                    $this->month->name(),\n                    $this->month->year()->number(),\n                    $day->number(),\n                    $day->month()->name(),\n                    $day->month()->year()->number(),\n                )\n            );\n        }\n\n        return Days::fromDateTimeIterator(\n            new DateTimeIntervalIterator(\n                $this->midnight(TimeZone::UTC()),\n                $day->midnight(TimeZone::UTC()),\n                TimeUnit::day(),\n                $interval\n            )\n        );\n    }\n\n    public function since(self $day, Interval $interval) : Days\n    {\n        if ($this->isBefore($day)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%d %s %d is before %d %s %d',\n                    $this->number,\n                    $this->month->name(),\n                    $this->month->year()->number(),\n                    $day->number(),\n                    $day->month()->name(),\n                    $day->month()->year()->number(),\n                )\n            );\n        }\n\n        return Days::fromDateTimeIterator(\n            new DateTimeIntervalIterator(\n                $this->midnight(TimeZone::UTC()),\n                $day->midnight(TimeZone::UTC()),\n                TimeUnit::day()->toNegative(),\n                $interval\n            )\n        );\n    }\n\n    public function distance(self $to) : TimeUnit\n    {\n        return (new TimePeriod($this->midnight(TimeZone::UTC()), $to->midnight(TimeZone::UTC())))->distance();\n    }\n\n    public function quarter() : Quarter\n    {\n        return $this->year()->quarter((int) \\ceil($this->month->number() / 3));\n    }\n\n    public function compareTo(self $day) : int\n    {\n        if ($this->isEqualTo($day)) {\n            return 0;\n        }\n\n        return $this->isBefore($day)\n            ? -1\n            : 1;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Days.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n *\n * @implements \\IteratorAggregate<Day>\n */\nfinal class Days implements \\Countable, \\IteratorAggregate\n{\n    /**\n     * @var \\Iterator<int|string, Day>\n     */\n    private \\Iterator $days;\n\n    /**\n     * @param \\Iterator<int|string, Day> $days\n     */\n    private function __construct(\\Iterator $days)\n    {\n        $this->days = $days;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromArray(Day ...$days) : self\n    {\n        /** @psalm-suppress ImpureMethodCall */\n        return new self(new \\ArrayIterator($days));\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromDateTimeIterator(DateTimeIntervalIterator $iterator) : self\n    {\n        /** @psalm-suppress ImpureMethodCall */\n        return new self(DaysIterator::fromDateTimeIterator($iterator));\n    }\n\n    /**\n     * @return array<Day>\n     *\n     * @psalm-suppress ImpureFunctionCall\n     */\n    public function all() : array\n    {\n        return \\iterator_to_array($this->days);\n    }\n\n    /**\n     * @psalm-template MapResultType\n     *\n     * @param callable(Day $day) : MapResultType $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : MapResultType $iterator\n     *\n     * @return array<MapResultType>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map($iterator, $this->all());\n    }\n\n    /**\n     * @psalm-suppress InvalidScalarArgument\n     *\n     * @param callable(Day $day) : bool $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : bool $iterator\n     */\n    public function filter(callable $iterator) : self\n    {\n        return new self(new \\CallbackFilterIterator($this->days, $iterator));\n    }\n\n    public function count() : int\n    {\n        return \\iterator_count($this->days);\n    }\n\n    /**\n     * @return \\Traversable<int|string, Day>\n     */\n    public function getIterator() : \\Traversable\n    {\n        return $this->days;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/DaysIterator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nfinal class DaysIterator extends \\IteratorIterator\n{\n    /**\n     * @param \\Traversable<DateTime> $iterator\n     */\n    private function __construct(\\Traversable $iterator)\n    {\n        parent::__construct($iterator);\n    }\n\n    /**\n     * @psalm-suppress MixedArgumentTypeCoercion\n     */\n    public static function fromDateTimeIterator(DateTimeIntervalIterator $datePeriod) : self\n    {\n        return new self($datePeriod);\n    }\n\n    public function current() : ?Day\n    {\n        /** @var null|DateTime|Day $current */\n        $current = parent::current();\n\n        if ($current === null) {\n            return null;\n        }\n\n        if ($current instanceof Day) {\n            return $current;\n        }\n\n        return $current->day();\n    }\n\n    public function reverse() : self\n    {\n        /** @phpstan-ignore-next-line  */\n        return new self(new \\ArrayIterator(\\array_reverse(\\iterator_to_array($this))));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/GregorianCalendar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n */\nfinal class GregorianCalendar implements Calendar\n{\n    private TimeZone $timeZone;\n\n    public function __construct(TimeZone $timeZone)\n    {\n        $this->timeZone = $timeZone;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function UTC() : self\n    {\n        return new self(TimeZone::UTC());\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureFunctionCall\n     */\n    public static function systemDefault() : self\n    {\n        return new self(TimeZone::fromString(\\date_default_timezone_get()));\n    }\n\n    public function timeZone() : TimeZone\n    {\n        return $this->timeZone;\n    }\n\n    public function currentYear() : Year\n    {\n        return $this->now()->year();\n    }\n\n    public function currentMonth() : Month\n    {\n        return $this->now()->month();\n    }\n\n    public function currentDay() : Day\n    {\n        return $this->now()->day();\n    }\n\n    public function now() : DateTime\n    {\n        if ($this->timeZone->name() === 'UTC') {\n            return DateTime::fromDateTime(new \\DateTimeImmutable('now', new \\DateTimeZone('UTC')));\n        }\n\n        return DateTime::fromDateTime(new \\DateTimeImmutable('now', new \\DateTimeZone('UTC')))\n            ->toTimeZone($this->timeZone);\n    }\n\n    public function yesterday() : DateTime\n    {\n        return $this->now()->yesterday();\n    }\n\n    public function tomorrow() : DateTime\n    {\n        return $this->now()->tomorrow();\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/GregorianCalendarStub.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n *\n * @codeCoverageIgnore\n */\nfinal class GregorianCalendarStub implements Calendar\n{\n    private TimeZone $timeZone;\n\n    private ?DateTime $currentDate;\n\n    public function __construct(TimeZone $timeZone, ?DateTime $currentDate = null)\n    {\n        $this->timeZone = $timeZone;\n        $this->currentDate = $currentDate;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function UTC(?DateTime $currentDate = null) : self\n    {\n        return new self(TimeZone::UTC(), $currentDate);\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureFunctionCall\n     */\n    public static function systemDefault(?DateTime $currentDate = null) : self\n    {\n        return new self(TimeZone::fromString(\\date_default_timezone_get()), $currentDate);\n    }\n\n    public function timeZone() : TimeZone\n    {\n        return $this->timeZone;\n    }\n\n    public function currentYear() : Year\n    {\n        return $this->now()->year();\n    }\n\n    public function currentMonth() : Month\n    {\n        return $this->now()->month();\n    }\n\n    public function currentDay() : Day\n    {\n        return $this->now()->day();\n    }\n\n    public function now() : DateTime\n    {\n        return $this->currentDate\n            ? $this->currentDate\n            : DateTime::fromDateTime(new \\DateTimeImmutable('now', $this->timeZone->toDateTimeZone()));\n    }\n\n    public function yesterday() : DateTime\n    {\n        return $this->now()->yesterday();\n    }\n\n    public function tomorrow() : DateTime\n    {\n        return $this->now()->tomorrow();\n    }\n\n    /**\n     * @psalm-suppress InaccessibleProperty\n     */\n    public function setNow(DateTime $dateTime) : void\n    {\n        $this->currentDate = $dateTime;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Interval.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n */\nfinal class Interval\n{\n    public const CLOSED = 0;\n\n    public const LEFT_OPEN = 1;\n\n    public const RIGHT_OPEN = 2;\n\n    public const OPEN = 4;\n\n    private int $type;\n\n    private function __construct(int $type)\n    {\n        $this->type = $type;\n    }\n\n    /**\n     * A <= x <= B.\n     *\n     * @psalm-pure\n     */\n    public static function closed() : self\n    {\n        return new self(self::CLOSED);\n    }\n\n    /**\n     * A <= x < B.\n     *\n     * @psalm-pure\n     */\n    public static function rightOpen() : self\n    {\n        return new self(self::RIGHT_OPEN);\n    }\n\n    /**\n     * A < x <= B.\n     *\n     * @psalm-pure\n     */\n    public static function leftOpen() : self\n    {\n        return new self(self::LEFT_OPEN);\n    }\n\n    /**\n     * A < x < B.\n     *\n     * @psalm-pure\n     */\n    public static function open() : self\n    {\n        return new self(self::OPEN);\n    }\n\n    public function isOpen() : bool\n    {\n        return $this->type === self::OPEN;\n    }\n\n    public function isLeftOpen() : bool\n    {\n        return $this->type === self::LEFT_OPEN || $this->type === self::OPEN;\n    }\n\n    public function isRightOpen() : bool\n    {\n        return $this->type === self::RIGHT_OPEN || $this->type === self::OPEN;\n    }\n\n    public function isClosed() : bool\n    {\n        return $this->type === self::CLOSED;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/LeapSecond.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class LeapSecond\n{\n    private DateTime $dateTime;\n\n    private TimeUnit $offsetTAI;\n\n    public function __construct(DateTime $dateTime, TimeUnit $offsetTAI)\n    {\n        if ($offsetTAI->inSeconds() < 10) {\n            throw new InvalidArgumentException('Leap second TAI offset must be greater or equal 10');\n        }\n\n        $this->dateTime = $dateTime;\n        $this->offsetTAI = $offsetTAI;\n    }\n\n    /**\n     * @return array{dateTime: DateTime, offsetTAI: TimeUnit}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'dateTime' => $this->dateTime,\n            'offsetTAI' => $this->offsetTAI,\n        ];\n    }\n\n    public function dateTime() : DateTime\n    {\n        return $this->dateTime;\n    }\n\n    public function offsetTAI() : TimeUnit\n    {\n        return $this->offsetTAI;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $second) : bool\n    {\n        return $this->isEqualTo($second);\n    }\n\n    public function isEqualTo(self $second) : bool\n    {\n        return $this->dateTime->isEqualTo($second->dateTime()) && $this->offsetTAI->isEqualTo($second->offsetTAI());\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/LeapSeconds.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class LeapSeconds\n{\n    private static ?self $instance = null;\n\n    private DateTime $listExpirationDate;\n\n    /**\n     * @var array<LeapSecond>\n     */\n    private array $leapSeconds;\n\n    private function __construct(DateTime $listExpiration, LeapSecond ...$leapSeconds)\n    {\n        $this->leapSeconds = $leapSeconds;\n        $this->listExpirationDate = $listExpiration;\n    }\n\n    /**\n     * List taken from https://www.ietf.org/timezones/data/leap-seconds.list.\n     *\n     * Leap seconds are announced by https://www.iers.org/ (The International Earth Rotation and Reference Systems Service)\n     * in their Bulletin C, list of all available releases https://www.iers.org/IERS/EN/Publications/Bulletins/bulletins.html\n     *\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureStaticProperty\n     */\n    public static function load() : self\n    {\n        if (self::$instance instanceof self) {\n            return self::$instance;\n        }\n\n        self::$instance = new self(\n            DateTime::fromString('2026-06-28 00:00:00 UTC'),\n            new LeapSecond(DateTime::fromString('1972-01-01 00:00:00 UTC'), TimeUnit::seconds(10)),\n            new LeapSecond(DateTime::fromString('1972-07-01 00:00:00 UTC'), TimeUnit::seconds(11)),\n            new LeapSecond(DateTime::fromString('1973-01-01 00:00:00 UTC'), TimeUnit::seconds(12)),\n            new LeapSecond(DateTime::fromString('1974-01-01 00:00:00 UTC'), TimeUnit::seconds(13)),\n            new LeapSecond(DateTime::fromString('1975-01-01 00:00:00 UTC'), TimeUnit::seconds(14)),\n            new LeapSecond(DateTime::fromString('1976-01-01 00:00:00 UTC'), TimeUnit::seconds(15)),\n            new LeapSecond(DateTime::fromString('1977-01-01 00:00:00 UTC'), TimeUnit::seconds(16)),\n            new LeapSecond(DateTime::fromString('1978-01-01 00:00:00 UTC'), TimeUnit::seconds(17)),\n            new LeapSecond(DateTime::fromString('1979-01-01 00:00:00 UTC'), TimeUnit::seconds(18)),\n            new LeapSecond(DateTime::fromString('1980-01-01 00:00:00 UTC'), TimeUnit::seconds(19)),\n            new LeapSecond(DateTime::fromString('1981-07-01 00:00:00 UTC'), TimeUnit::seconds(20)),\n            new LeapSecond(DateTime::fromString('1982-07-01 00:00:00 UTC'), TimeUnit::seconds(21)),\n            new LeapSecond(DateTime::fromString('1983-07-01 00:00:00 UTC'), TimeUnit::seconds(22)),\n            new LeapSecond(DateTime::fromString('1985-07-01 00:00:00 UTC'), TimeUnit::seconds(23)),\n            new LeapSecond(DateTime::fromString('1988-01-01 00:00:00 UTC'), TimeUnit::seconds(24)),\n            new LeapSecond(DateTime::fromString('1990-01-01 00:00:00 UTC'), TimeUnit::seconds(24)),\n            new LeapSecond(DateTime::fromString('1991-01-01 00:00:00 UTC'), TimeUnit::seconds(25)),\n            new LeapSecond(DateTime::fromString('1992-07-01 00:00:00 UTC'), TimeUnit::seconds(26)),\n            new LeapSecond(DateTime::fromString('1993-07-01 00:00:00 UTC'), TimeUnit::seconds(27)),\n            new LeapSecond(DateTime::fromString('1994-07-01 00:00:00 UTC'), TimeUnit::seconds(28)),\n            new LeapSecond(DateTime::fromString('1996-01-01 00:00:00 UTC'), TimeUnit::seconds(29)),\n            new LeapSecond(DateTime::fromString('1997-07-01 00:00:00 UTC'), TimeUnit::seconds(30)),\n            new LeapSecond(DateTime::fromString('1999-01-01 00:00:00 UTC'), TimeUnit::seconds(31)),\n            new LeapSecond(DateTime::fromString('2006-01-01 00:00:00 UTC'), TimeUnit::seconds(32)),\n            new LeapSecond(DateTime::fromString('2009-01-01 00:00:00 UTC'), TimeUnit::seconds(33)),\n            new LeapSecond(DateTime::fromString('2012-07-01 00:00:00 UTC'), TimeUnit::seconds(34)),\n            new LeapSecond(DateTime::fromString('2015-07-01 00:00:00 UTC'), TimeUnit::seconds(36)),\n            new LeapSecond(DateTime::fromString('2017-01-01 00:00:00 UTC'), TimeUnit::seconds(37)),\n        );\n\n        return self::$instance;\n    }\n\n    public function expirationDate() : DateTime\n    {\n        return $this->listExpirationDate;\n    }\n\n    public function since(DateTime $dateTime) : self\n    {\n        /* @phpstan-ignore-next-line  */\n        return $this->filter(function (LeapSecond $leapSecond) use ($dateTime) : bool {\n            return $dateTime\n                    ->toTimeZone(TimeZone::UTC())\n                    ->isBefore($leapSecond->dateTime());\n        });\n    }\n\n    public function until(DateTime $dateTime) : self\n    {\n        /* @phpstan-ignore-next-line  */\n        return $this->filter(function (LeapSecond $leapSecond) use ($dateTime) : bool {\n            return $dateTime\n                ->toTimeZone(TimeZone::UTC())\n                ->isAfterOrEqualTo($leapSecond->dateTime());\n        });\n    }\n\n    public function findAllBetween(TimePeriod $timePeriod) : self\n    {\n        /* @phpstan-ignore-next-line  */\n        return $this->filter(function (LeapSecond $leapSecond) use ($timePeriod) : bool {\n            return $timePeriod->start()\n                    ->toTimeZone(TimeZone::UTC())\n                    ->isBeforeOrEqualTo($leapSecond->dateTime())\n                && $timePeriod->end()\n                    ->toTimeZone(TimeZone::UTC())\n                    ->isAfter($leapSecond->dateTime());\n        });\n    }\n\n    public function offsetTAI() : TimeUnit\n    {\n        return $this->leapSeconds[\\count($this->leapSeconds) - 1]->offsetTAI();\n    }\n\n    /**\n     * @param callable(LeapSecond $leapSecond) : bool $filter\n     *\n     * @psalm-param pure-callable(LeapSecond $leapSecond) : bool $filter\n     */\n    public function filter(callable $filter) : self\n    {\n        return new self(\n            $this->expirationDate(),\n            ...\\array_filter($this->all(), $filter)\n        );\n    }\n\n    public function count() : TimeUnit\n    {\n        return TimeUnit::seconds(\\count($this->all()));\n    }\n\n    /**\n     * @return array<LeapSecond>\n     */\n    public function all() : array\n    {\n        return $this->leapSeconds;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Month.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class Month\n{\n    private const TOTAL_MONTHS = 12;\n\n    private Year $year;\n\n    private MonthDays $days;\n\n    private int $number;\n\n    public function __construct(Year $year, int $number)\n    {\n        if ($number <= 0 || $number > self::TOTAL_MONTHS) {\n            throw new InvalidArgumentException('Month number must be greater or equal 1 and less or equal than 12');\n        }\n\n        $this->year = $year;\n        $this->number = $number;\n        $this->days = new MonthDays($this);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function create(int $year, int $month) : self\n    {\n        return new self(\n            new Year($year),\n            $month\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromDateTime(\\DateTimeInterface $dateTime) : self\n    {\n        return new self(\n            new Year((int) $dateTime->format('Y')),\n            (int) $dateTime->format('m')\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromString(string $date) : self\n    {\n        try {\n            return self::fromDateTime(new \\DateTimeImmutable($date));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"Value \\\"{$date}\\\" is not valid month format.\");\n        }\n    }\n\n    /**\n     * @return array{year: int, month: int}\n     */\n    public function __debugInfo() : array\n    {\n        return [\n            'year' => $this->year()->number(),\n            'month' => $this->number(),\n        ];\n    }\n\n    /**\n     * @return array{year: Year, number: int}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'year' => $this->year(),\n            'number' => $this->number(),\n        ];\n    }\n\n    /**\n     * @param array{year: Year, number: int} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->year = $data['year'];\n        $this->number = $data['number'];\n        $this->days = new MonthDays($this);\n    }\n\n    public function __toString() : string\n    {\n        return $this->toString();\n    }\n\n    public function toString() : string\n    {\n        return $this->toDateTimeImmutable()->format('Y-m');\n    }\n\n    public function previous() : self\n    {\n        return $this->subMonths(1);\n    }\n\n    public function next() : self\n    {\n        return $this->addMonths(1);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `add` instead. Will be removed with 2.0\n     */\n    public function plus(int $years, int $months) : self\n    {\n        return $this->add($years, $months);\n    }\n\n    public function add(int $years, int $months) : self\n    {\n        $month = $this;\n\n        if ($months !== 0) {\n            $month = $month->addMonths($months);\n        }\n\n        if ($years !== 0) {\n            $month = $month->addYears($years);\n        }\n\n        return $month;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `sub` instead. Will be removed with 2.0\n     */\n    public function minus(int $years, int $months) : self\n    {\n        return $this->sub($years, $months);\n    }\n\n    public function sub(int $years, int $months) : self\n    {\n        $month = $this;\n\n        if ($months !== 0) {\n            $month = $month->subMonths($months);\n        }\n\n        if ($years !== 0) {\n            $month = $month->subYears($years);\n        }\n\n        return $month;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `addMonths` instead. Will be removed with 2.0\n     */\n    public function plusMonths(int $months) : self\n    {\n        return $this->addMonths($months);\n    }\n\n    public function addMonths(int $months) : self\n    {\n        $years = (int) ($months / self::TOTAL_MONTHS);\n        $monthsRemainder = $months % self::TOTAL_MONTHS;\n\n        $year = $this->year->number() + $years;\n        $month = $this->number() + $monthsRemainder;\n\n        if ($month > self::TOTAL_MONTHS) {\n            $year += ((int) ($month / self::TOTAL_MONTHS));\n            $month %= self::TOTAL_MONTHS;\n        }\n\n        if ($month === 0) {\n            $month = 12;\n        }\n\n        return new self(new Year($year), $month);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `subMonths` instead. Will be removed with 2.0\n     */\n    public function minusMonths(int $months) : self\n    {\n        return $this->subMonths($months);\n    }\n\n    public function subMonths(int $months) : self\n    {\n        $years = (int) ($months / self::TOTAL_MONTHS);\n        $monthsRemainder = $months % self::TOTAL_MONTHS;\n\n        $year = $this->year->number() - $years;\n        $month = $this->number() - $monthsRemainder;\n\n        if ($month === 0) {\n            $month = self::TOTAL_MONTHS;\n            $year--;\n        }\n\n        if ($month < 0) {\n            $month = self::TOTAL_MONTHS - \\abs($month);\n            $year--;\n        }\n\n        return new self(new Year($year), $month);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `addYears` instead. Will be removed with 2.0\n     */\n    public function plusYears(int $years) : self\n    {\n        return $this->addYears($years);\n    }\n\n    public function addYears(int $years) : self\n    {\n        return new self($this->year->add($years), $this->number);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `subYears` instead. Will be removed with 2.0\n     */\n    public function minusYears(int $years) : self\n    {\n        return $this->subYears($years);\n    }\n\n    public function subYears(int $years) : self\n    {\n        return new self($this->year->sub($years), $this->number);\n    }\n\n    public function firstDay() : Day\n    {\n        return $this->days()->first();\n    }\n\n    public function lastDay() : Day\n    {\n        return $this->days()->last();\n    }\n\n    public function number() : int\n    {\n        return $this->number;\n    }\n\n    public function year() : Year\n    {\n        return $this->year;\n    }\n\n    public function days() : MonthDays\n    {\n        return $this->days;\n    }\n\n    public function numberOfDays() : int\n    {\n        return 31 - (($this->number == 2) ?\n                (3 - (int) $this->year->isLeap()) : (($this->number - 1) % 7 % 2));\n    }\n\n    public function shortName() : string\n    {\n        return $this->toDateTimeImmutable()->format('M');\n    }\n\n    public function name() : string\n    {\n        return $this->toDateTimeImmutable()->format('F');\n    }\n\n    public function toDateTimeImmutable() : \\DateTimeImmutable\n    {\n        return new \\DateTimeImmutable(\\sprintf(\n            '%d-%d-01 00:00:00.000000 UTC',\n            $this->year()->number(),\n            $this->number()\n        ));\n    }\n\n    public function iterate(self $destination, Interval $interval) : Months\n    {\n        return $this->isAfter($destination)\n            ? $this->since($destination, $interval)\n            : $this->until($destination, $interval);\n    }\n\n    public function until(self $month, Interval $interval) : Months\n    {\n        if ($this->isAfter($month)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%s %d is after %s %d',\n                    $this->name(),\n                    $this->year->number(),\n                    $month->name(),\n                    $month->year->number(),\n                )\n            );\n        }\n\n        return Months::fromDateTimeIterator(\n            new DateTimeIntervalIterator(\n                $this->firstDay()->midnight(TimeZone::UTC()),\n                $month->firstDay()->midnight(TimeZone::UTC()),\n                RelativeTimeUnit::month(),\n                $interval\n            )\n        );\n    }\n\n    public function since(self $month, Interval $interval) : Months\n    {\n        if ($this->isBefore($month)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%s %d is before %s %d',\n                    $this->name(),\n                    $this->year->number(),\n                    $month->name(),\n                    $month->year->number(),\n                )\n            );\n        }\n\n        return Months::fromDateTimeIterator(\n            new DateTimeIntervalIterator(\n                $month->firstDay()->midnight(TimeZone::UTC()),\n                $this->firstDay()->midnight(TimeZone::UTC()),\n                RelativeTimeUnit::month(),\n                $interval\n            )\n        );\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $month) : bool\n    {\n        return $this->isEqualTo($month);\n    }\n\n    public function isEqualTo(self $month) : bool\n    {\n        return $this->number() == $month->number()\n            && $this->year()->isEqualTo($month->year());\n    }\n\n    public function isBefore(self $month) : bool\n    {\n        if ($this->year()->isBefore($month->year())) {\n            return true;\n        }\n\n        if ($this->year()->isAfter($month->year())) {\n            return false;\n        }\n\n        return $this->number() < $month->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBeforeOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isBeforeOrEqual(self $month) : bool\n    {\n        return $this->isBeforeOrEqualTo($month);\n    }\n\n    public function isBeforeOrEqualTo(self $month) : bool\n    {\n        if ($this->year()->isBefore($month->year())) {\n            return true;\n        }\n\n        if ($this->year()->isAfter($month->year())) {\n            return false;\n        }\n\n        return $this->number() <= $month->number();\n    }\n\n    public function isAfter(self $month) : bool\n    {\n        if ($this->year()->isAfter($month->year())) {\n            return true;\n        }\n\n        if ($this->year()->isBefore($month->year())) {\n            return false;\n        }\n\n        return $this->number() > $month->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfterOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isAfterOrEqual(self $month) : bool\n    {\n        return $this->isAfterOrEqualTo($month);\n    }\n\n    public function isAfterOrEqualTo(self $month) : bool\n    {\n        if ($this->year()->isAfter($month->year())) {\n            return true;\n        }\n\n        if ($this->year()->isBefore($month->year())) {\n            return false;\n        }\n\n        return $this->number() >= $month->number();\n    }\n\n    public function distance(self $to) : TimeUnit\n    {\n        return (new TimePeriod($this->firstDay()->midnight(TimeZone::UTC()), $to->firstDay()->midnight(TimeZone::UTC())))->distance();\n    }\n\n    public function quarter() : Quarter\n    {\n        return $this->year->quarter((int) \\ceil($this->number / 3));\n    }\n\n    public function compareTo(self $month) : int\n    {\n        if ($this->isEqualTo($month)) {\n            return 0;\n        }\n\n        return $this->isBefore($month)\n            ? -1\n            : 1;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/MonthDays.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n */\nfinal class MonthDays implements \\Countable\n{\n    private Month $month;\n\n    public function __construct(Month $month)\n    {\n        $this->month = $month;\n    }\n\n    public function count() : int\n    {\n        return $this->month->numberOfDays();\n    }\n\n    public function first() : Day\n    {\n        return new Day($this->month, 1);\n    }\n\n    public function last() : Day\n    {\n        return new Day($this->month, $this->month->numberOfDays());\n    }\n\n    /**\n     * @return array<int, Day>\n     */\n    public function all() : array\n    {\n        /** @psalm-suppress ImpureFunctionCall */\n        return \\array_map(\n            fn (int $dayNumber) : Day => new Day($this->month, $dayNumber),\n            \\range(1, $this->month->numberOfDays())\n        );\n    }\n\n    /**\n     * @param callable(Day $day) : void $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : void $iterator\n     *\n     * @return array<mixed>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map(\n            $iterator,\n            $this->all()\n        );\n    }\n\n    /**\n     * @param callable(Day $day) : bool $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : bool $iterator\n     *\n     * @return Days\n     */\n    public function filter(callable $iterator) : Days\n    {\n        return Days::fromArray(\n            ...\\array_filter(\n                $this->all(),\n                $iterator\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Months.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n *\n * @implements \\IteratorAggregate<Month>\n */\nfinal class Months implements \\Countable, \\IteratorAggregate\n{\n    /**\n     * @var \\Iterator<int|string, Month>\n     */\n    private \\Iterator $months;\n\n    /**\n     * @param \\Iterator<int|string, Month> $months\n     */\n    private function __construct(\\Iterator $months)\n    {\n        $this->months = $months;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromArray(Month ...$days) : self\n    {\n        /** @psalm-suppress ImpureMethodCall */\n        return new self(new \\ArrayIterator($days));\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromDateTimeIterator(DateTimeIntervalIterator $iterator) : self\n    {\n        /** @psalm-suppress ImpureMethodCall */\n        return new self(MonthsIterator::fromDateTimeIterator($iterator));\n    }\n\n    /**\n     * @return array<Month>\n     *\n     * @psalm-suppress ImpureFunctionCall\n     */\n    public function all() : array\n    {\n        return \\iterator_to_array($this->months);\n    }\n\n    /**\n     * @param callable(Month $month) : mixed $iterator\n     *\n     * @psalm-param pure-callable(Month $month) : mixed $iterator\n     *\n     * @return array<mixed>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map($iterator, $this->all());\n    }\n\n    /**\n     * @psalm-suppress InvalidScalarArgument\n     *\n     * @param callable(Month $month) : bool $iterator\n     *\n     * @psalm-param pure-callable(Month $month) : bool $iterator\n     */\n    public function filter(callable $iterator) : self\n    {\n        return new self(new \\CallbackFilterIterator($this->months, $iterator));\n    }\n\n    public function count() : int\n    {\n        return \\iterator_count($this->months);\n    }\n\n    /**\n     * @return \\Traversable<int|string, Month>\n     */\n    public function getIterator() : \\Traversable\n    {\n        return $this->months;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/MonthsIterator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nfinal class MonthsIterator extends \\IteratorIterator\n{\n    /**\n     * @param \\Traversable<DateTime> $iterator\n     */\n    private function __construct(\\Traversable $iterator)\n    {\n        parent::__construct($iterator);\n    }\n\n    /**\n     * @psalm-suppress MixedArgumentTypeCoercion\n     */\n    public static function fromDateTimeIterator(DateTimeIntervalIterator $iterator) : self\n    {\n        return new self($iterator);\n    }\n\n    public function current() : ?Month\n    {\n        /** @var null|DateTime|Month $current */\n        $current = parent::current();\n\n        if ($current === null) {\n            return null;\n        }\n\n        if ($current instanceof Month) {\n            return $current;\n        }\n\n        return $current->month();\n    }\n\n    public function reverse() : self\n    {\n        /** @phpstan-ignore-next-line  */\n        return new self(new \\ArrayIterator(\\array_reverse(\\iterator_to_array($this))));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Psr20CalendarAdapter.php",
    "content": "<?php\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Psr\\Clock\\ClockInterface;\n\nfinal class Psr20CalendarAdapter implements ClockInterface\n{\n    public function __construct(private readonly Calendar $calendar)\n    {\n    }\n\n    public function now() : \\DateTimeImmutable\n    {\n        return $this->calendar->now()->toDateTimeImmutable();\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Quarter.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\n\n/**\n * @psalm-immutable\n */\nfinal class Quarter\n{\n    private int $number;\n\n    private Months $months;\n\n    public function __construct(int $number, Months $months)\n    {\n        if ($number < 1 || $number > 4) {\n            throw new InvalidArgumentException('Quarter number must be greater or equal 1 and less or equal than 4');\n        }\n\n        if (\\count($months) !== 3) {\n            throw new InvalidArgumentException('Quarter must have exactly 3 months');\n        }\n\n        switch ($number) {\n            case 1:\n                /* @phpstan-ignore-next-line  */\n                if ($months->map(fn (Month $month) => $month->number()) !== [1, 2, 3]) {\n                    throw new InvalidArgumentException('Quarter 1 must must have Jan, Feb and Mar');\n                }\n\n                break;\n            case 2:\n                /* @phpstan-ignore-next-line  */\n                if ($months->map(fn (Month $month) => $month->number()) !== [4, 5, 6]) {\n                    throw new InvalidArgumentException('Quarter 2 must must have Apr, May and Jun');\n                }\n\n                break;\n            case 3:\n                /* @phpstan-ignore-next-line  */\n                if ($months->map(fn (Month $month) => $month->number()) !== [7, 8, 9]) {\n                    throw new InvalidArgumentException('Quarter 3 must must have Jul, Aug and Sep');\n                }\n\n                break;\n            case 4:\n                /* @phpstan-ignore-next-line  */\n                if ($months->map(fn (Month $month) => $month->number()) !== [10, 11, 12]) {\n                    throw new InvalidArgumentException('Quarter 4 must must have Oct, Nov and Dec');\n                }\n\n                break;\n        }\n\n        $this->number = $number;\n        $this->months = $months;\n    }\n\n    public function number() : int\n    {\n        return $this->number;\n    }\n\n    public function months() : Months\n    {\n        return $this->months;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Time.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class Time\n{\n    private const PRECISION_MICROSECOND = 6;\n\n    private int $hour;\n\n    private int $minute;\n\n    private int $second;\n\n    private int $microsecond;\n\n    public function __construct(int $hour, int $minute, int $second, int $microsecond = 0)\n    {\n        if ($hour < 0 || $hour > 23) {\n            throw new InvalidArgumentException('Hour must be greater or equal 0 and less or equal than 23');\n        }\n\n        if ($minute < 0 || $minute >= 60) {\n            throw new InvalidArgumentException('Minut must be greater or equal 0 and less than 60');\n        }\n\n        if ($second < 0 || $second >= 60) {\n            throw new InvalidArgumentException('Second must be greater or equal 0 and less than 60');\n        }\n\n        if ($microsecond < 0 || $microsecond >= 1000000) {\n            throw new InvalidArgumentException('Microsecond must be greater or equal 0 and less than 1000000');\n        }\n\n        $this->hour = $hour;\n        $this->minute = $minute;\n        $this->second = $second;\n        $this->microsecond = $microsecond;\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     * @psalm-suppress PossiblyNullArrayAccess\n     * @psalm-suppress PossiblyInvalidArgument\n     */\n    public static function fromDateTime(\\DateTimeInterface $dateTime) : self\n    {\n        /**\n         * @phpstan-ignore-next-line\n         */\n        [$hour, $minute, $second, $microsecond] = \\sscanf($dateTime->format('H-i-s.u'), '%d-%d-%d.%d');\n\n        /** @phpstan-ignore-next-line */\n        return new self($hour, $minute, $second, $microsecond);\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromString(string $time) : self\n    {\n        try {\n            return self::fromDateTime(new \\DateTimeImmutable($time));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"Value \\\"{$time}\\\" is not valid time format.\");\n        }\n    }\n\n    /**\n     * @return array{hour: int, minute: int, second: int, microsecond: int}\n     */\n    public function __debugInfo() : array\n    {\n        return [\n            'hour' => $this->hour,\n            'minute' => $this->minute,\n            'second' => $this->second,\n            'microsecond' => $this->microsecond,\n        ];\n    }\n\n    /**\n     * @return array{hour: int, minute: int, second: int, microsecond: int}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'hour' => $this->hour,\n            'minute' => $this->minute,\n            'second' => $this->second,\n            'microsecond' => $this->microsecond,\n        ];\n    }\n\n    /**\n     * @param array{hour: int, minute: int, second: int, microsecond: int} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->hour = $data['hour'];\n        $this->minute = $data['minute'];\n        $this->second = $data['second'];\n        $this->microsecond = $data['microsecond'];\n    }\n\n    public function format(string $format) : string\n    {\n        return $this->toDateTimeImmutable()->format($format);\n    }\n\n    public function toTimeUnit() : TimeUnit\n    {\n        return TimeUnit::positive($this->second, $this->microsecond)\n            ->add(TimeUnit::minutes($this->minute))\n            ->add(TimeUnit::hours($this->hour));\n    }\n\n    public function toString() : string\n    {\n        return \\str_pad((string) $this->hour, 2, '0', STR_PAD_LEFT) . ':'\n            . \\str_pad((string) $this->minute, 2, '0', STR_PAD_LEFT) . ':'\n            . \\str_pad((string) $this->second, 2, '0', STR_PAD_LEFT) . '.'\n            . \\str_pad((string) $this->microsecond, self::PRECISION_MICROSECOND, '0', STR_PAD_LEFT);\n    }\n\n    public function hour() : int\n    {\n        return $this->hour;\n    }\n\n    public function minute() : int\n    {\n        return $this->minute;\n    }\n\n    public function second() : int\n    {\n        return $this->second;\n    }\n\n    public function microsecond() : int\n    {\n        return $this->microsecond;\n    }\n\n    public function millisecond() : int\n    {\n        return \\intval($this->microsecond / 1000);\n    }\n\n    public function isAM() : bool\n    {\n        return $this->toDateTimeImmutable()->format('a') === 'am';\n    }\n\n    public function isPM() : bool\n    {\n        return $this->toDateTimeImmutable()->format('a') === 'pm';\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfter` instead. Will be removed with 2.0\n     */\n    public function isGreaterThan(self $time) : bool\n    {\n        return $this->isAfter($time);\n    }\n\n    public function isAfter(self $time) : bool\n    {\n        $dateTimeImmutable = $this->toDateTimeImmutable();\n        $nextDateTimeImmutable = $dateTimeImmutable->setTime($time->hour, $time->minute, $time->second, $time->microsecond);\n\n        return $dateTimeImmutable > $nextDateTimeImmutable;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfterOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isGreaterThanEq(self $time) : bool\n    {\n        return $this->isAfterOrEqualTo($time);\n    }\n\n    public function isAfterOrEqualTo(self $time) : bool\n    {\n        $dateTimeImmutable = $this->toDateTimeImmutable();\n        $nextDateTimeImmutable = $dateTimeImmutable->setTime($time->hour, $time->minute, $time->second, $time->microsecond);\n\n        return $dateTimeImmutable >= $nextDateTimeImmutable;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $time) : bool\n    {\n        return $this->isEqualTo($time);\n    }\n\n    public function isEqualTo(self $time) : bool\n    {\n        $dateTimeImmutable = $this->toDateTimeImmutable();\n        $nextDateTimeImmutable = $dateTimeImmutable->setTime($time->hour, $time->minute, $time->second, $time->microsecond);\n\n        return $dateTimeImmutable == $nextDateTimeImmutable;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBefore` instead. Will be removed with 2.0\n     */\n    public function isLessThan(self $time) : bool\n    {\n        return $this->isBefore($time);\n    }\n\n    public function isBefore(self $time) : bool\n    {\n        $dateTimeImmutable = $this->toDateTimeImmutable();\n        $nextDateTimeImmutable = $dateTimeImmutable->setTime($time->hour, $time->minute, $time->second, $time->microsecond);\n\n        return $dateTimeImmutable < $nextDateTimeImmutable;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBeforeOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isLessThanEq(self $time) : bool\n    {\n        return $this->isBeforeOrEqualTo($time);\n    }\n\n    public function isBeforeOrEqualTo(self $time) : bool\n    {\n        $dateTimeImmutable = $this->toDateTimeImmutable();\n        $nextDateTimeImmutable = $dateTimeImmutable->setTime($time->hour, $time->minute, $time->second, $time->microsecond);\n\n        return $dateTimeImmutable <= $nextDateTimeImmutable;\n    }\n\n    public function isMidnight() : bool\n    {\n        return $this->hour() === 0\n            && $this->minute() === 0\n            && $this->second() === 0\n            && $this->microsecond() === 0;\n    }\n\n    public function isNotMidnight() : bool\n    {\n        return !$this->isMidnight();\n    }\n\n    public function add(TimeUnit $timeUnit) : self\n    {\n        return self::fromDateTime($this->toDateTimeImmutable()->add($timeUnit->toDateInterval()));\n    }\n\n    public function sub(TimeUnit $timeUnit) : self\n    {\n        /** @psalm-suppress PossiblyFalseArgument */\n        return self::fromDateTime($this->toDateTimeImmutable()->sub($timeUnit->toDateInterval()));\n    }\n\n    public function compareTo(self $time) : int\n    {\n        if ($this->isEqualTo($time)) {\n            return 0;\n        }\n\n        return $this->isBefore($time)\n            ? -1\n            : 1;\n    }\n\n    private function toDateTimeImmutable() : \\DateTimeImmutable\n    {\n        return (new \\DateTimeImmutable('now', new \\DateTimeZone('UTC')))\n            ->setTime(\n                $this->hour,\n                $this->minute,\n                $this->second,\n                $this->microsecond\n            );\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimeEpoch.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class TimeEpoch\n{\n    public const UNIX = 0;\n\n    public const UTC = 1;\n\n    public const TAI = 2;\n\n    public const GPS = 3;\n\n    private int $type;\n\n    private DateTime $dateTime;\n\n    private function __construct(int $type, DateTime $dateTime)\n    {\n        $this->type = $type;\n        $this->dateTime = $dateTime;\n    }\n\n    /**\n     * Unix Epoch, started at 1970-01-01 00:00:00 UTC, not including leap seconds.\n     *\n     * @psalm-pure\n     */\n    public static function UNIX() : self\n    {\n        return new self(self::UNIX, DateTime::fromString('1970-01-01 00:00:00 UTC'));\n    }\n\n    /**\n     * Other name for UNIX epoch.\n     *\n     * @psalm-pure\n     */\n    public static function POSIX() : self\n    {\n        return self::UNIX();\n    }\n\n    /**\n     * UTC Epoch, started at 1972-01-01 00:00:00 UTC, including leap seconds.\n     *\n     * @psalm-pure\n     */\n    public static function UTC() : self\n    {\n        return new self(self::UTC, DateTime::fromString('1972-01-01 00:00:00 UTC'));\n    }\n\n    /**\n     * GPS Epoch, started at 1980-01-06 00:00:00 UTC, including leap seconds\n     * except first 9 there were added before epoch.\n     *\n     * @psalm-pure\n     */\n    public static function GPS() : self\n    {\n        return new self(self::GPS, DateTime::fromString('1980-01-06 00:00:00 UTC'));\n    }\n\n    /**\n     * TAI Epoch, started at 1958-01-01 00:00:00 UTC, including leap seconds.\n     *\n     * @psalm-pure\n     */\n    public static function TAI() : self\n    {\n        return new self(self::TAI, DateTime::fromString('1958-01-01 00:00:00 UTC'));\n    }\n\n    public function type() : int\n    {\n        return $this->type;\n    }\n\n    public function date() : DateTime\n    {\n        return $this->dateTime;\n    }\n\n    /**\n     * Returns difference in seconds between epoches without leap seconds.\n     */\n    public function distanceTo(self $timeEpoch) : TimeUnit\n    {\n        switch ($this->type) {\n            case self::UTC:\n                switch ($timeEpoch->type()) {\n                    case self::GPS:\n                        return TimeUnit::seconds(252892800); // 1972-01-01 00:00:00 UTC - 1980-01-06 00:00:00 UTC\n                    case self::UNIX:\n                        return TimeUnit::seconds(63072000)->invert();  // 1972-01-01 00:00:00 UTC - 1970-01-01 00:00:00 UTC\n                    case self::TAI:\n                        return TimeUnit::seconds(441763200)->invert(); // 1972-01-01 00:00:00 UTC - 1958-01-01 00:00:00 UTC\n\n                    default:\n                        return TimeUnit::seconds(0);\n                }\n                // no break\n            case self::GPS:\n                switch ($timeEpoch->type()) {\n                    case self::UTC:\n                        return TimeUnit::seconds(252892800)->invert(); // 1980-01-06 00:00:00 UTC - 1972-01-01 00:00:00 UTC\n                    case self::UNIX:\n                        return TimeUnit::seconds(315964800)->invert(); // 1980-01-06 00:00:00 UTC - 1970-01-01 00:00:00 UTC\n                    case self::TAI:\n                        return TimeUnit::seconds(694656000)->invert(); // 1980-01-06 00:00:00 UTC - 1958-01-01 00:00:00 UTC\n\n                    default:\n                        return TimeUnit::seconds(0);\n                }\n                // no break\n            case self::TAI:\n                switch ($timeEpoch->type()) {\n                    case self::UTC:\n                        return TimeUnit::seconds(441763200); // 1958-01-01 00:00:00 UTC - 1972-01-01 00:00:00 UTC\n                    case self::UNIX:\n                        return TimeUnit::seconds(378691200); // 1958-01-01 00:00:00 UTC - 1970-01-00 00:00:00 UTC\n                    case self::GPS:\n                        return TimeUnit::seconds(694656000); // 1958-01-01 00:00:00 UTC - 1980-01-06 00:00:00 UTC\n\n                    default:\n                        return TimeUnit::seconds(0);\n                }\n\n                // no break\n            default: // UNIX\n                switch ($timeEpoch->type()) {\n                    case self::UTC:\n                        return TimeUnit::seconds(63072000);  // 1970-01-01 00:00:00 UTC - 1972-01-01 00:00:00 UTC\n                    case self::GPS:\n                        return TimeUnit::seconds(315964800); // 1970-01-01 00:00:00 UTC - 1980-01-06 00:00:00 UTC\n                    case self::TAI:\n                        return TimeUnit::seconds(378691200)->invert(); // 1970-01-01 00:00:00 UTC - 1958-01-01 00:00:00 UTC\n\n                    default:\n                        return TimeUnit::seconds(0);\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimePeriod.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\nuse Aeon\\Calendar\\Unit;\n\n/**\n * @psalm-immutable\n */\nfinal class TimePeriod\n{\n    private DateTime $start;\n\n    private DateTime $end;\n\n    public function __construct(DateTime $start, DateTime $end)\n    {\n        $this->start = $start;\n        $this->end = $end;\n    }\n\n    /**\n     * @return array{start: DateTime, end: DateTime}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'start' => $this->start,\n            'end' => $this->end,\n        ];\n    }\n\n    public function start() : DateTime\n    {\n        return $this->start;\n    }\n\n    public function end() : DateTime\n    {\n        return $this->end;\n    }\n\n    public function isForward() : bool\n    {\n        return $this->distance()->isPositive();\n    }\n\n    public function isBackward() : bool\n    {\n        return $this->distance()->isNegative();\n    }\n\n    /**\n     * Calculate distance between 2 points in time without leap seconds.\n     */\n    public function distance() : TimeUnit\n    {\n        $startUnixTimestamp = $this->start->timestampUNIX();\n        $endUnixTimestamp = $this->end->timestampUNIX();\n\n        $result = $endUnixTimestamp\n            ->sub($startUnixTimestamp)\n            ->toPositive();\n\n        return $this->start->isAfter($this->end) ? $result->invert() : $result;\n    }\n\n    public function leapSeconds() : LeapSeconds\n    {\n        return LeapSeconds::load()->findAllBetween($this);\n    }\n\n    /**\n     * @psalm-suppress ImpureMethodCall\n     */\n    public function iterate(Unit $timeUnit, Interval $interval) : TimePeriods\n    {\n        return TimePeriods::fromIterator(new TimePeriodsIterator($this->start, $this->end, $timeUnit, $interval));\n    }\n\n    /**\n     * @psalm-suppress ImpureMethodCall\n     */\n    public function iterateBackward(Unit $timeUnit, Interval $interval) : TimePeriods\n    {\n        return TimePeriods::fromIterator(new TimePeriodsIterator($this->end, $this->start, $timeUnit->toNegative(), $interval));\n    }\n\n    public function overlaps(self $timePeriod) : bool\n    {\n        if ($this->isBackward()) {\n            $thisPeriodForward = $this->revert();\n        } else {\n            $thisPeriodForward = $this;\n        }\n\n        if ($timePeriod->isBackward()) {\n            $otherPeriodForward = $timePeriod->revert();\n        } else {\n            $otherPeriodForward = $timePeriod;\n        }\n\n        $thisPeriodStart = $thisPeriodForward->start();\n        $thisPeriodEnd = $thisPeriodForward->end();\n        $otherPeriodStart = $otherPeriodForward->start();\n        $otherPeriodEnd = $otherPeriodForward->end();\n\n        if ($thisPeriodForward->abuts($otherPeriodForward)) {\n            return false;\n        }\n\n        if ($thisPeriodStart->isBefore($otherPeriodStart) &&\n            $thisPeriodEnd->isBefore($otherPeriodStart) &&\n            $thisPeriodEnd->isBefore($otherPeriodEnd)\n        ) {\n            return false;\n        }\n\n        if ($thisPeriodEnd->isBefore($otherPeriodEnd)) {\n            return true;\n        }\n\n        if ($thisPeriodStart->isAfter($otherPeriodStart) &&\n            $thisPeriodStart->isBefore($otherPeriodEnd) &&\n            $thisPeriodEnd->isAfter($otherPeriodStart)\n        ) {\n            return true;\n        }\n\n        if ($thisPeriodStart->isAfter($otherPeriodStart) &&\n            $thisPeriodEnd->isAfter($otherPeriodStart) &&\n            $thisPeriodEnd->isAfter($otherPeriodEnd)\n        ) {\n            return false;\n        }\n\n        return true;\n    }\n\n    public function contains(self $timePeriod) : bool\n    {\n        return $this->start->isBeforeOrEqualTo($timePeriod->start()) && $this->end->isAfterOrEqualTo($timePeriod->end());\n    }\n\n    public function revert() : self\n    {\n        return new self($this->end(), $this->start());\n    }\n\n    public function merge(self $timePeriod) : self\n    {\n        if (!$this->overlaps($timePeriod) && !$this->abuts($timePeriod)) {\n            throw new InvalidArgumentException(\"Can't merge not overlapping time periods.\");\n        }\n\n        return new self(\n            $this->start->isBeforeOrEqualTo($timePeriod->start)\n                ? $this->start()\n                : $timePeriod->start,\n            $this->end->isAfterOrEqualTo($timePeriod->end)\n                ? $this->end()\n                : $timePeriod->end()\n        );\n    }\n\n    public function abuts(self $timePeriod) : bool\n    {\n        $thisPeriodForward = $this->isBackward()\n            ? $this->revert()\n            : $this;\n\n        $otherPeriodForward = $timePeriod->isBackward()\n            ? $timePeriod->revert()\n            : $timePeriod;\n\n        if ($thisPeriodForward->end()->isEqualTo($otherPeriodForward->start())) {\n            return true;\n        }\n\n        if ($thisPeriodForward->start()->isEqualTo($otherPeriodForward->end())) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $period) : bool\n    {\n        return $this->isEqualTo($period);\n    }\n\n    public function isEqualTo(self $period) : bool\n    {\n        return $this->start->isEqualTo($period->start()) && $this->end->isEqualTo($period->end());\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimePeriods.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @implements \\IteratorAggregate<TimePeriod>\n */\nfinal class TimePeriods implements \\Countable, \\IteratorAggregate\n{\n    /**\n     * @var \\Iterator<int|string, TimePeriod>\n     */\n    private \\Iterator $periods;\n\n    /**\n     * @param \\Iterator<int|string, TimePeriod> $periods\n     */\n    private function __construct(\\Iterator $periods)\n    {\n        $this->periods = $periods;\n    }\n\n    public static function fromArray(TimePeriod ...$periods) : self\n    {\n        return new self(new \\ArrayIterator($periods));\n    }\n\n    public static function fromIterator(TimePeriodsIterator $timePeriodsIterator) : self\n    {\n        return new self($timePeriodsIterator);\n    }\n\n    /**\n     * @return array<TimePeriod>\n     */\n    public function all() : array\n    {\n        return \\iterator_to_array($this->periods);\n    }\n\n    /**\n     * @param callable(TimePeriod $timePeriod) : void $iterator\n     *\n     * @psalm-param pure-callable(TimePeriod $timePeriod) : void $iterator\n     */\n    public function each(callable $iterator) : void\n    {\n        foreach ($this->periods as $period) {\n            $iterator($period);\n        }\n    }\n\n    /**\n     * @param callable(TimePeriod $timePeriod) : mixed $iterator\n     *\n     * @psalm-param pure-callable(TimePeriod $timePeriod) : mixed $iterator\n     *\n     * @return array<mixed>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map($iterator, $this->all());\n    }\n\n    /**\n     * @psalm-suppress InvalidScalarArgument\n     *\n     * @param callable(TimePeriod $timePeriod) : bool $iterator\n     *\n     * @psalm-param pure-callable(TimePeriod $timePeriod) : bool $iterator\n     *\n     * @return self\n     */\n    public function filter(callable $iterator) : self\n    {\n        return new self(new \\CallbackFilterIterator($this->periods, $iterator));\n    }\n\n    public function count() : int\n    {\n        return \\iterator_count($this->periods);\n    }\n\n    /**\n     * @return \\Traversable<int|string, TimePeriod>\n     */\n    public function getIterator() : \\Traversable\n    {\n        return $this->periods;\n    }\n\n    /**\n     * Find all gaps between time periods.\n     */\n    public function gaps() : self\n    {\n        $periods = \\array_map(\n            function (TimePeriod $timePeriod) : TimePeriod {\n                return $timePeriod->isBackward() ? $timePeriod->revert() : $timePeriod;\n            },\n            $this->sort()->all()\n        );\n\n        if (!\\count($periods)) {\n            return self::fromArray(...[]);\n        }\n\n        $gaps = [];\n        $totalPeriod = \\current($periods);\n\n        while ($period = \\next($periods)) {\n            if ($totalPeriod->overlaps($period) || $totalPeriod->abuts($period)) {\n                $totalPeriod = $totalPeriod->merge($period);\n            } else {\n                $gaps[] = new TimePeriod($totalPeriod->end(), $period->start());\n                $totalPeriod = $period;\n            }\n        }\n\n        return self::fromArray(...$gaps);\n    }\n\n    public function sort() : self\n    {\n        return $this->sortBy(TimePeriodsSort::asc());\n    }\n\n    public function sortBy(TimePeriodsSort $sort) : self\n    {\n        $periods = $this->all();\n\n        \\uasort(\n            $periods,\n            function (TimePeriod $timePeriodA, TimePeriod $timePeriodB) use ($sort) : int {\n                if ($sort->byStartDate()) {\n                    return $sort->isAscending()\n                        ? $timePeriodA->start()->toDateTimeImmutable() <=> $timePeriodB->start()->toDateTimeImmutable()\n                        : $timePeriodB->start()->toDateTimeImmutable() <=> $timePeriodA->start()->toDateTimeImmutable();\n                }\n\n                return $sort->isAscending()\n                    ? $timePeriodA->end()->toDateTimeImmutable() <=> $timePeriodB->end()->toDateTimeImmutable()\n                    : $timePeriodB->end()->toDateTimeImmutable() <=> $timePeriodA->end()->toDateTimeImmutable();\n            }\n        );\n\n        return self::fromArray(...$periods);\n    }\n\n    public function first() : ?TimePeriod\n    {\n        $this->periods->rewind();\n\n        return $this->periods->current();\n    }\n\n    public function last() : ?TimePeriod\n    {\n        $periods = $this->all();\n\n        if (!\\count($periods)) {\n            return null;\n        }\n\n        return \\end($periods);\n    }\n\n    public function add(TimePeriod ...$timePeriods) : self\n    {\n        return self::fromArray(...\\array_merge($this->all(), $timePeriods));\n    }\n\n    public function merge(self $timePeriods) : self\n    {\n        return self::fromArray(...\\array_merge($this->all(), $timePeriods->all()));\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $periods) : bool\n    {\n        return $this->isEqualTo($periods);\n    }\n\n    public function isEqualTo(self $periods) : bool\n    {\n        if ($periods->count() !== $this->count()) {\n            return false;\n        }\n\n        $selfArray = \\array_values($this->sort()->all());\n        $periodsArray = \\array_values($periods->sort()->all());\n\n        foreach ($selfArray as $i => $timePeriod) {\n            if (!$periodsArray[$i]->isEqualTo($timePeriod)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimePeriodsIterator.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Unit;\n\nfinal class TimePeriodsIterator extends \\FilterIterator\n{\n    private Interval $interval;\n\n    public function __construct(DateTime $start, DateTime $end, Unit $timeUnit, Interval $interval)\n    {\n        $this->interval = $interval;\n\n        parent::__construct(new DateTimeIterator($start, $end, $timeUnit));\n    }\n\n    public function current() : ?TimePeriod\n    {\n        $dateTimeIterator = $this->getInnerIterator();\n\n        $end = $dateTimeIterator->end();\n\n        $currentStart = $dateTimeIterator->current();\n        $currentEnd = $dateTimeIterator->current()->add($dateTimeIterator->unit());\n\n        if ($dateTimeIterator->isForward()) {\n            if ($currentEnd->isAfterOrEqualTo($end)) {\n                $currentEnd = $end;\n            }\n        } else {\n            if ($currentEnd->isBeforeOrEqualTo($end)) {\n                $currentEnd = $end;\n            }\n        }\n\n        return new TimePeriod($currentStart, $currentEnd);\n    }\n\n    public function accept() : bool\n    {\n        $dateTimeIterator = $this->getInnerIterator();\n        $current = $dateTimeIterator->current();\n        $end = $dateTimeIterator->end();\n\n        if ($dateTimeIterator->isForward()) {\n            if ($current->isAfterOrEqualTo($end)) {\n                return false;\n            }\n\n            if ($this->interval->isRightOpen() && $current->add($dateTimeIterator->unit())->isAfterOrEqualTo($end)) {\n                return false;\n            }\n\n            if ($this->interval->isLeftOpen() && $dateTimeIterator->isFirst()) {\n                return false;\n            }\n        } else {\n            if ($current->isBeforeOrEqualTo($end)) {\n                return false;\n            }\n\n            if (($this->interval->isRightOpen()) && $dateTimeIterator->isFirst()) {\n                return false;\n            }\n\n            if (($this->interval->isLeftOpen()) && $current->add($dateTimeIterator->unit())->isBeforeOrEqualTo($end)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public function key() : int\n    {\n        $dateTimeIterator = $this->getInnerIterator();\n\n        if ($dateTimeIterator->isForward()) {\n            if ($this->interval->isLeftOpen()) {\n                return $dateTimeIterator->key() - 1;\n            }\n        } else {\n            if ($this->interval->isRightOpen()) {\n                return $dateTimeIterator->key() - 1;\n            }\n        }\n\n        return $dateTimeIterator->key();\n    }\n\n    /**\n     * @psalm-suppress InvalidReturnType\n     * @psalm-suppress InvalidReturnStatement\n     */\n    public function getInnerIterator() : DateTimeIterator\n    {\n        /** @phpstan-ignore-next-line  */\n        return parent::getInnerIterator();\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimePeriodsSort.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n */\nfinal class TimePeriodsSort\n{\n    private const START_DATE_ASC = 1;\n\n    private const START_DATE_DESC = 2;\n\n    private const END_DATE_ASC = 3;\n\n    private const END_DATE_DESC = 4;\n\n    private int $type;\n\n    private function __construct(int $type)\n    {\n        $this->type = $type;\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function asc() : self\n    {\n        return self::startDate();\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function desc() : self\n    {\n        return self::startDate(false);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function startDate(bool $ascending = true) : self\n    {\n        return new self($ascending ? self::START_DATE_ASC : self::START_DATE_DESC);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function endDate(bool $ascending = true) : self\n    {\n        return new self($ascending ? self::END_DATE_ASC : self::END_DATE_DESC);\n    }\n\n    public function byStartDate() : bool\n    {\n        return $this->type === self::START_DATE_ASC || $this->type === self::START_DATE_DESC;\n    }\n\n    public function isAscending() : bool\n    {\n        return $this->type === self::START_DATE_ASC || $this->type === self::END_DATE_ASC;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimeZone/TimeOffset.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian\\TimeZone;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class TimeOffset\n{\n    private const OFFSET_REGEXP = '/^(GMT)?([+-]?)(2[0-3]|[01][0-9]):?([0-5][0-9])$/';\n\n    private bool $negative;\n\n    private int $hours;\n\n    private int $minutes;\n\n    private function __construct(bool $negative, int $hours, int $minutes)\n    {\n        $this->negative = $negative;\n        $this->hours = $hours;\n        $this->minutes = $minutes;\n    }\n\n    /** @psalm-pure */\n    public static function UTC() : self\n    {\n        return new self(false, 0, 0);\n    }\n\n    /** @psalm-pure */\n    public static function fromString(string $offset) : self\n    {\n        if (!\\preg_match(self::OFFSET_REGEXP, $offset, $matches)) {\n            throw new InvalidArgumentException(\"\\\"{$offset}\\\" is not a valid UTC Offset.\");\n        }\n\n        return new self($matches[2] === '-', (int) $matches[3], (int) $matches[4]);\n    }\n\n    /** @psalm-pure */\n    public static function isValid(string $offset) : bool\n    {\n        return (bool) \\preg_match(self::OFFSET_REGEXP, $offset, $matches);\n    }\n\n    /** @psalm-pure */\n    public static function fromTimeUnit(TimeUnit $timeUnit) : self\n    {\n        return self::fromString(\n            ($timeUnit->isNegative() ? '-' : '+')\n                . \\str_pad((string) $timeUnit->inHoursAbs(), 2, '0', STR_PAD_LEFT)\n                . \\str_pad((string) $timeUnit->inTimeMinutes(), 2, '0', STR_PAD_LEFT)\n        );\n    }\n\n    /**\n     * @return array{hours: int, minutes: int, negative: bool}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'hours' => $this->hours,\n            'minutes' => $this->minutes,\n            'negative' => $this->negative,\n        ];\n    }\n\n    public function toString() : string\n    {\n        return ($this->negative ? '-' : '+')\n            . \\str_pad((string) $this->hours, 2, '0', STR_PAD_LEFT) . ':' . \\str_pad((string) $this->minutes, 2, '0', STR_PAD_LEFT);\n    }\n\n    public function toTimeUnit() : TimeUnit\n    {\n        return $this->negative\n            ? TimeUnit::minutes($this->minutes)->add(TimeUnit::hours($this->hours))->invert()\n            : TimeUnit::minutes($this->minutes)->add(TimeUnit::hours($this->hours));\n    }\n\n    public function toDateTimeZone() : \\DateTimeZone\n    {\n        return new \\DateTimeZone($this->toString());\n    }\n\n    public function isUTC() : bool\n    {\n        return $this->hours === 0 && $this->minutes === 0;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $timeOffset) : bool\n    {\n        return $this->isEqualTo($timeOffset);\n    }\n\n    public function isEqualTo(self $timeOffset) : bool\n    {\n        return $this->negative === $timeOffset->negative\n            && $this->hours === $timeOffset->hours\n            && $this->minutes === $timeOffset->minutes;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/TimeZone.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\TimeZone\\TimeOffset;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @method static self africaAbidjan()\n * @method static self africaAccra()\n * @method static self africaAddisAbaba()\n * @method static self africaAlgiers()\n * @method static self africaAsmara()\n * @method static self africaBamako()\n * @method static self africaBangui()\n * @method static self africaBanjul()\n * @method static self africaBissau()\n * @method static self africaBlantyre()\n * @method static self africaBrazzaville()\n * @method static self africaBujumbura()\n * @method static self africaCairo()\n * @method static self africaCasablanca()\n * @method static self africaCeuta()\n * @method static self africaConakry()\n * @method static self africaDakar()\n * @method static self africaDarEsSalaam()\n * @method static self africaDjibouti()\n * @method static self africaDouala()\n * @method static self africaElAaiun()\n * @method static self africaFreetown()\n * @method static self africaGaborone()\n * @method static self africaHarare()\n * @method static self africaJohannesburg()\n * @method static self africaJuba()\n * @method static self africaKampala()\n * @method static self africaKhartoum()\n * @method static self africaKigali()\n * @method static self africaKinshasa()\n * @method static self africaLagos()\n * @method static self africaLibreville()\n * @method static self africaLome()\n * @method static self africaLuanda()\n * @method static self africaLubumbashi()\n * @method static self africaLusaka()\n * @method static self africaMalabo()\n * @method static self africaMaputo()\n * @method static self africaMaseru()\n * @method static self africaMbabane()\n * @method static self africaMogadishu()\n * @method static self africaMonrovia()\n * @method static self africaNairobi()\n * @method static self africaNdjamena()\n * @method static self africaNiamey()\n * @method static self africaNouakchott()\n * @method static self africaOuagadougou()\n * @method static self africaPortoNovo()\n * @method static self africaSaoTome()\n * @method static self africaTripoli()\n * @method static self africaTunis()\n * @method static self africaWindhoek()\n * @method static self americaAdak()\n * @method static self americaAnchorage()\n * @method static self americaAnguilla()\n * @method static self americaAntigua()\n * @method static self americaAraguaina()\n * @method static self americaArgentinaBuenosAires()\n * @method static self americaArgentinaCatamarca()\n * @method static self americaArgentinaCordoba()\n * @method static self americaArgentinaJujuy()\n * @method static self americaArgentinaLaRioja()\n * @method static self americaArgentinaMendoza()\n * @method static self americaArgentinaRioGallegos()\n * @method static self americaArgentinaSalta()\n * @method static self americaArgentinaSanJuan()\n * @method static self americaArgentinaSanLuis()\n * @method static self americaArgentinaTucuman()\n * @method static self americaArgentinaUshuaia()\n * @method static self americaAruba()\n * @method static self americaAsuncion()\n * @method static self americaAtikokan()\n * @method static self americaBahia()\n * @method static self americaBahiaBanderas()\n * @method static self americaBarbados()\n * @method static self americaBelem()\n * @method static self americaBelize()\n * @method static self americaBlancSablon()\n * @method static self americaBoaVista()\n * @method static self americaBogota()\n * @method static self americaBoise()\n * @method static self americaCambridgeBay()\n * @method static self americaCampoGrande()\n * @method static self americaCancun()\n * @method static self americaCaracas()\n * @method static self americaCayenne()\n * @method static self americaCayman()\n * @method static self americaChicago()\n * @method static self americaChihuahua()\n * @method static self americaCostaRica()\n * @method static self americaCreston()\n * @method static self americaCuiaba()\n * @method static self americaCuracao()\n * @method static self americaDanmarkshavn()\n * @method static self americaDawson()\n * @method static self americaDawsonCreek()\n * @method static self americaDenver()\n * @method static self americaDetroit()\n * @method static self americaDominica()\n * @method static self americaEdmonton()\n * @method static self americaEirunepe()\n * @method static self americaElSalvador()\n * @method static self americaFortNelson()\n * @method static self americaFortaleza()\n * @method static self americaGlaceBay()\n * @method static self americaGooseBay()\n * @method static self americaGrandTurk()\n * @method static self americaGrenada()\n * @method static self americaGuadeloupe()\n * @method static self americaGuatemala()\n * @method static self americaGuayaquil()\n * @method static self americaGuyana()\n * @method static self americaHalifax()\n * @method static self americaHavana()\n * @method static self americaHermosillo()\n * @method static self americaIndianaIndianapolis()\n * @method static self americaIndianaKnox()\n * @method static self americaIndianaMarengo()\n * @method static self americaIndianaPetersburg()\n * @method static self americaIndianaTellCity()\n * @method static self americaIndianaVevay()\n * @method static self americaIndianaVincennes()\n * @method static self americaIndianaWinamac()\n * @method static self americaInuvik()\n * @method static self americaIqaluit()\n * @method static self americaJamaica()\n * @method static self americaJuneau()\n * @method static self americaKentuckyLouisville()\n * @method static self americaKentuckyMonticello()\n * @method static self americaKralendijk()\n * @method static self americaLaPaz()\n * @method static self americaLima()\n * @method static self americaLosAngeles()\n * @method static self americaLowerPrinces()\n * @method static self americaMaceio()\n * @method static self americaManagua()\n * @method static self americaManaus()\n * @method static self americaMarigot()\n * @method static self americaMartinique()\n * @method static self americaMatamoros()\n * @method static self americaMazatlan()\n * @method static self americaMenominee()\n * @method static self americaMerida()\n * @method static self americaMetlakatla()\n * @method static self americaMexicoCity()\n * @method static self americaMiquelon()\n * @method static self americaMoncton()\n * @method static self americaMonterrey()\n * @method static self americaMontevideo()\n * @method static self americaMontserrat()\n * @method static self americaNassau()\n * @method static self americaNewYork()\n * @method static self americaNipigon()\n * @method static self americaNome()\n * @method static self americaNoronha()\n * @method static self americaNorthDakotaBeulah()\n * @method static self americaNorthDakotaCenter()\n * @method static self americaNorthDakotaNewSalem()\n * @method static self americaNuuk()\n * @method static self americaOjinaga()\n * @method static self americaPanama()\n * @method static self americaPangnirtung()\n * @method static self americaParamaribo()\n * @method static self americaPhoenix()\n * @method static self americaPortAuPrince()\n * @method static self americaPortOfSpain()\n * @method static self americaPortoVelho()\n * @method static self americaPuertoRico()\n * @method static self americaPuntaArenas()\n * @method static self americaRainyRiver()\n * @method static self americaRankinInlet()\n * @method static self americaRecife()\n * @method static self americaRegina()\n * @method static self americaResolute()\n * @method static self americaRioBranco()\n * @method static self americaSantarem()\n * @method static self americaSantiago()\n * @method static self americaSantoDomingo()\n * @method static self americaSaoPaulo()\n * @method static self americaScoresbysund()\n * @method static self americaSitka()\n * @method static self americaStBarthelemy()\n * @method static self americaStJohns()\n * @method static self americaStKitts()\n * @method static self americaStLucia()\n * @method static self americaStThomas()\n * @method static self americaStVincent()\n * @method static self americaSwiftCurrent()\n * @method static self americaTegucigalpa()\n * @method static self americaThule()\n * @method static self americaThunderBay()\n * @method static self americaTijuana()\n * @method static self americaToronto()\n * @method static self americaTortola()\n * @method static self americaVancouver()\n * @method static self americaWhitehorse()\n * @method static self americaWinnipeg()\n * @method static self americaYakutat()\n * @method static self americaYellowknife()\n * @method static self antarcticaCasey()\n * @method static self antarcticaDavis()\n * @method static self antarcticaDumontDUrville()\n * @method static self antarcticaMacquarie()\n * @method static self antarcticaMawson()\n * @method static self antarcticaMcMurdo()\n * @method static self antarcticaPalmer()\n * @method static self antarcticaRothera()\n * @method static self antarcticaSyowa()\n * @method static self antarcticaTroll()\n * @method static self antarcticaVostok()\n * @method static self arcticLongyearbyen()\n * @method static self asiaAden()\n * @method static self asiaAlmaty()\n * @method static self asiaAmman()\n * @method static self asiaAnadyr()\n * @method static self asiaAqtau()\n * @method static self asiaAqtobe()\n * @method static self asiaAshgabat()\n * @method static self asiaAtyrau()\n * @method static self asiaBaghdad()\n * @method static self asiaBahrain()\n * @method static self asiaBaku()\n * @method static self asiaBangkok()\n * @method static self asiaBarnaul()\n * @method static self asiaBeirut()\n * @method static self asiaBishkek()\n * @method static self asiaBrunei()\n * @method static self asiaChita()\n * @method static self asiaChoibalsan()\n * @method static self asiaColombo()\n * @method static self asiaDamascus()\n * @method static self asiaDhaka()\n * @method static self asiaDili()\n * @method static self asiaDubai()\n * @method static self asiaDushanbe()\n * @method static self asiaFamagusta()\n * @method static self asiaGaza()\n * @method static self asiaHebron()\n * @method static self asiaHoChiMinh()\n * @method static self asiaHongKong()\n * @method static self asiaHovd()\n * @method static self asiaIrkutsk()\n * @method static self asiaJakarta()\n * @method static self asiaJayapura()\n * @method static self asiaJerusalem()\n * @method static self asiaKabul()\n * @method static self asiaKamchatka()\n * @method static self asiaKarachi()\n * @method static self asiaKathmandu()\n * @method static self asiaKhandyga()\n * @method static self asiaKolkata()\n * @method static self asiaKrasnoyarsk()\n * @method static self asiaKualaLumpur()\n * @method static self asiaKuching()\n * @method static self asiaKuwait()\n * @method static self asiaMacau()\n * @method static self asiaMagadan()\n * @method static self asiaMakassar()\n * @method static self asiaManila()\n * @method static self asiaMuscat()\n * @method static self asiaNicosia()\n * @method static self asiaNovokuznetsk()\n * @method static self asiaNovosibirsk()\n * @method static self asiaOmsk()\n * @method static self asiaOral()\n * @method static self asiaPhnomPenh()\n * @method static self asiaPontianak()\n * @method static self asiaPyongyang()\n * @method static self asiaQatar()\n * @method static self asiaQostanay()\n * @method static self asiaQyzylorda()\n * @method static self asiaRiyadh()\n * @method static self asiaSakhalin()\n * @method static self asiaSamarkand()\n * @method static self asiaSeoul()\n * @method static self asiaShanghai()\n * @method static self asiaSingapore()\n * @method static self asiaSrednekolymsk()\n * @method static self asiaTaipei()\n * @method static self asiaTashkent()\n * @method static self asiaTbilisi()\n * @method static self asiaTehran()\n * @method static self asiaThimphu()\n * @method static self asiaTokyo()\n * @method static self asiaTomsk()\n * @method static self asiaUlaanbaatar()\n * @method static self asiaUrumqi()\n * @method static self asiaUstNera()\n * @method static self asiaVientiane()\n * @method static self asiaVladivostok()\n * @method static self asiaYakutsk()\n * @method static self asiaYangon()\n * @method static self asiaYekaterinburg()\n * @method static self asiaYerevan()\n * @method static self atlanticAzores()\n * @method static self atlanticBermuda()\n * @method static self atlanticCanary()\n * @method static self atlanticCapeVerde()\n * @method static self atlanticFaroe()\n * @method static self atlanticMadeira()\n * @method static self atlanticReykjavik()\n * @method static self atlanticSouthGeorgia()\n * @method static self atlanticStHelena()\n * @method static self atlanticStanley()\n * @method static self australiaAdelaide()\n * @method static self australiaBrisbane()\n * @method static self australiaBrokenHill()\n * @method static self australiaCurrie()\n * @method static self australiaDarwin()\n * @method static self australiaEucla()\n * @method static self australiaHobart()\n * @method static self australiaLindeman()\n * @method static self australiaLordHowe()\n * @method static self australiaMelbourne()\n * @method static self australiaPerth()\n * @method static self australiaSydney()\n * @method static self europeAmsterdam()\n * @method static self europeAndorra()\n * @method static self europeAstrakhan()\n * @method static self europeAthens()\n * @method static self europeBelgrade()\n * @method static self europeBerlin()\n * @method static self europeBratislava()\n * @method static self europeBrussels()\n * @method static self europeBucharest()\n * @method static self europeBudapest()\n * @method static self europeBusingen()\n * @method static self europeChisinau()\n * @method static self europeCopenhagen()\n * @method static self europeDublin()\n * @method static self europeGibraltar()\n * @method static self europeGuernsey()\n * @method static self europeHelsinki()\n * @method static self europeIsleOfMan()\n * @method static self europeIstanbul()\n * @method static self europeJersey()\n * @method static self europeKaliningrad()\n * @method static self europeKiev()\n * @method static self europeKirov()\n * @method static self europeLisbon()\n * @method static self europeLjubljana()\n * @method static self europeLondon()\n * @method static self europeLuxembourg()\n * @method static self europeMadrid()\n * @method static self europeMalta()\n * @method static self europeMariehamn()\n * @method static self europeMinsk()\n * @method static self europeMonaco()\n * @method static self europeMoscow()\n * @method static self europeOslo()\n * @method static self europeParis()\n * @method static self europePodgorica()\n * @method static self europePrague()\n * @method static self europeRiga()\n * @method static self europeRome()\n * @method static self europeSamara()\n * @method static self europeSanMarino()\n * @method static self europeSarajevo()\n * @method static self europeSaratov()\n * @method static self europeSimferopol()\n * @method static self europeSkopje()\n * @method static self europeSofia()\n * @method static self europeStockholm()\n * @method static self europeTallinn()\n * @method static self europeTirane()\n * @method static self europeUlyanovsk()\n * @method static self europeUzhgorod()\n * @method static self europeVaduz()\n * @method static self europeVatican()\n * @method static self europeVienna()\n * @method static self europeVilnius()\n * @method static self europeVolgograd()\n * @method static self europeWarsaw()\n * @method static self europeZagreb()\n * @method static self europeZaporozhye()\n * @method static self europeZurich()\n * @method static self indianAntananarivo()\n * @method static self indianChagos()\n * @method static self indianChristmas()\n * @method static self indianCocos()\n * @method static self indianComoro()\n * @method static self indianKerguelen()\n * @method static self indianMahe()\n * @method static self indianMaldives()\n * @method static self indianMauritius()\n * @method static self indianMayotte()\n * @method static self indianReunion()\n * @method static self pacificApia()\n * @method static self pacificAuckland()\n * @method static self pacificBougainville()\n * @method static self pacificChatham()\n * @method static self pacificChuuk()\n * @method static self pacificEaster()\n * @method static self pacificEfate()\n * @method static self pacificEnderbury()\n * @method static self pacificFakaofo()\n * @method static self pacificFiji()\n * @method static self pacificFunafuti()\n * @method static self pacificGalapagos()\n * @method static self pacificGambier()\n * @method static self pacificGuadalcanal()\n * @method static self pacificGuam()\n * @method static self pacificHonolulu()\n * @method static self pacificKiritimati()\n * @method static self pacificKosrae()\n * @method static self pacificKwajalein()\n * @method static self pacificMajuro()\n * @method static self pacificMarquesas()\n * @method static self pacificMidway()\n * @method static self pacificNauru()\n * @method static self pacificNiue()\n * @method static self pacificNorfolk()\n * @method static self pacificNoumea()\n * @method static self pacificPagoPago()\n * @method static self pacificPalau()\n * @method static self pacificPitcairn()\n * @method static self pacificPohnpei()\n * @method static self pacificPortMoresby()\n * @method static self pacificRarotonga()\n * @method static self pacificSaipan()\n * @method static self pacificTahiti()\n * @method static self pacificTarawa()\n * @method static self pacificTongatapu()\n * @method static self pacificWake()\n * @method static self pacificWallis()\n * @method static self ACDT()\n * @method static self ACST()\n * @method static self ADDT()\n * @method static self ADT()\n * @method static self AEDT()\n * @method static self AEST()\n * @method static self AHDT()\n * @method static self AHST()\n * @method static self AKDT()\n * @method static self AKST()\n * @method static self AMT()\n * @method static self APT()\n * @method static self AST()\n * @method static self AWDT()\n * @method static self AWST()\n * @method static self AWT()\n * @method static self BDST()\n * @method static self BDT()\n * @method static self BMT()\n * @method static self BST()\n * @method static self CAST()\n * @method static self CAT()\n * @method static self CDDT()\n * @method static self CDT()\n * @method static self CEMT()\n * @method static self CEST()\n * @method static self CET()\n * @method static self CMT()\n * @method static self CPT()\n * @method static self CST()\n * @method static self CWT()\n * @method static self CHST()\n * @method static self DMT()\n * @method static self EAT()\n * @method static self EDDT()\n * @method static self EDT()\n * @method static self EEST()\n * @method static self EET()\n * @method static self EMT()\n * @method static self EPT()\n * @method static self EST()\n * @method static self EWT()\n * @method static self FFMT()\n * @method static self FMT()\n * @method static self GDT()\n * @method static self GMT()\n * @method static self GST()\n * @method static self HDT()\n * @method static self HKST()\n * @method static self HKT()\n * @method static self HMT()\n * @method static self HPT()\n * @method static self HST()\n * @method static self HWT()\n * @method static self IDDT()\n * @method static self IDT()\n * @method static self IMT()\n * @method static self IST()\n * @method static self JDT()\n * @method static self JMT()\n * @method static self JST()\n * @method static self KDT()\n * @method static self KMT()\n * @method static self KST()\n * @method static self LST()\n * @method static self MDDT()\n * @method static self MDST()\n * @method static self MDT()\n * @method static self MEST()\n * @method static self MET()\n * @method static self MMT()\n * @method static self MPT()\n * @method static self MSD()\n * @method static self MSK()\n * @method static self MST()\n * @method static self MWT()\n * @method static self NDDT()\n * @method static self NDT()\n * @method static self NPT()\n * @method static self NST()\n * @method static self NWT()\n * @method static self NZDT()\n * @method static self NZMT()\n * @method static self NZST()\n * @method static self PDDT()\n * @method static self PDT()\n * @method static self PKST()\n * @method static self PKT()\n * @method static self PLMT()\n * @method static self PMT()\n * @method static self PPMT()\n * @method static self PPT()\n * @method static self PST()\n * @method static self PWT()\n * @method static self QMT()\n * @method static self RMT()\n * @method static self SAST()\n * @method static self SDMT()\n * @method static self SJMT()\n * @method static self SMT()\n * @method static self SST()\n * @method static self TBMT()\n * @method static self TMT()\n * @method static self UCT()\n * @method static self WAST()\n * @method static self WAT()\n * @method static self WEMT()\n * @method static self WEST()\n * @method static self WET()\n * @method static self WIB()\n * @method static self WITA()\n * @method static self WIT()\n * @method static self WMT()\n * @method static self YDDT()\n * @method static self YDT()\n * @method static self YPT()\n * @method static self YST()\n * @method static self YWT()\n *\n * @psalm-immutable\n */\nfinal class TimeZone\n{\n    private const TYPE_OFFSET = 1;\n\n    private const TYPE_ABBREVIATION = 2;\n\n    private const TYPE_IDENTIFIER = 3;\n\n    private string $name;\n\n    private int $type;\n\n    private function __construct(string $name, int $type)\n    {\n        $this->name = $name;\n        $this->type = $type;\n    }\n\n    /**\n     * @throws InvalidArgumentException\n     *\n     * @psalm-pure\n     */\n    public static function UTC() : self\n    {\n        return self::abbreviation('UTC');\n    }\n\n    /**\n     * @psalm-suppress PossiblyFalseIterator\n     *\n     * @psalm-pure\n     */\n    public static function id(string $identifier) : self\n    {\n        $normalized = \\strtolower($identifier);\n\n        if ($normalized === 'utc') {\n            throw new InvalidArgumentException('\"UTC\" is timezone abbreviation, not identifier.');\n        }\n\n        /** @var string $dateTimeZoneIdentifier */\n        foreach (\\DateTimeZone::listIdentifiers() as $dateTimeZoneIdentifier) {\n            $normalizedDateTimeZoneIdentifier = \\strtolower($dateTimeZoneIdentifier);\n\n            if (\\strtolower($dateTimeZoneIdentifier) === $normalized) {\n                return new self($dateTimeZoneIdentifier, self::TYPE_IDENTIFIER);\n            }\n\n            if (\\str_replace(['/', '_'], '', $normalizedDateTimeZoneIdentifier) === $normalized) {\n                return new self($dateTimeZoneIdentifier, self::TYPE_IDENTIFIER);\n            }\n        }\n\n        throw new InvalidArgumentException(\"\\\"{$identifier}\\\" is not a valid timezone identifier.\");\n    }\n\n    /**\n     * @psalm-suppress PossiblyFalseArgument\n     *\n     * @psalm-pure\n     */\n    public static function abbreviation(string $abbreviation) : self\n    {\n        $normalized = \\strtoupper($abbreviation);\n\n        /**\n         * @var string $dateTimeZoneAbbreviation\n         */\n        foreach (\\array_keys(\\DateTimeZone::listAbbreviations()) as $dateTimeZoneAbbreviation) {\n            if (\\strtoupper($dateTimeZoneAbbreviation) === $normalized) {\n                return new self(\\strtoupper($dateTimeZoneAbbreviation), self::TYPE_ABBREVIATION);\n            }\n        }\n\n        throw new InvalidArgumentException(\"\\\"{$abbreviation}\\\" is not a valid timezone abbreviation.\");\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function offset(string $offset) : self\n    {\n        if (!TimeOffset::isValid($offset)) {\n            throw new InvalidArgumentException(\"\\\"{$offset}\\\" is not a valid time offset.\");\n        }\n\n        return new self(TimeOffset::fromString($offset)->toString(), self::TYPE_OFFSET);\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromString(string $name) : self\n    {\n        try {\n            return self::id($name);\n        } catch (InvalidArgumentException $identifierException) {\n            try {\n                return self::abbreviation($name);\n            } catch (InvalidArgumentException $abbreviationException) {\n                try {\n                    return self::offset($name);\n                } catch (InvalidArgumentException $offsetException) {\n                    throw new InvalidArgumentException(\"\\\"{$name}\\\" is not a valid time zone.\");\n                }\n            }\n        }\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function isValid(string $name) : bool\n    {\n        try {\n            new \\DateTimeZone($name);\n\n            return true;\n        } catch (\\Exception $e) {\n            return false;\n        }\n    }\n\n    /**\n     * @psalm-pure\n     */\n    public static function fromDateTimeZone(\\DateTimeZone $dateTimeZone) : self\n    {\n        $name = $dateTimeZone->getName();\n        $type = self::TYPE_IDENTIFIER;\n\n        if ($name === 'UTC') {\n            $type = self::TYPE_ABBREVIATION;\n        }\n\n        if (TimeOffset::isValid($name)) {\n            $type = self::TYPE_OFFSET;\n        }\n\n        if (\\timezone_name_from_abbr($name) !== false) {\n            $type = self::TYPE_ABBREVIATION;\n        }\n\n        return new self($name, $type);\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress PossiblyFalseArgument\n     *\n     * @return array<TimeZone>\n     */\n    public static function allIdentifiers() : array\n    {\n        return \\array_map(\n            fn (string $identifier) : self => self::id($identifier),\n            \\array_filter(\\DateTimeZone::listIdentifiers(), fn (string $identifier) : bool => $identifier !== 'UTC')\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress PossiblyFalseArgument\n     *\n     * @return array<TimeZone>\n     */\n    public static function allAbbreviations() : array\n    {\n        /** @var array<string> $abbreviations */\n        $abbreviations = \\array_keys(\\DateTimeZone::listAbbreviations());\n\n        return \\array_map(\n            fn (string $abbreviation) : self => self::abbreviation($abbreviation),\n            \\array_filter($abbreviations, fn (string $abbreviation) : bool => \\strlen($abbreviation) > 1)\n        );\n    }\n\n    /**\n     * @param string $name\n     * @param array<mixed> $arguments\n     *\n     * @psalm-pure\n     */\n    public static function __callStatic(string $name, array $arguments) : self\n    {\n        try {\n            return self::id($name);\n        } catch (InvalidArgumentException $identifierException) {\n            try {\n                return self::abbreviation($name);\n            } catch (InvalidArgumentException $abbreviationException) {\n                throw new InvalidArgumentException(\"\\\"{$name}\\\" is not a valid time zone identifier or abbreviation.\");\n            }\n        }\n    }\n\n    /**\n     * @return array{name: string, type: int}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'name' => $this->name,\n            'type' => $this->type,\n        ];\n    }\n\n    /**\n     * @param array{name: string, type: int} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->name = $data['name'];\n        $this->type = $data['type'];\n    }\n\n    public function isOffset() : bool\n    {\n        return $this->type === self::TYPE_OFFSET;\n    }\n\n    public function isAbbreviation() : bool\n    {\n        return $this->type === self::TYPE_ABBREVIATION;\n    }\n\n    public function isIdentifier() : bool\n    {\n        return $this->type === self::TYPE_IDENTIFIER;\n    }\n\n    public function toDateTimeZone() : \\DateTimeZone\n    {\n        return new \\DateTimeZone($this->name);\n    }\n\n    public function name() : string\n    {\n        return $this->name;\n    }\n\n    /**\n     * Offset depends on date because daylight & saving time will have it different and\n     * the only way to get it is to take it from date time.\n     */\n    public function timeOffset(DateTime $dateTime) : TimeOffset\n    {\n        return TimeOffset::fromTimeUnit(TimeUnit::seconds($this->toDateTimeZone()->getOffset($dateTime->toDateTimeImmutable())));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Year.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class Year\n{\n    private int $year;\n\n    private YearMonths $months;\n\n    public function __construct(int $year)\n    {\n        $this->year = $year;\n        $this->months = new YearMonths($this);\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromDateTime(\\DateTimeInterface $dateTime) : self\n    {\n        return new self((int) $dateTime->format('Y'));\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     */\n    public static function fromString(string $date) : self\n    {\n        try {\n            if (\\is_numeric($date)) {\n                return new self((int) $date);\n            }\n\n            return self::fromDateTime(new \\DateTimeImmutable($date));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"Value \\\"{$date}\\\" is not valid year format.\");\n        }\n    }\n\n    /**\n     * @return array{year:int}\n     */\n    public function __debugInfo() : array\n    {\n        return [\n            'year' => $this->year,\n        ];\n    }\n\n    /**\n     * @return array{year:int}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'year' => $this->year,\n        ];\n    }\n\n    /**\n     * @param array{year:int} $data\n     */\n    public function __unserialize(array $data) : void\n    {\n        $this->year = $data['year'];\n        $this->months = new YearMonths($this);\n    }\n\n    public function toString() : string\n    {\n        return (string) $this->year;\n    }\n\n    public function january() : Month\n    {\n        return $this->months()->byNumber(1);\n    }\n\n    public function february() : Month\n    {\n        return $this->months()->byNumber(2);\n    }\n\n    public function march() : Month\n    {\n        return $this->months()->byNumber(3);\n    }\n\n    public function april() : Month\n    {\n        return $this->months()->byNumber(4);\n    }\n\n    public function may() : Month\n    {\n        return $this->months()->byNumber(5);\n    }\n\n    public function june() : Month\n    {\n        return $this->months()->byNumber(6);\n    }\n\n    public function july() : Month\n    {\n        return $this->months()->byNumber(7);\n    }\n\n    public function august() : Month\n    {\n        return $this->months()->byNumber(8);\n    }\n\n    public function september() : Month\n    {\n        return $this->months()->byNumber(9);\n    }\n\n    public function october() : Month\n    {\n        return $this->months()->byNumber(10);\n    }\n\n    public function november() : Month\n    {\n        return $this->months()->byNumber(11);\n    }\n\n    public function december() : Month\n    {\n        return $this->months()->byNumber(12);\n    }\n\n    public function months() : YearMonths\n    {\n        return $this->months;\n    }\n\n    public function number() : int\n    {\n        return $this->year;\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `add` instead. Will be removed with 2.0\n     */\n    public function plus(int $years) : self\n    {\n        return $this->add($years);\n    }\n\n    public function add(int $years) : self\n    {\n        return new self($this->year + $years);\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `sub` instead. Will be removed with 2.0\n     */\n    public function minus(int $years) : self\n    {\n        return $this->sub($years);\n    }\n\n    public function sub(int $years) : self\n    {\n        return new self($this->year - $years);\n    }\n\n    public function next() : self\n    {\n        return new self($this->year + 1);\n    }\n\n    public function previous() : self\n    {\n        return new self($this->year - 1);\n    }\n\n    public function numberOfMonths() : int\n    {\n        return 12;\n    }\n\n    public function numberOfDays() : int\n    {\n        return $this->isLeap() ? 366 : 365;\n    }\n\n    public function quarter(int $number) : Quarter\n    {\n        switch ($number) {\n            case 1:\n                return new Quarter($number, $this->months()->slice(0, 3));\n            case 2:\n                return new Quarter($number, $this->months()->slice(3, 3));\n            case 3:\n                return new Quarter($number, $this->months()->slice(6, 3));\n            case 4:\n                return new Quarter($number, $this->months()->slice(9, 3));\n\n            default:\n                throw new InvalidArgumentException('Quarter number must be greater or equal 1 and less or equal than 4');\n        }\n    }\n\n    /**\n     * @param callable(Day $day) : void $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : void $iterator\n     *\n     * @return array<mixed>\n     */\n    public function mapDays(callable $iterator) : array\n    {\n        /** @psalm-suppress ImpureFunctionCall */\n        return \\array_map(\n            $iterator,\n            \\array_merge(\n                ...\\array_map(\n                    fn (int $month) : array => $this->months()->byNumber($month)->days()->all(),\n                    \\range(1, 12)\n                )\n            )\n        );\n    }\n\n    /**\n     * @param callable(Day $day) : bool $iterator\n     *\n     * @psalm-param pure-callable(Day $day) : bool $iterator\n     *\n     * @return Days\n     */\n    public function filterDays(callable $iterator) : Days\n    {\n        /** @psalm-suppress ImpureFunctionCall */\n        return Days::fromArray(...\\array_filter(\n            \\array_merge(\n                ...\\array_map(\n                    fn (int $month) : array => $this->months()->byNumber($month)->days()->all(),\n                    \\range(1, 12)\n                )\n            ),\n            $iterator\n        ));\n    }\n\n    public function isLeap() : bool\n    {\n        return $this->year % 4 == 0 && ($this->year % 100 != 0 || $this->year % 400 == 0);\n    }\n\n    public function toDateTimeImmutable() : \\DateTimeImmutable\n    {\n        return new \\DateTimeImmutable(\\sprintf('%d-01-01 00:00:00.000000 UTC', $this->number()));\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $year) : bool\n    {\n        return $this->isEqualTo($year);\n    }\n\n    public function isEqualTo(self $year) : bool\n    {\n        return $this->number() === $year->number();\n    }\n\n    public function isBefore(self $year) : bool\n    {\n        return $this->number() < $year->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isBeforeOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isBeforeOrEqual(self $year) : bool\n    {\n        return $this->isBeforeOrEqualTo($year);\n    }\n\n    public function isBeforeOrEqualTo(self $year) : bool\n    {\n        return $this->number() <= $year->number();\n    }\n\n    public function isAfter(self $year) : bool\n    {\n        return $this->number() > $year->number();\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isAfterOrEqualTo` instead. Will be removed with 2.0\n     */\n    public function isAfterOrEqual(self $year) : bool\n    {\n        return $this->isAfterOrEqualTo($year);\n    }\n\n    public function isAfterOrEqualTo(self $year) : bool\n    {\n        return $this->number() >= $year->number();\n    }\n\n    public function iterate(self $destination, Interval $interval) : Years\n    {\n        return $this->isAfter($destination)\n            ? $this->since($destination, $interval)\n            : $this->until($destination, $interval);\n    }\n\n    public function until(self $month, Interval $interval) : Years\n    {\n        if ($this->isAfter($month)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%d is after %d',\n                    $this->number(),\n                    $month->number(),\n                )\n            );\n        }\n\n        /**\n         * @var array<DateTime> $years\n         *\n         * @psalm-suppress ImpureMethodCall\n         * @psalm-suppress ImpureFunctionCall\n         */\n        $years = \\iterator_to_array(\n            new DateTimeIntervalIterator(\n                $this->january()->firstDay()->midnight(TimeZone::UTC()),\n                $month->january()->firstDay()->midnight(TimeZone::UTC()),\n                RelativeTimeUnit::year(),\n                $interval\n            )\n        );\n\n        return new Years(\n            ...\\array_map(\n                function (DateTime $dateTime) : self {\n                    return $dateTime->year();\n                },\n                $years\n            )\n        );\n    }\n\n    public function since(self $month, Interval $interval) : Years\n    {\n        if ($this->isBefore($month)) {\n            throw new InvalidArgumentException(\n                \\sprintf(\n                    '%d is before %d',\n                    $this->number(),\n                    $month->number(),\n                )\n            );\n        }\n\n        /**\n         * @var array<DateTime> $years\n         *\n         * @psalm-suppress ImpureMethodCall\n         * @psalm-suppress ImpureFunctionCall\n         */\n        $years = \\iterator_to_array(\n            new DateTimeIntervalIterator(\n                $this->january()->firstDay()->midnight(TimeZone::UTC()),\n                $month->january()->firstDay()->midnight(TimeZone::UTC()),\n                RelativeTimeUnit::year()->toNegative(),\n                $interval\n            )\n        );\n\n        return new Years(\n            ...\\array_map(\n                function (DateTime $dateTime) : self {\n                    return $dateTime->year();\n                },\n                \\array_reverse($years)\n            )\n        );\n    }\n\n    public function distance(self $to) : TimeUnit\n    {\n        return (new TimePeriod($this->january()->firstDay()->midnight(TimeZone::UTC()), $to->january()->firstDay()->midnight(TimeZone::UTC())))->distance();\n    }\n\n    public function compareTo(self $year) : int\n    {\n        if ($this->isEqualTo($year)) {\n            return 0;\n        }\n\n        return $this->isBefore($year)\n            ? -1\n            : 1;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/YearMonths.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\n\n/**\n * @psalm-immutable\n */\nfinal class YearMonths implements \\Countable\n{\n    private Year $year;\n\n    public function __construct(Year $year)\n    {\n        $this->year = $year;\n    }\n\n    public function count() : int\n    {\n        return $this->year->numberOfMonths();\n    }\n\n    public function byNumber(int $number) : Month\n    {\n        return new Month($this->year, $number);\n    }\n\n    /**\n     * @return array<int, Month>\n     */\n    public function all() : array\n    {\n        /** @psalm-suppress ImpureFunctionCall */\n        return \\array_map(\n            fn (int $monthNumber) : Month => new Month($this->year, $monthNumber),\n            \\range(1, $this->year->numberOfMonths())\n        );\n    }\n\n    /**\n     * @param callable(Month $day) : void $iterator\n     *\n     * @psalm-param pure-callable(Month $day) : void $iterator\n     *\n     * @return array<mixed>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map(\n            $iterator,\n            $this->all()\n        );\n    }\n\n    /**\n     * @param callable(Month $day) : bool $iterator\n     *\n     * @psalm-param pure-callable(Month $day) : bool $iterator\n     *\n     * @return array<Month>\n     */\n    public function filter(callable $iterator) : array\n    {\n        return \\array_filter(\n            $this->all(),\n            $iterator\n        );\n    }\n\n    public function slice(int $from, int $size) : Months\n    {\n        if ($from < 0) {\n            throw new InvalidArgumentException('Slice out of range.');\n        }\n\n        if ($from + $size > 12) {\n            throw new InvalidArgumentException('Slice out of range.');\n        }\n\n        return Months::fromArray(...\\array_slice($this->all(), $from, $size));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Gregorian/Years.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Gregorian;\n\n/**\n * @psalm-immutable\n *\n * @implements \\IteratorAggregate<int, Year>\n * @implements \\ArrayAccess<int, Year>\n */\nfinal class Years implements \\ArrayAccess, \\Countable, \\IteratorAggregate\n{\n    /**\n     * @var array<Year>\n     */\n    private array $years;\n\n    public function __construct(Year ...$years)\n    {\n        $this->years = $years;\n    }\n\n    public function offsetExists($offset) : bool\n    {\n        return isset($this->all()[\\intval($offset)]);\n    }\n\n    public function offsetGet($offset) : ?Year\n    {\n        return isset($this->all()[\\intval($offset)]) ? $this->all()[\\intval($offset)] : null;\n    }\n\n    public function offsetSet($offset, $value) : void\n    {\n        throw new \\RuntimeException(__CLASS__ . ' is immutable.');\n    }\n\n    public function offsetUnset($offset) : void\n    {\n        throw new \\RuntimeException(__CLASS__ . ' is immutable.');\n    }\n\n    /**\n     * @return array<Year>\n     */\n    public function all() : array\n    {\n        return $this->years;\n    }\n\n    /**\n     * @param callable(Year $year) : mixed $iterator\n     *\n     * @psalm-param pure-callable(Year $year) : mixed $iterator\n     *\n     * @return array<mixed>\n     */\n    public function map(callable $iterator) : array\n    {\n        return \\array_map($iterator, $this->all());\n    }\n\n    /**\n     * @param callable(Year $year) : bool $iterator\n     *\n     * @psalm-param pure-callable(Year $year) : bool $iterator\n     */\n    public function filter(callable $iterator) : self\n    {\n        return new self(...\\array_filter($this->all(), $iterator));\n    }\n\n    public function count() : int\n    {\n        return \\count($this->all());\n    }\n\n    public function getIterator() : \\Traversable\n    {\n        return new \\ArrayIterator($this->all());\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/RelativeTimeUnit.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar;\n\n/**\n * @psalm-immutable\n */\nfinal class RelativeTimeUnit implements Unit\n{\n    private ?int $months;\n\n    private ?int $years;\n\n    private function __construct(?int $months = null, ?int $years = null)\n    {\n        $this->months = $months;\n        $this->years = $years;\n    }\n\n    /** @psalm-pure */\n    public static function month() : self\n    {\n        return new self(1, null);\n    }\n\n    /** @psalm-pure */\n    public static function months(int $number) : self\n    {\n        return new self($number, null);\n    }\n\n    /** @psalm-pure */\n    public static function years(int $number) : self\n    {\n        return new self(null, $number);\n    }\n\n    /** @psalm-pure */\n    public static function year() : self\n    {\n        return new self(null, 1);\n    }\n\n    public function toDateInterval() : \\DateInterval\n    {\n        $dateInterval = new \\DateInterval(\\sprintf('P%dY%dM', $this->inYears() ? \\abs($this->inYears()) : 0, $this->inCalendarMonths() ? \\abs($this->inCalendarMonths()) : 0));\n\n        if ($this->isNegative()) {\n            /** @psalm-suppress ImpurePropertyAssignment */\n            $dateInterval->invert = 1;\n        }\n\n        return $dateInterval;\n    }\n\n    public function invert() : self\n    {\n        if ($this->years) {\n            return $this->isNegative() ? self::years(\\abs($this->years)) : self::years(-$this->years);\n        }\n\n        /**\n         * @psalm-suppress PossiblyNullArgument\n         *\n         * @phpstan-ignore-next-line\n         */\n        return $this->isNegative() ? self::months(\\abs($this->months)) : self::months(-$this->months);\n    }\n\n    public function inCalendarMonths() : int\n    {\n        if ($this->months === null) {\n            return 0;\n        }\n\n        return \\abs($this->months % 12);\n    }\n\n    public function isNegative() : bool\n    {\n        return $this->years < 0 || $this->months < 0;\n    }\n\n    /**\n     * @psalm-suppress PossiblyNullOperand\n     * @psalm-suppress InvalidNullableReturnType\n     */\n    public function inYears() : int\n    {\n        if ($this->years !== null) {\n            return $this->years;\n        }\n\n        /**\n         * @psalm-suppress PossiblyNullArgument\n         *\n         * @phpstan-ignore-next-line\n         */\n        $years = (int) \\floor(\\abs($this->months) / 12);\n\n        return $this->isNegative() ? -$years : $years;\n    }\n\n    public function toNegative() : self\n    {\n        if ($this->isNegative()) {\n            return $this;\n        }\n\n        return $this->invert();\n    }\n\n    public function toPositive() : self\n    {\n        if (!$this->isNegative()) {\n            return $this;\n        }\n\n        return $this->invert();\n    }\n\n    /**\n     * @psalm-suppress NullableReturnStatement\n     * @psalm-suppress InvalidNullableReturnType\n     */\n    public function inMonths() : int\n    {\n        if ($this->years !== null) {\n            return $this->years * 12;\n        }\n\n        /**\n         * @phpstan-ignore-next-line\n         */\n        return $this->months;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Stopwatch.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar;\n\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\TimeUnit\\HRTime;\n\n/**\n * @psalm-pure\n */\nfinal class Stopwatch\n{\n    /**\n     * @var null|array{int, int}\n     */\n    private ?array $start;\n\n    /**\n     * @var array<array{int, int}>\n     */\n    private array $laps;\n\n    /**\n     * @var null|array{int, int}\n     */\n    private ?array $end;\n\n    public function __construct()\n    {\n        $this->start = null;\n        $this->laps = [];\n        $this->end = null;\n    }\n\n    public function isStarted() : bool\n    {\n        return $this->start !== null;\n    }\n\n    public function isStopped() : bool\n    {\n        return $this->end !== null;\n    }\n\n    /**\n     * Stopwatch::lap() used once will generate two laps, first between start and lap[0] and\n     * second between lap[0] and end.\n     */\n    public function laps() : int\n    {\n        return \\count($this->laps) > 0\n            ? \\count($this->laps) + 1\n            : 0;\n    }\n\n    public function start() : void\n    {\n        $this->start = \\hrtime(false);\n    }\n\n    public function lap() : void\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        $this->laps[] = \\hrtime(false);\n    }\n\n    public function stop() : void\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        if ($this->end !== null) {\n            throw new Exception('Stopwatch already stopped');\n        }\n\n        $this->end = \\hrtime(false);\n    }\n\n    public function elapsedTime(int $lap) : TimeUnit\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        if (\\count($this->laps) === 0) {\n            throw new Exception('Stopwatch does not have any laps.');\n        }\n\n        if ($lap === 1) {\n            return $this->firstLapElapsedTime();\n        }\n\n        if ($lap - 1 === \\count($this->laps)) {\n            return $this->lastLapElapsedTime();\n        }\n\n        if (!isset($this->laps[$lap - 1])) {\n            throw new Exception(\\sprintf('Lap %d not exists', $lap));\n        }\n\n        return HRTime::convert($this->laps[$lap - 1][0], $this->laps[$lap - 1][1])\n            ->sub(HRTime::convert($this->laps[$lap - 2][0], $this->laps[$lap - 2][1]));\n    }\n\n    public function firstLapElapsedTime() : TimeUnit\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        if (\\count($this->laps) === 0) {\n            throw new Exception('Stopwatch does not have any laps.');\n        }\n\n        return HRTime::convert($this->laps[0][0], $this->laps[0][1])\n            ->sub(HRTime::convert($this->start[0], $this->start[1]));\n    }\n\n    public function lastLapElapsedTime() : TimeUnit\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        if ($this->end === null) {\n            throw new Exception('Stopwatch not stopped');\n        }\n\n        if (\\count($this->laps) === 0) {\n            throw new Exception('Stopwatch does not have any laps.');\n        }\n\n        return HRTime::convert($this->end[0], $this->end[1])\n            ->sub(HRTime::convert(\\end($this->laps)[0], \\end($this->laps)[1]));\n    }\n\n    public function totalElapsedTime() : TimeUnit\n    {\n        if ($this->start === null) {\n            throw new Exception('Stopwatch not started');\n        }\n\n        if ($this->end === null) {\n            throw new Exception('Stopwatch not stopped');\n        }\n\n        return HRTime::convert($this->end[0], $this->end[1])\n            ->sub(HRTime::convert($this->start[0], $this->start[1]));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/TimeUnit/HRTime.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\TimeUnit;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\n\n/**\n * @psalm-immutable\n */\nfinal class HRTime\n{\n    /**\n     * @psalm-pure\n     */\n    public static function convert(int $seconds, int $nanosecond) : TimeUnit\n    {\n        if ($nanosecond < 0) {\n            throw new InvalidArgumentException(\"Nanoseconds can't be less than 0, given \" . $nanosecond);\n        }\n\n        return TimeUnit::precise(\\floatval(\\sprintf(\n            '%d.%s',\n            $seconds,\n            \\str_pad(\\strval($nanosecond), 9, '0', STR_PAD_LEFT)\n        )));\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/TimeUnit.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar;\n\nuse Aeon\\Calculator\\PreciseCalculator;\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\n\n/**\n * @psalm-immutable\n */\nfinal class TimeUnit implements Unit\n{\n    private const PRECISION_MICROSECOND = 6;\n\n    private const MICROSECONDS_IN_SECOND = 1_000_000;\n\n    private const MICROSECONDS_IN_MILLISECOND = 1_000;\n\n    private const MILLISECONDS_IN_SECOND = 1_000;\n\n    private const SECONDS_IN_MINUTE = 60;\n\n    private const MINUTES_IN_HOUR = 60;\n\n    private const HOURS_IN_DAY = 24;\n\n    /**\n     * @var null|\\ReflectionClass<TimeUnit>\n     */\n    private static ?\\ReflectionClass $reflectionClass = null;\n\n    private ?int $seconds = null;\n\n    private ?int $microsecond = null;\n\n    private ?\\DateInterval $dateInterval = null;\n\n    private bool $negative = false;\n\n    private function __construct(bool $negative, int $seconds, int $microsecond)\n    {\n        if ($seconds < 0) {\n            throw new InvalidArgumentException('Seconds must be greater or equal 0, got ' . $seconds);\n        }\n\n        if ($microsecond < 0 || $microsecond >= self::MICROSECONDS_IN_SECOND) {\n            throw new InvalidArgumentException('Microsecond must be greater or equal 0 and less than 1000000, got ' . $microsecond);\n        }\n\n        $this->negative = $negative;\n        $this->seconds = $seconds;\n        $this->microsecond = $microsecond;\n        $this->dateInterval = null;\n    }\n\n    /**\n     * @psalm-pure\n     * Create from number of seconds with 6 decimal point precision.\n     * 0.500000 is half of the second, 500000 represents number of microseconds.\n     */\n    public static function precise(float $seconds) : self\n    {\n        $secondsString = \\number_format(\\round($seconds, self::PRECISION_MICROSECOND, PHP_ROUND_HALF_UP), self::PRECISION_MICROSECOND, '.', '');\n\n        $secondsStringParts = \\explode('.', $secondsString);\n\n        return new self(\n            \\floatval($secondsString) < 0,\n            \\abs(\\intval($secondsStringParts[0])),\n            \\abs(\\intval($secondsStringParts[1])),\n        );\n    }\n\n    /**\n     * @psalm-pure\n     *\n     * @psalm-suppress ImpureMethodCall\n     * @psalm-suppress ImpurePropertyFetch\n     * @psalm-suppress ImpureStaticProperty\n     * @psalm-suppress ImpurePropertyAssignment\n     * @psalm-suppress InaccessibleProperty\n     *\n     * Limitations: TimeUnit can't be created from relative DateIntervals like \\DateInterval::createFromDateString('4 months')\n     * or \\DateInterval::createFromDateString('1 years'). It's because years and months are can't be precisely\n     * converted into seconds/days/hours.\n     */\n    public static function fromDateInterval(\\DateInterval $dateInterval) : self\n    {\n        if ($dateInterval->y && !$dateInterval->days) {\n            throw new Exception('Can\\'t convert ' . $dateInterval->format('P%yY%mM%dDT%hH%iM%sS') . ' precisely to time unit because year can\\'t be directly converted to number of seconds.');\n        }\n\n        if ($dateInterval->m && !$dateInterval->days) {\n            throw new Exception('Can\\'t convert ' . $dateInterval->format('P%yY%mM%dDT%hH%iM%sS') . ' precisely to time unit because month can\\'t be directly converted to number of seconds.');\n        }\n\n        if (self::$reflectionClass === null) {\n            self::$reflectionClass = new \\ReflectionClass(self::class);\n        }\n\n        $newTimeUnit = self::$reflectionClass->newInstanceWithoutConstructor();\n\n        $newTimeUnit->dateInterval = $dateInterval;\n\n        return $newTimeUnit;\n    }\n\n    public static function fromDateString(string $dateString) : self\n    {\n        /** @var \\DateInterval $interval */\n        $interval = \\DateInterval::createFromDateString($dateString);\n\n        return self::fromDateInterval($interval);\n    }\n\n    /** @psalm-pure */\n    public static function millisecond() : self\n    {\n        return new self(false, 0, self::MICROSECONDS_IN_MILLISECOND);\n    }\n\n    /** @psalm-pure */\n    public static function milliseconds(int $milliseconds) : self\n    {\n        return new self(\n            $milliseconds < 0,\n            \\abs(\\intval($milliseconds / self::MILLISECONDS_IN_SECOND)),\n            \\abs(($milliseconds * self::MICROSECONDS_IN_MILLISECOND) % self::MICROSECONDS_IN_SECOND)\n        );\n    }\n\n    /** @psalm-pure */\n    public static function day() : self\n    {\n        return new self(false, self::HOURS_IN_DAY * self::MINUTES_IN_HOUR * self::SECONDS_IN_MINUTE, 0);\n    }\n\n    /** @psalm-pure */\n    public static function days(int $days) : self\n    {\n        return new self($days < 0, \\abs($days * self::HOURS_IN_DAY * self::MINUTES_IN_HOUR * self::SECONDS_IN_MINUTE), 0);\n    }\n\n    /** @psalm-pure */\n    public static function hour() : self\n    {\n        return new self(false, self::MINUTES_IN_HOUR * self::SECONDS_IN_MINUTE, 0);\n    }\n\n    /** @psalm-pure */\n    public static function hours(int $hours) : self\n    {\n        return new self($hours < 0, \\abs($hours * self::MINUTES_IN_HOUR * self::SECONDS_IN_MINUTE), 0);\n    }\n\n    /** @psalm-pure */\n    public static function minute() : self\n    {\n        return new self(false, self::SECONDS_IN_MINUTE, 0);\n    }\n\n    /** @psalm-pure */\n    public static function minutes(int $minutes) : self\n    {\n        return new self($minutes < 0, \\abs($minutes * self::SECONDS_IN_MINUTE), 0);\n    }\n\n    /** @psalm-pure */\n    public static function second() : self\n    {\n        return new self(false, 1, 0);\n    }\n\n    /** @psalm-pure */\n    public static function seconds(int $seconds) : self\n    {\n        return new self($seconds < 0, \\abs($seconds), 0);\n    }\n\n    /** @psalm-pure */\n    public static function negative(int $seconds, int $microsecond) : self\n    {\n        return new self(true, $seconds, $microsecond);\n    }\n\n    /** @psalm-pure */\n    public static function positive(int $seconds, int $microsecond) : self\n    {\n        return new self(false, $seconds, $microsecond);\n    }\n\n    /**\n     * @return array{seconds: int, microsecond: int, negative: bool}\n     */\n    public function __serialize() : array\n    {\n        return [\n            'seconds' => $this->getSeconds(),\n            'microsecond' => $this->microsecond(),\n            'negative' => $this->isNegative(),\n        ];\n    }\n\n    /**\n     * @psalm-suppress InaccessibleProperty\n     * @psalm-suppress NullableReturnStatement\n     * @psalm-suppress InvalidNullableReturnType\n     */\n    public function toDateInterval() : \\DateInterval\n    {\n        if ($this->dateInterval === null) {\n            $interval = new \\DateInterval(\\sprintf('PT%dS', $this->getSeconds()));\n\n            if ($this->negative) {\n                /** @psalm-suppress ImpurePropertyAssignment */\n                $interval->invert = 1;\n            }\n\n            if ($this->microsecond) {\n                /** @psalm-suppress ImpurePropertyAssignment */\n                $interval->f = $this->microsecond / self::MICROSECONDS_IN_SECOND;\n            }\n\n            $this->dateInterval = $interval;\n        }\n\n        return $this->dateInterval;\n    }\n\n    public function isZero() : bool\n    {\n        return $this->getSeconds() === 0 && $this->microsecond() === 0;\n    }\n\n    public function isNegative() : bool\n    {\n        /** @psalm-suppress UnusedMethodCall */\n        $this->getSeconds();\n\n        return $this->negative;\n    }\n\n    public function isPositive() : bool\n    {\n        return !$this->isNegative();\n    }\n\n    public function add(self $timeUnit) : self\n    {\n        return self::precise((float) (PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->add($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise())));\n    }\n\n    public function sub(self $timeUnit) : self\n    {\n        return self::precise((float) (PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->sub($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise())));\n    }\n\n    public function multiply(self $multiplier) : self\n    {\n        return self::precise((float) (PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->multiply($this->inSecondsPrecise(), $multiplier->inSecondsPrecise())));\n    }\n\n    public function divide(self $divider) : self\n    {\n        return self::precise((float) (PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->divide($this->inSecondsPrecise(), $divider->inSecondsPrecise())));\n    }\n\n    public function modulo(self $divider) : self\n    {\n        return self::precise((float) (PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->modulo($this->inSecondsPrecise(), $divider->inSecondsPrecise())));\n    }\n\n    public function isGreaterThan(self $timeUnit) : bool\n    {\n        return PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->isGreaterThan($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise());\n    }\n\n    /** @deprecated Use `isGreaterThanOrEqualTo` instead. Will be removed with 2.0 */\n    public function isGreaterThanEq(self $timeUnit) : bool\n    {\n        return $this->isGreaterThanOrEqualTo($timeUnit);\n    }\n\n    public function isGreaterThanOrEqualTo(self $timeUnit) : bool\n    {\n        return PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->isGreaterThanOrEqualTo($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise());\n    }\n\n    public function isLessThan(self $timeUnit) : bool\n    {\n        return PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->isLessThan($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise());\n    }\n\n    /** @deprecated Use `isLessThanOrEqualTo` instead. Will be removed with 2.0 */\n    public function isLessThanEq(self $timeUnit) : bool\n    {\n        return $this->isLessThanOrEqualTo($timeUnit);\n    }\n\n    public function isLessThanOrEqualTo(self $timeUnit) : bool\n    {\n        return PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->isLessThanOrEqualTo($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise());\n    }\n\n    /**\n     * @infection-ignore-all\n     *\n     * @deprecated Use `isEqualTo` instead. Will be removed with 2.0\n     */\n    public function isEqual(self $timeUnit) : bool\n    {\n        return $this->isEqualTo($timeUnit);\n    }\n\n    public function isEqualTo(self $timeUnit) : bool\n    {\n        return PreciseCalculator::initialize(self::PRECISION_MICROSECOND)->isEqualTo($this->inSecondsPrecise(), $timeUnit->inSecondsPrecise());\n    }\n\n    /**\n     * @psalm-suppress InvalidNullableReturnType\n     */\n    public function inSeconds() : int\n    {\n        return $this->isNegative() ? -$this->getSeconds() : $this->getSeconds();\n    }\n\n    public function inSecondsPrecise() : string\n    {\n        return \\sprintf(\n            '%s%d.%s',\n            $this->isNegative() === true ? '-' : '',\n            $this->getSeconds(),\n            $this->microsecondString()\n        );\n    }\n\n    public function inSecondsAbs() : int\n    {\n        return \\abs($this->getSeconds());\n    }\n\n    public function inTimeSeconds() : int\n    {\n        return \\abs($this->getSeconds() % 60);\n    }\n\n    public function inHours() : int\n    {\n        return $this->isNegative()\n            ? -\\intval(($this->getSeconds() / self::SECONDS_IN_MINUTE) / self::MINUTES_IN_HOUR)\n            : \\intval(($this->getSeconds() / self::SECONDS_IN_MINUTE) / self::MINUTES_IN_HOUR);\n    }\n\n    public function inHoursAbs() : int\n    {\n        return \\abs($this->inHours());\n    }\n\n    public function inTimeHours() : int\n    {\n        return \\abs($this->inHours() % 24);\n    }\n\n    public function inMinutes() : int\n    {\n        return $this->negative\n            ? -\\intval($this->getSeconds() / self::SECONDS_IN_MINUTE)\n            : \\intval($this->getSeconds() / self::SECONDS_IN_MINUTE);\n    }\n\n    public function inMinutesAbs() : int\n    {\n        return \\abs($this->inMinutes());\n    }\n\n    public function inTimeMinutes() : int\n    {\n        return \\abs($this->inMinutes() % 60);\n    }\n\n    public function inDays() : int\n    {\n        return $this->isNegative()\n            ? -\\intval((($this->getSeconds() / self::SECONDS_IN_MINUTE) / self::MINUTES_IN_HOUR) / self::HOURS_IN_DAY)\n            : \\intval((($this->getSeconds() / self::SECONDS_IN_MINUTE) / self::MINUTES_IN_HOUR) / self::HOURS_IN_DAY);\n    }\n\n    public function inDaysAbs() : int\n    {\n        return \\abs($this->inDays());\n    }\n\n    /**\n     * @psalm-suppress NullableReturnStatement\n     * @psalm-suppress InvalidNullableReturnType\n     *\n     * Number of microseconds from last full second to the next full second.\n     * Do not use this method to combine float seconds because for 50000 it returns 50000 not \"050000\".\n     */\n    public function microsecond() : int\n    {\n        /** @psalm-suppress UnusedMethodCall */\n        $this->getSeconds();\n\n        /** @phpstan-ignore-next-line  */\n        return $this->microsecond;\n    }\n\n    /**\n     * Number of microseconds from last full second to the next full second.\n     * Use this method to combine float seconds because for 50000 it returns \"050000\" not 50000.\n     */\n    public function microsecondString() : string\n    {\n        return \\str_pad((string) $this->microsecond(), self::PRECISION_MICROSECOND, '0', STR_PAD_LEFT);\n    }\n\n    public function inMilliseconds() : int\n    {\n        return $this->isNegative()\n            ? -($this->getSeconds() * 1000 + \\intval($this->microsecond() / self::MICROSECONDS_IN_MILLISECOND))\n            : ($this->getSeconds() * 1000 + \\intval($this->microsecond() / self::MICROSECONDS_IN_MILLISECOND));\n    }\n\n    public function inTimeMilliseconds() : int\n    {\n        return \\abs($this->inMilliseconds() % 1000);\n    }\n\n    public function inMillisecondsAbs() : int\n    {\n        return \\abs($this->inMilliseconds());\n    }\n\n    public function invert() : self\n    {\n        return new self(!$this->isNegative(), $this->getSeconds(), $this->microsecond());\n    }\n\n    public function toNegative() : self\n    {\n        if ($this->isNegative()) {\n            return $this;\n        }\n\n        return $this->invert();\n    }\n\n    public function toPositive() : self\n    {\n        if (!$this->isNegative()) {\n            return $this;\n        }\n\n        return $this->invert();\n    }\n\n    /**\n     * @psalm-suppress PossiblyNullArgument\n     * @psalm-suppress PossiblyNullPropertyFetch\n     * @psalm-suppress InaccessibleProperty\n     * @psalm-suppress NullableReturnStatement\n     * @psalm-suppress InvalidNullableReturnType\n     */\n    private function getSeconds() : int\n    {\n        if ($this->seconds === null) {\n            /** @phpstan-ignore-next-line */\n            $timeUnit = self::days($this->dateInterval->days ? \\intval($this->dateInterval->days) : $this->dateInterval->d)\n                /** @phpstan-ignore-next-line  */\n                ->add(self::hours($this->dateInterval->h))\n                /** @phpstan-ignore-next-line  */\n                ->add(self::minutes($this->dateInterval->i))\n                /** @phpstan-ignore-next-line  */\n                ->add(self::seconds($this->dateInterval->s))\n                /** @phpstan-ignore-next-line  */\n                ->add(self::precise($this->dateInterval->f));\n\n            /** @phpstan-ignore-next-line  */\n            $timeUnit = $this->dateInterval->invert === 1 ? $timeUnit->invert() : $timeUnit;\n\n            $this->negative = $timeUnit->isNegative();\n            $this->seconds = $timeUnit->getSeconds();\n            $this->microsecond = $timeUnit->microsecond();\n        }\n\n        return $this->seconds;\n    }\n}\n"
  },
  {
    "path": "src/Aeon/Calendar/Unit.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Aeon\\Calendar;\n\n/**\n * @psalm-immutable\n */\ninterface Unit\n{\n    public function toDateInterval() : \\DateInterval;\n\n    public function invert() : self;\n\n    public function isNegative() : bool;\n\n    public function toNegative() : self;\n\n    public function toPositive() : self;\n}\n"
  },
  {
    "path": "tests/Aeon/Calculator/Tests/Unit/BCMathCalculatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator\\Tests\\Unit;\n\nuse Aeon\\Calculator\\BCMathCalculator;\nuse Aeon\\Calculator\\Exception\\InvalidTypeException;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class BCMathCalculatorTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function add_data_provider() : \\Generator\n    {\n        yield ['3.000000', 2.0, 1.0];\n        yield ['3.000000', 2, 1];\n        yield ['-1.000000', -2, 1];\n        yield ['0.000200', 0.000_100, 0.000_100];\n        yield ['0.000201', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function sub_provider() : \\Generator\n    {\n        yield ['1.000000', 2.0, 1.0];\n        yield ['1.000000', 2, 1];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000001', 0.000_101, 0.000_100];\n        yield ['-0.000001', 0.000_000, 0.000_001];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function multiply_provider() : \\Generator\n    {\n        yield ['2.000000', 2.0, 1.0];\n        yield ['2.000000', 2, 1];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000000', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function divide_provider() : \\Generator\n    {\n        yield ['1.000000', 1.0, 1.0];\n        yield ['2.000000', 2, 1];\n        yield ['1.000000', 0.000_100, 0.000_100];\n        yield ['1.010000', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['4.900000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function modulo_provider() : \\Generator\n    {\n        yield ['0.000000', 1.0, 1.0];\n        yield ['0.000000', 2, 1];\n        yield ['1.000000', 7, 2];\n        yield ['1.999980', 7, 2.50001];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000001', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_equal_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [true, 0.000_100, 0.000_100];\n        yield [false, 0.000_101, 0.000_100];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_less_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [false, 0.000_000, 0.000_000_1];\n        yield [false, 0.000_000_49, 0.000_000_10];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_greater_data_provider() : \\Generator\n    {\n        yield [true, 2.0, 1.0];\n        yield [true, 2, 1];\n        yield [false, 0.000_000, 0.000_000_1];\n        yield [false, 0.000_000_49, 0.000_000_1];\n        yield [false, 0.000_000_1, 0.000_000_51];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_greater_than_eq_data_provider() : \\Generator\n    {\n        yield [true, 2.0, 1.0];\n        yield [true, 2, 1];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_less_than_eq_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n        yield [true, 0.000_000_1, 0.000_000_51];\n    }\n\n    #[DataProvider('add_data_provider')]\n    public function test_add(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new BCMathCalculator(6))->add(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('sub_provider')]\n    public function test_sub(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new BCMathCalculator(6))->sub(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('multiply_provider')]\n    public function test_multiply(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new BCMathCalculator(6))->multiply(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('divide_provider')]\n    public function test_divide(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new BCMathCalculator(6))->divide(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('modulo_provider')]\n    public function test_modulo(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new BCMathCalculator(6))->modulo(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('is_equal_data_provider')]\n    public function test_is_equal(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new BCMathCalculator(6))->isEqualTo(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('is_less_data_provider')]\n    public function test_is_less(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new BCMathCalculator(6))->isLessThan(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n\n    #[DataProvider('is_greater_data_provider')]\n    public function test_is_greater(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new BCMathCalculator(6))->isGreaterThan(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('is_greater_than_eq_data_provider')]\n    public function test_is_greater_than_eq(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new BCMathCalculator(6))->isGreaterThanOrEqualTo(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('is_less_than_eq_data_provider')]\n    public function test_is_less_than_eq(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new BCMathCalculator(6))->isLessThanOrEqualTo(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    public function test_invalid_value_in_divide() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->divide('test', '10');\n    }\n\n    public function test_invalid_division_in_divide() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->divide('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_modulo() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->modulo('test', '10');\n    }\n\n    public function test_invalid_division_in_modulo() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->modulo('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_multiply() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->multiply('test', '10');\n    }\n\n    public function test_invalid_next_value_in_multiply() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->multiply('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_add() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->add('test', '10');\n    }\n\n    public function test_invalid_next_value_in_add() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->add('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_sub() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->sub('test', '10');\n    }\n\n    public function test_invalid_next_value_in_sub() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->sub('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_is_greater_than() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->multiply('test', '10');\n    }\n\n    public function test_invalid_next_value_in_is_greater_than() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isGreaterThan('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_less_than() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isLessThan('test', '10');\n    }\n\n    public function test_invalid_next_value_in_less_than() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isLessThan('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_is_greater_than_eq() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isGreaterThanOrEqualTo('test', '10');\n    }\n\n    public function test_invalid_next_value_in_is_greater_than_eq() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isGreaterThanOrEqualTo('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_is_less_than_eq() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isLessThanOrEqualTo('test', '10');\n    }\n\n    public function test_invalid_next_value_in_is_less_than_eq() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isLessThanOrEqualTo('10', 'invalid');\n    }\n\n    public function test_invalid_value_in_is_equal() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isEqualTo('test', '10');\n    }\n\n    public function test_invalid_next_value_in_is_equal() : void\n    {\n        $this->expectException(InvalidTypeException::class);\n        (new BCMathCalculator(6))->isEqualTo('10', 'invalid');\n    }\n\n    public function test_division_by_zero() : void\n    {\n        $this->expectException(\\LogicException::class);\n\n        (new BCMathCalculator(6))->divide('10', '0');\n    }\n\n    public function test_mod_by_zero() : void\n    {\n        $this->expectException(\\LogicException::class);\n\n        (new BCMathCalculator(6))->modulo('10', '0');\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calculator/Tests/Unit/PHPCalculatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator\\Tests\\Unit;\n\nuse Aeon\\Calculator\\PHPCalculator;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PHPCalculatorTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function add_data_provider() : \\Generator\n    {\n        yield ['3.000000', 2.0, 1.0];\n        yield ['3.000000', 2, 1];\n        yield ['0.000200', 0.000_100, 0.000_100];\n        yield ['0.000201', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000001', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function add_sub_provider() : \\Generator\n    {\n        yield ['1.000000', 2.0, 1.0];\n        yield ['1.000000', 2, 1];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000001', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function multiply_provider() : \\Generator\n    {\n        yield ['2.000000', 2.0, 1.0];\n        yield ['2.000000', 2, 1];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000000', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function divide_provider() : \\Generator\n    {\n        yield ['1.000000', 1.0, 1.0];\n        yield ['2.000000', 2, 1];\n        yield ['1.000000', 0.000_100, 0.000_100];\n        yield ['1.010000', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['4.900000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float, float}, mixed, void>\n     */\n    public static function modulo_provider() : \\Generator\n    {\n        yield ['0.000000', 1.0, 1.0];\n        yield ['0.000000', 2, 1];\n        yield ['1.000000', 7, 2];\n        yield ['1.999980', 7, 2.50001];\n        yield ['0.000000', 0.000_100, 0.000_100];\n        yield ['0.000001', 0.000_101, 0.000_100];\n        yield ['0.000000', 0.000_000, 0.000_000_1];\n        yield ['0.000000', 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_equal_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [true, 0.000_100, 0.000_100];\n        yield [false, 0.000_101, 0.000_100];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_less_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [false, 0.000_000, 0.000_000_1];\n        yield [false, 0.000_000_49, 0.000_000_1];\n        yield [true, 0.000_000_1, 0.000_000_51];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_greater_data_provider() : \\Generator\n    {\n        yield [true, 2.0, 1.0];\n        yield [true, 2, 1];\n        yield [false, 0.000_000, 0.000_000_1];\n        yield [false, 0.000_000_49, 0.000_000_1];\n        yield [false, 0.000_000_1, 0.000_000_51];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_greater_than_eq_data_provider() : \\Generator\n    {\n        yield [true, 2.0, 1.0];\n        yield [true, 2, 1];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n        yield [false, 0.000_000_1, 0.000_000_51];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, float, float}, mixed, void>\n     */\n    public static function is_less_than_eq_data_provider() : \\Generator\n    {\n        yield [false, 2.0, 1.0];\n        yield [false, 2, 1];\n        yield [true, 0.000_000, 0.000_000_1];\n        yield [true, 0.000_000_49, 0.000_000_1];\n        yield [true, 0.000_000_1, 0.000_000_51];\n    }\n\n    public function test_precision() : void\n    {\n        $this->assertSame(6, (new PHPCalculator(6))->precision());\n    }\n\n    #[DataProvider('add_data_provider')]\n    public function test_add(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new PHPCalculator(6))->add(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('add_sub_provider')]\n    public function test_sub(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new PHPCalculator(6))->sub(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('multiply_provider')]\n    public function test_multiply(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new PHPCalculator(6))->multiply(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('divide_provider')]\n    public function test_divide(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new PHPCalculator(6))->divide(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('modulo_provider')]\n    public function test_modulo(string $result, float $value, float $nextValue) : void\n    {\n        $this->assertSame($result, (new PHPCalculator(6))->modulo(\\number_format($value, 9), \\number_format($nextValue, 9)));\n    }\n\n    #[DataProvider('is_equal_data_provider')]\n    public function test_is_equal(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new PHPCalculator(6))->isEqualTo(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n\n    #[DataProvider('is_less_data_provider')]\n    public function test_is_less(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new PHPCalculator(6))->isLessThan(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n\n    #[DataProvider('is_greater_data_provider')]\n    public function test_is_greater(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new PHPCalculator(6))->isGreaterThan(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n\n    #[DataProvider('is_greater_than_eq_data_provider')]\n    public function test_is_greater_than_eq(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new PHPCalculator(6))->isGreaterThanOrEqualTo(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n\n    #[DataProvider('is_less_than_eq_data_provider')]\n    public function test_is_less_than_eq(bool $equal, float $value, float $nextValue) : void\n    {\n        $this->assertSame($equal, (new PHPCalculator(6))->isLessThanOrEqualTo(\\number_format($value, 8), \\number_format($nextValue, 8)));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calculator/Tests/Unit/PreciseCalculatorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calculator\\Tests\\Unit;\n\nuse Aeon\\Calculator\\PreciseCalculator;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PreciseCalculatorTest extends TestCase\n{\n    public function test_initialization() : void\n    {\n        $calculator = PreciseCalculator::initialize(6);\n        $secondInitialization = PreciseCalculator::initialize(6);\n        $differentPrecision = PreciseCalculator::initialize(8);\n\n        $this->assertEquals($calculator, $secondInitialization);\n        $this->assertNotEquals($calculator, $differentPrecision);\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Functional/Gregorian/GregorianCalendarTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Functional\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\GregorianCalendar;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\TimePeriod;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class GregorianCalendarTest extends TestCase\n{\n    public function setUp() : void\n    {\n        \\date_default_timezone_set('UTC');\n    }\n\n    public function test_creating_calendar_with_system_default_tz() : void\n    {\n        $timezone = (GregorianCalendar::systemDefault())->now()->timeZone();\n\n        $this->assertSame(\n            \\date_default_timezone_get(),\n            $timezone instanceof TimeZone\n                ? $timezone->name()\n                : null\n        );\n    }\n\n    public function test_current_date() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable())->format('Y-m-d'),\n            (GregorianCalendar::UTC())->currentDay()->toDateTimeImmutable()->format('Y-m-d')\n        );\n    }\n\n    public function test_timezone() : void\n    {\n        $this->assertEquals(\n            TimeZone::UTC(),\n            (GregorianCalendar::UTC())->timeZone()\n        );\n    }\n\n    public function test_current_year() : void\n    {\n        $this->assertSame(\n            (int) (new \\DateTimeImmutable())->format('Y'),\n            (GregorianCalendar::UTC())->currentYear()->number()\n        );\n    }\n\n    public function test_current_month() : void\n    {\n        $this->assertSame(\n            (int) (new \\DateTimeImmutable())->format('m'),\n            (GregorianCalendar::UTC())->currentMonth()->number()\n        );\n    }\n\n    public function test_yesterday() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('yesterday'))->format('Y-m-d'),\n            GregorianCalendar::UTC()->yesterday()->format('Y-m-d')\n        );\n    }\n\n    public function test_tomorrow() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('tomorrow'))->format('Y-m-d'),\n            (GregorianCalendar::UTC())->tomorrow()->format('Y-m-d')\n        );\n    }\n\n    public function test_today_midnight() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('midnight'))->format('Y-m-d 00:00:00'),\n            (GregorianCalendar::UTC())->now()->midnight()->format('Y-m-d H:i:s')\n        );\n    }\n\n    public function test_today_midnight_with_custom_tz() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('midnight', new \\DateTimeZone('Europe/Warsaw')))->format('c'),\n            (new GregorianCalendar(TimeZone::europeWarsaw()))->now()->midnight()->format('c')\n        );\n    }\n\n    public function test_today_noon() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('noon'))->format('Y-m-d 12:00:00'),\n            (GregorianCalendar::UTC())->now()->noon()->format('Y-m-d H:i:s')\n        );\n    }\n\n    public function test_today_noon_with_custom_tz() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('noon', new \\DateTimeZone('Europe/Warsaw')))->format('c'),\n            (new GregorianCalendar(TimeZone::europeWarsaw()))->now()->noon()->format('c')\n        );\n    }\n\n    public function test_today_end_of_day() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('midnight'))->format('Y-m-d 23:59:59'),\n            (GregorianCalendar::UTC())->now()->endOfDay()->format('Y-m-d H:i:s')\n        );\n    }\n\n    public function test_today_end_of_day_with_custom_tz() : void\n    {\n        $this->assertSame(\n            (new \\DateTimeImmutable('tomorrow midnight', new \\DateTimeZone('Europe/Warsaw')))->sub(new \\DateInterval('PT1S'))->format('c'),\n            (new GregorianCalendar(TimeZone::europeWarsaw()))->now()->endOfDay()->format('c')\n        );\n    }\n\n    public function test_iterating_overt_time() : void\n    {\n        $timePeriods = ($calendar = GregorianCalendar::UTC())\n            ->yesterday()\n            ->midnight()\n            ->until($calendar->tomorrow()->midnight())\n            ->iterate(TimeUnit::hour(), Interval::closed())\n            ->map(function (TimePeriod $timePeriod) use (&$iterations) {\n                return $timePeriod->start()->toDateTimeImmutable()->format('Y-m-d H:i:s');\n            });\n\n        $this->assertCount(48, $timePeriods);\n\n        $this->assertSame(\n            (new \\DateTimeImmutable('yesterday midnight'))->format('Y-m-d H:i:s'),\n            $timePeriods[0]\n        );\n\n        $this->assertSame(\n            (new \\DateTimeImmutable('tomorrow midnight -1 hour'))->format('Y-m-d H:i:s'),\n            $timePeriods[47]\n        );\n    }\n\n    public function test_iterating_overt_time_and_taking_every_second_hour() : void\n    {\n        $timePeriods = ($calendar = GregorianCalendar::UTC())\n            ->yesterday()\n            ->midnight()\n            ->until($calendar->tomorrow()->midnight())\n            ->iterate(TimeUnit::hour(), Interval::closed())\n            ->filter(function (TimePeriod $timePeriod) use (&$iterations) : bool {\n                return $timePeriod->start()->time()->hour() % 2 === 0;\n            });\n\n        $this->assertCount(24, $timePeriods);\n    }\n\n    public function test_iterating_overt_time_backward() : void\n    {\n        $timePeriods = ($calendar = GregorianCalendar::UTC())\n            ->yesterday()\n            ->midnight()\n            ->until($calendar->tomorrow()->midnight())\n            ->iterateBackward(TimeUnit::hour(), Interval::leftOpen())\n            ->map(function (TimePeriod $interval) {\n                return $interval->start()->toDateTimeImmutable()->format('Y-m-d H:i:s');\n            });\n\n        $this->assertCount(47, $timePeriods);\n\n        $this->assertSame(\n            (new \\DateTimeImmutable('tomorrow midnight'))->format('Y-m-d H:i:s'),\n            $timePeriods[0]\n        );\n\n        $this->assertSame(\n            (new \\DateTimeImmutable('yesterday midnight +2 hours'))->format('Y-m-d H:i:s'),\n            $timePeriods[46]\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DateTimeIntervalIteratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\DateTimeIntervalIterator;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class DateTimeIntervalIteratorTest extends TestCase\n{\n    public function test_forward_even_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-05 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::rightOpen()\n        );\n\n        $this->assertEquals(TimeUnit::day(), $iterator->unit());\n        $this->assertEquals(Interval::rightOpen(), $iterator->interval());\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->start());\n        $this->assertEquals(DateTime::fromString('2020-01-05 00:00:00 UTC'), $iterator->end());\n        $this->assertTrue($iterator->isForward());\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(4, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_forward_even_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-03 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_forward_even_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-03 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_forward_even_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-03 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(3, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[1]);\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[2]);\n    }\n\n    public function test_forward_exceeded_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            TimeUnit::days(2),\n            Interval::rightOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_forward_exceeded_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            TimeUnit::days(2),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_forward_exceeded_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            TimeUnit::days(2),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_forward_exceeded_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            TimeUnit::days(2),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(1, $iterator);\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_forward_odd_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3),\n            Interval::rightOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_forward_odd_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_forward_odd_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_forward_odd_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_backward_even_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::rightOpen()\n        );\n\n        $this->assertFalse($iterator->isForward());\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(3, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[2]);\n    }\n\n    public function test_backward_even_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(3, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[2]);\n    }\n\n    public function test_backward_even_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_backward_even_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(4, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[3]);\n    }\n\n    public function test_backward_exceeded_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative(),\n            Interval::rightOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_backward_exceeded_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative(),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_backward_exceeded_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative(),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_backward_exceeded_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative(),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_backward_odd_iteration_right_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative(),\n            Interval::rightOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_backward_odd_iteration_left_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative(),\n            Interval::leftOpen()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_backward_odd_iteration_open() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative(),\n            Interval::open()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(null, $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $this->assertCount(0, $iterator);\n    }\n\n    public function test_backward_odd_iteration_closed() : void\n    {\n        $iterator = new DateTimeIntervalIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative(),\n            Interval::closed()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[1]);\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DateTimeIteratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\DateTimeIterator;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class DateTimeIteratorTest extends TestCase\n{\n    public function test_forward_even_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-03 00:00:00 UTC'),\n            TimeUnit::day()\n        );\n\n        $this->assertSame(0, $iterator->key());\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(3, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[2]);\n    }\n\n    public function test_forward_exceeded_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            TimeUnit::days(2)\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_forward_odd_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3)\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_backward_even_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(4, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[3]);\n    }\n\n    public function test_backward_exceeded_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-02 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(1, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]);\n    }\n\n    public function test_backward_odd_iteration() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative()\n        );\n\n        $iterator->rewind();\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $iterator->current());\n        $this->assertTrue($iterator->hasNext());\n\n        $iterator->next();\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $iterator->current());\n        $this->assertFalse($iterator->hasNext());\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertCount(2, $iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[1]);\n    }\n\n    public function test_forward_with_negative_time_unit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Forward DateTimeIterator 2020-01-01 00:00:00+00:00...2020-01-04 00:00:00+00:00 requires positive TimeUnit');\n\n        new DateTimeIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            TimeUnit::days(3)->toNegative()\n        );\n    }\n\n    public function test_backward_with_positive_time_unit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Backward DateTimeIterator 2020-01-04 00:00:00+00:00...2020-01-01 00:00:00+00:00 requires negative TimeUnit');\n\n        new DateTimeIterator(\n            DateTime::fromString('2020-01-04 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(3)\n        );\n    }\n\n    public function test_empty() : void\n    {\n        $iterator = new DateTimeIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()\n        );\n\n        $this->assertFalse($iterator->valid());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DateTimeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Time;\nuse Aeon\\Calendar\\Gregorian\\TimeEpoch;\nuse Aeon\\Calendar\\Gregorian\\TimePeriod;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class DateTimeTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string, DateTime, string}, mixed, void>\n     */\n    public static function creating_datetime_data_provider() : \\Generator\n    {\n        yield ['2020-01-01 00:00:00+00:00', DateTime::fromString('2020-01-01 00:00:00+00:00'), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+00:00', DateTime::create(2020, 01, 01, 00, 00, 00), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+00:00', DateTime::fromDateTime(new \\DateTimeImmutable('2020-01-01 00:00:00+00:00')), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+00:00', new DateTime(new Day(new Month(new Year(2020), 01), 01), new Time(0, 0, 0), TimeZone::UTC()), 'Y-m-d H:i:sP'];\n\n        yield ['2020-11-01 02:00:00-08:00', DateTime::fromDateTime(new \\DateTimeImmutable('2020-11-01 02:00 America/Los_Angeles')), 'Y-m-d H:i:sP'];\n\n        yield ['2020-01-01 00:00:00+01:00', DateTime::fromString('2020-01-01 00:00:00+01:00'), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+01:00', DateTime::create(2020, 01, 01, 00, 00, 00, 0, 'Europe/Warsaw'), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+01:00', DateTime::fromDateTime(new \\DateTimeImmutable('2020-01-01 00:00:00+01:00')), 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+01:00', new DateTime(new Day(new Month(new Year(2020), 01), 01), new Time(0, 0, 0), TimeZone::europeWarsaw()), 'Y-m-d H:i:sP'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, string, string}, mixed, void>\n     */\n    public static function creating_datetime_data_provider_from_string() : \\Generator\n    {\n        yield ['2020-01-01 00:00:00+00:00', '2020-01 00:00:00', 'Y-m-d H:i:sP'];\n        yield ['2020-01-02 00:00:00+00:00', '2020-01 00:00:00 +1 day', 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+00:00', '2020-01 00:00', 'Y-m-d H:i:sP'];\n        yield ['2020-01-01 00:00:00+00:00.000000', '2020-01 00:00', 'Y-m-d H:i:sP.u'];\n        yield ['2020-01-01 00:00:00+00:00.000500', '2020-01 00:00:00.0005', 'Y-m-d H:i:sP.u'];\n        yield ['1999-10-13 00:00:00', '1999-10-13 00:00:00', 'Y-m-d H:i:s'];\n        yield ['1999-10-13 00:00:00', 'Oct 13  1999 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2000-01-19 00:00:00', '2000-01-19 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2000-01-19 00:00:00', 'Jan 19  2000 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2001-12-21 00:00:00', '2001-12-21 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2001-12-21 00:00:00', 'Dec 21  2001 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2001-12-21 12:16:00', '2001-12-21 12:16', 'Y-m-d H:i:s'];\n        yield ['2001-12-21 12:16:00', 'Dec 21 2001 12:16', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22 21:19:58', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22 21:19:58-02', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22 21:19:58-0213', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22 21:19:58+02', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22 21:19:58+0213', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:20:58', '2001-10-22T21:20:58-03:40', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '2001-10-22T211958-2', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:19:58', '20011022T211958+0213', 'Y-m-d H:i:s'];\n        yield ['2001-10-22 21:20:00', '20011022T21:20+0215', 'Y-m-d H:i:s'];\n        yield ['1996-12-30 00:00:00', '1997W011 00:00:00', 'Y-m-d H:i:s'];\n        yield ['2004-03-01 05:00:00', '2004W101T05:00+0', 'Y-m-d H:i:s'];\n        // DTS switch +1 hour\n        yield ['2020-03-29 03:30:00+02:00', '2020-03-29 02:30:00 Europe/Warsaw', 'Y-m-d H:i:sP'];\n\n        // DTS switch -1 hour\n        if (PHP_VERSION_ID >= 81000) {\n            yield ['2020-10-25 02:30:00+02:00', '2020-10-25 02:30:00 Europe/Warsaw', 'Y-m-d H:i:sP'];\n            yield ['2020-10-25 01:30:00+02:00', '2020-10-25 01:30:00 Europe/Warsaw', 'Y-m-d H:i:sP'];\n        }\n        // now\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m-d'), 'now', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m-d'), 'noW', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('now GMT'))->format('Y-m-d'), 'noW GMT', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('now America/Los_Angeles'))->format('Y-m-d'), 'noW America/Los_Angeles', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('now +01:00'))->format('Y-m-d'), 'noW +01:00', 'Y-m-d'];\n        // today\n        yield [(new \\DateTimeImmutable('today'))->format('Y-m-d'), 'today ', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('today America/Los_Angeles'))->format('Y-m-d'), 'today America/Los_Angeles', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('today +01:00'))->format('Y-m-d'), 'today +01:00', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('today GMT'))->format('Y-m-d'), 'today GMT', 'Y-m-d'];\n        // noon\n        yield [(new \\DateTimeImmutable('noon'))->format('Y-m-d'), ' noON ', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('yesterday noon'))->format('Y-m-d'), 'yesterday noon', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('noon Europe/Warsaw'))->format('Y-m-d'), ' noON  Europe/Warsaw', 'Y-m-d'];\n        // tomorrow\n        yield [(new \\DateTimeImmutable('tomorrow'))->format('Y-m-d'), 'tomorrow', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('tomorrow midnight'))->format('Y-m-d'), 'tomorrow midnight', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('tomorrow +01:00'))->format('Y-m-d'), 'tomorrow +01:00', 'Y-m-d'];\n        // yesterday\n        yield [(new \\DateTimeImmutable('yesterday'))->format('Y-m-d'), 'yesterday', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('yesterday GMT'))->format('Y-m-d'), 'yesterday GMT', 'Y-m-d'];\n        // midnight\n        yield [(new \\DateTimeImmutable('midnight'))->format('Y-m-d'), 'midnight', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('midnight PST'))->format('Y-m-d'), 'midnight  PST', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('24 week'))->format('Y-m-d'), '24 week', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('today +1 hour'))->format('Y-m-d'), 'today +1 hour', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('tomorrow +1 hour'))->format('Y-m-d'), 'tomorrow +1 hour', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('-2 days'))->format('Y-m-d'), '-2 days', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('Monday'))->format('Y-m-d'), 'Monday', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('Monday next week'))->format('Y-m-d'), 'Monday next week', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('next year'))->format('Y-m-d'), 'next year', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('fifth day'))->format('Y-m-d'), 'fifth day', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('last day'))->format('Y-m-d'), 'last day', 'Y-m-d'];\n        yield [(new \\DateTimeImmutable('first day of January 2019'))->format('Y-m-d'), 'first day of January 2019', 'Y-m-d'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function invalid_date_time_string() : \\Generator\n    {\n        yield ['2020-31-01'];\n        yield ['2020-01-32'];\n        yield ['something'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, string, string}, mixed, void>\n     */\n    public static function modify_datetime() : \\Generator\n    {\n        yield ['2021-01-31 00:00:00 UTC', '+1 second', '2021-01-31 00:00:01 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+1 minute', '2021-01-31 00:01:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+1 hour', '2021-01-31 01:00:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+1 day', '2021-02-01 00:00:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+1 week', '2021-02-07 00:00:00 UTC'];\n\n        yield ['2021-01-31 00:00:00 UTC', '+1 fortnight', '2021-02-14 00:00:00 UTC'];\n\n        yield ['2020-01-01 00:00:00 UTC', '+1 hour', '2020-01-01 01:00:00 UTC'];\n        yield ['2020-01-01 00:00:00 UTC', '1 hour', '2020-01-01 01:00:00 UTC'];\n        yield ['2020-01-01 00:00:00 UTC', '-1 hour', '2019-12-31 23:00:00 UTC'];\n\n        yield ['2021-03-30 00:00:00 UTC', '-1 month', '2021-02-28 00:00:00 UTC'];\n        yield ['2021-02-28 00:00:00 UTC', '+1 month', '2021-03-28 00:00:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+1 month', '2021-02-28 00:00:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '+2 month', '2021-03-31 00:00:00 UTC'];\n\n        yield ['2021-01-31 00:00:00 UTC', '+1 year', '2022-01-31 00:00:00 UTC'];\n        yield ['2021-01-31 00:00:00 UTC', '-1 year', '2020-01-31 00:00:00 UTC'];\n\n        yield ['2022-10-25 15:00:00 UTC', 'next Saturday', '2022-10-29 15:00:00 UTC'];\n        yield ['2022-10-25 15:00:00 UTC', 'previous Saturday', '2022-10-22 15:00:00 UTC'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, int, string}, mixed, void>\n     */\n    public static function add_relative_timeunit_months() : \\Generator\n    {\n        yield ['2020-01-01', 1, '2020-02-01'];\n        yield ['2020-01-01', 6, '2020-07-01'];\n        yield ['2020-01-01', 12, '2021-01-01'];\n        yield ['2020-01-01', 16, '2021-05-01'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, int, string}, mixed, void>\n     */\n    public static function sub_relative_timeunit_months() : \\Generator\n    {\n        yield ['2020-01-01', 1, '2019-12-01'];\n        yield ['2020-01-01', 6, '2019-07-01'];\n        yield ['2020-01-01', 12, '2019-01-01'];\n        yield ['2020-01-01', 16, '2018-09-01'];\n    }\n\n    /**\n     * @return \\Generator<int, array{DateTime}, mixed, void>\n     */\n    public static function ambiguous_time_data_provider() : \\Generator\n    {\n        // yield [DateTime::fromString('2020-10-25 02:00:00 Europe/Warsaw')]; TODO: verify why this is failing only at CI, at PHP 8.1.8\n        yield [DateTime::fromString('2020-10-25 02:30:30 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-10-25 02:59:59 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-10-25 03:00:00 Europe/Warsaw')];\n    }\n\n    /**\n     * @return \\Generator<int, array{DateTime}, mixed, void>\n     */\n    public static function not_ambiguous_time_data_provider() : \\Generator\n    {\n        yield [new DateTime(Day::fromString('2020-01-01'), Time::fromString('00:00:00'), TimeZone::UTC())];\n        yield [DateTime::fromString('2020-10-25 01:59:59 UTC')];\n        yield [DateTime::fromString('2020-10-25 00:00:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-10-25 01:00:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-10-25 01:59:59 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 01:59:58 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 01:59:59 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 02:00:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 02:59:59 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 03:00:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 03:01:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 04:00:00 Europe/Warsaw')];\n        yield [DateTime::fromString('2020-03-29 05:00:00 Europe/Warsaw')];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, string}, mixed, void>\n     */\n    public static function timezone_abbreviation_provider() : \\Generator\n    {\n        yield ['PST', '2021-01-01 00:00:00 America/Los_Angeles'];\n        yield ['PDT', '2021-07-01 00:00:00 America/Los_Angeles'];\n        yield ['CEST', '2021-07-01 00:00:00 Europe/Warsaw'];\n        yield ['CET', '2021-01-01 00:00:00 Europe/Warsaw'];\n        yield ['CEST', '2021-01-01 00:00:00 CEST'];\n    }\n\n    /**\n     * @return \\Generator<int, array{DateTime, DateTime, int}>\n     */\n    public static function compare_to_provider() : \\Generator\n    {\n        yield [DateTime::fromString('2022-10-26 11:53:12'), DateTime::fromString('2022-10-26 11:53:12'), 0];\n        yield [DateTime::fromString('2022-10-26'), DateTime::fromString('2022-10-26'), 0];\n\n        yield [DateTime::fromString('2022-10-25 11:53:12'), DateTime::fromString('2022-10-26 11:53:12'), -1];\n        yield [DateTime::fromString('2022-10-25 11:53:12'), DateTime::fromString('2022-11-26 11:53:12'), -1];\n\n        yield [DateTime::fromString('2022-10-25 11:53:12'), DateTime::fromString('2022-10-25 00:53:12'), 1];\n        yield [DateTime::fromString('2022-11-26 11:53:12'), DateTime::fromString('2022-10-26 11:53:12'), 1];\n    }\n\n    public function setUp() : void\n    {\n        \\date_default_timezone_set('UTC');\n    }\n\n    #[DataProvider('creating_datetime_data_provider')]\n    public function test_creating_datetime(string $dateTimeString, DateTime $dateTime, string $format) : void\n    {\n        try {\n            $this->assertSame($dateTimeString, $dateTime->format($format));\n        } catch (InvalidArgumentException $exception) {\n            $this->fail($exception->getMessage());\n        }\n    }\n\n    #[DataProvider('creating_datetime_data_provider_from_string')]\n    public function test_creating_datetime_from_string(string $dateTimeString, string $dateTime, string $format) : void\n    {\n        try {\n            $this->assertSame($dateTimeString, DateTime::fromString($dateTime)->format($format));\n        } catch (InvalidArgumentException $exception) {\n            $this->fail($exception->getMessage());\n        }\n    }\n\n    #[DataProvider('invalid_date_time_string')]\n    public function test_creating_datetime_from_invalid_string(string $dateTimeInvalidString) : void\n    {\n        $this->expectExceptionMessage(\"Value \\\"{$dateTimeInvalidString}\\\" is not valid date time format.\");\n        $this->expectException(InvalidArgumentException::class);\n\n        DateTime::fromString($dateTimeInvalidString);\n    }\n\n    public function test_creating_datetime_from_string_relative_with_system_default_timezone_different_from_UTC() : void\n    {\n        \\date_default_timezone_set('Europe/Warsaw');\n\n        $dateTime = DateTime::fromString('tomorrow');\n\n        $this->assertSame('UTC', $dateTime->timeZone()->name());\n        $this->assertSame('Europe/Warsaw', \\date_default_timezone_get());\n    }\n\n    public function test_creating_datetime_from_string_relative_with_timezone_and_with_system_default_timezone_different_from_UTC() : void\n    {\n        \\date_default_timezone_set('Europe/Warsaw');\n\n        $dateTime = DateTime::fromString('tomorrow PST');\n\n        $this->assertSame('PST', $dateTime->timeZone()->name());\n        $this->assertSame('Europe/Warsaw', \\date_default_timezone_get());\n    }\n\n    public function test_debug_info() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 America/Los_Angeles');\n\n        $this->assertSame(\n            [\n                'datetime' => $dateTime->toISO8601(),\n                'day' => $dateTime->day(),\n                'time' => $dateTime->time(),\n                'timeZone' => $dateTime->timeZone(),\n            ],\n            $dateTime->__debugInfo()\n        );\n    }\n\n    public function test_create_with_timezone_and_without_time_offset() : void\n    {\n        $this->assertSame(\n            '2020-01-01 00:00:00.000000-0800',\n            (new DateTime(new Day(new Month(new Year(2020), 1), 1), new Time(0, 0, 0), TimeZone::americaLosAngeles()))->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_create_from_string_without_offset_and_timezone() : void\n    {\n        $this->assertSame(\n            'UTC',\n            DateTime::fromString('2020-01-01 00:00:00')->timeZone()->name()\n        );\n    }\n\n    public function test_create_from_string_with_default_system_timezone() : void\n    {\n        \\date_default_timezone_set('Europe/Warsaw');\n\n        $this->assertSame(\n            'UTC',\n            DateTime::fromString('2020-01-01 00:00:00')->timeZone()->name()\n        );\n        $this->assertSame('Europe/Warsaw', \\date_default_timezone_get());\n    }\n\n    public function test_create_from_timestamp_with_default_system_timezone() : void\n    {\n        \\date_default_timezone_set('Europe/Warsaw');\n\n        $this->assertSame(\n            'UTC',\n            DateTime::fromTimestampUnix(1577836800)->timeZone()->name()\n        );\n\n        $this->assertSame('Europe/Warsaw', \\date_default_timezone_get());\n    }\n\n    public function test_compare_objects_create_through_different_constructors() : void\n    {\n        \\date_default_timezone_set('Europe/Warsaw');\n\n        $dateTimeFromString = DateTime::fromString('2020-01-01 00:00:00');\n        $dateTimeFromTimestamp = DateTime::fromTimestampUnix(1577836800);\n        $dateTimeCreate = DateTime::create(2020, 01, 01, 00, 00, 00);\n        $dateTime = new DateTime(new Day(new Month(new Year(2020), 01), 01), new Time(00, 00, 00), TimeZone::UTC());\n\n        $this->assertTrue($dateTime->isEqualTo($dateTimeFromString));\n        $this->assertTrue($dateTime->isEqualTo($dateTimeFromTimestamp));\n        $this->assertTrue($dateTime->isEqualTo($dateTimeCreate));\n\n        $this->assertTrue($dateTimeFromString->isEqualTo($dateTimeCreate));\n        $this->assertTrue($dateTimeFromString->isEqualTo($dateTimeFromTimestamp));\n\n        $this->assertTrue($dateTimeFromTimestamp->isEqualTo($dateTimeCreate));\n    }\n\n    public function test_compare_source_datetime_immutable_with_converted_one() : void\n    {\n        $dateTimeImmutable = new \\DateTimeImmutable('2020-01-01 00:00:00');\n\n        $dateTime = DateTime::fromDateTime($dateTimeImmutable);\n\n        $this->assertEquals($dateTimeImmutable, $dateTime->toDateTimeImmutable());\n    }\n\n    public function test_compare_source_datetime_immutable_with_timezone_with_converted_one() : void\n    {\n        $dateTimeImmutable = new \\DateTimeImmutable('2020-01-01 00:00:00 America/Los_Angeles');\n\n        $dateTime = DateTime::fromDateTime($dateTimeImmutable);\n\n        $this->assertEquals($dateTimeImmutable, $dateTime->toDateTimeImmutable());\n    }\n\n    public function test_compare_source_from_string_with_timezone_with_converted_one() : void\n    {\n        $dateTimeImmutable = new \\DateTimeImmutable('2020-01-01 00:00:00 America/Los_Angeles');\n\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 America/Los_Angeles');\n\n        $this->assertEquals($dateTimeImmutable, $dateTime->toDateTimeImmutable());\n    }\n\n    public function test_create_from_string_with_timezone() : void\n    {\n        $this->assertSame(\n            '2020-01-01 00:00:00.000000-0800',\n            DateTime::fromString('2020-01-01 00:00:00 America/Los_Angeles')->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_to_string() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n        $this->assertSame($dateTime->toISO8601(), $dateTime->__toString());\n    }\n\n    public function test_to_iso8601_extended_format() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n        $this->assertSame('2020-01-01T00:00:00+00:00', $dateTime->toISO8601());\n    }\n\n    public function test_to_iso8601_basic_format() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n        $this->assertSame('20200101T000000+0000', $dateTime->toISO8601($extended = false));\n    }\n\n    public function test_create() : void\n    {\n        $this->assertTrue(\n            DateTime::create(2020, 01, 01, 00, 00, 00, 0, 'America/Los_Angeles')\n                ->isEqualTo(DateTime::fromString('2020-01-01 00:00:00 America/Los_Angeles'))\n        );\n    }\n\n    public function test_create_with_default_values() : void\n    {\n        $this->assertTrue(\n            DateTime::create(2020, 01, 01, 00, 00, 00)\n                ->isEqualTo(DateTime::fromString('2020-01-01 00:00:00.000000 UTC'))\n        );\n    }\n\n    public function test_from_timestamp() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 00:00:00')->isEqualTo(DateTime::fromTimestampUnix(1577836800))\n        );\n    }\n\n    public function test_year() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame(2020, $dateTime->year()->number());\n    }\n\n    public function test_month() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame(1, $dateTime->month()->number());\n    }\n\n    public function test_day() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame(1, $dateTime->day()->number());\n    }\n\n    public function test_midnight() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 UTC');\n\n        $this->assertSame('2020-01-01 00:00:00.000000+00:00', $dateTime->midnight()->format('Y-m-d H:i:s.uP'));\n    }\n\n    public function test_noon() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 UTC');\n\n        $this->assertSame('2020-01-01 12:00:00.000000+00:00', $dateTime->noon()->format('Y-m-d H:i:s.uP'));\n    }\n\n    public function test_end_of_day() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 UTC');\n\n        $this->assertSame('2020-01-01 23:59:59.999999+00:00', $dateTime->endOfDay()->format('Y-m-d H:i:s.uP'));\n    }\n\n    public function test_modify_with_non_relative_value() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Value \"2020-02-03 00:00:00 UTC\" is not valid relative time format.');\n\n        DateTime::fromString('2020-01-01 00:00:00 UTC')->modify('2020-02-03 00:00:00 UTC');\n    }\n\n    #[DataProvider('modify_datetime')]\n    public function test_modify(string $date, string $modifier, string $expectedDate) : void\n    {\n        $this->assertSame(\n            $expectedDate,\n            DateTime::fromString($date)->modify($modifier)->format('Y-m-d H:i:s T')\n        );\n    }\n\n    public function test_time() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 12:54:23.001000');\n\n        $this->assertSame(12, $dateTime->time()->hour());\n        $this->assertSame(54, $dateTime->time()->minute());\n        $this->assertSame(23, $dateTime->time()->second());\n        $this->assertSame(1, $dateTime->time()->millisecond());\n        $this->assertSame(1000, $dateTime->time()->microsecond());\n    }\n\n    public function test_timezone_conversion() : void\n    {\n        $dateTimeString = '2020-01-01 00:00:00.000000+0000';\n        $dateTime = DateTime::fromString($dateTimeString);\n        $timeZone = 'Europe/Warsaw';\n\n        $this->assertSame(\n            (new \\DateTimeImmutable($dateTimeString))->setTimezone(new \\DateTimeZone($timeZone))->format('Y-m-d H:i:s.uO'),\n            $dateTime->toTimeZone(TimeZone::fromString($timeZone))->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_daylight() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00')\n            ->toTimeZone(TimeZone::fromString('Europe/Warsaw'));\n\n        $this->assertFalse($dateTime->isDaylightSaving());\n        $this->assertTrue($dateTime->isDaylight());\n    }\n\n    public function test_saving_time() : void\n    {\n        $dateTime = DateTime::fromString('2020-08-01 00:00:00')\n            ->toTimeZone(TimeZone::fromString('Europe/Warsaw'));\n\n        $this->assertTrue($dateTime->isDaylightSaving());\n        $this->assertFalse($dateTime->isDaylight());\n    }\n\n    public function test_unix_timestamp() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame(1577836800, $dateTime->timestampUNIX()->inSeconds());\n    }\n\n    public function test_unix_zero_timestamp() : void\n    {\n        $dateTime = DateTime::fromString('1970-01-01 00:00:00');\n\n        $this->assertSame(0, $dateTime->timestampUNIX()->inSeconds());\n        $this->assertTrue($dateTime->timestampUNIX()->isPositive());\n    }\n\n    public function test_timestamp() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00 UTC');\n\n        $this->assertSame(1577836800, $dateTime->timestampUNIX()->inSeconds());\n        $this->assertSame(1577836800, $dateTime->timestamp(TimeEpoch::UNIX())->inSeconds());\n        $this->assertSame(1577836800, $dateTime->timestamp(TimeEpoch::POSIX())->inSeconds());\n        $this->assertSame(1514764837, $dateTime->timestamp(TimeEpoch::UTC())->inSeconds());\n        $this->assertSame(1261872018, $dateTime->timestamp(TimeEpoch::GPS())->inSeconds());\n        $this->assertSame(1956528037, $dateTime->timestamp(TimeEpoch::TAI())->inSeconds());\n    }\n\n    public function test_to_atomic_time() : void\n    {\n        $now = DateTime::fromString('2020-06-17 20:57:07 UTC');\n\n        $this->assertSame('2020-06-17T20:57:44+00:00', $now->toAtomicTime()->toISO8601());\n    }\n\n    public function test_to_gps_time() : void\n    {\n        $now = DateTime::fromString('2020-06-17 20:57:07 UTC');\n\n        $this->assertSame('2020-06-17T20:57:25+00:00', $now->toGPSTime()->toISO8601());\n    }\n\n    public function test_timestamp_before_epoch_start() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Given epoch started at 1970-01-01T00:00:00+00:00 which was after 1969-01-01T00:00:00+00:00');\n\n        $dateTime = DateTime::fromString('1969-01-01 00:00:00 UTC');\n\n        $this->assertSame(1577836800, $dateTime->timestamp(TimeEpoch::UNIX())->inSeconds());\n    }\n\n    public function test_add_hour() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame('2020-01-01T01:00:00+00:00', $dateTime->addHour()->format('c'));\n    }\n\n    public function test_add_hour_during_ambiguous_time() : void\n    {\n        $dateTime = DateTime::fromString('2020-11-01 01:30:00 America/Los_Angeles');\n        $dateTime = DateTime::fromString(\n            $dateTime->add(TimeUnit::minutes(30))->format('Y-m-d H:i:s.u') . ' America/Los_Angeles'\n        );\n\n        $this->assertSame('2020-11-01T01:00:00-07:00', $dateTime->toISO8601());\n    }\n\n    public function test_sub_hour() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame('2019-12-31T23:00:00+00:00', $dateTime->subHour()->format('c'));\n    }\n\n    public function test_add_hours() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame('2020-01-01T05:00:00+00:00', $dateTime->addHours(5)->format('c'));\n    }\n\n    public function test_sub_hours() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00');\n\n        $this->assertSame('2019-12-31T19:00:00+00:00', $dateTime->subHours(5)->format('c'));\n    }\n\n    public function test_iterating_until_forward() : void\n    {\n        $timePeriods = DateTime::fromString('2020-01-01 00:00:00')\n            ->iterate(\n                DateTime::fromString('2020-01-02 00:00:00'),\n                TimeUnit::hour()\n            );\n\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[0]);\n        $this->assertSame('2020-01-01 00:00:00', $timePeriods->all()[0]->start()->format('Y-m-d H:i:s'));\n        $this->assertFalse($timePeriods->all()[0]->distance()->isNegative());\n        $this->assertSame('2020-01-01 01:00:00', $timePeriods->all()[0]->end()->format('Y-m-d H:i:s'));\n    }\n\n    public function test_iterating_until_backward() : void\n    {\n        $timePeriods = DateTime::fromString('2020-01-02 00:00:00')\n            ->iterate(\n                DateTime::fromString('2020-01-01 00:00:00'),\n                TimeUnit::hour()\n            );\n\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[0]);\n        $this->assertSame('2020-01-02 00:00:00', $timePeriods->all()[0]->start()->format('Y-m-d H:i:s'));\n        $this->assertTrue($timePeriods->all()[0]->distance()->isNegative());\n        $this->assertSame('2020-01-01 23:00:00', $timePeriods->all()[0]->end()->format('Y-m-d H:i:s'));\n    }\n\n    public function test_equal_dates_in_different_timezones() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 00:00:00.100001')\n                ->toTimeZone(TimeZone::australiaSydney())\n                ->isEqualTo(\n                    DateTime::fromString('2020-01-01 00:00:00.100001')->toTimeZone(TimeZone::europeWarsaw())\n                )\n        );\n    }\n\n    public function test_add_second() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(1, $dateTime->addSecond()->time()->second());\n    }\n\n    public function test_add_seconds() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(5, $dateTime->addSeconds(5)->time()->second());\n    }\n\n    public function test_sub_second() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(59, $dateTime->subSecond()->time()->second());\n    }\n\n    public function test_sub_seconds() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(55, $dateTime->subSeconds(5)->time()->second());\n    }\n\n    public function test_add_minute() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(1, $dateTime->addMinute()->time()->minute());\n    }\n\n    public function test_add_minutes() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(5, $dateTime->addMinutes(5)->time()->minute());\n    }\n\n    public function test_sub_minute() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(59, $dateTime->subMinute()->time()->minute());\n    }\n\n    public function test_sub_minutes() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame(55, $dateTime->subMinutes(5)->time()->minute());\n    }\n\n    public function test_add_day() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2020-01-02 00:00:00+0000', $dateTime->addDay()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_days() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2020-01-03 00:00:00+0000', $dateTime->addDays(2)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_day() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-02 00:00:00+00');\n\n        $this->assertSame('2020-01-01 00:00:00+0000', $dateTime->subDay()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_days() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-05 00:00:00+00');\n\n        $this->assertSame('2020-01-01 00:00:00+0000', $dateTime->subDays(4)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_month() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2020-02-01 00:00:00+0000', $dateTime->addMonth()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_months() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2020-03-01 00:00:00+0000', $dateTime->addMonths(2)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_2_months_to_the_last_day_of_march() : void\n    {\n        $dateTime = DateTime::fromString('2020-03-31 00:00:00+00');\n\n        $this->assertSame('2020-04-30 00:00:00+0000', $dateTime->addMonths(1)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_month() : void\n    {\n        $dateTime = DateTime::fromString('2020-02-01 00:00:00+00');\n\n        $this->assertSame('2020-01-01 00:00:00+0000', $dateTime->subMonth()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_month_from_day_before_last_day_of_march() : void\n    {\n        $dateTime = DateTime::fromString('2021-03-30 00:00:00+00');\n\n        $this->assertSame('2021-02-28 00:00:00+0000', $dateTime->subMonth()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_months() : void\n    {\n        $dateTime = DateTime::fromString('2020-03-01 00:00:00+00');\n\n        $this->assertSame('2020-01-01 00:00:00+0000', $dateTime->subMonths(2)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_year() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2021-01-01 00:00:00+0000', $dateTime->addYear()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_years() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2022-01-01 00:00:00+0000', $dateTime->addYears(2)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_year() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2019-01-01 00:00:00+0000', $dateTime->subYear()->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_sub_years() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00+00');\n\n        $this->assertSame('2018-01-01 00:00:00+0000', $dateTime->subYears(2)->format('Y-m-d H:i:sO'));\n    }\n\n    public function test_add_timeunit_hour() : void\n    {\n        $this->assertSame(\n            '2020-01-01 01:00:00+0000',\n            DateTime::fromString('2020-01-01 00:00:00+00')->add(TimeUnit::hour())->format('Y-m-d H:i:sO')\n        );\n    }\n\n    #[DataProvider('add_relative_timeunit_months')]\n    public function test_add_relative_timeunit_months(string $date, int $addMonths, string $expectedDate) : void\n    {\n        $this->assertSame(\n            $expectedDate,\n            DateTime::fromString($date)->add(RelativeTimeUnit::months($addMonths))->format('Y-m-d')\n        );\n    }\n\n    #[DataProvider('sub_relative_timeunit_months')]\n    public function test_sub_relative_timeunit_months(string $date, int $addMonths, string $expectedDate) : void\n    {\n        $this->assertSame(\n            $expectedDate,\n            DateTime::fromString($date)->sub(RelativeTimeUnit::months($addMonths))->format('Y-m-d')\n        );\n    }\n\n    public function test_add_precse_timeunit() : void\n    {\n        $this->assertSame(\n            '2020-01-01 00:00:02.500000+0000',\n            DateTime::fromString('2020-01-01 00:00:00.000000+00')->add(TimeUnit::precise(2.500000))->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_sub_timeunit() : void\n    {\n        $this->assertSame(\n            '2020-01-01 00:00:00+0000',\n            DateTime::fromString('2020-01-01 01:00:00+00')->sub(TimeUnit::hour())->format('Y-m-d H:i:sO')\n        );\n    }\n\n    public function test_sub_precse_timeunit() : void\n    {\n        $this->assertSame(\n            '2020-01-01 00:59:57.500000+0000',\n            DateTime::fromString('2020-01-01 01:00:00.000000+00')->sub(TimeUnit::precise(2.500000))->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_is_after() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 01:00:00+00')\n                ->isAfter(DateTime::fromString('2020-01-01 00:00:00+00'))\n        );\n    }\n\n    public function test_is_before() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->isBefore(DateTime::fromString('2020-01-01 01:00:00+00'))\n        );\n        $this->assertFalse(\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->isBefore(DateTime::fromString('2020-01-01 00:00:00+00'))\n        );\n    }\n\n    public function test_is_after_or_equal() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->isAfterOrEqualTo(DateTime::fromString('2020-01-01 00:00:00+00'))\n        );\n    }\n\n    public function test_is_before_or_equal() : void\n    {\n        $this->assertTrue(\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->isBeforeOrEqualTo(DateTime::fromString('2020-01-01 00:00:00+00'))\n        );\n    }\n\n    public function test_until() : void\n    {\n        $this->assertSame(\n            1,\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->until(DateTime::fromString('2020-01-01 01:00:00+00'))\n                ->distance()\n                ->inHours()\n        );\n    }\n\n    public function test_until_with_date_before_datetime() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Backward DateTimeIterator 2020-01-01 00:00:00+00:00...2019-12-01 01:00:00+00:00 requires negative TimeUnit');\n\n        DateTime::fromString('2020-01-01 00:00:00+00')\n            ->until(DateTime::fromString('2019-12-01 01:00:00+00'))\n            ->iterate(TimeUnit::day(), Interval::rightOpen());\n    }\n\n    public function test_since_with_date_after_datetime() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Backward DateTimeIterator 2020-01-01 01:00:00+00:00...2019-12-01 00:00:00+00:00 requires negative TimeUnit');\n\n        DateTime::fromString('2019-12-01 00:00:00+00')\n            ->since(DateTime::fromString('2020-01-01 01:00:00+00'))\n            ->iterate(TimeUnit::day(), Interval::rightOpen());\n    }\n\n    public function test_distance_until() : void\n    {\n        $this->assertSame(\n            1,\n            DateTime::fromString('2020-01-01 00:00:00+00')\n                ->distanceUntil(DateTime::fromString('2020-01-01 01:00:00+00'))\n                ->inHours()\n        );\n    }\n\n    public function test_since() : void\n    {\n        $this->assertSame(\n            1,\n            DateTime::fromString('2020-01-01 01:00:00+00')\n                ->since(DateTime::fromString('2020-01-01 00:00:00+00'))\n                ->distance()\n                ->inHours()\n        );\n    }\n\n    public function test_distance_since() : void\n    {\n        $this->assertSame(\n            1,\n            DateTime::fromString('2020-01-01 01:00:00+00')\n                ->distanceSince(DateTime::fromString('2020-01-01 00:00:00+00'))\n                ->inHours()\n        );\n    }\n\n    #[DataProvider('ambiguous_time_data_provider')]\n    public function test_checking_is_ambiguous(DateTime $dateTime) : void\n    {\n        $this->assertTrue($dateTime->isAmbiguous(), $dateTime->toISO8601() . ' is not ambiguous, timezonedb version: ' . \\timezone_version_get());\n    }\n\n    #[DataProvider('not_ambiguous_time_data_provider')]\n    public function test_checking_is_not_ambiguous(DateTime $dateTime) : void\n    {\n        $this->assertFalse($dateTime->isAmbiguous());\n    }\n\n    public function test_using_create_constructor_during_dst_gap() : void\n    {\n        $this->assertSame(\n            '02:30:00.000000',\n            DateTime::create(2020, 03, 29, 02, 30, 00, 0, 'Europe/Warsaw')->time()->toString()\n        );\n    }\n\n    public function test_using_constructor_during_dst_gap() : void\n    {\n        $this->assertSame(\n            '02:30:00.000000',\n            (new DateTime(\n                new Day(new Month(new Year(2020), 03), 29),\n                new Time(02, 30, 00, 0),\n                TimeZone::europeWarsaw()\n            ))->time()->toString()\n        );\n    }\n\n    public function test_timezone_when_not_explicitly_provided() : void\n    {\n        $timeZone = DateTime::fromString('2020-03-29 00:00:00')->timeZone();\n\n        $this->assertInstanceOf(TimeZone::class, $timeZone);\n        $this->assertSame('UTC', $timeZone->name());\n    }\n\n    public function test_time_zone_when_only_time_offset_explicitly_provided() : void\n    {\n        $this->assertSame('+01:00', DateTime::fromString('2020-01-01 00:00:00+0100')->timeZone()->name());\n    }\n\n    public function test_yesterday() : void\n    {\n        $this->assertSame('2019-12-31T00:00:00+00:00', DateTime::fromString('2020-01-01 01:00:00')->yesterday()->toISO8601());\n    }\n\n    public function test_yesterday_with_tz() : void\n    {\n        $this->assertSame('2019-12-31T00:00:00+01:00', DateTime::fromString('2020-01-01 01:00:00 Europe/Warsaw')->yesterday()->toISO8601());\n    }\n\n    public function test_tomorrow() : void\n    {\n        $this->assertSame('2020-01-02T00:00:00+00:00', DateTime::fromString('2020-01-01 01:00:00')->tomorrow()->toISO8601());\n    }\n\n    public function test_tomorrow_with_tz() : void\n    {\n        $this->assertSame('2020-01-02T00:00:00+01:00', DateTime::fromString('2020-01-01 01:00:00 Europe/Warsaw')->tomorrow()->toISO8601());\n    }\n\n    public function test_set_time() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00.00000')->toTimeZone(TimeZone::europeWarsaw());\n        $newDateTime = $dateTime->setTime(new Time(1, 1, 1, 1));\n\n        $this->assertSame(\n            '2020-01-01 01:01:01.000001+01:00',\n            $newDateTime->format('Y-m-d H:i:s.uP')\n        );\n    }\n\n    public function test_set_time_in() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00.00000 UTC');\n        $newDateTime = $dateTime->setTimeIn(new Time(15, 0, 0, 0), TimeZone::americaNewYork());\n\n        $this->assertSame(\n            '2020-01-01 15:00:00.000000-05:00',\n            $newDateTime->format('Y-m-d H:i:s.uP')\n        );\n    }\n\n    public function test_set_day() : void\n    {\n        $dateTime = DateTime::fromString('2020-01-01 00:00:00.00000')->toTimeZone(TimeZone::europeWarsaw());\n        $newDateTime = $dateTime->setDay(Day::fromString('2020-01-05'));\n\n        $this->assertSame(\n            '2020-01-05 01:00:00.000000+01:00',\n            $newDateTime->format('Y-m-d H:i:s.uP')\n        );\n    }\n\n    public function test_distance_to() : void\n    {\n        $this->assertSame(58, DateTime::fromString('2020-01-01 01:00:00 UTC')->distance(DateTime::fromString('2020-01-03 12:00:00 Europe/Warsaw'))->inHours());\n    }\n\n    public function test_quarter() : void\n    {\n        $this->assertSame(1, DateTime::fromString('2020-01-01 00:00:00')->quarter()->number());\n        $this->assertSame(2, DateTime::fromString('2020-04-01 00:00:00')->quarter()->number());\n        $this->assertSame(3, DateTime::fromString('2020-07-01 00:00:00')->quarter()->number());\n        $this->assertSame(4, DateTime::fromString('2020-10-01 00:00:00')->quarter()->number());\n    }\n\n    public function test_serialization() : void\n    {\n        $dateTime = DateTime::create(2020, 03, 29, 02, 30, 00, 0, 'Europe/Warsaw');\n\n        $this->assertObjectEquals(\n            $dateTime,\n            \\unserialize(\\serialize($dateTime)),\n            'isEqual'\n        );\n    }\n\n    #[DataProvider('timezone_abbreviation_provider')]\n    public function test_timezone_abbreviation(string $abbreviation, string $date) : void\n    {\n        $this->assertSame($abbreviation, DateTime::fromString($date)->timeZoneAbbreviation()->name());\n    }\n\n    public function test_timezone_abbreviation_from_time_offset() : void\n    {\n        $this->expectException(Exception::class);\n\n        DateTime::fromString('2020-01-01 00:00:00+01:00')->timeZoneAbbreviation();\n    }\n\n    public function test_from_date_time_immutable() : void\n    {\n        $this->assertSame('2020-01-01 00:00:00', DateTime::fromDateTime(new \\DateTimeImmutable('2020-01-01 00:00:00'))->format('Y-m-d H:i:s'));\n    }\n\n    public function test_from_date_time() : void\n    {\n        $this->assertSame('2020-01-01 00:00:00', DateTime::fromDateTime(new \\DateTime('2020-01-01 00:00:00'))->format('Y-m-d H:i:s'));\n    }\n\n    public function test_equals_works() : void\n    {\n        $this->assertEquals(\n            DateTime::fromDateTime(new \\DateTimeImmutable('2022-01-01 15:00:00')),\n            DateTime::fromString('2022-01-01 15:00:00')\n        );\n\n        $this->assertEquals(\n            DateTime::fromDateTime(new \\DateTimeImmutable('2022-01-01 15:00:00')),\n            new DateTime(\n                new Day(\n                    new Month(\n                        new Year(2022),\n                        1\n                    ),\n                    1\n                ),\n                new Time(15, 0, 0),\n                TimeZone::UTC()\n            )\n        );\n    }\n\n    #[DataProvider('compare_to_provider')]\n    public function test_compare_to(DateTime $dateTime, DateTime $comparable, int $compareResult) : void\n    {\n        $this->assertSame($compareResult, $dateTime->compareTo($comparable));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/Day/WeekDayTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian\\Day;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Day\\WeekDay;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class WeekDayTest extends TestCase\n{\n    public function test_equals() : void\n    {\n        $this->assertTrue(WeekDay::monday()->isEqualTo(WeekDay::monday()));\n        $this->assertTrue(WeekDay::tuesday()->isEqualTo(WeekDay::tuesday()));\n        $this->assertTrue(WeekDay::wednesday()->isEqualTo(WeekDay::wednesday()));\n        $this->assertTrue(WeekDay::thursday()->isEqualTo(WeekDay::thursday()));\n        $this->assertTrue(WeekDay::friday()->isEqualTo(WeekDay::friday()));\n        $this->assertTrue(WeekDay::saturday()->isEqualTo(WeekDay::saturday()));\n        $this->assertTrue(WeekDay::sunday()->isEqualTo(WeekDay::sunday()));\n    }\n\n    public function test_create_for_week_day_less_than_0() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        $this->assertTrue(new WeekDay(0));\n    }\n\n    public function test_create_for_week_day_greater_than_12() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        $this->assertTrue(new WeekDay(8));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DayTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Time;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class DayTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{int}, mixed, void>\n     */\n    public static function create_day_with_invalid_number_provider() : \\Generator\n    {\n        yield [0];\n        yield [32];\n        yield [40];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, string, string}, mixed, void>\n     */\n    public static function creating_day_data_provider_from_string() : \\Generator\n    {\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m-d 00:00:00+00:00'), 'now', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m-d 00:00:00+00:00'), 'now ', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('today'))->format('Y-m-d 00:00:00+00:00'), 'today', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('today'))->format('Y-m-d 00:00:00+00:00'), ' tOday', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('noon'))->format('Y-m-d 00:00:00+00:00'), 'noon', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('noon'))->format('Y-m-d 00:00:00+00:00'), 'noon  ', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('yesterday noon'))->format('Y-m-d 00:00:00+00:00'), 'yesterday noon', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('tomorrow'))->format('Y-m-d 00:00:00+00:00'), 'tomorrow', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('tomorrow midnight'))->format('Y-m-d 00:00:00+00:00'), 'tomorrow midnight', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('yesterday'))->format('Y-m-d 00:00:00+00:00'), 'yesterday', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('midnight'))->format('Y-m-d 00:00:00+00:00'), 'midnight', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('24 week'))->format('Y-m-d 00:00:00+00:00'), '24 week', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('today +1 hour'))->format('Y-m-d 00:00:00+00:00'), 'today +1 hour', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('tomorrow +1 hour'))->format('Y-m-d 00:00:00+00:00'), 'tomorrow +1 hour', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('-2 days'))->format('Y-m-d 00:00:00+00:00'), '-2 days', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('Monday'))->format('Y-m-d 00:00:00+00:00'), 'Monday', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('Monday next week'))->format('Y-m-d 00:00:00+00:00'), 'Monday next week', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('next year'))->format('Y-m-d 00:00:00+00:00'), 'next year', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('fifth day'))->format('Y-m-d 00:00:00+00:00'), 'fifth day', 'Y-m-d H:i:sP'];\n        yield [(new \\DateTimeImmutable('first day of January 2019'))->format('Y-m-d 00:00:00+00:00'), 'first day of January 2019', 'Y-m-d H:i:sP'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function invalid_string_day_format() : \\Generator\n    {\n        yield ['2020-32'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, Day}, mixed, void>\n     */\n    public static function valid_string_day_format() : \\Generator\n    {\n        yield ['2020-01', new Day(new Month(new Year(2020), 1), 1)];\n        yield ['2020-01-02 +1 month', new Day(new Month(new Year(2020), 2), 2)];\n    }\n\n    /**\n     * @return \\Generator<int, array{Day, Day, int}>\n     */\n    public static function compare_to_provider() : \\Generator\n    {\n        yield [Day::fromString('2022-10-26'), Day::fromString('2022-10-26'), 0];\n        yield [Day::fromString('2022-10'), Day::fromString('2022-10'), 0];\n\n        yield [Day::fromString('2022-10-25'), Day::fromString('2022-10-26'), -1];\n        yield [Day::fromString('2022-10-25'), Day::fromString('2022-11-25'), -1];\n\n        yield [Day::fromString('2022-11-26'), Day::fromString('2022-10-26'), 1];\n        yield [Day::fromString('2022-10-26'), Day::fromString('2022-10-25'), 1];\n    }\n\n    #[DataProvider('create_day_with_invalid_number_provider')]\n    public function test_create_day_with_invalid_number(int $number) : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Day number must be greater or equal 1 and less or equal than 31');\n\n        new Day(new Month(new Year(2020), 01), $number);\n    }\n\n    public function test_debug_info() : void\n    {\n        $this->assertSame(\n            [\n                'year' => 2020,\n                'month' => 1,\n                'day' => 1,\n            ],\n            Day::fromString('2020-01-01')->__debugInfo()\n        );\n    }\n\n    #[DataProvider('creating_day_data_provider_from_string')]\n    public function test_creating_day_from_string(string $dateTimeString, string $dateTime, string $format) : void\n    {\n        try {\n            $this->assertSame($dateTimeString, Day::fromString($dateTime)->format($format));\n        } catch (InvalidArgumentException $exception) {\n            $this->fail($exception->getMessage());\n        }\n    }\n\n    public function test_to_string() : void\n    {\n        $this->assertSame(\n            '2020-01-02',\n            Day::fromString('2020-01-01 +1 day')->toString()\n        );\n    }\n\n    #[DataProvider('invalid_string_day_format')]\n    public function test_from_invalid_string(string $invalidValue) : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Value \\\"{$invalidValue}\\\" is not valid day format.\");\n\n        Day::fromString($invalidValue);\n    }\n\n    #[DataProvider('valid_string_day_format')]\n    public function test_from_string(string $invalidValue, Day $month) : void\n    {\n        $this->assertObjectEquals($month, Day::fromString($invalidValue), 'isEqual');\n    }\n\n    public function test_midnight() : void\n    {\n        $day = Day::fromString('2020-01-01');\n\n        $this->assertSame('2020-01-01 00:00:00.000000+00:00', $day->midnight(TimeZone::UTC())->format('Y-m-d H:i:s.uP'));\n        $this->assertSame(2020, $day->year()->number());\n    }\n\n    public function test_noon() : void\n    {\n        $day = Day::fromString('2020-01-01');\n\n        $this->assertSame('2020-01-01 12:00:00.000000+00:00', $day->noon(TimeZone::UTC())->format('Y-m-d H:i:s.uP'));\n    }\n\n    public function test_end_of_day() : void\n    {\n        $day = Day::fromString('2020-01-01');\n\n        $this->assertSame('2020-01-01 23:59:59.999999+0000', $day->endOfDay(TimeZone::UTC())->format('Y-m-d H:i:s.uO'));\n    }\n\n    public function test_set_time() : void\n    {\n        $day = Day::fromString('2020-01-01');\n\n        $this->assertSame(\n            '2020-01-01 11:00:00.000000+0100',\n            $day->setTime(Time::fromString('11:00:00'), TimeZone::europeWarsaw())->format('Y-m-d H:i:s.uO')\n        );\n    }\n\n    public function test_next() : void\n    {\n        $day = Day::fromString('2020-01-01')->next();\n\n        $this->assertSame('2020-01-02 00:00:00', $day->format('Y-m-d H:i:s'));\n    }\n\n    public function test_previous() : void\n    {\n        $day = Day::fromString('2020-01-02')->previous();\n\n        $this->assertSame('2020-01-01 00:00:00', $day->format('Y-m-d H:i:s'));\n    }\n\n    public function test_week_of_year() : void\n    {\n        $day = Day::fromString('2020-02-01');\n\n        $this->assertSame(5, $day->weekOfYear());\n    }\n\n    public function test_week_of_month() : void\n    {\n        $this->assertSame(1, Day::fromString('2020-01-05')->weekOfMonth());\n        $this->assertSame(2, Day::fromString('2020-01-12')->weekOfMonth());\n        $this->assertSame(3, Day::fromString('2020-01-19')->weekOfMonth());\n        $this->assertSame(4, Day::fromString('2020-01-26')->weekOfMonth());\n        $this->assertSame(5, Day::fromString('2020-01-31')->weekOfMonth());\n        $this->assertSame(1, Day::fromString('2020-02-1')->weekOfMonth());\n        $this->assertSame(2, Day::fromString('2020-02-9')->weekOfMonth());\n        $this->assertSame(3, Day::fromString('2020-02-16')->weekOfMonth());\n        $this->assertSame(4, Day::fromString('2020-02-23')->weekOfMonth());\n        $this->assertSame(5, Day::fromString('2020-02-29')->weekOfMonth());\n    }\n\n    public function test_day_of_year() : void\n    {\n        $day = Day::fromString('2020-02-01');\n\n        $this->assertSame(32, $day->dayOfYear());\n    }\n\n    public function test_week_day() : void\n    {\n        $this->assertSame(5, Day::fromString('2020-01-03')->weekDay()->number());\n        $this->assertSame('Friday', Day::fromString('2020-01-03')->weekDay()->name());\n        $this->assertSame('Fri', Day::fromString('2020-01-03')->weekDay()->shortName());\n    }\n\n    public function test_format() : void\n    {\n        $this->assertSame('2020-01-03', Day::fromString('2020-01-03')->format('Y-m-d'));\n    }\n\n    public function test_is_weekend() : void\n    {\n        $this->assertFalse(Day::fromString('2020-01-03')->isWeekend());\n        $this->assertTrue(Day::fromString('2020-01-04')->isWeekend());\n        $this->assertTrue(Day::fromString('2020-01-05')->isWeekend());\n        $this->assertFalse(Day::fromString('2020-01-06')->isWeekend());\n    }\n\n    public function test_is_equal() : void\n    {\n        $this->assertTrue(Day::fromString('2020-01-01')->isEqualTo(Day::fromString('2020-01-01')));\n        $this->assertFalse(Day::fromString('2020-01-02')->isEqualTo(Day::fromString('2020-01-01')));\n    }\n\n    public function test_is_before() : void\n    {\n        $this->assertTrue(Day::fromString('2019-01-01')->isBefore(Day::fromString('2020-01-01')));\n        $this->assertFalse(Day::fromString('2019-01-01')->isBefore(Day::fromString('2019-01-01')));\n        $this->assertTrue(Day::fromString('2020-01-01')->isBeforeOrEqualTo(Day::fromString('2020-01-01')));\n\n        $this->assertFalse(Day::fromString('2021-01-01')->isBefore(Day::fromString('2020-01-01')));\n        $this->assertFalse(Day::fromString('2021-01-01')->isBeforeOrEqualTo(Day::fromString('2020-01-01')));\n        $this->assertTrue(Day::fromString('2020-01-01')->isBeforeOrEqualTo(Day::fromString('2020-05-01')));\n        $this->assertTrue(Day::fromString('2020-05-01')->isAfterOrEqualTo(Day::fromString('2020-01-01')));\n    }\n\n    public function test_is_after() : void\n    {\n        $this->assertTrue(Day::fromString('2022-01-01')->isAfter(Day::fromString('2020-02-01')));\n        $this->assertFalse(Day::fromString('2020-01-01')->isAfter(Day::fromString('2020-01-01')));\n        $this->assertTrue(Day::fromString('2020-01-01')->isAfterOrEqualTo(Day::fromString('2020-01-01')));\n\n        $this->assertFalse(Day::fromString('2019-01-01')->isAfter(Day::fromString('2020-02-01')));\n        $this->assertFalse(Day::fromString('2019-01-01')->isAfterOrEqualTo(Day::fromString('2020-02-01')));\n    }\n\n    public function test_reset_time_in_to_datetime_immutable() : void\n    {\n        $day = new Day(new Month(new Year(2020), 1), 1);\n\n        $dateTimeImmutable1 = $day->toDateTimeImmutable();\n        \\sleep(1);\n        $dateTimeImmutable2 = $day->toDateTimeImmutable();\n\n        $this->assertTrue($dateTimeImmutable1 == $dateTimeImmutable2);\n    }\n\n    public function test_modify_months() : void\n    {\n        $this->assertSame('2020-05-02', Day::fromString('2020-06-01')->subDays(30)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-05-31', Day::fromString('2020-06-01')->subDays(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-07-01', Day::fromString('2020-06-01')->addDays(30)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->assertSame('2017-12-01', Day::fromString('2020-06-01')->subMonths(30)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-05-01', Day::fromString('2020-06-01')->subMonths(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2022-12-01', Day::fromString('2020-06-01')->addMonths(30)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->assertSame('1990-06-01', Day::fromString('2020-06-01')->subYears(30)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-06-01', Day::fromString('2020-06-01')->subYears(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2050-06-01', Day::fromString('2020-06-01')->addYears(30)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->assertSame('2020-01-01', Day::fromString('2020-01-01')->add(0, 0, 0)->format('Y-m-d'));\n        $this->assertSame('2021-02-02', Day::fromString('2020-01-01')->add(1, 1, 1)->format('Y-m-d'));\n        $this->assertSame('2018-11-30', Day::fromString('2020-01-01')->add(-1, -1, -1)->format('Y-m-d'));\n        $this->assertSame('2021-02-02', Day::fromString('2020-01-01')->sub(-1, -1, -1)->format('Y-m-d'));\n        $this->assertSame('2018-11-30', Day::fromString('2020-01-01')->sub(1, 1, 1)->format('Y-m-d'));\n    }\n\n    public function test_until_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('1 January 2020 is after 1 January 2019');\n        Day::fromString('2020-01-01')->until(Day::fromString('2019-01-01'), Interval::rightOpen());\n    }\n\n    public function test_since_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('1 January 2019 is before 1 January 2020');\n        Day::fromString('2019-01-01')->since(Day::fromString('2020-01-01'), Interval::rightOpen());\n    }\n\n    public function test_until() : void\n    {\n        $this->assertCount(5, $days = Day::fromString('2020-01-01')->until(Day::fromString('2020-01-06'), Interval::rightOpen()));\n        $this->assertInstanceOf(Day::class, $days->all()[0]);\n        $this->assertInstanceOf(Day::class, $days->all()[4]);\n        $this->assertSame(1, $days->all()[0]->number());\n        $this->assertSame(5, $days->all()[4]->number());\n    }\n\n    public function test_since() : void\n    {\n        $this->assertCount(5, $days = Day::fromString('2020-01-06')->since(Day::fromString('2020-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Day::class, $days->all()[0]);\n        $this->assertInstanceOf(Day::class, $days->all()[4]);\n        $this->assertSame(6, $days->all()[0]->number());\n        $this->assertSame(2, $days->all()[4]->number());\n    }\n\n    public function test_iterate_until() : void\n    {\n        $this->assertCount(5, $days = Day::fromString('2020-01-01')->iterate(Day::fromString('2020-01-06'), Interval::rightOpen()));\n        $this->assertInstanceOf(Day::class, $days->all()[0]);\n        $this->assertInstanceOf(Day::class, $days->all()[4]);\n        $this->assertSame(1, $days->all()[0]->number());\n        $this->assertSame(5, $days->all()[4]->number());\n    }\n\n    public function test_iterate_since() : void\n    {\n        $this->assertCount(5, $days = Day::fromString('2020-01-06')->iterate(Day::fromString('2020-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Day::class, $days->all()[0]);\n        $this->assertInstanceOf(Day::class, $days->all()[4]);\n        $this->assertSame(6, $days->all()[0]->number());\n        $this->assertSame(2, $days->all()[4]->number());\n    }\n\n    public function test_days_between() : void\n    {\n        $day1 = Day::fromString('2020-01-02');\n        $day2 = Day::fromString('2020-01-01');\n        $this->assertInstanceOf(TimeUnit::class, $day1->timeBetween($day2));\n        $this->assertSame(1, $day1->timeBetween($day2)->inDays());\n        $this->assertSame(1, $day2->timeBetween($day1)->inDays());\n    }\n\n    public function test_day_static_create() : void\n    {\n        $day = Day::create(2020, 12, 24);\n        $this->assertTrue(Day::fromString('2020-12-24')->isEqualTo($day));\n    }\n\n    public function test_distance_to() : void\n    {\n        $this->assertSame(9, Day::create(2020, 01, 01)->distance(Day::create(2020, 01, 10))->inDays());\n    }\n\n    public function test_quarter() : void\n    {\n        $this->assertSame(1, Day::fromString('2020-01-01')->quarter()->number());\n        $this->assertSame(2, Day::fromString('2020-04-01')->quarter()->number());\n        $this->assertSame(3, Day::fromString('2020-07-01')->quarter()->number());\n        $this->assertSame(4, Day::fromString('2020-10-01')->quarter()->number());\n    }\n\n    public function test_serialization() : void\n    {\n        $day = Day::create(2020, 01, 01);\n\n        $this->assertObjectEquals(\n            $day,\n            \\unserialize(\\serialize($day)),\n            'isEqual'\n        );\n    }\n\n    #[DataProvider('compare_to_provider')]\n    public function test_compare_to(Day $time, Day $comparable, int $compareResult) : void\n    {\n        $this->assertSame($compareResult, $time->compareTo($comparable));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DaysIteratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\DateTimeIntervalIterator;\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\DaysIterator;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * @psalm-immutable\n */\nfinal class DaysIteratorTest extends TestCase\n{\n    public function test_reverse_days_iterator() : void\n    {\n        $begin = DateTime::fromString('2020-01-01 00:00:00 UTC');\n        $end = DateTime::fromString('2020-01-10 00:00:00 UTC');\n\n        $timeUnit = TimeUnit::day();\n\n        $array = \\iterator_to_array(\n            DaysIterator::fromDateTimeIterator(new DateTimeIntervalIterator($begin, $end, $timeUnit, Interval::closed()))\n                ->reverse()\n        );\n\n        $this->assertEquals($array[0], Day::fromString('2020-01-10 00:00:00 UTC'));\n        $this->assertEquals($array[9], Day::fromString('2020-01-01 00:00:00 UTC'));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/DaysTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Days;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class DaysTest extends TestCase\n{\n    public function test_array_access() : void\n    {\n        $days = Days::fromArray(\n            Day::fromString('2002-01-01'),\n            Day::fromString('2002-01-02'),\n            Day::fromString('2002-01-03')\n        );\n\n        $this->assertTrue(isset($days->all()[0]));\n        $this->assertInstanceOf(Day::class, $days->all()[0]);\n        $this->assertSame(3, \\iterator_count($days->getIterator()));\n        $this->assertSame(3, $days->count());\n    }\n\n    public function test_map() : void\n    {\n        $days = Days::fromArray(\n            Day::fromString('2002-01-01'),\n            Day::fromString('2002-01-02'),\n            Day::fromString('2002-01-03')\n        );\n\n        $this->assertSame(\n            [1, 2, 3],\n            $days->map(function (Day $day) {\n                return $day->number();\n            })\n        );\n    }\n\n    public function test_filter() : void\n    {\n        $days = Days::fromArray(\n            Day::fromString('2002-01-01'),\n            Day::fromString('2002-01-02'),\n            Day::fromString('2002-01-03')\n        );\n\n        $this->assertObjectEquals(\n            Day::fromString('2002-01-01'),\n            $days->filter(function (Day $day) {\n                return $day->number() === 1;\n            })->all()[0],\n            'isEqual'\n        );\n    }\n\n    public function test_foreach() : void\n    {\n        $days = Days::fromArray(\n            Day::fromString('2002-01-01'),\n            Day::fromString('2002-01-02'),\n            Day::fromString('2002-01-03')\n        );\n\n        foreach ($days as $day) {\n            $this->assertInstanceOf(Day::class, $day);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/LeapSecondTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\LeapSecond;\nuse Aeon\\Calendar\\Gregorian\\LeapSeconds;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class LeapSecondTest extends TestCase\n{\n    public function test_serialization() : void\n    {\n        $leapSecond = LeapSeconds::load()->all()[0];\n\n        $this->assertObjectEquals(\n            $leapSecond,\n            \\unserialize(\\serialize($leapSecond)),\n            'isEqual'\n        );\n    }\n\n    public function test_is_equal() : void\n    {\n        $leapSeconds = LeapSeconds::load()->all();\n\n        $this->assertFalse($leapSeconds[0]->isEqualTo($leapSeconds[1]));\n        $this->assertFalse($leapSeconds[0]->isEqualTo(new LeapSecond(DateTime::fromString('1972-01-01 00:00:00 UTC'), TimeUnit::seconds(12)), ));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/LeapSecondsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\GregorianCalendar;\nuse Aeon\\Calendar\\Gregorian\\LeapSecond;\nuse Aeon\\Calendar\\Gregorian\\LeapSeconds;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class LeapSecondsTest extends TestCase\n{\n    /**\n     * @runInSeparateProcess\n     *\n     * This test will start failing 5 days before expiration date of the current leap seconds list\n     * Once it does please visit https://data.iana.org/time-zones/tzdb/leapseconds\n     * and if there is a new leap second announced add it to the list or extend expiration\n     * date according to the document.\n     */\n    public function test_expiration_of_current_leap_seconds_list() : void\n    {\n        $leapSeconds = LeapSeconds::load();\n\n        $this->assertFalse(\n            $leapSeconds->expirationDate()->isBefore(\n                GregorianCalendar::UTC()->now()->add(TimeUnit::days(5))\n            )\n        );\n        $this->assertSame(\n            37,\n            $leapSeconds->offsetTAI()->inSeconds()\n        );\n    }\n\n    /**\n     * @runInSeparateProcess\n     */\n    public function test_finding_leap_seconds_between_1970_jan_1_and_1980_jan_1() : void\n    {\n        $leapSeconds = LeapSeconds::load();\n\n        $this->assertEquals(\n            19,\n            $leapSeconds->findAllBetween(\n                DateTime::fromString('1970-01-01 00:00:00 UTC')->until(DateTime::fromString('1980-01-06 00:00:00 UTC'))\n            )->offsetTAI()->inSeconds()\n        );\n        $this->assertSame(\n            9,\n            $leapSeconds->findAllBetween(\n                DateTime::fromString('1970-01-01 00:00:00 UTC')->until(DateTime::fromString('1980-01-01 00:00:00 UTC'))\n            )->count()\n                ->inSeconds()\n        );\n    }\n\n    /**\n     * @runInSeparateProcess\n     */\n    public function test_finding_leap_seconds_since_date() : void\n    {\n        $leapSeconds = LeapSeconds::load();\n\n        $this->assertSame(\n            28,\n            $leapSeconds->since(\n                DateTime::fromString('1970-01-01 00:00:00 UTC')\n            )->count()\n                ->inSeconds()\n        );\n    }\n\n    public function test_creating_leap_second_with_invalid_offset() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Leap second TAI offset must be greater or equal 10');\n\n        new LeapSecond(DateTime::fromString('1970-01-01 00:00:00 UTC'), TimeUnit::seconds(5));\n    }\n\n    /**\n     * @runInSeparateProcess\n     */\n    public function test_all_leap_seconds() : void\n    {\n        $leapSeconds = LeapSeconds::load();\n\n        $this->assertSame(\n            [\n                10, 11, 12, 13, 14, 15, 16, 17, 18, 19,\n                20, 21, 22, 23, 24, 24, 25, 26, 27,\n                28, 29, 30, 31, 32, 33, 34, 36, 37,\n            ],\n            \\array_map(\n                function (LeapSecond $leapSecond) : int {\n                    return $leapSecond->offsetTAI()->inSeconds();\n                },\n                $leapSeconds->all()\n            )\n        );\n    }\n\n    /**\n     * @runInSeparateProcess\n     */\n    public function test_filter_leap_seconds() : void\n    {\n        $leapSeconds = LeapSeconds::load();\n\n        $this->assertSame(\n            10,\n            $leapSeconds->filter(fn (LeapSecond $leapSecond) => $leapSecond->offsetTAI()->inSeconds() === 10)->offsetTAI()->inSeconds()\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/MonthDaysTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class MonthDaysTest extends TestCase\n{\n    public function test_count() : void\n    {\n        $this->assertSame(\n            31,\n            (new Month(new Year(2020), 01))->days()->count()\n        );\n    }\n\n    public function test_first_day() : void\n    {\n        $this->assertSame(\n            1,\n            (new Month(new Year(2020), 01))->days()->first()->number()\n        );\n    }\n\n    public function test_last_day() : void\n    {\n        $this->assertSame(\n            31,\n            (new Month(new Year(2020), 01))->days()->last()->number()\n        );\n    }\n\n    public function test_map_days() : void\n    {\n        $this->assertSame(\n            \\range(1, 31),\n            (new Month(new Year(2020), 01))->days()->map(fn (Day $day) => $day->number())\n        );\n    }\n\n    public function test_filter_days() : void\n    {\n        $this->assertSame(\n            [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30],\n            \\array_map(\n                function (Day $day) : int {\n                    return $day->number();\n                },\n                \\array_values((new Month(new Year(2020), 01))->days()->filter(fn (Day $day) => $day->number() % 2 === 0)->all())\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/MonthTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class MonthTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string, string}, mixed, void>\n     */\n    public static function creating_month_data_provider_from_string() : \\Generator\n    {\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m'), 'now'];\n        yield [(new \\DateTimeImmutable('now'))->format('Y-m'), 'NoW'];\n        yield [(new \\DateTimeImmutable('today'))->format('Y-m'), 'today'];\n        yield [(new \\DateTimeImmutable('today'))->format('Y-m'), 'today '];\n        yield [(new \\DateTimeImmutable('noon'))->format('Y-m'), 'noon'];\n        yield [(new \\DateTimeImmutable('noon'))->format('Y-m'), ' noon'];\n        yield [(new \\DateTimeImmutable('yesterday noon'))->format('Y-m'), 'yesterday noon'];\n        yield [(new \\DateTimeImmutable('tomorrow'))->format('Y-m'), 'tomorrow'];\n        yield [(new \\DateTimeImmutable('tomorrow midnight'))->format('Y-m'), 'tomorrow midnight'];\n        yield [(new \\DateTimeImmutable('yesterday'))->format('Y-m'), 'yesterday'];\n        yield [(new \\DateTimeImmutable('midnight'))->format('Y-m'), 'midnight'];\n        yield [(new \\DateTimeImmutable('24 week'))->format('Y-m'), '24 week'];\n        yield [(new \\DateTimeImmutable('today +1 hour'))->format('Y-m'), 'today +1 hour'];\n        yield [(new \\DateTimeImmutable('tomorrow +1 hour'))->format('Y-m'), 'tomorrow +1 hour'];\n        yield [(new \\DateTimeImmutable('-2 days'))->format('Y-m'), '-2 days'];\n        yield [(new \\DateTimeImmutable('Monday'))->format('Y-m'), 'Monday'];\n        yield [(new \\DateTimeImmutable('Monday next week'))->format('Y-m'), 'Monday next week'];\n        yield [(new \\DateTimeImmutable('next year'))->format('Y-m'), 'next year'];\n        yield [(new \\DateTimeImmutable('fifth day'))->format('Y-m'), 'fifth day'];\n        yield [(new \\DateTimeImmutable('first day of January 2019'))->format('Y-m'), 'first day of January 2019'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function invalid_string_day_format() : \\Generator\n    {\n        yield ['test'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, Time}, mixed, void>\n     */\n    public static function valid_string_day_format() : \\Generator\n    {\n        yield ['2020-01', new Month(new Year(2020), 1)];\n        yield ['2020-01 +1 month', new Month(new Year(2020), 2)];\n        yield ['2020-01 +1 year', new Month(new Year(2021), 1)];\n        yield ['2020-01-01', new Month(new Year(2020), 1)];\n    }\n\n    /**\n     * @return \\Generator<int, array{Month, Month, int}>\n     */\n    public static function compare_to_provider() : \\Generator\n    {\n        yield [Month::fromString('2022-10'), Month::fromString('2022-10'), 0];\n        yield [Month::fromString('2022'), Month::fromString('2022'), 0];\n\n        yield [Month::fromString('2022-09'), Month::fromString('2022-10'), -1];\n        yield [Month::fromString('2021-10'), Month::fromString('2022-10'), -1];\n\n        yield [Month::fromString('2022-11'), Month::fromString('2022-10'), 1];\n        yield [Month::fromString('2022-10'), Month::fromString('2021-10'), 1];\n    }\n\n    public function test_create_with_month_number_lower_than_0() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Month(new Year(2020), 0);\n    }\n\n    public function test_create_with_month_number_greater_than_12() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Month(new Year(2020), 13);\n    }\n\n    public function test_first_day_of_month() : void\n    {\n        $month = Month::fromString('2020-01-01');\n\n        $this->assertSame(1, $month->firstDay()->number());\n    }\n\n    public function test_create_from_datetime() : void\n    {\n        $month = Month::fromDateTime(new \\DateTimeImmutable('2020-02-01'));\n\n        $this->assertSame(2, $month->number());\n    }\n\n    #[DataProvider('creating_month_data_provider_from_string')]\n    public function test_creating_month_from_string(string $dateTimeString, string $dateTime) : void\n    {\n        try {\n            $this->assertSame($dateTimeString, Month::fromString($dateTime)->toString());\n        } catch (InvalidArgumentException $exception) {\n            $this->fail($exception->getMessage());\n        }\n    }\n\n    #[DataProvider('invalid_string_day_format')]\n    public function test_from_invalid_string(string $invalidValue) : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Value \\\"{$invalidValue}\\\" is not valid month format.\");\n\n        Month::fromString($invalidValue);\n    }\n\n    #[DataProvider('valid_string_day_format')]\n    public function test_from_string(string $invalidValue, Month $month) : void\n    {\n        $this->assertObjectEquals($month, Month::fromString($invalidValue), 'isEqual');\n    }\n\n    public function test_debug_info() : void\n    {\n        $this->assertSame(\n            [\n                'year' => 2020,\n                'month' => 1,\n            ],\n            Month::fromString('2020-01-01')->__debugInfo()\n        );\n    }\n\n    public function test_to_string() : void\n    {\n        $this->assertSame(\n            '2020-01',\n            Month::fromString('2020-01-01')->toString()\n        );\n\n        $this->assertSame(\n            '2020-01',\n            (string) Month::fromString('2020-01-01')\n        );\n    }\n\n    public function test_last_day_of_month() : void\n    {\n        $month = Month::fromString('2020-01-01');\n\n        $this->assertSame(31, $month->lastDay()->number());\n    }\n\n    public function test_next_month() : void\n    {\n        $this->assertSame(2, Month::fromString('2020-01-01')->next()->number());\n    }\n\n    public function test_previous_month() : void\n    {\n        $this->assertSame(1, Month::fromString('2020-02-01')->previous()->number());\n    }\n\n    public function test_name() : void\n    {\n        $this->assertSame('January', Month::fromString('2020-01-01')->name());\n    }\n\n    public function test_short_name() : void\n    {\n        $this->assertSame('Jan', Month::fromString('2020-01-01')->shortName());\n    }\n\n    public function test_reset_time_in_to_datetime_immutable() : void\n    {\n        $month = new Month(new Year(2020), 1);\n\n        $dateTimeImmutable1 = $month->toDateTimeImmutable();\n        \\sleep(1);\n        $dateTimeImmutable2 = $month->toDateTimeImmutable();\n\n        $this->assertTrue($dateTimeImmutable1 == $dateTimeImmutable2);\n    }\n\n    public function test_is_equal() : void\n    {\n        $this->assertTrue(Month::fromString('2020-01-01')->isEqualTo(Month::fromString('2020-01-01')));\n        $this->assertFalse(Month::fromString('2020-01-02')->isEqualTo(Month::fromString('2020-02-01')));\n    }\n\n    public function test_is_before() : void\n    {\n        $this->assertTrue(Month::fromString('2019-01-01')->isBefore(Month::fromString('2020-01-01')));\n        $this->assertTrue(Month::fromString('2020-01-01')->isBeforeOrEqualTo(Month::fromString('2020-01-01')));\n\n        $this->assertFalse(Month::fromString('2021-01-01')->isBefore(Month::fromString('2020-01-01')));\n        $this->assertFalse(Month::fromString('2021-01-01')->isBeforeOrEqualTo(Month::fromString('2020-01-01')));\n\n        $this->assertTrue(Month::fromString('2019-01-01')->isBeforeOrEqualTo(Month::fromString('2020-01-01')));\n        $this->assertTrue(Month::fromString('2021-01-01')->isAfterOrEqualTo(Month::fromString('2020-01-01')));\n    }\n\n    public function test_is_after() : void\n    {\n        $this->assertTrue(Month::fromString('2022-01-01')->isAfter(Month::fromString('2020-02-01')));\n        $this->assertTrue(Month::fromString('2020-01-01')->isAfterOrEqualTo(Month::fromString('2020-01-01')));\n\n        $this->assertFalse(Month::fromString('2019-01-01')->isAfter(Month::fromString('2020-02-01')));\n        $this->assertFalse(Month::fromString('2019-01-01')->isAfterOrEqualTo(Month::fromString('2020-02-01')));\n    }\n\n    public function test_until_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('January 2020 is after January 2019');\n        Month::fromString('2020-01-01')->until(Month::fromString('2019-01-01'), Interval::rightOpen());\n    }\n\n    public function test_since_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('January 2019 is before January 2020');\n        Month::fromString('2019-01-01')->since(Month::fromString('2020-01-01'), Interval::leftOpen());\n    }\n\n    public function test_until() : void\n    {\n        $this->assertCount(12, $months = Month::fromString('2020-01-01')->until(Month::fromString('2021-01-01'), Interval::rightOpen()));\n        $this->assertInstanceOf(Month::class, $months->all()[0]);\n        $this->assertInstanceOf(Month::class, $months->all()[11]);\n        $this->assertSame('January', $months->all()[0]->name());\n        $this->assertSame('December', $months->all()[11]->name());\n    }\n\n    public function test_since() : void\n    {\n        $this->assertCount(12, $months = Month::fromString('2022-01-01')->since(Month::fromString('2021-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Month::class, $months->all()[0]);\n        $this->assertInstanceOf(Month::class, $months->all()[11]);\n\n        $this->assertSame('February', $months->all()[0]->name());\n        $this->assertSame('January', $months->all()[11]->name());\n    }\n\n    public function test_iterate_until() : void\n    {\n        $this->assertCount(12, $months = Month::fromString('2020-01-01')->iterate(Month::fromString('2021-01-01'), Interval::rightOpen()));\n        $this->assertInstanceOf(Month::class, $months->all()[0]);\n        $this->assertInstanceOf(Month::class, $months->all()[11]);\n        $this->assertSame('January', $months->all()[0]->name());\n        $this->assertSame('December', $months->all()[11]->name());\n    }\n\n    public function test_iterate_since() : void\n    {\n        $this->assertCount(12, $months = Month::fromString('2022-01-01')->iterate(Month::fromString('2021-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Month::class, $months->all()[0]);\n        $this->assertInstanceOf(Month::class, $months->all()[11]);\n\n        $this->assertSame('February', $months->all()[0]->name());\n        $this->assertSame('January', $months->all()[11]->name());\n    }\n\n    public function test_modify_months() : void\n    {\n        $this->assertSame('2020-01-01', Month::fromString('2020-06-01')->subMonths(5)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-05-01', Month::fromString('2020-06-01')->subMonths(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-12-01', Month::fromString('2020-01-01')->subMonths(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-12-01', Month::fromString('2020-01-01')->addMonths(23)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-12-01', Month::fromString('2020-06-01')->addMonths(6)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-10-01', Month::fromString('2020-06-01')->addMonths(16)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-07-01', Month::fromString('2020-06-01')->addMonths(13)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-12-01', Month::fromString('2020-06-01')->addMonths(18)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2022-01-01', Month::fromString('2020-06-01')->addMonths(19)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2022-07-01', Month::fromString('2020-12-01')->addMonths(19)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2026-08-01', Month::fromString('2020-12-01')->addMonths(68)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-12-01', Month::fromString('2020-08-01')->addMonths(4)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->assertSame('2019-12-01', Month::fromString('2020-06-01')->subMonths(6)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-02-01', Month::fromString('2020-06-01')->subMonths(16)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-05-01', Month::fromString('2020-06-01')->subMonths(13)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2018-12-01', Month::fromString('2020-06-01')->subMonths(18)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2018-11-01', Month::fromString('2020-06-01')->subMonths(19)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-05-01', Month::fromString('2020-12-01')->subMonths(19)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2015-04-01', Month::fromString('2020-12-01')->subMonths(68)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2020-04-01', Month::fromString('2020-08-01')->subMonths(4)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->assertSame('2015-06-01', Month::fromString('2020-06-01')->subYears(5)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-06-01', Month::fromString('2020-06-01')->subYears(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2026-06-01', Month::fromString('2020-06-01')->addYears(6)->toDateTimeImmutable()->format('Y-m-d'));\n\n        $this->asserTsame('2021-02-01', Month::fromString('2020-01-01')->add(1, 1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-12-01', Month::fromString('2020-01-01')->add(-1, -1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2018-12-01', Month::fromString('2020-01-01')->sub(1, 1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-02-01', Month::fromString('2020-01-01')->sub(-1, -1)->toDateTimeImmutable()->format('Y-m-d'));\n    }\n\n    public function test_day_static_create() : void\n    {\n        $month = Month::create(2020, 12);\n        $this->assertTrue(Month::fromString('2020-12-24')->isEqualTo($month));\n    }\n\n    public function test_distance_to() : void\n    {\n        $this->assertSame(31, Month::create(2020, 01)->distance(Month::create(2020, 02))->inDays());\n    }\n\n    public function test_quarter() : void\n    {\n        $this->assertSame(1, Month::fromString('2020-01-01')->quarter()->number());\n        $this->assertSame(2, Month::fromString('2020-04-01')->quarter()->number());\n        $this->assertSame(3, Month::fromString('2020-07-01')->quarter()->number());\n        $this->assertSame(4, Month::fromString('2020-10-01')->quarter()->number());\n    }\n\n    public function test_serialization() : void\n    {\n        $month = Month::create(2020, 1);\n\n        $this->assertObjectEquals(\n            $month,\n            \\unserialize(\\serialize($month)),\n            'isEqual'\n        );\n    }\n\n    public function test_number_of_days() : void\n    {\n        $this->assertSame(31, (new Month(new Year(2020), 1))->numberOfDays());\n        $this->assertSame(29, (new Month(new Year(2020), 2))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 3))->numberOfDays());\n        $this->assertSame(30, (new Month(new Year(2020), 4))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 5))->numberOfDays());\n        $this->assertSame(30, (new Month(new Year(2020), 6))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 7))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 8))->numberOfDays());\n        $this->assertSame(30, (new Month(new Year(2020), 9))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 10))->numberOfDays());\n        $this->assertSame(30, (new Month(new Year(2020), 11))->numberOfDays());\n        $this->assertSame(31, (new Month(new Year(2020), 12))->numberOfDays());\n\n        $this->assertSame(28, (new Month(new Year(2021), 2))->numberOfDays());\n    }\n\n    #[DataProvider('compare_to_provider')]\n    public function test_compare_to(Month $time, Month $comparable, int $compareResult) : void\n    {\n        $this->assertSame($compareResult, $time->compareTo($comparable));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/MonthsIteratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\DateTimeIntervalIterator;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\MonthsIterator;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * @psalm-immutable\n */\nfinal class MonthsIteratorTest extends TestCase\n{\n    public function test_reverse_days_iterator() : void\n    {\n        $begin = DateTime::fromString('2020-01-01 00:00:00 UTC');\n        $end = DateTime::fromString('2021-01-01 00:00:00 UTC');\n\n        $timeUnit = RelativeTimeUnit::month();\n\n        $array = \\iterator_to_array(MonthsIterator::fromDateTimeIterator(new DateTimeIntervalIterator($begin, $end, $timeUnit, Interval::closed()))->reverse());\n\n        $this->assertObjectEquals($array[0], Month::fromString('2021-01-01 00:00:00 UTC'), 'isEqual');\n        $this->assertObjectEquals($array[12], Month::fromString('2020-01-01 00:00:00 UTC'), 'isEqual');\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/MonthsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Months;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class MonthsTest extends TestCase\n{\n    public function test_array_access() : void\n    {\n        $months = Months::fromArray(\n            Month::fromString('2002-01-01'),\n            Month::fromString('2002-02-02'),\n            Month::fromString('2002-03-03')\n        );\n\n        $this->assertTrue(isset($months->all()[0]));\n        $this->assertInstanceOf(Month::class, $months->all()[0]);\n        $this->assertSame(3, $months->count());\n    }\n\n    public function test_map() : void\n    {\n        $days = Months::fromArray(\n            Month::fromString('2002-01-01'),\n            Month::fromString('2002-02-02'),\n            Month::fromString('2002-03-03')\n        );\n\n        $this->assertSame(\n            [1, 2, 3],\n            $days->map(function (Month $day) {\n                return $day->number();\n            })\n        );\n    }\n\n    public function test_filter() : void\n    {\n        $months = Months::fromArray(\n            Month::fromString('2002-01-01'),\n            Month::fromString('2002-02-02'),\n            Month::fromString('2002-03-03')\n        );\n\n        $this->assertEquals(\n            Month::fromString('2002-01-01'),\n            $months->filter(function (Month $day) {\n                return $day->number() === 1;\n            })->all()[0]\n        );\n    }\n\n    public function test_slice_below_lower_limit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        (new Year(2020))->months()->slice(-1, 5);\n    }\n\n    public function test_slice_above_upper_limit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        (new Year(2020))->months()->slice(12, 1);\n    }\n\n    public function test_slice() : void\n    {\n        $this->assertSame(12, (new Year(2020))->months()->slice(11, 1)->all()[0]->number());\n        $this->assertSame(2, (new Year(2020))->months()->slice(1, 1)->all()[0]->number());\n        $this->assertSame(1, (new Year(2020))->months()->slice(0, 1)->all()[0]->number());\n        $this->assertCount(5, (new Year(2020))->months()->slice(0, 5)->all());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/Psr20ClockAdapterTest.php",
    "content": "<?php\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\GregorianCalendarStub;\nuse Aeon\\Calendar\\Gregorian\\Psr20CalendarAdapter;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class Psr20ClockAdapterTest extends TestCase\n{\n    public function test_now() : void\n    {\n        $date = DateTime::fromTimestampUnix(\\time());\n        $calendar = new GregorianCalendarStub(TimeZone::UTC(), $date);\n        $sut = new Psr20CalendarAdapter($calendar);\n        $this->assertEquals($date->toDateTimeImmutable(), $sut->now());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/QuarterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Months;\nuse Aeon\\Calendar\\Gregorian\\Quarter;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class QuarterTest extends TestCase\n{\n    public function test_number_below_range() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(0, Months::fromArray(Month::fromString('2020-01'), Month::fromString('2020-02'), Month::fromString('2020-03')));\n    }\n\n    public function test_number_above_range() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(5, Months::fromArray(Month::fromString('2020-01'), Month::fromString('2020-02'), Month::fromString('2020-03')));\n    }\n\n    public function test_invalid_number_of_months() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(1, Months::fromArray(Month::fromString('2020-01')));\n    }\n\n    public function test_invalid_months_quarter_1() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(1, Months::fromArray(Month::fromString('2020-05'), Month::fromString('2020-02'), Month::fromString('2020-03')));\n    }\n\n    public function test_invalid_months_quarter_2() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(2, Months::fromArray(Month::fromString('2020-01'), Month::fromString('2020-05'), Month::fromString('2020-06')));\n    }\n\n    public function test_invalid_months_quarter_3() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(3, Months::fromArray(Month::fromString('2020-01'), Month::fromString('2020-08'), Month::fromString('2020-09')));\n    }\n\n    public function test_invalid_months_quarter_4() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new Quarter(4, Months::fromArray(Month::fromString('2020-01'), Month::fromString('2020-11'), Month::fromString('2020-12')));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimeEpochTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\TimeEpoch;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimeEpochTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{TimeEpoch, TimeEpoch, DateTime, DateTime}, mixed, void>\n     */\n    public static function seconds_since_data_provider() : \\Generator\n    {\n        yield [TimeEpoch::UTC(), TimeEpoch::GPS(), DateTime::fromString('1972-01-01 00:00:00 UTC'), DateTime::fromString('1980-01-06 00:00:00 UTC')];\n        yield [TimeEpoch::UTC(), TimeEpoch::TAI(), DateTime::fromString('1972-01-01 00:00:00 UTC'), DateTime::fromString('1958-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::UTC(), TimeEpoch::UNIX(), DateTime::fromString('1972-01-01 00:00:00 UTC'), DateTime::fromString('1970-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::UTC(), TimeEpoch::UTC(), DateTime::fromString('1972-01-01 00:00:00 UTC'), DateTime::fromString('1972-01-01 00:00:00 UTC')];\n\n        yield [TimeEpoch::GPS(), TimeEpoch::UTC(), DateTime::fromString('1980-01-06 00:00:00 UTC'), DateTime::fromString('1972-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::GPS(), TimeEpoch::TAI(), DateTime::fromString('1980-01-06 00:00:00 UTC'), DateTime::fromString('1958-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::GPS(), TimeEpoch::UNIX(), DateTime::fromString('1980-01-06 00:00:00 UTC'), DateTime::fromString('1970-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::GPS(), TimeEpoch::GPS(), DateTime::fromString('1980-01-06 00:00:00 UTC'), DateTime::fromString('1980-01-06 00:00:00 UTC')];\n\n        yield [TimeEpoch::UNIX(), TimeEpoch::UTC(), DateTime::fromString('1970-01-01 00:00:00 UTC'), DateTime::fromString('1972-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::UNIX(), TimeEpoch::TAI(), DateTime::fromString('1970-01-01 00:00:00 UTC'), DateTime::fromString('1958-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::UNIX(), TimeEpoch::GPS(), DateTime::fromString('1970-01-01 00:00:00 UTC'), DateTime::fromString('1980-01-06 00:00:00 UTC')];\n        yield [TimeEpoch::POSIX(), TimeEpoch::POSIX(), DateTime::fromString('1970-01-01 00:00:00 UTC'), DateTime::fromString('1970-01-01 00:00:00 UTC')];\n\n        yield [TimeEpoch::TAI(), TimeEpoch::UTC(), DateTime::fromString('1958-01-01 00:00:00 UTC'), DateTime::fromString('1972-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::TAI(), TimeEpoch::GPS(), DateTime::fromString('1958-01-01 00:00:00 UTC'), DateTime::fromString('1980-01-06 00:00:00 UTC')];\n        yield [TimeEpoch::TAI(), TimeEpoch::UNIX(), DateTime::fromString('1958-01-01 00:00:00 UTC'), DateTime::fromString('1970-01-01 00:00:00 UTC')];\n        yield [TimeEpoch::TAI(), TimeEpoch::TAI(), DateTime::fromString('1958-01-01 00:00:00 UTC'), DateTime::fromString('1958-01-01 00:00:00 UTC')];\n    }\n\n    #[DataProvider('seconds_since_data_provider')]\n    public function test_distance_to_epoch(TimeEpoch $epoch, TimeEpoch $sinceEpoch, DateTime $dateTime, DateTime $sinceDateTime) : void\n    {\n        $this->assertSame(\n            $epoch->distanceTo($sinceEpoch)->inSeconds(),\n            $dateTime->until($sinceDateTime)->distance()->inSeconds()\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimePeriodTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\TimePeriod;\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimePeriodTest extends TestCase\n{\n    public static function iterating_through_intervals_provider() : \\Generator\n    {\n        yield ['2020-01-01', '2020-01-03', true, TimeUnit::day(), Interval::closed(), ['2020-01-01...2020-01-02', '2020-01-02...2020-01-03']];\n        yield ['2020-01-01', '2020-01-03', true, TimeUnit::day(), Interval::open(), []];\n        yield ['2020-01-01', '2020-01-03', true, TimeUnit::day(), Interval::leftOpen(), ['2020-01-02...2020-01-03']];\n        yield ['2020-01-01', '2020-01-03', true, TimeUnit::day(), Interval::rightOpen(), ['2020-01-01...2020-01-02']];\n\n        yield ['2020-01-01', '2020-01-06', true, TimeUnit::days(2), Interval::closed(), ['2020-01-01...2020-01-03', '2020-01-03...2020-01-05', '2020-01-05...2020-01-06']];\n        yield ['2020-01-01', '2020-01-06', true, TimeUnit::days(2), Interval::open(), ['2020-01-03...2020-01-05']];\n        yield ['2020-01-01', '2020-01-06', true, TimeUnit::days(2), Interval::leftOpen(), ['2020-01-03...2020-01-05', '2020-01-05...2020-01-06']];\n        yield ['2020-01-01', '2020-01-06', true, TimeUnit::days(2), Interval::rightOpen(), ['2020-01-01...2020-01-03', '2020-01-03...2020-01-05']];\n\n        yield ['2020-01-01', '2020-01-03', false, TimeUnit::day(), Interval::closed(), ['2020-01-03...2020-01-02', '2020-01-02...2020-01-01']];\n        yield ['2020-01-01', '2020-01-03', false, TimeUnit::day(), Interval::open(), []];\n        yield ['2020-01-01', '2020-01-03', false, TimeUnit::days(1), Interval::leftOpen(), ['2020-01-03...2020-01-02']];\n        yield ['2020-01-01', '2020-01-03', false, TimeUnit::days(1), Interval::rightOpen(), ['2020-01-02...2020-01-01']];\n\n        yield ['2020-01-01', '2020-01-06', false, TimeUnit::days(2), Interval::closed(), ['2020-01-06...2020-01-04', '2020-01-04...2020-01-02', '2020-01-02...2020-01-01']];\n        yield ['2020-01-01', '2020-01-06', false, TimeUnit::days(2), Interval::open(), ['2020-01-04...2020-01-02']];\n        yield ['2020-01-01', '2020-01-06', false, TimeUnit::days(2), Interval::leftOpen(), ['2020-01-06...2020-01-04', '2020-01-04...2020-01-02']];\n        yield ['2020-01-01', '2020-01-06', false, TimeUnit::days(2), Interval::rightOpen(), ['2020-01-04...2020-01-02', '2020-01-02...2020-01-01']];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, TimePeriod, TimePeriod}, mixed, void>\n     */\n    public static function overlapping_time_periods_data_provider() : \\Generator\n    {\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-05-02 00:00:00.0000'), DateTime::fromString('2020-05-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-05-02 00:00:00.0000'), DateTime::fromString('2020-05-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-05-03 00:00:00.0000'), DateTime::fromString('2020-05-02 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-04 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-04 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-10 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.0000'), DateTime::fromString('2020-01-10 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.0000'), DateTime::fromString('2020-01-07 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-10 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-08 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-08 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-13 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-08 00:00:00.0000')),\n        ];\n    }\n\n    /**\n     * @return \\Generator<int, array{bool, TimePeriod, TimePeriod}, mixed, void>\n     */\n    public static function period_abuts_other_period_data_provider() : \\Generator\n    {\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.0000'), DateTime::fromString('2020-01-04 00:00:00.0000')),\n        ];\n\n        yield [\n            true,\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.0000'), DateTime::fromString('2020-01-04 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n        ];\n\n        yield [\n            false,\n            new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-03 00:00:00.0000')),\n            new TimePeriod(DateTime::fromString('2020-01-04 00:00:00.0000'), DateTime::fromString('2020-01-05 00:00:00.0000')),\n        ];\n    }\n\n    public function test_distance_in_time_unit_from_start_to_end_date() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-02 00:00:00.0000')\n        );\n\n        $this->assertSame(86400, $period->distance()->inSeconds());\n        $this->assertFalse($period->distance()->isNegative());\n    }\n\n    public function test_distance_in_time_unit_before_and_after_unix_epoch() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('1969-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-01 00:00:00.0000')\n        );\n\n        $this->assertSame(1609372800, $period->distance()->inSeconds());\n        $this->assertFalse($period->distance()->isNegative());\n    }\n\n    public function test_distance_in_time_unit_before_and_after_unix_epoch_inverse() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('1969-01-01 00:00:00.0000')\n        );\n\n        $this->assertSame(-1609372800, $period->distance()->inSeconds());\n        $this->assertTrue($period->distance()->isNegative());\n    }\n\n    public function test_distance_in_time_unit_before_and_before_unix_epoch() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('1969-01-01 00:00:00.0000'),\n            DateTime::fromString('1969-01-01 01:00:00.0000')\n        );\n\n        $this->assertSame(3600, $period->distance()->inSeconds());\n        $this->assertFalse($period->distance()->isNegative());\n    }\n\n    public function test_distance_in_time_unit_before_and_before_unix_epoch_inverse() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('1969-01-01 01:00:00.0000'),\n            DateTime::fromString('1969-01-01 00:00:00.0000')\n        );\n\n        $this->assertSame(-3600, $period->distance()->inSeconds());\n        $this->assertTrue($period->distance()->isNegative());\n    }\n\n    public function test_precise_distance_in_time_unit_from_start_to_end() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 12:25:30.079635'),\n            DateTime::fromString('2020-01-01 12:25:32.588460')\n        );\n\n        $this->assertSame('2.508825', $period->distance()->inSecondsPrecise());\n    }\n\n    public function test_distance_in_time_unit_from_start_to_end_date_between_years() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2021-01-01 00:00:00.0000')\n        );\n\n        $this->assertSame(DateTime::fromString('2020-01-01 00:00:00.0000')->year()->numberOfDays(), $period->distance()->inDays());\n        $this->assertFalse($period->distance()->isNegative());\n        $this->assertTrue(DateTime::fromString('2020-01-01 00:00:00.0000')->year()->isLeap());\n    }\n\n    #[DataProvider('iterating_through_intervals_provider')]\n    public function test_iterating_through_intervals(string $startDate, string $endDate, bool $forward, TimeUnit $timeUnit, Interval $interval, array $periods, string $format = 'Y-m-d') : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString($startDate),\n            DateTime::fromString($endDate)\n        );\n\n        $timePeriods = ($forward) ? $period->iterate($timeUnit, $interval) : $period->iterateBackward($timeUnit, $interval);\n\n        $periodsResult = $timePeriods->map(fn (TimePeriod $timePeriod) : string => $timePeriod->start()->format($format) . '...' . $timePeriod->end()->format($format));\n\n        $this->assertSame($periods, $periodsResult);\n    }\n\n    public function test_iterating_through_day_by_hour() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-02 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(TimeUnit::hour(), Interval::rightOpen());\n\n        $this->assertCount(23, $timePeriods);\n\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[0]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[1]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[2]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[22]);\n\n        $this->assertSame(0, $timePeriods->all()[0]->start()->time()->hour());\n        $this->assertSame(1, $timePeriods->all()[0]->end()->time()->hour());\n        $this->assertSame(1, $timePeriods->all()[1]->start()->time()->hour());\n        $this->assertSame(2, $timePeriods->all()[1]->end()->time()->hour());\n        $this->assertSame(2, $timePeriods->all()[2]->start()->time()->hour());\n        $this->assertSame(3, $timePeriods->all()[2]->end()->time()->hour());\n        $this->assertSame(22, $timePeriods->all()[22]->start()->time()->hour());\n        $this->assertSame(23, $timePeriods->all()[22]->end()->time()->hour());\n    }\n\n    public function test_iterating_through_day_backward_by_hour() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-02 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterateBackward(TimeUnit::hour(), Interval::leftOpen());\n\n        $this->assertCount(23, $timePeriods);\n\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[0]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[1]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[2]);\n        $this->assertInstanceOf(TimePeriod::class, $timePeriods->all()[22]);\n\n        $this->assertSame(0, $timePeriods->all()[0]->start()->time()->hour());\n        $this->assertSame(23, $timePeriods->all()[0]->end()->time()->hour());\n        $this->assertSame(23, $timePeriods->all()[1]->start()->time()->hour());\n        $this->assertSame(22, $timePeriods->all()[1]->end()->time()->hour());\n        $this->assertSame(22, $timePeriods->all()[2]->start()->time()->hour());\n        $this->assertSame(21, $timePeriods->all()[2]->end()->time()->hour());\n        $this->assertSame(2, $timePeriods->all()[22]->start()->time()->hour());\n        $this->assertSame(1, $timePeriods->all()[22]->end()->time()->hour());\n    }\n\n    public function test_iterating_by_2days_interval_closed_both_ways() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-05 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(TimeUnit::days(3), $interval = Interval::closed());\n        $timePeriodsBackward = $period->iterateBackward(TimeUnit::days(3), Interval::closed());\n\n        $this->assertTrue($interval->isClosed());\n        $this->assertCount(2, $timePeriods);\n        $this->assertCount(2, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-05', $timePeriods->all()[1]->end()->format('Y-m-d'));\n\n        $this->assertSame('2020-01-05', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-01', $timePeriodsBackward->all()[1]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_day_interval_closed_both_ways() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-05 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(TimeUnit::day(), $interval = Interval::closed());\n        $timePeriodsBackward = $period->iterateBackward(TimeUnit::day(), Interval::closed());\n\n        $this->assertTrue($interval->isClosed());\n        $this->assertCount(4, $timePeriods);\n        $this->assertCount(4, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriods->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriods->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-05', $timePeriods->all()[3]->end()->format('Y-m-d'));\n\n        $this->assertSame('2020-01-05', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriodsBackward->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-01', $timePeriodsBackward->all()[3]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_day_interval_left_open_both_ways() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-05 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(TimeUnit::day(), Interval::leftOpen());\n        $timePeriodsBackward = $period->iterateBackward(TimeUnit::day(), Interval::leftOpen());\n\n        $this->assertCount(3, $timePeriods);\n        $this->assertCount(3, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-02', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriods->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriods->all()[1]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriods->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-05', $timePeriods->all()[2]->end()->format('Y-m-d'));\n\n        $this->assertSame('2020-01-05', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriodsBackward->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriodsBackward->all()[1]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriodsBackward->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[2]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_day_interval_right_open_forward() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-05 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(TimeUnit::day(), Interval::rightOpen());\n\n        $this->assertCount(3, $timePeriods);\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriods->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriods->all()[1]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriods->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-04', $timePeriods->all()[2]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_day_interval_right_open_backward() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-05 00:00:00.0000')\n        );\n\n        $timePeriodsBackward = $period->iterateBackward(TimeUnit::day(), Interval::rightOpen());\n\n        $this->assertCount(3, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-04', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriodsBackward->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-03', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[1]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriodsBackward->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-01', $timePeriodsBackward->all()[2]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_month_interval_closed_both_ways() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2021-01-01 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(RelativeTimeUnit::month(), Interval::closed());\n        $timePeriodsBackward = $period->iterateBackward(RelativeTimeUnit::month(), Interval::closed());\n\n        $this->assertCount(12, $timePeriods);\n        $this->assertCount(12, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[0]->distance()->inDays());\n        $this->assertSame('2020-02-01', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame(29, $timePeriods->all()[1]->distance()->inDays());\n        $this->assertSame('2020-03-01', $timePeriods->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[2]->distance()->inDays());\n        $this->assertSame('2020-04-01', $timePeriods->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame(30, $timePeriods->all()[3]->distance()->inDays());\n        $this->assertSame('2020-05-01', $timePeriods->all()[4]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[4]->distance()->inDays());\n        $this->assertSame('2020-06-01', $timePeriods->all()[5]->start()->format('Y-m-d'));\n        $this->assertSame(30, $timePeriods->all()[5]->distance()->inDays());\n        $this->assertSame('2020-07-01', $timePeriods->all()[6]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[6]->distance()->inDays());\n        $this->assertSame('2020-08-01', $timePeriods->all()[7]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[7]->distance()->inDays());\n        $this->assertSame('2020-09-01', $timePeriods->all()[8]->start()->format('Y-m-d'));\n        $this->assertSame(30, $timePeriods->all()[8]->distance()->inDays());\n        $this->assertSame('2020-10-01', $timePeriods->all()[9]->start()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[9]->distance()->inDays());\n        $this->assertSame('2020-11-01', $timePeriods->all()[10]->start()->format('Y-m-d'));\n        $this->assertSame(30, $timePeriods->all()[10]->distance()->inDays());\n        $this->assertSame('2020-12-01', $timePeriods->all()[11]->start()->format('Y-m-d'));\n        $this->assertSame('2021-01-01', $timePeriods->all()[11]->end()->format('Y-m-d'));\n        $this->assertSame(31, $timePeriods->all()[11]->distance()->inDays());\n\n        $this->assertSame('2021-01-01', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2020-12-01', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2020-11-01', $timePeriodsBackward->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2020-10-01', $timePeriodsBackward->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame('2020-09-01', $timePeriodsBackward->all()[4]->start()->format('Y-m-d'));\n        $this->assertSame('2020-08-01', $timePeriodsBackward->all()[5]->start()->format('Y-m-d'));\n        $this->assertSame('2020-07-01', $timePeriodsBackward->all()[6]->start()->format('Y-m-d'));\n        $this->assertSame('2020-06-01', $timePeriodsBackward->all()[7]->start()->format('Y-m-d'));\n        $this->assertSame('2020-05-01', $timePeriodsBackward->all()[8]->start()->format('Y-m-d'));\n        $this->assertSame('2020-04-01', $timePeriodsBackward->all()[9]->start()->format('Y-m-d'));\n        $this->assertSame('2020-03-01', $timePeriodsBackward->all()[10]->start()->format('Y-m-d'));\n        $this->assertSame('2020-02-01', $timePeriodsBackward->all()[11]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-01', $timePeriodsBackward->all()[11]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_by_year_interval_closed_both_ways() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2025-01-01 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterate(RelativeTimeUnit::year(), Interval::closed());\n        $timePeriodsBackward = $period->iterateBackward(RelativeTimeUnit::year(), Interval::closed());\n\n        $this->assertCount(5, $timePeriods);\n        $this->assertCount(5, $timePeriodsBackward);\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2021-01-01', $timePeriods->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2022-01-01', $timePeriods->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2023-01-01', $timePeriods->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame('2024-01-01', $timePeriods->all()[4]->start()->format('Y-m-d'));\n        $this->assertSame('2025-01-01', $timePeriods->all()[4]->end()->format('Y-m-d'));\n\n        $this->assertSame('2025-01-01', $timePeriodsBackward->all()[0]->start()->format('Y-m-d'));\n        $this->assertSame('2024-01-01', $timePeriodsBackward->all()[1]->start()->format('Y-m-d'));\n        $this->assertSame('2023-01-01', $timePeriodsBackward->all()[2]->start()->format('Y-m-d'));\n        $this->assertSame('2022-01-01', $timePeriodsBackward->all()[3]->start()->format('Y-m-d'));\n        $this->assertSame('2021-01-01', $timePeriodsBackward->all()[4]->start()->format('Y-m-d'));\n        $this->assertSame('2020-01-01', $timePeriodsBackward->all()[4]->end()->format('Y-m-d'));\n    }\n\n    public function test_iterating_through_day_backward_by_2_days() : void\n    {\n        $period = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00.0000'),\n            DateTime::fromString('2020-01-02 00:00:00.0000')\n        );\n\n        $timePeriods = $period->iterateBackward(TimeUnit::days(2), Interval::closed());\n\n        $this->assertSame('2020-01-01', $timePeriods->all()[0]->end()->format('Y-m-d'));\n        $this->assertSame('2020-01-02', $timePeriods->all()[0]->start()->format('Y-m-d'));\n\n        $this->assertCount(1, $timePeriods);\n    }\n\n    #[DataProvider('overlapping_time_periods_data_provider')]\n    public function test_overlapping_time_periods(bool $overlap, TimePeriod $firstPeriod, TimePeriod $secondPeriod) : void\n    {\n        $this->assertSame($overlap, $firstPeriod->overlaps($secondPeriod));\n    }\n\n    public function test_period_is_forward() : void\n    {\n        $this->assertTrue(\n            (new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')))\n                ->isForward()\n        );\n        $this->assertFalse(\n            (new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.0000'), DateTime::fromString('2020-01-02 00:00:00.0000')))\n                ->isBackward()\n        );\n    }\n\n    public function test_period_is_backward() : void\n    {\n        $this->assertTrue(\n            (new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')))\n                ->isBackward()\n        );\n        $this->assertFalse(\n            (new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.0000'), DateTime::fromString('2020-01-01 00:00:00.0000')))\n                ->isForward()\n        );\n    }\n\n    #[DataProvider('period_abuts_other_period_data_provider')]\n    public function test_period_abuts_other_period(bool $abuts, TimePeriod $firstPeriod, TimePeriod $secondPeriod) : void\n    {\n        $this->assertSame($abuts, $firstPeriod->abuts($secondPeriod));\n    }\n\n    public function test_one_period_contains_the_same_period() : void\n    {\n        $this->assertTrue(\n            (new TimePeriod(DateTime::fromString('2020-01-01 00:00:00'), DateTime::fromString('2020-01-02 00:00:00')))\n                ->contains(new TimePeriod(DateTime::fromString('2020-01-01 00:00:00'), DateTime::fromString('2020-01-02 00:00:00')))\n        );\n    }\n\n    public function test_one_period_contains_shorted_period() : void\n    {\n        $this->assertTrue(\n            (new TimePeriod(DateTime::fromString('2020-01-01 00:00:00'), DateTime::fromString('2020-01-05 00:00:00')))\n                ->contains(new TimePeriod(DateTime::fromString('2020-01-02 00:00:00'), DateTime::fromString('2020-01-03 00:00:00')))\n        );\n    }\n\n    public function test_one_period_not_contains_other_overlapping_period() : void\n    {\n        $this->assertFalse(\n            (new TimePeriod(DateTime::fromString('2020-01-05 00:00:00'), DateTime::fromString('2020-01-10 00:00:00')))\n                ->contains(new TimePeriod(DateTime::fromString('2020-01-02 00:00:00'), DateTime::fromString('2020-01-07 00:00:00')))\n        );\n    }\n\n    public function test_merge_not_overlapping_time_periods() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Can't merge not overlapping time periods\");\n\n        (new TimePeriod(DateTime::fromString('2020-01-05 00:00:00'), DateTime::fromString('2020-01-10 00:00:00')))\n            ->merge(new TimePeriod(DateTime::fromString('2020-01-20 00:00:00'), DateTime::fromString('2020-01-25 00:00:00')));\n    }\n\n    public function test_merge_left_overlapping_time_periods() : void\n    {\n        $newPeriod = (new TimePeriod(DateTime::fromString('2020-01-05 00:00:00'), DateTime::fromString('2020-01-10 00:00:00')))\n            ->merge(new TimePeriod(DateTime::fromString('2020-01-08 00:00:00'), DateTime::fromString('2020-01-25 00:00:00')));\n\n        $this->assertSame(\n            '2020-01-05',\n            $newPeriod->start()->format('Y-m-d')\n        );\n\n        $this->assertSame(\n            '2020-01-25',\n            $newPeriod->end()->format('Y-m-d')\n        );\n    }\n\n    public function test_merge_abuts_time_periods() : void\n    {\n        $newPeriod = (new TimePeriod(DateTime::fromString('2020-01-01 00:00:00'), DateTime::fromString('2020-01-02 00:00:00')))\n            ->merge(new TimePeriod(DateTime::fromString('2020-01-02 00:00:00'), DateTime::fromString('2020-01-05 00:00:00')));\n\n        $this->assertSame(\n            '2020-01-01',\n            $newPeriod->start()->format('Y-m-d')\n        );\n\n        $this->assertSame(\n            '2020-01-05',\n            $newPeriod->end()->format('Y-m-d')\n        );\n    }\n\n    public function test_merge_right_overlapping_time_periods() : void\n    {\n        $newPeriod = (new TimePeriod(DateTime::fromString('2020-01-10 00:00:00'), DateTime::fromString('2020-02-10 00:00:00')))\n            ->merge(new TimePeriod(DateTime::fromString('2020-01-08 00:00:00'), DateTime::fromString('2020-01-25 00:00:00')));\n\n        $this->assertSame(\n            '2020-01-08',\n            $newPeriod->start()->format('Y-m-d')\n        );\n\n        $this->assertSame(\n            '2020-02-10',\n            $newPeriod->end()->format('Y-m-d')\n        );\n    }\n\n    public function test_serialization() : void\n    {\n        $timePeriod = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00'),\n            DateTime::fromString('2020-01-02 00:00:00')\n        );\n\n        $this->assertObjectEquals(\n            $timePeriod,\n            \\unserialize(\\serialize($timePeriod)),\n            'isEqual'\n        );\n    }\n\n    public function test_is_equal() : void\n    {\n        $timePeriod1 = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00'),\n            DateTime::fromString('2020-01-02 00:00:00')\n        );\n\n        $timePeriod2 = new TimePeriod(\n            DateTime::fromString('2020-01-01 00:00:00'),\n            DateTime::fromString('2020-01-03 00:00:00')\n        );\n\n        $this->assertFalse($timePeriod1->isEqualTo($timePeriod2));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimePeriodsIteratorTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\TimePeriodsIterator;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimePeriodsIteratorTest extends TestCase\n{\n    public function test_forward_even_iterator_right_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::rightOpen()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-08 00:00:00 UTC'), $array[7]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[7]->end());\n    }\n\n    public function test_forward_even_iterator_left_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::leftOpen()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[7]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-10 00:00:00 UTC'), $array[7]->end());\n    }\n\n    public function test_forward_even_iterator_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::open()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-07 00:00:00 UTC'), $array[5]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-08 00:00:00 UTC'), $array[5]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-08 00:00:00 UTC'), $array[6]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[6]->end());\n    }\n\n    public function test_forward_even_iterator_closed() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            TimeUnit::day(),\n            Interval::closed()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[8]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-10 00:00:00 UTC'), $array[8]->end());\n    }\n\n    public function test_backward_even_iterator_left_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::leftOpen()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-10 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[7]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[7]->end());\n    }\n\n    public function test_backward_even_iterator_right_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::rightOpen()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-08 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[6]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[6]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[7]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[7]->end());\n    }\n\n    public function test_backward_even_iterator_open() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::open()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-08 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[5]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[5]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[6]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[6]->end());\n    }\n\n    public function test_backward_even_iterator_closed() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-10 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::day()->toNegative(),\n            Interval::closed()\n        );\n\n        $array = \\iterator_to_array($iterator);\n\n        $this->assertEquals(DateTime::fromString('2020-01-10 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-09 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[8]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[8]->end());\n    }\n\n    public function test_forward_exceeded_iteration_closed() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            DateTime::fromString('2020-01-06 00:00:00 UTC'),\n            TimeUnit::days(2),\n            Interval::closed()\n        );\n\n        $this->assertCount(3, $iterator);\n\n        $array = \\iterator_to_array($iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-03 00:00:00 UTC'), $array[1]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-05 00:00:00 UTC'), $array[1]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-05 00:00:00 UTC'), $array[2]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-06 00:00:00 UTC'), $array[2]->end());\n    }\n\n    public function test_backward_exceeded_iteration_closed() : void\n    {\n        $iterator = new TimePeriodsIterator(\n            DateTime::fromString('2020-01-06 00:00:00 UTC'),\n            DateTime::fromString('2020-01-01 00:00:00 UTC'),\n            TimeUnit::days(2)->toNegative(),\n            Interval::closed()\n        );\n\n        $this->assertCount(3, $iterator);\n\n        $array = \\iterator_to_array($iterator);\n        $this->assertEquals(DateTime::fromString('2020-01-06 00:00:00 UTC'), $array[0]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[0]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-04 00:00:00 UTC'), $array[1]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[1]->end());\n        $this->assertEquals(DateTime::fromString('2020-01-02 00:00:00 UTC'), $array[2]->start());\n        $this->assertEquals(DateTime::fromString('2020-01-01 00:00:00 UTC'), $array[2]->end());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimePeriodsSortTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\TimePeriodsSort;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * @psalm-immutable\n */\nfinal class TimePeriodsSortTest extends TestCase\n{\n    public function test_sort_by_start_date() : void\n    {\n        $this->assertEquals(TimePeriodsSort::asc(), TimePeriodsSort::startDate(true));\n        $this->assertEquals(TimePeriodsSort::desc(), TimePeriodsSort::startDate(false));\n        $this->assertTrue(TimePeriodsSort::startDate(true)->isAscending());\n        $this->assertTrue(TimePeriodsSort::startDate(true)->byStartDate());\n        $this->assertFalse(TimePeriodsSort::startDate(false)->isAscending());\n    }\n\n    public function test_sort_by_end_date() : void\n    {\n        $this->assertTrue(TimePeriodsSort::endDate(true)->isAscending());\n        $this->assertFalse(TimePeriodsSort::endDate(true)->byStartDate());\n        $this->assertFalse(TimePeriodsSort::endDate(false)->isAscending());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimePeriodsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\TimePeriod;\nuse Aeon\\Calendar\\Gregorian\\TimePeriods;\nuse Aeon\\Calendar\\Gregorian\\TimePeriodsSort;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimePeriodsTest extends TestCase\n{\n    public function test_each() : void\n    {\n        $counter = 0;\n        DateTime::fromString('2020-01-01 00:00:00.000000')\n            ->until(DateTime::fromString('2020-01-01 01:00:00.000000'))\n            ->iterate(TimeUnit::minute(), Interval::rightOpen())\n            ->each(function (TimePeriod $timePeriod) use (&$counter) : void {\n                /** @psalm-suppress MixedOperand */\n                $counter += 1;\n            });\n\n        $this->assertSame(59, $counter);\n    }\n\n    public function test_get_iterator() : void\n    {\n        $timePeriods = DateTime::fromString('2020-01-01 00:00:00.000000')\n            ->until(DateTime::fromString('2020-01-01 01:00:00.000000'))\n            ->iterate(TimeUnit::minute(), Interval::closed());\n\n        $this->assertEquals($timePeriods->all(), \\iterator_to_array($timePeriods->getIterator()));\n    }\n\n    public function test_gap_for_empty_periods() : void\n    {\n        $this->assertCount(0, (TimePeriods::fromArray())->gaps());\n    }\n\n    public function test_gap_for_periods_with_one_period() : void\n    {\n        $this->assertCount(\n            0,\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-01 01:00:00.000000'), DateTime::fromString('2020-01-02 01:00:00.000000'))\n            ))->gaps()\n        );\n    }\n\n    public function test_gap_for_periods_with_equal_time_periods() : void\n    {\n        $this->assertCount(\n            0,\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-01 01:00:00.000000'), DateTime::fromString('2020-01-02 01:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 01:00:00.000000'), DateTime::fromString('2020-01-02 01:00:00.000000'))\n            ))->gaps()\n        );\n    }\n\n    public function test_gap_periods() : void\n    {\n        $this->assertObjectEquals(\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            )),\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n            ))->gaps(),\n            'isEqual'\n        );\n    }\n\n    public function test_compare_different_periods() : void\n    {\n        $this->assertFalse(\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            ))->isEqualTo((TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n            )))\n        );\n\n        $this->assertFalse(\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            ))->isEqualTo((TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000001')),\n            )))\n        );\n    }\n\n    public function test_compare_identical_periods() : void\n    {\n        $this->assertTrue(\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            ))->isEqualTo((TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-02 00:00:00.000000'), DateTime::fromString('2020-01-03 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-07 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            )))\n        );\n    }\n\n    public function test_no_gaps_in_overlapping_periods() : void\n    {\n        $this->assertEquals(\n            (TimePeriods::fromArray()),\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2021-01-15T00:00:00+00:00'), DateTime::fromString('2021-01-29T00:00:00+00:00')),\n                new TimePeriod(DateTime::fromString('2021-01-06T14:32:01+00:00'), DateTime::fromString('2021-01-20T15:24:53+00:00')),\n                new TimePeriod(DateTime::fromString('2021-01-10T13:03:08+00:00'), DateTime::fromString('2021-01-13T14:24:54+00:00')),\n                new TimePeriod(DateTime::fromString('2020-12-11T13:03:08+00:00'), DateTime::fromString('2021-01-10T13:03:08+00:00')),\n            ))->gaps()\n        );\n    }\n\n    public function test_no_gaps_in_abuts_periods() : void\n    {\n        $this->assertEquals(\n            (TimePeriods::fromArray()),\n            (TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2019-09-09T12:53:30+00:00'), DateTime::fromString('2019-10-09T12:53:30+00:00')),\n                new TimePeriod(DateTime::fromString('2019-08-10T12:53:30+00:00'), DateTime::fromString('2019-09-09T12:53:30+00:00')),\n                new TimePeriod(DateTime::fromString('2019-07-11T12:53:30+00:00'), DateTime::fromString('2019-08-10T12:53:30+00:00')),\n            ))->gaps()\n        );\n    }\n\n    public function test_map_periods() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000'))\n        );\n\n        $this->assertCount(\n            4,\n            $distances = $timePeriods->map(fn (TimePeriod $timePeriod) => $timePeriod->distance())\n        );\n        $this->assertInstanceOf(TimeUnit::class, $distances[0]);\n    }\n\n    public function test_filter_periods() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000'))\n        );\n\n        $this->assertCount(\n            1,\n            $distances = $timePeriods->filter(fn (TimePeriod $timePeriod) => $timePeriod->start()->isEqualTo(DateTime::fromString('2020-01-03 00:00:00.000000')))\n        );\n        $this->assertInstanceOf(TimePeriod::class, \\array_values($distances->all())[0]);\n    }\n\n    public function test_sort_by_start_date_asc() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            ),\n            $timePeriods->sort()\n        );\n    }\n\n    public function test_sort_by_start_date_desc() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            ),\n            $timePeriods->sortBy(TimePeriodsSort::desc())\n        );\n    }\n\n    public function test_sort_by_end_date_asc() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            ),\n            $timePeriods->sortBy(TimePeriodsSort::endDate())\n        );\n    }\n\n    public function test_sort_by_end_date_desc() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            ),\n            $timePeriods->sortBy(TimePeriodsSort::endDate(false))\n        );\n    }\n\n    public function test_first() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            $timePeriods->first(),\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n        );\n    }\n\n    public function test_first_empty() : void\n    {\n        $this->assertNull(\n            (TimePeriods::fromArray())->first(),\n        );\n    }\n\n    public function test_last() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            $timePeriods->last(),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n    }\n\n    public function test_last_empty() : void\n    {\n        $this->assertNull(\n            (TimePeriods::fromArray())->first(),\n        );\n    }\n\n    public function test_add() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n        );\n        $timePeriods = $timePeriods->add(\n            new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n        );\n\n        $this->assertEquals(\n            $timePeriods,\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n            )\n        );\n    }\n\n    public function test_merge() : void\n    {\n        $timePeriods = TimePeriods::fromArray(\n            new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n            new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n        );\n        $timePeriods = $timePeriods->merge(\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n            )\n        );\n\n        $this->assertEquals(\n            $timePeriods,\n            TimePeriods::fromArray(\n                new TimePeriod(DateTime::fromString('2020-01-10 00:00:00.000000'), DateTime::fromString('2020-01-08 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-01 00:00:00.000000'), DateTime::fromString('2020-01-02 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-05 00:00:00.000000'), DateTime::fromString('2020-01-07 00:00:00.000000')),\n                new TimePeriod(DateTime::fromString('2020-01-03 00:00:00.000000'), DateTime::fromString('2020-01-06 00:00:00.000000')),\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Time;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimeTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function invalid_string_day_format() : \\Generator\n    {\n        yield ['2020-32'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, Time}, mixed, void>\n     */\n    public static function valid_string_day_format() : \\Generator\n    {\n        yield ['01:00:01.5', new Time(01, 00, 01, 500000)];\n        yield ['01:00:01.005', new Time(01, 00, 01, 5000)];\n        yield ['01:00:01.00001', new Time(01, 00, 01, 10)];\n        yield ['01:12', new Time(01, 12, 00)];\n        yield ['01:12 +1 minute + 10 seconds', new Time(01, 13, 10)];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, string, string}, mixed, void>\n     */\n    public static function creating_time_data_provider_from_string() : \\Generator\n    {\n        yield ['noW', 'H:i:s'];\n        yield ['now ', 'H:i:s'];\n        yield ['today', 'H:i:s'];\n        yield [' tOday', 'H:i:s'];\n        yield ['noon', 'H:i:s'];\n        yield ['noon  ', 'H:i:s'];\n        yield ['midnight  ', 'H:i:s'];\n        yield ['noon +1 minute', 'H:i:s'];\n        yield ['back of 7pm', 'H:i:s'];\n        yield ['last hour', 'H:i:s'];\n    }\n\n    /**\n     * @return \\Generator<int, array{Time, Time, int}>\n     */\n    public static function compare_to_provider() : \\Generator\n    {\n        yield [Time::fromString('11:53:12'), Time::fromString('11:53:12'), 0];\n        yield [Time::fromString('11:53'), Time::fromString('11:53'), 0];\n\n        yield [Time::fromString('11:53:00'), Time::fromString('11:53:12'), -1];\n        yield [Time::fromString('11:00:12'), Time::fromString('11:53:12'), -1];\n\n        yield [Time::fromString('11:53:12'), Time::fromString('00:53:12'), 1];\n        yield [Time::fromString('11:53:12'), Time::fromString('11:00:12'), 1];\n    }\n\n    public function test_debug_info() : void\n    {\n        $this->assertSame(\n            [\n                'hour' => 0,\n                'minute' => 0,\n                'second' => 0,\n                'microsecond' => 0,\n            ],\n            (new Time(0, 0, 0, 0))->__debugInfo()\n        );\n    }\n\n    #[DataProvider('invalid_string_day_format')]\n    public function test_from_invalid_string(string $invalidValue) : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Value \\\"{$invalidValue}\\\" is not valid time format.\");\n\n        Time::fromString($invalidValue);\n    }\n\n    public function test_each_part_of_time() : void\n    {\n        $time = Time::fromString('00:00');\n\n        $this->assertSame(0, $time->hour());\n        $this->assertSame(0, $time->minute());\n        $this->assertSame(0, $time->second());\n        $this->assertSame(0, $time->microsecond());\n    }\n\n    #[DataProvider('valid_string_day_format')]\n    public function test_from_string(string $invalidValue, Time $time) : void\n    {\n        $this->assertObjectEquals($time, Time::fromString($invalidValue), 'isEqual');\n    }\n\n    #[DataProvider('creating_time_data_provider_from_string')]\n    public function test_creating_time_from_string(string $dateTime, string $format) : void\n    {\n        try {\n            $this->assertEqualsWithDelta(\n                (new \\DateTime($dateTime))->getTimestamp(),\n                (new \\DateTime(Time::fromString($dateTime)->format($format)))->getTimestamp(),\n                20,\n                'Expected ' . (new \\DateTime($dateTime))->format('c') . ' got ' . (new \\DateTime(Time::fromString($dateTime)->format($format)))->format('c')\n            );\n        } catch (InvalidArgumentException $exception) {\n            $this->fail($exception->getMessage());\n        }\n    }\n\n    public function test_time_millisecond() : void\n    {\n        $this->assertSame(101, (new Time(0, 0, 0, 101999))->millisecond());\n        $this->assertSame(0, (new Time(0, 0, 0, 0))->microsecond());\n    }\n\n    public function test_to_string() : void\n    {\n        $this->assertSame('23:59:59.599999', (new Time(23, 59, 59, 599999))->toString());\n        $this->assertSame('00:00:00.000000', (new Time(0, 0, 0, 0))->toString());\n    }\n\n    public function test_format() : void\n    {\n        $this->assertSame('23 59 59', (new Time(23, 59, 59, 599999))->format('H i s'));\n    }\n\n    public function test_is_am() : void\n    {\n        $this->assertTrue((new Time(0, 0, 0, 0))->isAM());\n        $this->assertfalse((new Time(0, 0, 0, 0))->isPM());\n    }\n\n    public function test_is_pm() : void\n    {\n        $this->assertFalse((Time::fromString('13:00:00'))->isAM());\n        $this->assertTrue((Time::fromDateTime(new \\DateTimeImmutable('13:00:00')))->isPM());\n    }\n\n    public function test_equal() : void\n    {\n        $this->assertTrue((new Time(10, 0, 0, 0))->isEqualTo(new Time(10, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isEqualTo(new Time(0, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isEqualTo(new Time(15, 0, 0, 0)));\n    }\n\n    public function test_greater() : void\n    {\n        $this->assertFalse((new Time(10, 0, 0, 0))->isAfter(new Time(10, 0, 0, 0)));\n        $this->assertTrue((new Time(10, 0, 0, 0))->isAfter(new Time(0, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isAfter(new Time(15, 0, 0, 0)));\n    }\n\n    public function test_greater_or_equal() : void\n    {\n        $this->assertTrue((new Time(10, 0, 0, 0))->isAfterOrEqualTo(new Time(10, 0, 0, 0)));\n        $this->assertTrue((new Time(10, 0, 0, 0))->isAfterOrEqualTo(new Time(0, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isAfterOrEqualTo(new Time(15, 0, 0, 0)));\n    }\n\n    public function test_less() : void\n    {\n        $this->assertFalse((new Time(10, 0, 0, 0))->isBefore(new Time(10, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isBefore(new Time(0, 0, 0, 0)));\n        $this->assertTrue((new Time(10, 0, 0, 0))->isBefore(new Time(15, 0, 0, 0)));\n    }\n\n    public function test_less_or_equal() : void\n    {\n        $this->assertTrue((new Time(10, 0, 0, 0))->isBeforeOrEqualTo(new Time(10, 0, 0, 0)));\n        $this->assertFalse((new Time(10, 0, 0, 0))->isBeforeOrEqualTo(new Time(0, 0, 0, 0)));\n        $this->assertTrue((new Time(10, 0, 0, 0))->isBeforeOrEqualTo(new Time(15, 0, 0, 0)));\n    }\n\n    public function test_to_time_unit() : void\n    {\n        $this->assertSame('36000.000000', ((new Time(10, 0, 0, 0))->toTimeUnit()->inSecondsPrecise()));\n    }\n\n    public function test_creating_using_invalid_hour() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        new Time(24, 0, 0, 0);\n    }\n\n    public function test_creating_using_invalid_minute() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        new Time(0, 60, 0, 0);\n    }\n\n    public function test_creating_using_invalid_second() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        new Time(0, 0, 60, 0);\n    }\n\n    public function test_creating_using_invalid_microsecond() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        new Time(0, 0, 0, 1_000_000);\n    }\n\n    public function test_add() : void\n    {\n        $this->assertSame('01:00:00.000000', Time::fromString('00:00')->add(TimeUnit::hour())->toString());\n        $this->assertSame('00:00:00.000000', Time::fromString('00:00')->add(TimeUnit::days(2))->toString());\n        $this->assertSame('03:00:00.000000', Time::fromString('00:00')->add(TimeUnit::hours(27))->toString());\n    }\n\n    public function test_sub() : void\n    {\n        $this->assertSame('04:00:00.000000', Time::fromString('05:00')->sub(TimeUnit::hour())->toString());\n        $this->assertSame('00:00:00.000000', Time::fromString('00:00')->sub(TimeUnit::days(2))->toString());\n        $this->assertSame('21:00:00.000000', Time::fromString('00:00')->sub(TimeUnit::hours(27))->toString());\n    }\n\n    public function test_serialization() : void\n    {\n        $time = new Time(10, 00, 00, 00);\n\n        $this->assertSame(\n            [\n                'hour' => 10,\n                'minute' => 00,\n                'second' => 00,\n                'microsecond' => 00,\n            ],\n            $serializedTime = $time->__serialize()\n        );\n        $this->assertSame(\n            'O:28:\"' . Time::class . '\":4:{s:4:\"hour\";i:10;s:6:\"minute\";i:0;s:6:\"second\";i:0;s:11:\"microsecond\";i:0;}',\n            $serializedTimeString = \\serialize($time)\n        );\n        $this->assertEquals(\n            \\unserialize($serializedTimeString),\n            $time\n        );\n    }\n\n    public function test_is_midnight() : void\n    {\n        $this->assertTrue(Time::fromString('00:00')->isMidnight());\n        $this->assertTrue(Time::fromString('00:00:00')->isMidnight());\n\n        $this->assertFalse(Time::fromString('15:00:00')->isMidnight());\n        $this->assertFalse(Time::fromString('15:27:00')->isMidnight());\n        $this->assertFalse(Time::fromString('15:27:28')->isMidnight());\n        $this->assertFalse(Time::fromString('15:27:28.000001')->isMidnight());\n    }\n\n    public function test_is_not_midnight() : void\n    {\n        $this->assertTrue(Time::fromString('15:00:00')->isNotMidnight());\n        $this->assertTrue(Time::fromString('15:27:00')->isNotMidnight());\n        $this->assertTrue(Time::fromString('15:27:28')->isNotMidnight());\n        $this->assertTrue(Time::fromString('15:27:28.000001')->isNotMidnight());\n\n        $this->assertFalse(Time::fromString('00:00')->isNotMidnight());\n        $this->assertFalse(Time::fromString('00:00:00')->isNotMidnight());\n    }\n\n    #[DataProvider('compare_to_provider')]\n    public function test_compare_to(Time $time, Time $comparable, int $compareResult) : void\n    {\n        $this->assertSame($compareResult, $time->compareTo($comparable));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimeZone/TimeOffsetTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian\\TimeZone;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse Aeon\\Calendar\\Gregorian\\TimeZone\\TimeOffset;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimeOffsetTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function valid_time_offset_data_provider() : \\Generator\n    {\n        yield ['00:00'];\n        yield ['+00:00'];\n        yield ['+0000'];\n        yield ['-00:00'];\n        yield ['-0000'];\n        yield ['-10:00'];\n        yield ['-1000'];\n        yield ['-10:30'];\n        yield ['-1030'];\n        yield ['-10:15'];\n        yield ['10:30'];\n        yield ['10:15'];\n        yield ['1015'];\n        yield ['+14:00'];\n        yield ['+1400'];\n    }\n\n    /**\n     * @return \\Generator<int, array{string}, mixed, void>\n     */\n    public static function invalid_time_offset_data_provider() : \\Generator\n    {\n        yield ['abcd'];\n        yield ['9999'];\n        yield ['45:45'];\n    }\n\n    public function test_create_from_invalid_string() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        TimeOffset::fromString('invalid_string');\n    }\n\n    public function test_create_from_time_unit_zero() : void\n    {\n        $this->assertSame(\n            '+00:00',\n            TimeOffset::fromTimeUnit(TimeUnit::seconds(0))->toString()\n        );\n    }\n\n    public function test_create_UTC() : void\n    {\n        $this->assertSame(\n            '+00:00',\n            TimeOffset::UTC()->toString()\n        );\n        $this->assertTrue(TimeOffset::UTC()->isUTC());\n    }\n\n    public function test_create_from_time_unit_one_and_half_hours__positive() : void\n    {\n        $this->assertSame(\n            '+01:30',\n            TimeOffset::fromTimeUnit(TimeUnit::minutes(90))->toString()\n        );\n    }\n\n    public function test_create_from_time_unit_one_and_half_hours_negative() : void\n    {\n        $this->assertSame(\n            '-01:30',\n            TimeOffset::fromTimeUnit(TimeUnit::minutes(90)->toNegative())->toString()\n        );\n    }\n\n    public function test_is_equal() : void\n    {\n        $this->assertTrue(TimeOffset::fromString('+00:00')->isEqualTo(TimeOffset::fromString('+00:00')));\n        $this->assertFalse(TimeOffset::fromString('+00:00')->isEqualTo(TimeOffset::fromString('+01:00')));\n    }\n\n    public function test_create_from_time_unit_zero_negative() : void\n    {\n        $this->assertSame(\n            '-01:30',\n            TimeOffset::fromTimeUnit(TimeUnit::minutes(90)->invert())->toString()\n        );\n    }\n\n    #[DataProvider('valid_time_offset_data_provider')]\n    public function test_valid_time_offset(string $offset) : void\n    {\n        $this->assertTrue(TimeOffset::isValid($offset));\n    }\n\n    #[DataProvider('invalid_time_offset_data_provider')]\n    public function test_invalid_time_offset(string $offset) : void\n    {\n        $this->assertFalse(TimeOffset::isValid($offset));\n    }\n\n    public function test_to_date_time_zone() : void\n    {\n        $this->assertInstanceOf(\\DateTimeZone::class, TimeOffset::UTC()->toDateTimeZone());\n    }\n\n    public function test_serialization() : void\n    {\n        $timeOffset = TimeOffset::fromString('+01:00');\n\n        $this->assertSame(\n            [\n                'hours' => 1,\n                'minutes' => 0,\n                'negative' => false,\n            ],\n            $serializedTimeZone = $timeOffset->__serialize()\n        );\n        $this->assertSame(\n            'O:43:\"' . TimeOffset::class . '\":3:{s:5:\"hours\";i:1;s:7:\"minutes\";i:0;s:8:\"negative\";b:0;}',\n            $serializedTimeOffsetString = \\serialize($timeOffset)\n        );\n        $this->assertEquals(\n            \\unserialize($serializedTimeOffsetString),\n            $timeOffset\n        );\n    }\n\n    public function test_creat_timezone_abbreviation() : void\n    {\n        $this->assertTrue(TimeZone::PDDT()->isAbbreviation());\n    }\n\n    public function test_creat_timezone_id() : void\n    {\n        $this->assertTrue(TimeZone::americaLosAngeles()->isIdentifier());\n    }\n\n    public function test_creat_timezone_offset() : void\n    {\n        $this->assertTrue(TimeZone::offset('01:00')->isOffset());\n    }\n\n    public function test_creat_timezone_offset_from_date_time_zone() : void\n    {\n        $this->assertTrue(TimeZone::fromDateTimeZone(new \\DateTimeZone('+01:00'))->isOffset());\n    }\n\n    public function test_creat_timezone_id_from_date_time_zone() : void\n    {\n        $this->assertTrue(TimeZone::fromDateTimeZone(new \\DateTimeZone('America/Los_Angeles'))->isIdentifier());\n    }\n\n    public function test_creat_timezone_abbreviation_from_date_Time_zone() : void\n    {\n        $this->assertTrue(TimeZone::fromDateTimeZone(new \\DateTimeZone('PDDT'))->isAbbreviation());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/TimeZoneTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\DateTime;\nuse Aeon\\Calendar\\Gregorian\\TimeZone;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimeZoneTest extends TestCase\n{\n    public function test_creating_from_invalid_string() : void\n    {\n        $this->expectExceptionMessage('\"not_a_timezone\" is not a valid time zone.');\n\n        TimeZone::fromString('not_a_timezone');\n    }\n\n    public function test_creating_from_invalid_abbreviation() : void\n    {\n        $this->expectExceptionMessage('\"not_a_timezone\" is not a valid timezone abbreviation.');\n\n        TimeZone::abbreviation('not_a_timezone');\n    }\n\n    public function test_creating_from_invalid_identifier() : void\n    {\n        $this->expectExceptionMessage('\"not_a_timezone\" is not a valid timezone identifier.');\n\n        TimeZone::id('not_a_timezone');\n    }\n\n    public function test_creating_UTC_as_identifier() : void\n    {\n        $this->expectExceptionMessage('\"UTC\" is timezone abbreviation, not identifier.');\n\n        TimeZone::id('UTC');\n    }\n\n    public function test_creating_from_invalid_offset() : void\n    {\n        $this->expectExceptionMessage('\"not_a_timezone\" is not a valid time offset.');\n\n        TimeZone::offset('not_a_timezone');\n    }\n\n    public function test_creating_from_invalid_static_name() : void\n    {\n        $this->expectExceptionMessage('\"blabla\" is not a valid time zone identifier or abbreviation.');\n        $tz = TimeZone::blabla();\n    }\n\n    public function test_creating_from_offset() : void\n    {\n        $tz = TimeZone::fromString('+0100');\n        $this->assertSame('+01:00', $tz->name());\n        $this->assertTrue($tz->isOffset());\n        $this->assertFalse($tz->isAbbreviation());\n        $this->assertFalse($tz->isIdentifier());\n    }\n\n    public function test_creating_from_abbreviation() : void\n    {\n        $tz = TimeZone::fromString('gmt');\n        $this->assertSame('GMT', $tz->name());\n        $this->assertTrue($tz->isAbbreviation());\n        $this->assertFalse($tz->isOffset());\n        $this->assertFalse($tz->isIdentifier());\n    }\n\n    public function test_creating_from_id() : void\n    {\n        $tz = TimeZone::fromString('Europe/Warsaw');\n        $this->assertSame('Europe/Warsaw', $tz->name());\n        $this->assertTrue($tz->isIdentifier());\n        $this->assertFalse($tz->isAbbreviation());\n        $this->assertFalse($tz->isOffset());\n    }\n\n    public function test_creating_from_static_id() : void\n    {\n        $tz = TimeZone::europeWarsaw();\n        $this->assertSame('Europe/Warsaw', $tz->name());\n    }\n\n    public function test_creating_from_static_abbreviation() : void\n    {\n        $tz = TimeZone::GMT();\n        $this->assertSame('GMT', $tz->name());\n    }\n\n    public function test_calculating_offset_for_date() : void\n    {\n        $tz = TimeZone::europeWarsaw();\n\n        $this->assertSame('+01:00', $tz->timeOffset(DateTime::fromString('2020-01-01 12:00:00'))->toString());\n        $this->assertSame(3600, $tz->timeOffset(DateTime::fromString('2020-01-01 12:00:00'))->toTimeUnit()->inSeconds());\n        $this->assertSame('+02:00', $tz->timeOffset(DateTime::fromString('2020-06-01 12:00:00'))->toString());\n        $this->assertSame(7200, $tz->timeOffset(DateTime::fromString('2020-06-01 12:00:00'))->toTimeUnit()->inSeconds());\n    }\n\n    public function test_is_valid() : void\n    {\n        $this->assertFalse(TimeZone::isValid('invalid_time_zone'));\n        $this->assertTrue(TimeZone::isValid('Europe/Warsaw'));\n    }\n\n    public function test_all_timezone_identifiers() : void\n    {\n        // \\DateTimeZone returns UTC as timezone ID but it's an timezone abbreviation\n        $this->assertCount(\\count(\\DateTimeZone::listIdentifiers()) - 1, TimeZone::allIdentifiers());\n        $this->assertContainsOnlyInstancesOf(TimeZone::class, TimeZone::allIdentifiers());\n    }\n\n    public function test_all_timezones() : void\n    {\n        // \\DateTimeZone::listAbbreviations() additionally returns all 25 alphabet letters as list abbreviations keys.\n        $this->assertCount(\\count(\\array_keys(\\DateTimeZone::listAbbreviations())) - 25, TimeZone::allAbbreviations());\n        $this->assertContainsOnlyInstancesOf(TimeZone::class, TimeZone::allAbbreviations());\n    }\n\n    public function test_serialization() : void\n    {\n        $timeZone = TimeZone::fromString('America/Los_Angeles');\n\n        $this->assertSame(\n            [\n                'name' => 'America/Los_Angeles',\n                'type' => 3,\n            ],\n            $serializedTimeZone = $timeZone->__serialize()\n        );\n        $this->assertSame(\n            'O:32:\"' . TimeZone::class . '\":2:{s:4:\"name\";s:19:\"America/Los_Angeles\";s:4:\"type\";i:3;}',\n            $serializedTimeZoneString = \\serialize($timeZone)\n        );\n        $this->assertEquals(\n            \\unserialize($serializedTimeZoneString),\n            $timeZone\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/YearMonthsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\Month;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class YearMonthsTest extends TestCase\n{\n    public function test_count() : void\n    {\n        $this->assertSame(12, (new Year(2020))->months()->count());\n        $this->assertCount(12, (new Year(2020))->months()->all());\n    }\n\n    public function test_map_months() : void\n    {\n        $this->assertSame(\n            \\range(1, 12),\n            (new Year(2020))->months()->map(fn (Month $month) => $month->number())\n        );\n    }\n\n    public function test_filter_months() : void\n    {\n        $this->assertSame(\n            [2, 4, 6, 8, 10, 12],\n            \\array_map(\n                fn (Month $month) : int => $month->number(),\n                \\array_values((new Year(2020))->months()->filter(fn (Month $month) => $month->number() % 2 === 0))\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/YearTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\Gregorian\\Day;\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class YearTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{int, int, int}, mixed, void>\n     */\n    public static function month_number_of_days_data_provider() : \\Generator\n    {\n        yield [2020, 1, 31];\n        yield [2020, 2, 29];\n        yield [2020, 3, 31];\n        yield [2020, 4, 30];\n        yield [2020, 5, 31];\n        yield [2020, 6, 30];\n        yield [2020, 7, 31];\n        yield [2020, 8, 31];\n        yield [2020, 9, 30];\n        yield [2020, 10, 31];\n        yield [2020, 11, 30];\n        yield [2020, 12, 31];\n        yield [2021, 1, 31];\n        yield [2021, 2, 28];\n    }\n\n    /**\n     * @return \\Generator<int, array{int, bool}, mixed, void>\n     */\n    public static function leap_years() : \\Generator\n    {\n        yield [2000, true];\n        yield [2100, false];\n        yield [2400, true];\n        yield [2404, true];\n        yield [2403, false];\n    }\n\n    /**\n     * @return \\Generator<int, array{Year, Year, int}>\n     */\n    public static function compare_to_provider() : \\Generator\n    {\n        yield [Year::fromString('2022'), Year::fromString('2022'), 0];\n\n        yield [Year::fromString('2021'), Year::fromString('2022'), -1];\n\n        yield [Year::fromString('2022'), Year::fromString('2021'), 1];\n    }\n\n    public function test_months() : void\n    {\n        $this->assertSame(1, Year::fromString('2020-01-01')->january()->number());\n        $this->assertSame(2, Year::fromString('2020-01-01')->february()->number());\n        $this->assertSame(3, Year::fromString('2020-01-01')->march()->number());\n        $this->assertSame(4, Year::fromString('2020-01-01')->april()->number());\n        $this->assertSame(5, Year::fromString('2020-01-01')->may()->number());\n        $this->assertSame(6, Year::fromString('2020-01-01')->june()->number());\n        $this->assertSame(7, Year::fromString('2020-01-01')->july()->number());\n        $this->assertSame(8, Year::fromString('2020-01-01')->august()->number());\n        $this->assertSame(9, Year::fromString('2020-01-01')->september()->number());\n        $this->assertSame(10, Year::fromString('2020-01-01')->october()->number());\n        $this->assertSame(11, Year::fromString('2020-01-01')->november()->number());\n        $this->assertSame(12, Year::fromString('2020-01-01')->december()->number());\n    }\n\n    public function test_from_string() : void\n    {\n        $this->assertSame(2018, Year::fromString('2018')->number());\n        $this->assertSame(2020, Year::fromString('2020')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('now'))->format('Y'), Year::fromString('now')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('midnight'))->format('Y'), Year::fromString('midnight')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('yesterday'))->format('Y'), Year::fromString('yesterday')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('yesterday'))->format('Y'), Year::fromString('yesterday ')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('noon'))->format('Y'), Year::fromString('NooN ')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('noon'))->format('Y'), Year::fromString('noon')->number());\n        $this->assertSame((int) (new \\DateTimeImmutable('tomorrow'))->format('Y'), Year::fromString('tomorrow')->number());\n    }\n\n    public function test_from_string_that_is_not_valid_number() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        Year::fromString('not a number');\n    }\n\n    public function test_from_date_time_immutable() : void\n    {\n        $this->assertSame(2020, Year::fromDateTime(new \\DateTimeImmutable('2020-05-01'))->number());\n    }\n\n    #[DataProvider('month_number_of_days_data_provider')]\n    public function test_month_number_of_days(int $year, int $month, int $numberOfDays) : void\n    {\n        $this->assertSame($numberOfDays, (new Year($year))->months()->byNumber($month)->numberOfDays());\n    }\n\n    public function test_debug_info() : void\n    {\n        $this->assertSame(\n            [\n                'year' => 2020,\n            ],\n            Year::fromString('2020-01-01')->__debugInfo()\n        );\n    }\n\n    public function test_to_string() : void\n    {\n        $this->assertSame(\n            '2020',\n            Year::fromString('2020-01-01')->toString()\n        );\n    }\n\n    public function test_map_days() : void\n    {\n        $days = (new Year(2020))->mapDays(fn (Day $day) : int => $day->number());\n        $this->assertCount(366, $days);\n        $this->assertSame($days[0], 1);\n        $this->assertSame($days[365], 31);\n    }\n\n    public function test_filter_days() : void\n    {\n        $this->assertSame(52, \\count((new Year(2020))->filterDays(fn (Day $day) : bool => $day->isWeekend())) / 2);\n    }\n\n    public function test_next_year() : void\n    {\n        $this->assertSame(2021, (new Year(2020))->next()->number());\n    }\n\n    public function test_previous_year() : void\n    {\n        $this->assertSame(2019, (new Year(2020))->previous()->number());\n    }\n\n    public function test_leap_years() : void\n    {\n        $year = new Year(0);\n\n        while ($year->number() < 9999) {\n            if ($year->isLeap()) {\n                $this->assertTrue(\n                    $year->number() % 4 === 0 && ($year->number() % 100 !== 0 || $year->number() % 400 === 0)\n                );\n                $this->assertSame(366, $year->numberOfDays());\n            } else {\n                $this->assertSame(365, $year->numberOfDays());\n            }\n            $year = $year->next();\n        }\n    }\n\n    public function test_reset_time_in_to_datetime_immutable() : void\n    {\n        $year = new Year(2020);\n\n        $dateTimeImmutable1 = $year->toDateTimeImmutable();\n        \\sleep(1);\n        $dateTimeImmutable2 = $year->toDateTimeImmutable();\n\n        $this->assertTrue($dateTimeImmutable1 == $dateTimeImmutable2);\n    }\n\n    public function test_is_equal() : void\n    {\n        $this->assertTrue(Year::fromString('2020-01-01')->isEqualTo(Year::fromString('2020-01-01')));\n        $this->assertFalse(Year::fromString('2021-01-02')->isEqualTo(Year::fromString('2020-01-01')));\n    }\n\n    public function test_is_before() : void\n    {\n        $this->assertTrue(Year::fromString('2019-01-01')->isBefore(Year::fromString('2020-01-01')));\n        $this->assertTrue(Year::fromString('2020-01-01')->isBeforeOrEqualTo(Year::fromString('2020-01-01')));\n\n        $this->assertFalse(Year::fromString('2021-01-01')->isBefore(Year::fromString('2020-01-01')));\n        $this->assertFalse(Year::fromString('2021-01-01')->isBeforeOrEqualTo(Year::fromString('2020-01-01')));\n    }\n\n    public function test_is_after() : void\n    {\n        $this->assertTrue(Year::fromString('2022-01-01')->isAfter(Year::fromString('2020-02-01')));\n        $this->assertTrue(Year::fromString('2020-01-01')->isAfterOrEqualTo(Year::fromString('2020-01-01')));\n\n        $this->assertFalse(Year::fromString('2019-01-01')->isAfter(Year::fromString('2020-02-01')));\n        $this->assertFalse(Year::fromString('2019-01-01')->isAfterOrEqualTo(Year::fromString('2020-02-01')));\n    }\n\n    public function test_until_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('2020 is after 2019');\n        Year::fromString('2020-01-01')->until(Year::fromString('2019-01-01'), Interval::rightOpen());\n    }\n\n    public function test_since_with_wrong_destination_month() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('2019 is before 2020');\n        Year::fromString('2019-01-01')->since(Year::fromString('2020-01-01'), Interval::rightOpen());\n    }\n\n    public function test_modify_months() : void\n    {\n        $this->assertSame('2015-01-01', Year::fromString('2020-01-01')->sub(5)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-01-01', Year::fromString('2020-01-01')->sub(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2026-01-01', Year::fromString('2020-01-01')->add(6)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2021-01-01', Year::fromString('2020-01-01')->add(1)->toDateTimeImmutable()->format('Y-m-d'));\n        $this->assertSame('2019-01-01', Year::fromString('2020-01-01')->add(-1)->toDateTimeImmutable()->format('Y-m-d'));\n    }\n\n    public function test_until() : void\n    {\n        $this->assertCount(5, $years = Year::fromString('2020-01-01')->until(Year::fromString('2025-01-01'), Interval::rightOpen()));\n        $this->assertInstanceOf(Year::class, $years[0]);\n        $this->assertInstanceOf(Year::class, $years[4]);\n        $this->assertSame(2020, $years[0]->number());\n        $this->assertSame(2024, $years[4]->number());\n    }\n\n    public function test_since() : void\n    {\n        $this->assertCount(5, $years = Year::fromString('2025-01-01')->since(Year::fromString('2020-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Year::class, $years[0]);\n        $this->assertInstanceOf(Year::class, $years[4]);\n        $this->assertSame(2021, $years[0]->number());\n        $this->assertSame(2025, $years[4]->number());\n    }\n\n    public function test_iterate_until() : void\n    {\n        $this->assertCount(5, $years = Year::fromString('2020-01-01')->iterate(Year::fromString('2025-01-01'), Interval::rightOpen()));\n        $this->assertInstanceOf(Year::class, $years[0]);\n        $this->assertInstanceOf(Year::class, $years[4]);\n        $this->assertSame(2020, $years[0]->number());\n        $this->assertSame(2024, $years[4]->number());\n    }\n\n    public function test_iterate_since() : void\n    {\n        $this->assertCount(5, $years = Year::fromString('2025-01-01')->iterate(Year::fromString('2020-01-01'), Interval::leftOpen()));\n        $this->assertInstanceOf(Year::class, $years[0]);\n        $this->assertInstanceOf(Year::class, $years[4]);\n        $this->assertSame(2021, $years[0]->number());\n        $this->assertSame(2025, $years[4]->number());\n    }\n\n    public function test_quarter_below_limit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        (new Year(2020))->quarter(0);\n    }\n\n    public function test_quarter_above_limit() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        (new Year(2020))->quarter(5);\n    }\n\n    public function test_distance_to() : void\n    {\n        $this->assertSame(366, (new Year(2020))->distance(new Year(2021))->inDays());\n    }\n\n    public function test_quarters() : void\n    {\n        $this->assertSame(1, (new Year(2020))->quarter(1)->number());\n\n        $this->assertCount(3, (new Year(2020))->quarter(1)->months());\n        $this->assertSame(1, (new Year(2020))->quarter(1)->months()->all()[0]->number());\n        $this->assertSame(2, (new Year(2020))->quarter(1)->months()->all()[1]->number());\n        $this->assertSame(3, (new Year(2020))->quarter(1)->months()->all()[2]->number());\n\n        $this->assertCount(3, (new Year(2020))->quarter(2)->months());\n        $this->assertSame(4, (new Year(2020))->quarter(2)->months()->all()[0]->number());\n        $this->assertSame(5, (new Year(2020))->quarter(2)->months()->all()[1]->number());\n        $this->assertSame(6, (new Year(2020))->quarter(2)->months()->all()[2]->number());\n\n        $this->assertCount(3, (new Year(2020))->quarter(3)->months());\n        $this->assertSame(7, (new Year(2020))->quarter(3)->months()->all()[0]->number());\n        $this->assertSame(8, (new Year(2020))->quarter(3)->months()->all()[1]->number());\n        $this->assertSame(9, (new Year(2020))->quarter(3)->months()->all()[2]->number());\n\n        $this->assertCount(3, (new Year(2020))->quarter(4)->months());\n        $this->assertSame(10, (new Year(2020))->quarter(4)->months()->all()[0]->number());\n        $this->assertSame(11, (new Year(2020))->quarter(4)->months()->all()[1]->number());\n        $this->assertSame(12, (new Year(2020))->quarter(4)->months()->all()[2]->number());\n    }\n\n    public function test_serialization() : void\n    {\n        $year = new Year(2020);\n\n        $this->assertSame(\n            [\n                'year' => 2020,\n            ],\n            $serializedYear = $year->__serialize()\n        );\n        $this->assertSame(\n            'O:28:\"' . Year::class . '\":1:{s:4:\"year\";i:2020;}',\n            $serializedYearString = \\serialize($year)\n        );\n        $this->assertEquals(\n            \\unserialize($serializedYearString),\n            $year\n        );\n    }\n\n    #[DataProvider('leap_years')]\n    public function test_leap_year(int $year, bool $isLeap) : void\n    {\n        $this->assertSame($isLeap, (new Year($year))->isLeap());\n    }\n\n    #[DataProvider('compare_to_provider')]\n    public function test_compare_to(Year $time, Year $comparable, int $compareResult) : void\n    {\n        $this->assertSame($compareResult, $time->compareTo($comparable));\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/Gregorian/YearsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\Gregorian;\n\nuse Aeon\\Calendar\\Gregorian\\Year;\nuse Aeon\\Calendar\\Gregorian\\Years;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class YearsTest extends TestCase\n{\n    public function test_array_access() : void\n    {\n        $years = new Years(\n            Year::fromString('2000-01-01'),\n            Year::fromString('2001-02-02'),\n            Year::fromString('2002-03-03')\n        );\n\n        $this->assertTrue(isset($years[0]));\n        $this->assertInstanceOf(Year::class, $years[0]);\n        $this->assertSame(3, \\iterator_count($years->getIterator()));\n        $this->assertCount(3, $years->all());\n    }\n\n    public function test_map() : void\n    {\n        $yeras = new Years(\n            Year::fromString('2000-01-01'),\n            Year::fromString('2001-02-02'),\n            Year::fromString('2002-03-03')\n        );\n\n        $this->assertSame(\n            [2000, 2001, 2002],\n            $yeras->map(function (Year $day) {\n                return $day->number();\n            })\n        );\n    }\n\n    public function test_filter() : void\n    {\n        $years = new Years(\n            Year::fromString('2000-01-01'),\n            Year::fromString('2001-02-02'),\n            Year::fromString('2002-03-03')\n        );\n\n        $this->assertEquals(\n            new Years(Year::fromString('2000-01-01')),\n            $years->filter(function (Year $day) {\n                return $day->number() === 2000;\n            })\n        );\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/IntervalTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit;\n\nuse Aeon\\Calendar\\Gregorian\\Interval;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IntervalTest extends TestCase\n{\n    public function test_open() : void\n    {\n        $interval = Interval::open();\n\n        $this->assertTrue($interval->isLeftOpen());\n        $this->assertTrue($interval->isRightOpen());\n        $this->assertTrue($interval->isOpen());\n        $this->assertFalse($interval->isClosed());\n    }\n\n    public function test_closed() : void\n    {\n        $interval = Interval::closed();\n\n        $this->assertFalse($interval->isLeftOpen());\n        $this->assertFalse($interval->isRightOpen());\n        $this->assertFalse($interval->isOpen());\n        $this->assertTrue($interval->isClosed());\n    }\n\n    public function test_left_open() : void\n    {\n        $interval = Interval::leftOpen();\n\n        $this->assertTrue($interval->isLeftOpen());\n        $this->assertFalse($interval->isRightOpen());\n        $this->assertFalse($interval->isOpen());\n        $this->assertFalse($interval->isClosed());\n    }\n\n    public function test_right_open() : void\n    {\n        $interval = Interval::rightOpen();\n\n        $this->assertFalse($interval->isLeftOpen());\n        $this->assertTrue($interval->isRightOpen());\n        $this->assertFalse($interval->isOpen());\n        $this->assertFalse($interval->isClosed());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/RelativeTimeUnitTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit;\n\nuse Aeon\\Calendar\\RelativeTimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * @psalm-immutable\n */\nfinal class RelativeTimeUnitTest extends TestCase\n{\n    public function test_months() : void\n    {\n        $this->assertSame('02', (RelativeTimeUnit::months(2))->toDateInterval()->format('%M'));\n        $this->assertSame('02', (RelativeTimeUnit::months(-2))->toDateInterval()->format('%M'));\n        $this->assertSame(1, (RelativeTimeUnit::months(-2))->toDateInterval()->invert);\n        $this->assertSame('01', (RelativeTimeUnit::month())->toDateInterval()->format('%M'));\n    }\n\n    public function test_invert() : void\n    {\n        $this->assertSame(2, (RelativeTimeUnit::months(-2))->invert()->inMonths());\n        $this->assertSame(-2, (RelativeTimeUnit::months(2))->invert()->inMonths());\n        $this->assertSame(-2, (RelativeTimeUnit::years(2))->invert()->inYears());\n        $this->assertSame(-24, (RelativeTimeUnit::years(2))->invert()->inMonths());\n        $this->assertSame(2, (RelativeTimeUnit::years(-2))->invert()->inYears());\n        $this->assertSame(24, (RelativeTimeUnit::years(-2))->invert()->inMonths());\n    }\n\n    public function test_years() : void\n    {\n        $this->assertSame('02', (RelativeTimeUnit::years(2))->toDateInterval()->format('%Y'));\n        $this->assertSame('02', (RelativeTimeUnit::years(-2))->toDateInterval()->format('%Y'));\n        $this->assertSame('01', (RelativeTimeUnit::year())->toDateInterval()->format('%Y'));\n    }\n\n    public function test_in_years() : void\n    {\n        $this->assertSame(0, RelativeTimeUnit::months(11)->inYears());\n        $this->assertSame(1, RelativeTimeUnit::months(12)->inYears());\n        $this->assertSame(-1, RelativeTimeUnit::months(-12)->inYears());\n        $this->assertSame(0, RelativeTimeUnit::months(-1)->inYears());\n        $this->assertSame(2, RelativeTimeUnit::months(26)->inYears());\n        $this->assertSame(2, RelativeTimeUnit::years(2)->inYears());\n    }\n\n    public function test_in_calendar_months() : void\n    {\n        $this->assertSame(4, RelativeTimeUnit::months(28)->inCalendarMonths());\n        $this->assertSame(4, RelativeTimeUnit::months(-28)->inCalendarMonths());\n        $this->assertSame(0, RelativeTimeUnit::years(2)->inCalendarMonths());\n    }\n\n    public function test_in_months() : void\n    {\n        $this->assertSame(24, RelativeTimeUnit::years(2)->inMonths());\n        $this->assertSame(24, RelativeTimeUnit::months(24)->inMonths());\n    }\n\n    public function test_to_date_interval() : void\n    {\n        $this->assertEquals(new \\DateInterval('P0Y2M'), RelativeTimeUnit::months(2)->toDateInterval());\n        $this->assertEquals(new \\DateInterval('P2Y0M'), RelativeTimeUnit::years(2)->toDateInterval());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/StopwatchTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit;\n\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\Stopwatch;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class StopwatchTest extends TestCase\n{\n    public function test_stopping_not_stared_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->stop();\n    }\n\n    public function test_elapsed_time_on_not_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch does not have any laps.');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->elapsedTime(0);\n    }\n\n    public function test_stopping_already_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch already stopped');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->stop();\n        $stopwatch->stop();\n    }\n\n    public function test_taking_lap_time_of_non_started_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->lap();\n    }\n\n    public function test_getting_last_lap_elapsed_time_from_not_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch does not have any laps');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->stop();\n        $stopwatch->lastLapElapsedTime();\n    }\n\n    public function test_getting_last_lap_elapsed_time_from_not_started_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->lastLapElapsedTime();\n    }\n\n    public function test_getting_first_lap_elapsed_time_from_not_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch does not have any laps');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->firstLapElapsedTime();\n    }\n\n    public function test_getting_first_lap_elapsed_time_from_not_started_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->firstLapElapsedTime();\n    }\n\n    public function test_getting_total_elapsed_time_from_not_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not stopped');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->totalElapsedTime();\n    }\n\n    public function test_getting_total_elapsed_time_from_not_started_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->totalElapsedTime();\n    }\n\n    public function test_elapsed_time_from_not_stopped_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch does not have any laps');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->elapsedTime(1);\n    }\n\n    public function test_elapsed_time_from_not_started_stopwatch() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not started');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->elapsedTime(1);\n    }\n\n    public function test_elapsed_time_from_not_existing_measure() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Lap 3 not exists');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->lap();\n        $stopwatch->stop();\n        $this->assertSame(2, $stopwatch->laps());\n        $stopwatch->elapsedTime(3);\n    }\n\n    public function test_laps_count_without_laps() : void\n    {\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->stop();\n        $this->assertSame(0, $stopwatch->laps());\n    }\n\n    public function test_elapsed_time_from_not_ended_measure() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Stopwatch not stopped');\n\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->lap();\n        $stopwatch->lastLapElapsedTime();\n    }\n\n    public function test_getting_elapsed_time_from_two_laps() : void\n    {\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->lap(); // lap #1\n        $stopwatch->stop(); // lap #2\n\n        $this->assertSame(2, $stopwatch->laps());\n        $this->assertSame(\n            $stopwatch->lastLapElapsedTime()->inSecondsPrecise(),\n            $stopwatch->elapsedTime(2)->inSecondsPrecise()\n        );\n        $this->assertSame(\n            $stopwatch->firstLapElapsedTime()->inSecondsPrecise(),\n            $stopwatch->elapsedTime(1)->inSecondsPrecise()\n        );\n    }\n\n    public function test_getting_total_elapsed_time_from_three_laps() : void\n    {\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        \\usleep(TimeUnit::milliseconds(100)->microsecond()); // lap #1 aka first\n        $stopwatch->lap();\n        \\usleep(TimeUnit::milliseconds(100)->microsecond()); // lap #2\n        $stopwatch->lap();\n        \\usleep(TimeUnit::milliseconds(100)->microsecond()); // lap #3 aka last\n        $stopwatch->stop();\n\n        $this->assertSame(\n            $stopwatch->totalElapsedTime()->inSecondsPrecise(),\n            $stopwatch->firstLapElapsedTime()\n                ->add($stopwatch->elapsedTime(2))\n                ->add($stopwatch->lastLapElapsedTime())\n                ->inSecondsPrecise()\n        );\n    }\n\n    public function test_getting_total_elapsed_time_from_one_lap_stop() : void\n    {\n        $stopwatch = new Stopwatch();\n        $stopwatch->start();\n        $stopwatch->lap();\n        \\usleep(TimeUnit::milliseconds(100)->microsecond());\n        $stopwatch->stop();\n\n        $this->assertSame(\n            $stopwatch->totalElapsedTime()->inSecondsPrecise(),\n            $stopwatch->firstLapElapsedTime()->add($stopwatch->lastLapElapsedTime())->inSecondsPrecise()\n        );\n        $this->assertSame(2, $stopwatch->laps());\n        $this->assertTrue($stopwatch->isStarted());\n        $this->assertTrue($stopwatch->isStopped());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/TimeUnit/HRTimeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit\\TimeUnit;\n\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit\\HRTime;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class HRTimeTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{string, int, int}, mixed, void>\n     */\n    public static function converting_hr_time_to_timeunit_data_provider() : \\Generator\n    {\n        yield ['0.000000', 0, 0];\n        yield ['0.100000', 0, 100_000_000];\n        yield ['0.100000', 0, 100_000_100];\n        yield ['0.100009', 0, 100_009_000];\n        yield ['0.100009', 0, 100_009_000_001];\n        yield ['0.000005', 0, 5_000];\n    }\n\n    public function test_conversion_with_nanoseconds_smaller_than_0() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage(\"Nanoseconds can't be less than 0, given -1\");\n\n        HRTime::convert(0, -1);\n    }\n\n    #[DataProvider('converting_hr_time_to_timeunit_data_provider')]\n    public function test_converting_hr_time_to_timeunit(string $expected, int $seconds, int $nanoseconds) : void\n    {\n        $this->assertSame($expected, HRTime::convert($seconds, $nanoseconds)->inSecondsPrecise());\n    }\n}\n"
  },
  {
    "path": "tests/Aeon/Calendar/Tests/Unit/TimeUnitTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Aeon\\Calendar\\Tests\\Unit;\n\nuse Aeon\\Calendar\\Exception\\Exception;\nuse Aeon\\Calendar\\Exception\\InvalidArgumentException;\nuse Aeon\\Calendar\\TimeUnit;\nuse PHPUnit\\Framework\\Attributes\\DataProvider;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TimeUnitTest extends TestCase\n{\n    /**\n     * @return \\Generator<int, array{TimeUnit, TimeUnit, bool}, mixed, void>\n     */\n    public static function greater_than_data_provider() : \\Generator\n    {\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(-5), true];\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(5), false];\n        yield [TimeUnit::seconds(5), TimeUnit::seconds(5), false];\n        yield [TimeUnit::seconds(10), TimeUnit::seconds(5), true];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000010), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000010), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000000), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000001), false];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, TimeUnit, bool}, mixed, void>\n     */\n    public static function greater_than_eq_data_provider() : \\Generator\n    {\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(-5), true];\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(5), false];\n        yield [TimeUnit::seconds(5), TimeUnit::seconds(5), true];\n        yield [TimeUnit::seconds(10), TimeUnit::seconds(5), true];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000001), false];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, TimeUnit, bool}, mixed, void>\n     */\n    public static function less_than_data_provider() : \\Generator\n    {\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(-5), false];\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(5), true];\n        yield [TimeUnit::seconds(5), TimeUnit::seconds(5), false];\n        yield [TimeUnit::seconds(10), TimeUnit::seconds(5), false];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000000), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000000), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000001), true];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, TimeUnit, bool}, mixed, void>\n     */\n    public static function less_than_eq_data_provider() : \\Generator\n    {\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(-5), false];\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(5), true];\n        yield [TimeUnit::seconds(5), TimeUnit::seconds(5), true];\n        yield [TimeUnit::seconds(10), TimeUnit::seconds(5), false];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000000), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000001), true];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, TimeUnit, bool}, mixed, void>\n     */\n    public static function equal_data_provider() : \\Generator\n    {\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(-5), false];\n        yield [TimeUnit::seconds(1), TimeUnit::seconds(5), false];\n        yield [TimeUnit::seconds(5), TimeUnit::seconds(5), true];\n        yield [TimeUnit::seconds(10), TimeUnit::seconds(5), false];\n        yield [TimeUnit::precise(0.000001), TimeUnit::precise(0.000000), false];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000000), true];\n        yield [TimeUnit::precise(0.000000), TimeUnit::precise(0.000001), false];\n    }\n\n    /**\n     * @return \\Generator<int, array{int, int, int}, mixed, void>\n     */\n    public static function adding_time_test_data_provider() : \\Generator\n    {\n        yield [5, 5, 10];\n        yield [-20, 10, -10];\n        yield [-5, 10, 5];\n    }\n\n    /**\n     * @return \\Generator<int, array{int, int, int}, mixed, void>\n     */\n    public static function subtracting_time_test_data_provider() : \\Generator\n    {\n        yield [5, 5, 0];\n        yield [5, 15, -10];\n    }\n\n    /**\n     * @return \\Generator<int, array{int, int, string, float, float}, mixed, void>\n     */\n    public static function adding_precise_time_test_data_provider() : \\Generator\n    {\n        yield [0, 0, '0.000000', 0.0, 0.0];\n        yield [0, 500001, '0.500001', 0.0, 0.500001];\n        yield [0, 10001, '0.010001', 0.0, 0.010001];\n        yield [0, 0, '0.000000', -0.500000, 0.500000];\n        yield [0, 0, '0.000000', -0.500000, 0.5000001]; // 7+ decimal points are ignored\n        yield [-1, 300000, '-1.300000', -1.500000, 0.200000];\n    }\n\n    /**\n     * @return \\Generator<int, array{int, int, string, float, float}, mixed, void>\n     */\n    public static function subtracting_precise_time_test_data_provider() : \\Generator\n    {\n        yield [0, 0, '0.000000', 0.0, 0.0];\n        yield [0, 500000, '-0.500000', 0.0, 0.500000];\n        yield [0, 50000, '-0.050000', 0.0, 0.050000];\n        yield [2, 508825, '2.508825', 2.588460, 0.079635];\n    }\n\n    /**\n     * @return \\Generator<int, array{\\DateInterval, TimeUnit}, mixed, void>\n     */\n    public static function creating_from_date_interval_provider() : \\Generator\n    {\n        yield [\\DateInterval::createFromDateString('5 microseconds'), TimeUnit::precise(0.000005)];\n        yield [\\DateInterval::createFromDateString('1 second 5 microseconds'), TimeUnit::precise(1.000005)];\n        yield [\\DateInterval::createFromDateString('1 second'), TimeUnit::second()];\n        yield [\\DateInterval::createFromDateString('5 seconds'), TimeUnit::seconds(5)];\n        yield [\\DateInterval::createFromDateString('1 minute'), TimeUnit::minute()];\n        yield [\\DateInterval::createFromDateString('5 minutes'), TimeUnit::minutes(5)];\n        yield [\\DateInterval::createFromDateString('1 hour'), TimeUnit::hour()];\n        yield [\\DateInterval::createFromDateString('10 hours'), TimeUnit::hours(10)];\n        yield [\\DateInterval::createFromDateString('28 hours'), TimeUnit::hours(28)];\n        yield [\\DateInterval::createFromDateString('1 day'), TimeUnit::day()];\n        yield [\\DateInterval::createFromDateString('365 days'), TimeUnit::days(365)];\n        yield [(new \\DateTimeImmutable('2020-01-01'))->diff(new \\DateTimeImmutable('2019-01-01')), TimeUnit::days(365)->invert()];\n        yield [(new \\DateTimeImmutable('2020-01-01'))->diff(new \\DateTimeImmutable('2020-03-01')), TimeUnit::days(60)];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, TimeUnit}, mixed, void>\n     */\n    public static function creating_from_date_string_provider() : \\Generator\n    {\n        yield ['5 microseconds', TimeUnit::precise(0.000005)];\n        yield ['1 second 5 microsecond', TimeUnit::precise(1.000005)];\n        yield ['1 second', TimeUnit::second()];\n        yield ['5 seconds', TimeUnit::seconds(5)];\n        yield ['1 minute', TimeUnit::minute()];\n        yield ['5 minutes', TimeUnit::minutes(5)];\n        yield ['1 hour', TimeUnit::hour()];\n        yield ['10 hours', TimeUnit::hours(10)];\n        yield ['28 hours', TimeUnit::hours(28)];\n        yield ['1 day', TimeUnit::day()];\n        yield ['365 days', TimeUnit::days(365)];\n    }\n\n    /**\n     * @return \\Generator<int, array{string, float}, mixed, void>\n     */\n    public static function half_round_up_to_microsecond_data_provider() : \\Generator\n    {\n        yield ['0.000000', 0.000_000_1];\n        yield ['0.000000', -0.000_000_1];\n        yield ['0.000001', 0.000_000_5];\n        yield ['-0.000001', -0.000_000_5];\n        yield ['-0.000001', -0.000_000_94];\n        yield ['0.000001', 0.000_000_99];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, float, TimeUnit}, mixed, void>\n     */\n    public static function multiplication_data_provider() : \\Generator\n    {\n        yield [TimeUnit::precise(1.00), TimeUnit::precise(2.00), TimeUnit::precise(2.00)];\n        yield [TimeUnit::precise(1.00), TimeUnit::precise(0.50), TimeUnit::precise(0.50)];\n        yield [TimeUnit::seconds(1), TimeUnit::precise(0.50), TimeUnit::milliseconds(500)];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, float, TimeUnit}, mixed, void>\n     */\n    public static function division_data_provider() : \\Generator\n    {\n        yield [TimeUnit::precise(1.00), TimeUnit::precise(2.00), TimeUnit::milliseconds(500)];\n        yield [TimeUnit::precise(10.00), TimeUnit::precise(10.00), TimeUnit::seconds(1)];\n        yield [TimeUnit::hours(1), TimeUnit::precise(60.00), TimeUnit::minutes(1)];\n    }\n\n    /**\n     * @return \\Generator<int, array{TimeUnit, float, TimeUnit}, mixed, void>\n     */\n    public static function modulo_data_provider() : \\Generator\n    {\n        yield [TimeUnit::precise(1.00), TimeUnit::precise(2.00), TimeUnit::second()];\n        yield [TimeUnit::precise(10.00), TimeUnit::precise(10.00), TimeUnit::seconds(0)];\n        yield [TimeUnit::hours(1), TimeUnit::precise(60.00), TimeUnit::seconds(0)];\n        yield [TimeUnit::hours(1), TimeUnit::minutes(37), TimeUnit::minutes(23)];\n    }\n\n    public function test_invert() : void\n    {\n        $timeUnit = TimeUnit::day()->invert();\n\n        $this->assertSame(-86400, $timeUnit->inSeconds());\n    }\n\n    public function test_creating_with_negative_seconds() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Seconds must be greater or equal 0, got -1');\n\n        TimeUnit::negative(-1, 0);\n    }\n\n    public function test_creating_with_negative_microsecond() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Microsecond must be greater or equal 0 and less than 1000000, got -5');\n\n        TimeUnit::negative(0, -5);\n    }\n\n    public function test_creating_with_invalid_microsecond() : void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $this->expectExceptionMessage('Microsecond must be greater or equal 0 and less than 1000000, got 1000000');\n\n        TimeUnit::negative(0, 1_000_000);\n    }\n\n    public function test_time_unit_create_from_day() : void\n    {\n        $unit = TimeUnit::day();\n\n        $this->assertSame(1, $unit->inDays());\n    }\n\n    public function test_time_unit_create_from_days() : void\n    {\n        $unit = TimeUnit::days(5);\n\n        $this->assertSame(5, $unit->inDays());\n        $this->assertSame(120, $unit->inHours());\n        $this->assertSame(7200, $unit->inMinutes());\n        $this->assertSame(432000, $unit->inSeconds());\n        $this->assertSame('432000.000000', $unit->inSecondsPrecise());\n        $this->assertSame(432000000, $unit->inMilliseconds());\n        $this->assertSame(432000, $unit->toDateInterval()->s);\n    }\n\n    public function test_time_unit_create_from_days_negative() : void\n    {\n        $unit = TimeUnit::days(-5);\n\n        $this->assertSame(5, $unit->inDaysAbs());\n        $this->assertSame(-5, $unit->inDays());\n        $this->assertSame(-120, $unit->inHours());\n        $this->assertSame(120, $unit->inHoursAbs());\n        $this->assertSame(-7200, $unit->inMinutes());\n        $this->assertSame(7200, $unit->inMinutesAbs());\n        $this->assertSame(-432000, $unit->inSeconds());\n        $this->assertSame(432000, $unit->inSecondsAbs());\n        $this->assertSame('-432000.000000', $unit->inSecondsPrecise());\n        $this->assertSame(-432000000, $unit->inMilliseconds());\n        $this->assertSame(432000000, $unit->inMillisecondsAbs());\n        $this->assertSame(432000, $unit->toDateInterval()->s);\n        $this->assertSame(1, $unit->toDateInterval()->invert);\n    }\n\n    public function test_time_unit_create_with_value_1() : void\n    {\n        $this->assertSame(1000, TimeUnit::millisecond()->microsecond());\n        $this->assertSame(0, TimeUnit::second()->microsecond());\n        $this->assertSame(0, TimeUnit::minute()->microsecond());\n        $this->assertSame(0, TimeUnit::hour()->microsecond());\n        $this->assertSame(0, TimeUnit::day()->microsecond());\n    }\n\n    public function test_time_unit_create_with_0_value() : void\n    {\n        $this->assertTrue(TimeUnit::milliseconds(0)->isPositive());\n        $this->assertTrue(TimeUnit::seconds(0)->isPositive());\n        $this->assertTrue(TimeUnit::minutes(0)->isPositive());\n        $this->assertTrue(TimeUnit::hours(0)->isPositive());\n        $this->assertTrue(TimeUnit::days(0)->isPositive());\n    }\n\n    public function test_time_unit_create_from_hours() : void\n    {\n        $unit = TimeUnit::hours(2);\n\n        $this->assertSame(0, $unit->inDays());\n        $this->assertSame(2, $unit->inHours());\n        $this->assertSame(120, $unit->inMinutes());\n        $this->assertSame(7200, $unit->inSeconds());\n    }\n\n    public function test_time_unit_create_from_minute() : void\n    {\n        $unit = TimeUnit::minute();\n\n        $this->assertSame(1, $unit->inMinutes());\n    }\n\n    public function test_time_unit_create_from_0_minutes() : void\n    {\n        $unit = TimeUnit::minutes(0);\n\n        $this->assertTrue($unit->isPositive());\n    }\n\n    public function test_time_unit_create_from_minutes() : void\n    {\n        $unit = TimeUnit::minutes(2);\n\n        $this->assertSame(0, $unit->inDays());\n        $this->assertSame(0, $unit->inHours());\n        $this->assertSame(2, $unit->inMinutes());\n        $this->assertSame(120, $unit->inSeconds());\n    }\n\n    public function test_in_time_values() : void\n    {\n        $this->assertSame(2, TimeUnit::hours(26)->inTimeHours());\n        $this->assertSame(2, TimeUnit::hours(-26)->inTimeHours());\n        $this->assertSame(8, TimeUnit::seconds(-68)->inTimeSeconds());\n        $this->assertSame(8, TimeUnit::seconds(68)->inTimeSeconds());\n        $this->assertSame(8, TimeUnit::minutes(-68)->inTimeMinutes());\n        $this->assertSame(8, TimeUnit::minutes(68)->inTimeMinutes());\n        $this->assertSame(15, TimeUnit::minutes(135)->inTimeMinutes());\n        $this->assertSame(500, TimeUnit::milliseconds(1500)->inTimeMilliseconds());\n        $this->assertSame(500, TimeUnit::milliseconds(-1500)->inTimeMilliseconds());\n    }\n\n    public function test_complex_time_unit_in_time_values() : void\n    {\n        $timeUnit = TimeUnit::days(3)\n            ->add(TimeUnit::hours(4))\n            ->add(TimeUnit::minutes(85))\n            ->add(TimeUnit::seconds(30))\n            ->add(TimeUnit::milliseconds(1480));\n\n        $this->assertSame(5, $timeUnit->inTimeHours());\n        $this->assertSame(25, $timeUnit->inTimeMinutes());\n        $this->assertSame(31, $timeUnit->inTimeSeconds());\n        $this->assertSame(480, $timeUnit->inTimeMilliseconds());\n    }\n\n    public function test_is_zero() : void\n    {\n        $this->assertFalse(TimeUnit::second()->isZero());\n        $this->assertTrue(TimeUnit::seconds(0)->isZero());\n    }\n\n    public function test_second() : void\n    {\n        $timeUnit = TimeUnit::second();\n\n        $this->assertSame(1, $timeUnit->inSeconds());\n    }\n\n    public function test_millisecond() : void\n    {\n        $timeUnit = TimeUnit::millisecond();\n\n        $this->assertSame(1000, $timeUnit->microsecond());\n        $this->assertSame(1, $timeUnit->inMilliseconds());\n    }\n\n    public function test_milliseconds() : void\n    {\n        $timeUnit = TimeUnit::milliseconds(1500);\n\n        $this->assertSame(500000, $timeUnit->microsecond());\n        $this->assertSame(1, $timeUnit->inSeconds());\n        $this->assertsame(1500, $timeUnit->inMilliseconds());\n    }\n\n    public function test_creating_precise_timeunit() : void\n    {\n        $this->assertSame(500000, TimeUnit::precise(0.5)->microsecond());\n        $this->assertSame(0, TimeUnit::precise(0.5)->inSeconds());\n        $this->assertSame(500, TimeUnit::precise(0.5)->inMilliseconds());\n        $this->assertFalse(TimeUnit::precise(0.5)->isNegative());\n        $this->assertSame('0.500000', TimeUnit::precise(0.5)->inSecondsPrecise());\n    }\n\n    public function test_creating_negative_precise_timeunit() : void\n    {\n        $this->assertSame(500000, TimeUnit::precise(-0.5)->microsecond());\n        $this->assertSame(0, TimeUnit::precise(-0.5)->inSeconds());\n        $this->assertSame(-500, TimeUnit::precise(-0.5)->inMilliseconds());\n        $this->assertTrue(TimeUnit::precise(-0.5)->isNegative());\n        $this->assertSame('-0.500000', TimeUnit::precise(-0.5)->inSecondsPrecise());\n    }\n\n    #[DataProvider('greater_than_data_provider')]\n    public function test_greater_than(TimeUnit $timeUnit, TimeUnit $nextTimeUnit, bool $expectedResult) : void\n    {\n        $this->assertSame($expectedResult, $timeUnit->isGreaterThan($nextTimeUnit));\n    }\n\n    #[DataProvider('greater_than_eq_data_provider')]\n    public function test_greater_than_eq(TimeUnit $timeUnit, TimeUnit $nextTimeUnit, bool $expectedResult) : void\n    {\n        $this->assertSame($expectedResult, $timeUnit->isGreaterThanOrEqualTo($nextTimeUnit));\n    }\n\n    #[DataProvider('less_than_data_provider')]\n    public function test_less_than(TimeUnit $timeUnit, TimeUnit $nextTimeUnit, bool $expectedResult) : void\n    {\n        $this->assertSame($expectedResult, $timeUnit->isLessThan($nextTimeUnit));\n    }\n\n    #[DataProvider('less_than_eq_data_provider')]\n    public function test_less_than_eq(TimeUnit $timeUnit, TimeUnit $nextTimeUnit, bool $expectedResult) : void\n    {\n        $this->assertSame($expectedResult, $timeUnit->isLessThanOrEqualTo($nextTimeUnit));\n    }\n\n    #[DataProvider('equal_data_provider')]\n    public function test_equal(TimeUnit $timeUnit, TimeUnit $nextTimeUnit, bool $expectedResult) : void\n    {\n        $this->assertSame($expectedResult, $timeUnit->isEqualTo($nextTimeUnit));\n    }\n\n    #[DataProvider('adding_time_test_data_provider')]\n    public function test_adding_time_units(int $seconds, int $addedSeconds, int $expectedSeconds) : void\n    {\n        $this->assertSame($expectedSeconds, TimeUnit::seconds($seconds)->add(TimeUnit::seconds($addedSeconds))->inSeconds());\n    }\n\n    #[DataProvider('subtracting_time_test_data_provider')]\n    public function test_subtracting_time_units(int $seconds, int $substractedSeconds, int $expectedSeconds) : void\n    {\n        $this->assertSame($expectedSeconds, TimeUnit::seconds($seconds)->sub(TimeUnit::seconds($substractedSeconds))->inSeconds());\n    }\n\n    #[DataProvider('adding_precise_time_test_data_provider')]\n    public function test_adding_precise_time_units(int $expectedSeconds, int $expectedMicrosecond, string $expectedPreciseString, float $seconds, float $addedSeconds) : void\n    {\n        $this->assertSame(\n            $expectedSeconds,\n            TimeUnit::precise($seconds)->add(TimeUnit::precise($addedSeconds))->inSeconds()\n        );\n        $this->assertSame(\n            $expectedMicrosecond,\n            TimeUnit::precise($seconds)->add(TimeUnit::precise($addedSeconds))->microsecond()\n        );\n        $this->assertSame(\n            $expectedPreciseString,\n            TimeUnit::precise($seconds)->add(TimeUnit::precise($addedSeconds))->inSecondsPrecise()\n        );\n    }\n\n    #[DataProvider('subtracting_precise_time_test_data_provider')]\n    public function test_subtracting_precise_time_units(int $expectedSeconds, int $expectedMicrseond, string $expectedPreciseString, float $seconds, float $addedSeconds) : void\n    {\n        $this->assertSame(\n            $expectedSeconds,\n            TimeUnit::precise($seconds)->sub(TimeUnit::precise($addedSeconds))->inSeconds()\n        );\n        $this->assertSame(\n            $expectedMicrseond,\n            TimeUnit::precise($seconds)->sub(TimeUnit::precise($addedSeconds))->microsecond()\n        );\n        $this->assertSame(\n            $expectedPreciseString,\n            TimeUnit::precise($seconds)->sub(TimeUnit::precise($addedSeconds))->inSecondsPrecise()\n        );\n    }\n\n    #[DataProvider('creating_from_date_interval_provider')]\n    public function test_creating_from_date_interval(\\DateInterval $dateInterval, TimeUnit $timeUnit) : void\n    {\n        $this->assertObjectEquals(TimeUnit::fromDateInterval($dateInterval), $timeUnit, 'isEqual');\n    }\n\n    #[DataProvider('creating_from_date_string_provider')]\n    public function test_creating_from_date_string(string $dateString, TimeUnit $timeUnit) : void\n    {\n        $this->assertObjectEquals(TimeUnit::fromDateString($dateString), $timeUnit, 'isEqual');\n    }\n\n    public function test_creating_from_inaccurate_date_interval_years() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Can\\'t convert P1Y0M0DT0H0M0S precisely to time unit because year can\\'t be directly converted to number of seconds.');\n\n        TimeUnit::fromDateInterval(\\DateInterval::createFromDateString('1 year'));\n    }\n\n    public function test_creating_from_inaccurate_date_interval_months() : void\n    {\n        $this->expectException(Exception::class);\n        $this->expectExceptionMessage('Can\\'t convert P0Y4M0DT0H0M0S precisely to time unit because month can\\'t be directly converted to number of seconds.');\n\n        TimeUnit::fromDateInterval(\\DateInterval::createFromDateString('4 months'));\n    }\n\n    #[DataProvider('half_round_up_to_microsecond_data_provider')]\n    public function test_half_round_up_to_microsecond(string $stringFloat, float $float) : void\n    {\n        $this->assertSame(\n            $stringFloat,\n            TimeUnit::precise($float)->inSecondsPrecise(),\n            $stringFloat . ' is not equal ' . $float\n        );\n    }\n\n    #[DataProvider('multiplication_data_provider')]\n    public function test_multiplication(TimeUnit $timeUnit, TimeUnit $multiplier, TimeUnit $expectedResult) : void\n    {\n        $this->assertSame($expectedResult->inSecondsPrecise(), $timeUnit->multiply($multiplier)->inSecondsPrecise());\n    }\n\n    #[DataProvider('division_data_provider')]\n    public function test_division(TimeUnit $timeUnit, TimeUnit $multiplier, TimeUnit $expectedResult) : void\n    {\n        $this->assertSame($expectedResult->inSecondsPrecise(), $timeUnit->divide($multiplier)->inSecondsPrecise());\n    }\n\n    #[DataProvider('modulo_data_provider')]\n    public function test_modulo(TimeUnit $timeUnit, TimeUnit $multiplier, TimeUnit $expectedResult) : void\n    {\n        $this->assertSame($expectedResult->inSecondsPrecise(), $timeUnit->modulo($multiplier)->inSecondsPrecise());\n    }\n\n    public function test_to_negative() : void\n    {\n        $this->assertTrue(TimeUnit::negative(10, 0)->toNegative()->isNegative());\n        $this->assertTrue(TimeUnit::positive(10, 0)->toNegative()->isNegative());\n    }\n\n    public function test_to_positive() : void\n    {\n        $this->assertTrue(TimeUnit::positive(10, 0)->toPositive()->isPositive());\n        $this->assertTrue(TimeUnit::negative(10, 0)->toPositive()->isPositive());\n    }\n\n    public function test_to_date_interval() : void\n    {\n        $interval = new \\DateInterval('PT2S');\n        $interval->f = 0.500000;\n\n        $this->assertEquals($interval, TimeUnit::precise(2.500000)->toDateInterval());\n    }\n\n    public function test_getting_microsecond_string() : void\n    {\n        $this->assertSame('500000', TimeUnit::precise(2.500000)->microsecondString());\n    }\n\n    public function test_serialization() : void\n    {\n        $timeUnit = TimeUnit::positive(10, 10);\n\n        $this->assertSame(\n            [\n                'seconds' => 10,\n                'microsecond' => 10,\n                'negative' => false,\n            ],\n            $timeUnit->__serialize()\n        );\n    }\n}\n"
  }
]