├── .github └── workflows │ ├── ci.yml │ ├── documentation.yml │ ├── periodic.yml │ └── release.yml ├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── docs ├── assets │ ├── favicon.png │ ├── fonts │ │ └── MonaspaceNeon-Regular.woff │ ├── logo.svg │ └── stylesheets │ │ └── extra.css ├── getting-started │ ├── clocks.md │ ├── elapsed-period.md │ ├── formats.md │ ├── index.md │ ├── periods.md │ ├── points-in-time.md │ ├── time-offsets.md │ └── timezones.md ├── index.md ├── preface │ ├── philosophy.md │ └── terminology.md └── upgrade │ └── v3-to-v4.md ├── fixtures ├── Period.php └── PointInTime.php ├── mkdocs.yml ├── proofs ├── clock.php ├── elapsedPeriod.php ├── move │ ├── endOfDay.php │ ├── endOfMonth.php │ ├── endOfYear.php │ ├── startOfDay.php │ ├── startOfMonth.php │ └── startOfYear.php ├── period.php ├── pointInTime.php └── pointInTime │ └── highResolution.php ├── psalm.xml └── src ├── Calendar ├── Day.php └── Month.php ├── Clock.php ├── Clock ├── Frozen.php ├── Live.php ├── Logger.php └── OfFormat.php ├── ElapsedPeriod.php ├── Format.php ├── Format └── Custom.php ├── Move ├── EndOfDay.php ├── EndOfMonth.php ├── EndOfYear.php ├── Month.php ├── StartOfDay.php ├── StartOfMonth.php └── StartOfYear.php ├── Offset.php ├── Period.php ├── Period └── Value.php ├── PointInTime.php ├── PointInTime ├── Day.php ├── HighResolution.php ├── Hour.php ├── Microsecond.php ├── Millisecond.php ├── Minute.php ├── Month.php ├── Second.php └── Year.php ├── Timezone.php ├── Timezone ├── Africa.php ├── America.php ├── America │ ├── Argentina.php │ ├── Indiana.php │ └── NorthDakota.php ├── Antartica.php ├── Arctic.php ├── Asia.php ├── Atlantic.php ├── Australia.php ├── Europe.php ├── Indian.php └── Pacific.php └── Timezones.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | coverage: 7 | uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main 8 | secrets: inherit 9 | psalm: 10 | uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main 11 | cs: 12 | uses: innmind/github-workflows/.github/workflows/cs.yml@main 13 | with: 14 | php-version: '8.2' 15 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | on: 3 | push: 4 | branches: [master, main] 5 | permissions: 6 | contents: write 7 | jobs: 8 | deploy: 9 | uses: innmind/github-workflows/.github/workflows/documentation.yml@main 10 | secrets: inherit 11 | -------------------------------------------------------------------------------- /.github/workflows/periodic.yml: -------------------------------------------------------------------------------- 1 | name: Periodic CI 2 | 3 | on: 4 | schedule: 5 | - cron: '0 1 * * 0' 6 | push: 7 | paths: 8 | - '.github/workflows/periodic.yml' 9 | 10 | jobs: 11 | blackbox: 12 | uses: innmind/github-workflows/.github/workflows/coverage.yml@main 13 | secrets: inherit 14 | with: 15 | os: ubuntu-latest 16 | php-version: '8.3' 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | uses: innmind/github-workflows/.github/workflows/release.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | now(); // return an instance of PointInTime 29 | echo $now->toString(); // 2016-10-11T12:17:30.123456+02:00 30 | 31 | $epoch = $clock->at( 32 | '1970-01-01T00:00:00.000000+00:00', 33 | Format::iso8601(), 34 | ); // Maybe 35 | ``` 36 | 37 | Here we reference 2 points in time, the first is the exact moment we call `now` down to the microsecond and the second one is the epoch time. 38 | 39 | The method `at()` accepts any string that is allowed by `\DateTimeImmutable`. 40 | 41 | ## Documentation 42 | 43 | Full documentation is available at . 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "innmind/time-continuum", 3 | "type": "library", 4 | "description": "Library to manipulate time", 5 | "keywords": ["time"], 6 | "homepage": "http://github.com/Innmind/TimeContinuum", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Baptiste Langlade", 11 | "email": "baptiste.langlade@hey.com" 12 | } 13 | ], 14 | "support": { 15 | "issues": "http://github.com/Innmind/TimeContinuum/issues" 16 | }, 17 | "require": { 18 | "php": "~8.2", 19 | "psr/log": "~3.0", 20 | "innmind/immutable": "~5.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Innmind\\TimeContinuum\\": "src/", 25 | "Fixtures\\Innmind\\TimeContinuum\\": "fixtures/" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "Tests\\Innmind\\TimeContinuum\\": "tests/" 31 | } 32 | }, 33 | "require-dev": { 34 | "innmind/static-analysis": "^1.2.1", 35 | "innmind/black-box": "~5.8|^6.0.1", 36 | "innmind/coding-standard": "^2.0.1" 37 | }, 38 | "conflict": { 39 | "innmind/black-box": "<5.0|~7.0" 40 | }, 41 | "suggest": { 42 | "innmind/black-box": "For property based testing" 43 | }, 44 | "provide": { 45 | "innmind/black-box-sets": "5.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Innmind/TimeContinuum/4c5df986caf939798c9a3966f622d4686c04ef57/docs/assets/favicon.png -------------------------------------------------------------------------------- /docs/assets/fonts/MonaspaceNeon-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Innmind/TimeContinuum/4c5df986caf939798c9a3966f622d4686c04ef57/docs/assets/fonts/MonaspaceNeon-Regular.woff -------------------------------------------------------------------------------- /docs/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Monaspace Neon"; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url("../fonts/MonaspaceNeon-Regular.woff"); 6 | } 7 | 8 | :root { 9 | --md-code-font: "Monaspace Neon"; 10 | } 11 | 12 | :root { 13 | --light-md-code-hl-number-color: #f76d47; 14 | --light-md-code-hl-function-color: #6384b9; 15 | --light-md-code-hl-operator-color: #39adb5; 16 | --light-md-code-hl-constant-color: #7c4dff; 17 | --light-md-code-hl-string-color: #9fc06f; 18 | --light-md-code-hl-punctuation-color: #39adb5; 19 | --light-md-code-hl-keyword-color: #7c4dff; 20 | --light-md-code-hl-variable-color: #80cbc4; 21 | --light-md-code-hl-comment-color: #ccd7da; 22 | --light-md-code-bg-color: #fafafa; 23 | --light-md-code-fg-color: #ffb62c; 24 | --light-md-code-hl-variable-color: #6384b9; 25 | --dark-md-code-hl-number-color: #f78c6c; 26 | --dark-md-code-hl-function-color: #82aaff; 27 | --dark-md-code-hl-operator-color: #89ddff; 28 | --dark-md-code-hl-constant-color: #c792ea; 29 | --dark-md-code-hl-string-color: #c3e88d; 30 | --dark-md-code-hl-punctuation-color: #89ddff; 31 | --dark-md-code-hl-keyword-color: #c792ea; 32 | --dark-md-code-hl-variable-color: #e8f9f9; 33 | --dark-md-code-hl-comment-color: #546e7a; 34 | --dark-md-code-bg-color: #263238; 35 | --dark-md-code-fg-color: #ffcb6b; 36 | --dark-md-code-hl-variable-color: #82aaff; 37 | } 38 | 39 | @media (prefers-color-scheme: light) { 40 | .language-php > * { 41 | --md-code-hl-number-color: var(--light-md-code-hl-number-color); 42 | --md-code-hl-function-color: var(--light-md-code-hl-function-color); 43 | --md-code-hl-operator-color: var(--light-md-code-hl-operator-color); 44 | --md-code-hl-constant-color: var(--light-md-code-hl-constant-color); 45 | --md-code-hl-string-color: var(--light-md-code-hl-string-color); 46 | --md-code-hl-punctuation-color: var(--light-md-code-hl-punctuation-color); 47 | --md-code-hl-keyword-color: var(--light-md-code-hl-keyword-color); 48 | --md-code-hl-variable-color: var(--light-md-code-hl-variable-color); 49 | --md-code-hl-comment-color: var(--light-md-code-hl-comment-color); 50 | --md-code-bg-color: var(--light-md-code-bg-color); 51 | --md-code-fg-color: var(--light-md-code-fg-color); 52 | } 53 | 54 | .language-php .na { 55 | --md-code-hl-variable-color: var(--light-md-code-hl-variable-color); 56 | } 57 | } 58 | 59 | [data-md-color-media="(prefers-color-scheme: light)"] .language-php > * { 60 | --md-code-hl-number-color: var(--light-md-code-hl-number-color); 61 | --md-code-hl-function-color: var(--light-md-code-hl-function-color); 62 | --md-code-hl-operator-color: var(--light-md-code-hl-operator-color); 63 | --md-code-hl-constant-color: var(--light-md-code-hl-constant-color); 64 | --md-code-hl-string-color: var(--light-md-code-hl-string-color); 65 | --md-code-hl-punctuation-color: var(--light-md-code-hl-punctuation-color); 66 | --md-code-hl-keyword-color: var(--light-md-code-hl-keyword-color); 67 | --md-code-hl-variable-color: var(--light-md-code-hl-variable-color); 68 | --md-code-hl-comment-color: var(--light-md-code-hl-comment-color); 69 | --md-code-bg-color: var(--light-md-code-bg-color); 70 | --md-code-fg-color: var(--light-md-code-fg-color); 71 | } 72 | 73 | [data-md-color-media="(prefers-color-scheme: light)"] .language-php .na { 74 | --md-code-hl-variable-color: var(--light-md-code-hl-variable-color); 75 | } 76 | 77 | @media (prefers-color-scheme: dark) { 78 | .language-php > * { 79 | --md-code-hl-number-color: var(--dark-md-code-hl-number-color); 80 | --md-code-hl-function-color: var(--dark-md-code-hl-function-color); 81 | --md-code-hl-operator-color: var(--dark-md-code-hl-operator-color); 82 | --md-code-hl-constant-color: var(--dark-md-code-hl-constant-color); 83 | --md-code-hl-string-color: var(--dark-md-code-hl-string-color); 84 | --md-code-hl-punctuation-color: var(--dark-md-code-hl-punctuation-color); 85 | --md-code-hl-keyword-color: var(--dark-md-code-hl-keyword-color); 86 | --md-code-hl-variable-color: var(--dark-md-code-hl-variable-color); 87 | --md-code-hl-comment-color: var(--dark-md-code-hl-comment-color); 88 | --md-code-bg-color: var(--dark-md-code-bg-color); 89 | --md-code-fg-color: var(--dark-md-code-fg-color); 90 | } 91 | 92 | .language-php .na { 93 | --md-code-hl-variable-color: var(--dark-md-code-hl-variable-color); 94 | } 95 | } 96 | 97 | [data-md-color-media="(prefers-color-scheme: dark)"] .language-php > * { 98 | --md-code-hl-number-color: var(--dark-md-code-hl-number-color); 99 | --md-code-hl-function-color: var(--dark-md-code-hl-function-color); 100 | --md-code-hl-operator-color: var(--dark-md-code-hl-operator-color); 101 | --md-code-hl-constant-color: var(--dark-md-code-hl-constant-color); 102 | --md-code-hl-string-color: var(--dark-md-code-hl-string-color); 103 | --md-code-hl-punctuation-color: var(--dark-md-code-hl-punctuation-color); 104 | --md-code-hl-keyword-color: var(--dark-md-code-hl-keyword-color); 105 | --md-code-hl-variable-color: var(--dark-md-code-hl-variable-color); 106 | --md-code-hl-comment-color: var(--dark-md-code-hl-comment-color); 107 | --md-code-bg-color: var(--dark-md-code-bg-color); 108 | --md-code-fg-color: var(--dark-md-code-fg-color); 109 | } 110 | 111 | [data-md-color-media="(prefers-color-scheme: dark)"] .language-php .na { 112 | --md-code-hl-variable-color: var(--dark-md-code-hl-variable-color); 113 | } 114 | -------------------------------------------------------------------------------- /docs/getting-started/clocks.md: -------------------------------------------------------------------------------- 1 | # Clocks 2 | 3 | !!! success "Dependency injection" 4 | A `Clock` should be treated as a singleton and instanciated once in your program and then passed as argument everywhere you need it in your program. 5 | 6 | ## Live 7 | 8 | This is the clock you should use in your programs. By default it's set to the [UTC](https://en.wikipedia.org/wiki/UTC%2B00:00) [timezone](timezones.md) (no matter the configuration of your machine). 9 | 10 | To access the current time you would do: 11 | 12 | ```php 13 | use Innmind\TimeContinuum\{ 14 | Clock, 15 | PointInTime, 16 | }; 17 | 18 | $clock = Clock::live(); 19 | $point = $clock->now(); // instance of PointInTime 20 | echo $point->toString(); // prints something like 2024-11-24T12:34:25+00:00 21 | ``` 22 | 23 | And to build a [`PointInTime`](points-in-time.md) back from a `string`: 24 | 25 | ```php 26 | use Innmind\TimeContinuum\{ 27 | Clock, 28 | Format, 29 | PointInTime, 30 | }; 31 | use Innmind\Immutable\Maybe; 32 | 33 | $time = '2024-11-24T12:34:25+00:00'; 34 | 35 | $clock = Clock::live(); 36 | $at = $clock->at($time, Format::iso8601()); // instance of Maybe 37 | $point = $at->match( 38 | static fn(PointInTime $point) => $point, 39 | static fn() => null, 40 | ); 41 | ``` 42 | 43 | The `at` method returns a [`Maybe` monad](https://innmind.org/Immutable/structures/maybe/) that may contain a `PointInTime`. This is in case the `#!php $time` variable contains a value that doesn't correspond to the specified format (here `ISO8601`). 44 | 45 | This means that the `#!php $point` variable here is an instance of `PointInTime` because the `#!php $time` value is valid. If it's invalid then `#!php $point` is `#!php null`. 46 | 47 | ## Logger 48 | 49 | This clock will create a log everytime you call `#!php ->now()` or `#!php ->at()`. 50 | 51 | To build this clock you need another clock (typically a live one) and a [PSR logger](https://packagist.org/packages/psr/log): 52 | 53 | ```php 54 | use Innmind\TimeContinuum\Clock; 55 | use Psr\Log\LoggerInterface; 56 | 57 | $clock = Clock::logger( 58 | Clock::live(), 59 | /* any instance of LoggerInterface (1) */ 60 | ); 61 | ``` 62 | 63 | 1. Like [monolog](https://packagist.org/packages/monolog/monolog) for example. 64 | 65 | You can then use `#!php $clock` like any other clock. 66 | 67 | ## Frozen 68 | 69 | This clock is only useful when testing your program. It allows to specify the point in time at which your programs run. 70 | 71 | This way you can test your program for special scenarii like a leap year, daylight saving time and so on... 72 | 73 | ```php 74 | use Innmind\TimeContinuum\{ 75 | Clock, 76 | Format, 77 | }; 78 | 79 | $clock = Clock::live() 80 | ->at('2024-11-24T12:34:25+00:00', Format::iso8601()) 81 | ->match( 82 | Clock::frozen(...), 83 | static fn() => throw new \LogicException('Specify a valid date'), 84 | ); 85 | ``` 86 | 87 | ??? warning 88 | Bear in mind that `#!php $clock->now()` will always return the same object. This means that if your program rely on calculating an [elapsed period](elapsed-period.md) it will always return `#!php 0`. If run in a loop you may end up with an inifinite one. 89 | -------------------------------------------------------------------------------- /docs/getting-started/elapsed-period.md: -------------------------------------------------------------------------------- 1 | # Elapsed period 2 | 3 | This is the number of microseconds between two [points in time](points-in-time.md). 4 | 5 | ```php 6 | use Innmind\TimeContinuum\Clock; 7 | 8 | $clock = Clock::live(); 9 | $start = $clock->now(); 10 | // do some stuff 11 | $end = $clock->now(); 12 | 13 | $elapsed = $end->elapsedSince($start); 14 | ``` 15 | 16 | `$elapsed` is an instance of `#!php Innmind\TimeContinuum\ElapsedPeriod`. 17 | 18 | This is especially useful when working with network I/O to check for timeouts. 19 | 20 | !!! success "" 21 | This example uses a monotonic clock internally to avoid the problem where the server clock re-synchronize and jump back in time. In this case `$end` is _technically_ before `$start` but the elapsed period is still a positive `int`. 22 | 23 | ??? info 24 | Bear in mind that the monotonic clock only works on `PointInTime`s returned by `$clock->now()`. If `->elapsedSince()` is called on points returned by `$clock->at()` it will compare the number of microseconds since epoch. 25 | 26 | ??? warning 27 | `ElapsedPeriod` uses an `int` internally to represent the number of microseconds. If you compare two points far away between each other you may end up with an overflow. 28 | 29 | Since this feature is mainly intended for network I/O handling the points should be close from one another. So this bug won't be addresed at least for now. 30 | -------------------------------------------------------------------------------- /docs/getting-started/formats.md: -------------------------------------------------------------------------------- 1 | # Formats 2 | 3 | A `Format` is a representation on how to convert a [`PointInTime`](points-in-time.md) to a `string`, or vice versa. 4 | 5 | By default this library comes with these formats: 6 | 7 | ```php 8 | use Innmind\TimeContinuum\Format; 9 | 10 | Format::cookie(); 11 | Format::iso8601(); 12 | Format::rfc1036(); 13 | Format::rfc1123(); 14 | Format::rfc2822(); 15 | Format::rfc822(); 16 | Format::rfc850(); 17 | Format::rss(); 18 | ``` 19 | 20 | Formats are wrapped in an object in order to give them a name. When used in your application you can reference these names instead of duplicating the strings everywhere. 21 | 22 | ## Convert to a string 23 | 24 | ```php 25 | use Innmind\TimeContinuum\{ 26 | Clock, 27 | Format, 28 | }; 29 | 30 | echo Clock::live() 31 | ->now() 32 | ->format(Format::iso8601()); 33 | ``` 34 | 35 | This would print something like `#!php '2024-11-24T14:50:00+00:00'`. 36 | 37 | ## Convert from a string 38 | 39 | ```php 40 | use Innmind\TimeContinuum\{ 41 | Clock, 42 | Format, 43 | PointInTime, 44 | }; 45 | 46 | $point = Clock::live() 47 | ->at('some string', Format::iso8601()) 48 | ->match( 49 | static fn(PointInTime $point) => $point, 50 | static fn() => null, 51 | ); 52 | ``` 53 | 54 | Here `#!php $point` is `#!php null` because `#!php 'some string'` is not a valid date. 55 | 56 | ## Define your own format 57 | 58 | If you want to use your own format you can do this via `#!php Format::of('date format')`. The `string` can be anything accepted by [`#!php \DateTimeImmutable::format()`](https://www.php.net/manual/en/datetime.format.php). 59 | 60 | You're encouraged to statically define these formats somewhere in your program like this: 61 | 62 | === "Static method" 63 | ```php 64 | use Innmind\TimeContinuum\Format; 65 | 66 | final class MyFormats 67 | { 68 | public static function iso8601WithMicroseconds(): Format 69 | { 70 | return Format::of('Y-m-dT:H:i:s.uP'); 71 | } 72 | } 73 | ``` 74 | 75 | === "Enum" 76 | ```php 77 | use Innmind\TimeContinuum\Format; 78 | 79 | enum MyFormats: string implements Format\Custom 80 | { 81 | case iso8601WithMicroseconds = 'Y-m-dT:H:i:s.uP'; 82 | 83 | public function normalize(): Format 84 | { 85 | return Format::of($this->value); 86 | } 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/getting-started/index.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | ## Installation 4 | 5 | ```sh 6 | composer require innmind/time-continuum 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/getting-started/periods.md: -------------------------------------------------------------------------------- 1 | # Periods 2 | 3 | ## Go forward in time 4 | 5 | ```php 6 | use Innmind\TimeContinuum\{ 7 | Clock, 8 | Period, 9 | }; 10 | 11 | $future = Clock::live() 12 | ->now() 13 | ->goForward( 14 | Period::day(1) 15 | ->and(Period::hour(12)), 16 | ); 17 | ``` 18 | 19 | `#!php $future` is now a day and a half ahead of the current time. 20 | 21 | ## Go back in time 22 | 23 | ```php 24 | use Innmind\TimeContinuum\{ 25 | Clock, 26 | Period, 27 | }; 28 | 29 | $past = Clock::live() 30 | ->now() 31 | ->goBack( 32 | Period::day(1) 33 | ->and(Period::hour(12)), 34 | ); 35 | ``` 36 | 37 | `#!php $past` is now a day and a half behind of the current time. 38 | 39 | ## Compare to an elapsed period 40 | 41 | ```php 42 | use Innmind\TimeContinuum\{ 43 | Clock, 44 | Period, 45 | }; 46 | 47 | $clock = Clock::live(); 48 | $start = $clock->now(); 49 | // do some stuff 50 | $clock 51 | ->now() 52 | ->elapsedSince($start) 53 | ->longerThan( 54 | Period::second(10)->asElapsedPeriod(), 55 | ); // returns a bool 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/getting-started/points-in-time.md: -------------------------------------------------------------------------------- 1 | # Points in time 2 | 3 | See the [clocks](clocks.md) to learn to have access to these objects. 4 | 5 | All examples below use the `#!php $point` variable that reference an instance of `#!php Innmind\TimeContinuum\PointInTime`. 6 | 7 | ## Year 8 | 9 | ```php 10 | $point->year()->toInt(); 11 | ``` 12 | 13 | This will return the year as an `int`. 14 | 15 | ```php 16 | $point->year()->numberOfDays(); 17 | ``` 18 | 19 | This returns `#!php 365` or `#!php 366` on leap years. 20 | 21 | ## Month 22 | 23 | ```php 24 | $point->month()->ofYear(); 25 | ``` 26 | 27 | This returns a value from the enum `#!php Innmind\TimeContinuum\Calendar\Month`. 28 | 29 | ```php 30 | $point->month()->numberOfDays(); 31 | ``` 32 | 33 | This returns an `int` between `#!php 28` and `#!php 31`. 34 | 35 | ## Day 36 | 37 | ```php 38 | $point->day()->ofYear(); 39 | ``` 40 | 41 | This returns an `int` between `#!php 0` and `#!php 365`. 42 | 43 | ```php 44 | $point->day()->ofMonth(); 45 | ``` 46 | 47 | This returns an `int` between `#!php 1` and `#!php 31`. 48 | 49 | ```php 50 | $point->day()->ofWeek(); 51 | ``` 52 | 53 | This returns a value from the enum `#!php Innmind\TimeContinuum\Calendar\Day`. 54 | 55 | ## Hour 56 | 57 | ```php 58 | $point->hour()->toInt(); 59 | ``` 60 | 61 | This returns an `int` between `#!php 0` and `#!php 23`. 62 | 63 | ## Minute 64 | 65 | ```php 66 | $point->minute()->toInt(); 67 | ``` 68 | 69 | This returns an `int` between `#!php 0` and `#!php 59`. 70 | 71 | ## Second 72 | 73 | ```php 74 | $point->second()->toInt(); 75 | ``` 76 | 77 | This returns an `int` between `#!php 0` and `#!php 59`. 78 | 79 | ## Millisecond 80 | 81 | ```php 82 | $point->millisecond()->toInt(); 83 | ``` 84 | 85 | This returns an `int` between `#!php 0` and `#!php 999`. 86 | 87 | ## Microsecond 88 | 89 | ```php 90 | $point->microsecond()->toInt(); 91 | ``` 92 | 93 | This returns an `int` between `#!php 0` and `#!php 999`. 94 | 95 | ## Offset 96 | 97 | ```php 98 | $offset = $point->offset(); 99 | $hours = $offset->hours(); 100 | $minutes = $offset->minutes(); 101 | ``` 102 | 103 | `$hours` is an `int` between `#!php -12` and `#!php 14`. `$minutes` is an `int` between `#!php 0` and `#!php 59` but usually is either `#!php 0`, `#!php 15`, `#!php 30` or `#!php 45`. 104 | 105 | ## Comparing points 106 | 107 | ```php 108 | $point->aheadOf($anotherPoint); 109 | $point->equals($anotherPoint); 110 | ``` 111 | 112 | Both methods return a `bool`. 113 | -------------------------------------------------------------------------------- /docs/getting-started/time-offsets.md: -------------------------------------------------------------------------------- 1 | # Time offsets 2 | 3 | This is a value retrieved from a [point in time](points-in-time.md) and is expressed in a number of hours and minutes. 4 | 5 | ```php 6 | use Innmind\TimeContinuum\Clock; 7 | 8 | $offset = Clock::live()->now()->offset(); 9 | ``` 10 | 11 | - `#!php $offset->hours()` returns an `int` between `#!php -12` and `#!php 14` 12 | - `#!php $offset->minutes()` returns an `int` between `#!php 0` and `#!php 59` 13 | 14 | Via this object you cannot know in which [timezone](timezones.md) you're in. If you need to keep track of this you should model this in your program and not rely on this information. 15 | 16 | When you have access to a `PointInTime` you can change its offset like this: 17 | 18 | ```php 19 | use Innmind\TimeContinuum\{ 20 | Clock, 21 | Offset, 22 | }; 23 | 24 | $utc = Clock::live()->now(); 25 | $newYork = $utc->changeOffset(Offset::minus(5)); 26 | ``` 27 | 28 | If `#!php $utc` represents `#!php '2024-11-24T14:25:00+00:00'` then `#!php $newYork` represents `#!php '2024-11-24T09:25:00-05:00'`. 29 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - navigation 4 | - toc 5 | --- 6 | 7 | # Welcome to TimeContinnum 8 | 9 | TimeContinuum is a time abstraction library. 10 | 11 | The goal is to provide a safe way to manipulate time in your programs. 12 | 13 | It achieves this via: 14 | 15 | - an expressive object oriented API to avoid _magic strings_ 16 | - a clear separation between what's mutable and immutable 17 | 18 | ??? example "Sneak peek" 19 | ```php 20 | use Innmind\TimeContinuum\{ 21 | Clock, 22 | Format, 23 | }; 24 | 25 | $clock = Clock::live(); 26 | $start = $clock->now(); 27 | // do some stuff 28 | $end = $clock->now(); 29 | $took = $end 30 | ->elapsedSince($start) 31 | ->asPeriod(); 32 | 33 | \printf( 34 | 'The script ended at %s and it took %s second(s), %s millisecond(s) %s microsecond(s).', 35 | $end->format(Format::iso8601()), 36 | $took->seconds(), 37 | $took->milliseconds(), 38 | $took->microseconds(), 39 | ); 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/preface/philosophy.md: -------------------------------------------------------------------------------- 1 | # Philosophy 2 | 3 | TimeContinuum is designed around these objectives: 4 | 5 | - an expressive API through a clear naming 6 | - to handle time with precision 7 | - simplify common use cases 8 | 9 | Time is a tricky business full of gotchas. It's easy to make mistake that go undetected for a while. 10 | 11 | The first step to avoid mistakes is to provide a good naming. Here, each component of time is accessible via expressive methods to give the least possible room to error. 12 | 13 | One of the hardest aspect of time is to distinguish what can change from what can't. Once again naming helps but it's not enough. This package creates a _frontier_ between this two worlds. This is expressed via an `immutable` annotation (understood by [Psalm](https://psalm.dev)) on what cannot change, and the rest may change. 14 | -------------------------------------------------------------------------------- /docs/preface/terminology.md: -------------------------------------------------------------------------------- 1 | # Terminology 2 | 3 | ## Clock 4 | 5 | A `Clock` is the root object to access time. Like a real clock, each time you demand the time it will provide a new value. This is part of the world that can change. 6 | 7 | Unlike real clocks, in a program we often have the use case to convert a `string` back to a [time](#point-in-time) representation (1). Clocks provide a method for this. You can think of it as a factory. Even though this is done by a clock this method is determinist, meaning that you'll always have the same result for the same inputs. 8 | {.annotate} 9 | 10 | 1. Such as reading a value from a database. 11 | 12 | ## Point in time 13 | 14 | As its name suggest it represents a fixed point in time on Earth. It represents time down to the microsecond. It also stores the time offset from [UTC](https://en.wikipedia.org/wiki/UTC%2B00:00). 15 | 16 | These objects are generated by clocks. 17 | 18 | Once an object is created it can no longer change. But you can still create objects relative to it by specifying a [period](#period) and the direction. 19 | 20 | ## Timezone 21 | 22 | A timezone represents a time offset from [UTC](https://en.wikipedia.org/wiki/UTC%2B00:00) for a set of cities on Earth (1). 23 | {.annotate} 24 | 25 | 1. For example `Europe/Paris`. 26 | 27 | Due to politics a city may change its offset at any time (1). And for economic reasons some countries change their offset each year, a process called "daylight saving time" (2). 28 | {.annotate} 29 | 30 | 1. This means some points in time don't exist in certain countries or exist twice. 31 | 2. Usually they increase/decrease their offset by 1 or 2 hours. 32 | 33 | Because asking the offset for a timezone will yield different results depending on when you ask for it, it's part of the world that changes. This means that timezones are handled at the [clock](#clock) level. 34 | 35 | ## Period 36 | 37 | It represents the time between 2 points in time. It can be defined by a number of: 38 | 39 | - years 40 | - months 41 | - days 42 | - hours 43 | - minutes 44 | - seconds 45 | - milliseconds 46 | - microseconds 47 | 48 | Each of this component has to be a positive integer. It's when you apply it to a point in time that you decide if you want to go forward or back in time. 49 | 50 | Since these objects are not tied to a particular point in time they're immutable. 51 | 52 | ## Elapsed period 53 | 54 | It represents the number of microseconds between two points in time. 55 | 56 | Once an object is created it cannot change. 57 | 58 | This is useful to compute the time your program took to accomplish a task. Operation often used when dealing with network I/O to handle timeouts. 59 | 60 | ## Format 61 | 62 | A format defines a way to represent a point in time as a `string` (1). 63 | {.annotate} 64 | 65 | 1. Usually to store the value to a database or print it in a user interface. 66 | 67 | It's declared as an object to give meaning to the string format it encapsulate. The intent is to declare the string format once in an object in your program and reuse this object everywhere. This allows to attach a _name_ to the format. 68 | 69 | This is an attempt to solve the problem (1) where the same format is duplicated everywhere in a program. 70 | {.annotate} 71 | 72 | 1. We can see this practive in a lot of programs. 73 | -------------------------------------------------------------------------------- /docs/upgrade/v3-to-v4.md: -------------------------------------------------------------------------------- 1 | # From v3 to v4 2 | 3 | All namespaces are relative to `\Innmind\TimeContinuum\`. 4 | 5 | These are the main changes, for an extensive list of changes go to the [changelog](https://github.com/Innmind/TimeContinuum/blob/develop/CHANGELOG.md). 6 | 7 | ## Clocks 8 | 9 | ### Live clock 10 | 11 | === "Before" 12 | ```php 13 | $clock = new Earth\Clock; 14 | ``` 15 | 16 | === "After" 17 | ```php 18 | $clock = Clock::live(); 19 | ``` 20 | 21 | ### Logger 22 | 23 | === "Before" 24 | ```php 25 | $clock = new Logger\Clock( 26 | new Earth\Clock, 27 | /* an instance of Psr\Log\LoggerInterface */ 28 | ); 29 | ``` 30 | 31 | === "After" 32 | ```php 33 | $clock = Clock::logger( 34 | Clock::live(), 35 | /* an instance of Psr\Log\LoggerInterface */ 36 | ); 37 | ``` 38 | 39 | ### Frozen 40 | 41 | === "Before" 42 | ```php 43 | $clock = new Earth\FrozenClock( 44 | new Earth\PointInTime\PointInTime('some date'), 45 | ); 46 | ``` 47 | 48 | === "After" 49 | ```php 50 | $clock = Clock::live() 51 | ->at('some date', Format::iso8601()) 52 | ->match( 53 | Clock::frozen(...), 54 | static fn() => throw new \LogicException('Use a valid date'), 55 | ); 56 | ``` 57 | 58 | ## Timezones 59 | 60 | === "Before" 61 | ```php 62 | $clock = new Earth\Clock( 63 | new Earth\Timezone\Europe\Paris, 64 | ); 65 | ``` 66 | 67 | === "After" 68 | ```php 69 | $clock = Clock::live()->switch( 70 | static fn(Timezones $timezones) => $timezones 71 | ->europe() 72 | ->paris(), 73 | ); 74 | ``` 75 | 76 | ## Access point in time offset 77 | 78 | === "Before" 79 | ```php 80 | $clock->now()->timezone()->hours(); 81 | $clock->now()->timezone()->minutes(); 82 | ``` 83 | 84 | === "After" 85 | ```php 86 | $clock->now()->offset()->hours(); 87 | $clock->now()->offset()->minutes(); 88 | ``` 89 | 90 | ## Modify point in time offset 91 | 92 | === "Before" 93 | ```php 94 | $clock->now()->changeTimezone(new Earth\Timezone\UTC(2, 0)); 95 | ``` 96 | 97 | === "After" 98 | ```php 99 | $clock->now()->changeOffset(Offset::plus(2, 0)); 100 | ``` 101 | 102 | ## Periods 103 | 104 | === "Before" 105 | ```php 106 | new Earth\Period\Year(42); 107 | new Earth\Period\Month(42); 108 | new Earth\Period\Day(42); 109 | new Earth\Period\Hour(42); 110 | new Earth\Period\Minute(42); 111 | new Earth\Period\Second(42); 112 | new Earth\Period\Millisecond(42); 113 | ``` 114 | 115 | === "After" 116 | ```php 117 | Period::year(42); 118 | Period::month(42); 119 | Period::day(42); 120 | Period::hour(42); 121 | Period::minute(42); 122 | Period::second(42); 123 | Period::millisecond(42); 124 | ``` 125 | 126 | ## Formats 127 | 128 | === "Before" 129 | ```php 130 | $clock->now()->format(new Earth\Format\ISO8601); 131 | ``` 132 | 133 | === "After" 134 | ```php 135 | $clock->now()->format(Format::iso8601()); 136 | ``` 137 | 138 | ## Elapsed periods 139 | 140 | === "Before" 141 | ```php 142 | $elapsed = /* any instance of ElapsedPeriod */ 143 | $milliseconds = $elapsed->milliseconds(); 144 | ``` 145 | 146 | === "After" 147 | ```php 148 | $elapsed = /* any instance of ElapsedPeriod */ 149 | $period = $elapsed->asPeriod(); 150 | $milliseconds = Period\Value::day->seconds($period->days()); 151 | $milliseconds += Period\Value::hour->seconds($period->hours()); 152 | $milliseconds += Period\Value::minute->seconds($period->minutes()); 153 | $milliseconds *= 1_000; 154 | $milliseconds += $period->milliseconds(); 155 | ``` 156 | -------------------------------------------------------------------------------- /fixtures/Period.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public static function any(): Set 15 | { 16 | return Set\Composite::immutable( 17 | Model::of(...), 18 | Set\Integers::between(0, 9999), 19 | Set\Integers::between(0, 12), 20 | Set\Integers::between(0, 31), 21 | Set\Integers::between(0, 23), 22 | Set\Integers::between(0, 59), 23 | Set\Integers::between(0, 59), 24 | Set\Integers::between(0, 999), 25 | Set\Integers::between(0, 999), 26 | )->take(100); 27 | } 28 | 29 | /** 30 | * @return Set 31 | */ 32 | public static function anyNumberOfYear(): Set 33 | { 34 | return Set\Decorate::immutable( 35 | Model::year(...), 36 | Set\Integers::between(0, 9999), 37 | )->take(100); 38 | } 39 | 40 | /** 41 | * @return Set 42 | */ 43 | public static function lessThanAYear(): Set 44 | { 45 | return Set\Composite::immutable( 46 | static function($day, $hour, $minute, $second, $millisecond, $microsecond): Model { 47 | return Model::of( 48 | 0, 49 | 0, 50 | $day, 51 | $hour, 52 | $minute, 53 | $second, 54 | $millisecond, 55 | $microsecond, 56 | ); 57 | }, 58 | Set\Integers::between(0, 364), 59 | Set\Integers::between(0, 23), 60 | Set\Integers::between(0, 59), 61 | Set\Integers::between(0, 59), 62 | Set\Integers::between(0, 999), 63 | Set\Integers::between(0, 999), 64 | )->take(100); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /fixtures/PointInTime.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public static function any(): Set 15 | { 16 | return self::yearRange(0, 9999)->take(100); 17 | } 18 | 19 | /** 20 | * @param string $point String representation of a date 21 | * @return Set 22 | */ 23 | public static function after(string $point): Set 24 | { 25 | $lower = Model::at(new \DateTimeImmutable($point)); 26 | 27 | return self::yearRange($lower->year()->toInt(), 9999) 28 | ->filter(static function($point) use ($lower): bool { 29 | return $point->aheadOf($lower); 30 | }) 31 | ->take(100); 32 | } 33 | 34 | /** 35 | * @param string $point String representation of a date 36 | * @return Set 37 | */ 38 | public static function before(string $point): Set 39 | { 40 | $upper = Model::at(new \DateTimeImmutable($point)); 41 | 42 | return self::yearRange(0, $upper->year()->toInt()) 43 | ->filter(static function($point) use ($upper): bool { 44 | return $upper->aheadOf($point); 45 | }) 46 | ->take(100); 47 | } 48 | 49 | /** 50 | * @return Set 51 | */ 52 | private static function yearRange(int $lowerBound, int $upperBound): Set 53 | { 54 | return Set\Composite::immutable( 55 | static function( 56 | int|string ...$components, 57 | ): Model { 58 | return Model::at(new \DateTimeImmutable(\sprintf( 59 | '%02s-%02d-%02dT%02d:%02d:%02d.%03d%03d%s%02d:%s', 60 | ...$components, 61 | ))); 62 | }, 63 | Set\Integers::between($lowerBound, $upperBound), 64 | Set\Integers::between(1, 12), 65 | Set\Integers::between(1, 31), 66 | Set\Integers::between(0, 23), 67 | Set\Integers::between(0, 59), 68 | Set\Integers::between(0, 59), 69 | Set\Integers::between(0, 999), 70 | Set\Integers::between(0, 999), 71 | Set\Elements::of('-', '+'), 72 | Set\Integers::between(0, 12), 73 | Set\Elements::of('00', '15', '30', '45'), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Innmind/TimeContinuum 2 | repo_name: Innmind/TimeContinuum 3 | 4 | nav: 5 | - Home: index.md 6 | - Preface: 7 | - Philosophy: preface/philosophy.md 8 | - Terminology: preface/terminology.md 9 | - Getting started: 10 | - getting-started/index.md 11 | - Clocks: getting-started/clocks.md 12 | - Points in time: getting-started/points-in-time.md 13 | - Elapsed period: getting-started/elapsed-period.md 14 | - Time offsets: getting-started/time-offsets.md 15 | - Timezones: getting-started/timezones.md 16 | - Formats: getting-started/formats.md 17 | - Periods: getting-started/periods.md 18 | - Upgrade: 19 | - From v3 to v4: upgrade/v3-to-v4.md 20 | 21 | theme: 22 | name: material 23 | logo: assets/logo.svg 24 | favicon: assets/favicon.png 25 | font: false 26 | features: 27 | - content.code.copy 28 | - content.code.annotate 29 | - navigation.tracking 30 | - navigation.tabs 31 | - navigation.tabs.sticky 32 | - navigation.sections 33 | - navigation.expand 34 | - navigation.indexes 35 | - navigation.top 36 | - navigation.footer 37 | - search.suggest 38 | - search.highlight 39 | - content.action.edit 40 | palette: 41 | # Palette toggle for automatic mode 42 | - media: "(prefers-color-scheme)" 43 | toggle: 44 | icon: material/brightness-auto 45 | name: Switch to light mode 46 | primary: blue 47 | accent: deep orange 48 | # Palette toggle for light mode 49 | - media: "(prefers-color-scheme: light)" 50 | scheme: default 51 | toggle: 52 | icon: material/brightness-7 53 | name: Switch to dark mode 54 | primary: blue 55 | accent: deep orange 56 | # Palette toggle for dark mode 57 | - media: "(prefers-color-scheme: dark)" 58 | scheme: slate 59 | toggle: 60 | icon: material/brightness-4 61 | name: Switch to system preference 62 | primary: blue 63 | accent: deep orange 64 | 65 | markdown_extensions: 66 | - pymdownx.highlight: 67 | anchor_linenums: true 68 | line_spans: __span 69 | pygments_lang_class: true 70 | extend_pygments_lang: 71 | - name: php 72 | lang: php 73 | options: 74 | startinline: true 75 | - pymdownx.inlinehilite 76 | - pymdownx.snippets 77 | - attr_list 78 | - md_in_html 79 | - pymdownx.superfences 80 | - abbr 81 | - admonition 82 | - pymdownx.details: 83 | - pymdownx.tabbed: 84 | alternate_style: true 85 | - toc: 86 | permalink: true 87 | - footnotes 88 | - pymdownx.emoji: 89 | emoji_index: !!python/name:material.extensions.emoji.twemoji 90 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 91 | 92 | extra_css: 93 | - assets/stylesheets/extra.css 94 | 95 | plugins: 96 | - search 97 | - privacy 98 | 99 | extra: 100 | social: 101 | - icon: fontawesome/brands/github 102 | link: https://github.com/Innmind/TimeContinuum 103 | - icon: fontawesome/brands/x-twitter 104 | link: https://twitter.com/Baptouuuu 105 | -------------------------------------------------------------------------------- /proofs/clock.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 18 | )), 19 | static function($assert, $point) { 20 | $assert 21 | ->number($point->day()->ofYear()) 22 | ->int() 23 | ->greaterThanOrEqual(0) 24 | ->lessThanOrEqual(365); 25 | $assert 26 | ->number($point->day()->ofMonth()) 27 | ->int() 28 | ->greaterThanOrEqual(1) 29 | ->lessThanOrEqual(31); 30 | $assert 31 | ->number($point->hour()->toInt()) 32 | ->int() 33 | ->greaterThanOrEqual(0) 34 | ->lessThanOrEqual(23); 35 | $assert 36 | ->number($point->millisecond()->toInt()) 37 | ->int() 38 | ->greaterThanOrEqual(0) 39 | ->lessThanOrEqual(999); 40 | $assert 41 | ->number($point->microsecond()->toInt()) 42 | ->int() 43 | ->greaterThanOrEqual(0) 44 | ->lessThanOrEqual(999); 45 | $assert 46 | ->number($point->minute()->toInt()) 47 | ->int() 48 | ->greaterThanOrEqual(0) 49 | ->lessThanOrEqual(59); 50 | $assert 51 | ->number($point->second()->toInt()) 52 | ->int() 53 | ->greaterThanOrEqual(0) 54 | ->lessThanOrEqual(59); 55 | $assert 56 | ->number($point->month()->numberOfDays()) 57 | ->int() 58 | ->greaterThanOrEqual(28) 59 | ->lessThanOrEqual(31); 60 | $assert 61 | ->number($point->year()->numberOfDays()) 62 | ->int() 63 | ->greaterThanOrEqual(365) 64 | ->lessThanOrEqual(366); 65 | }, 66 | ); 67 | 68 | yield proof( 69 | 'Point in times precision is down to the microsecond', 70 | given(PointInTime::any()), 71 | static function($assert, $point) { 72 | $assert->false( 73 | $point->equals( 74 | $point->goBack(Period::microsecond(1)), 75 | ), 76 | ); 77 | $assert->false( 78 | $point->equals( 79 | $point->goForward(Period::microsecond(1)), 80 | ), 81 | ); 82 | 83 | $assert->true( 84 | $point->goForward(Period::microsecond(1))->aheadOf( 85 | $point, 86 | ), 87 | ); 88 | $assert->true( 89 | $point->aheadOf( 90 | $point->goBack(Period::microsecond(1)), 91 | ), 92 | ); 93 | }, 94 | ); 95 | 96 | yield proof( 97 | 'Clock::at() returns nothing for invalid strings', 98 | given( 99 | Set\Unicode::strings(), 100 | Set\Elements::of( 101 | Format::cookie(), 102 | Format::iso8601(), 103 | Format::rfc1036(), 104 | Format::rfc1123(), 105 | Format::rfc2822(), 106 | Format::rfc822(), 107 | Format::rfc850(), 108 | Format::rss(), 109 | Format::w3c(), 110 | ), 111 | ), 112 | static fn($assert, $string, $format) => $assert->null( 113 | Clock::live()->at($string, $format)->match( 114 | static fn($point) => $point, 115 | static fn() => null, 116 | ), 117 | ), 118 | ); 119 | 120 | yield proof( 121 | 'Clock::ofFormat()->at()', 122 | given( 123 | PointInTime::any(), 124 | Set\Elements::of( 125 | Format::iso8601(), 126 | new class implements Format\Custom { 127 | public function normalize(): Format 128 | { 129 | return Format::iso8601(); 130 | } 131 | }, 132 | ), 133 | ), 134 | static function($assert, $point, $format) { 135 | $parsed = Clock::live() 136 | ->ofFormat($format) 137 | ->at($point->format($format)) 138 | ->match( 139 | static fn($point) => $point->format($format), 140 | static fn() => null, 141 | ); 142 | 143 | $assert->not()->null($parsed); 144 | $assert->same( 145 | Clock::live() 146 | ->at($point->format($format), $format) 147 | ->match( 148 | static fn($point) => $point->format($format), 149 | static fn() => null, 150 | ), 151 | $parsed, 152 | ); 153 | }, 154 | ); 155 | 156 | yield proof( 157 | 'Each call to Clock::now() is ahead of the previous', 158 | given(Set\Nullable::of(Set\Integers::between(1, 2_000_000))), // up to 2 seconds 159 | static function($assert, $microsecond) { 160 | $clock = Clock::live(); 161 | $start = $clock->now(); 162 | 163 | if (\is_int($microsecond)) { 164 | \usleep($microsecond); 165 | } 166 | 167 | $assert->true( 168 | $clock->now()->aheadOf( 169 | $start, 170 | ), 171 | ); 172 | }, 173 | ); 174 | 175 | yield test( 176 | 'Clock::now() is equal to iteself', 177 | static function($assert) { 178 | $clock = Clock::live(); 179 | $now = $clock->now(); 180 | 181 | $assert->true($now->equals($now)); 182 | }, 183 | ); 184 | }; 185 | -------------------------------------------------------------------------------- /proofs/elapsedPeriod.php: -------------------------------------------------------------------------------- 1 | filter(static fn($start, $end) => $end > $start), 21 | static function( 22 | $assert, 23 | $startSeconds, 24 | $endSeconds, 25 | $startNanoseconds, 26 | $endNanoseconds, 27 | ) { 28 | $start = HighResolution::of($startSeconds, $startNanoseconds); 29 | $end = HighResolution::of($endSeconds, $endNanoseconds); 30 | 31 | $assert->true( 32 | $start 33 | ->elapsedSince($start) 34 | ->equals( 35 | Period::microsecond(0)->asElapsedPeriod(), 36 | ), 37 | ); 38 | $assert->true( 39 | $end 40 | ->elapsedSince($start) 41 | ->longerThan( 42 | Period::microsecond(1)->asElapsedPeriod(), 43 | ), 44 | ); 45 | }, 46 | ); 47 | 48 | yield proof( 49 | 'High resolution elapsed period within same second', 50 | given( 51 | Set\Integers::between(0, 999_999_999), 52 | Set\Integers::between(0, 999_999_999), 53 | )->filter(static fn($start, $end) => $end > $start && ($end - $start) > 1_000), 54 | static function( 55 | $assert, 56 | $startNanoseconds, 57 | $endNanoseconds, 58 | ) { 59 | $start = HighResolution::of(0, $startNanoseconds); 60 | $end = HighResolution::of(0, $endNanoseconds); 61 | 62 | $assert->true( 63 | $start 64 | ->elapsedSince($start) 65 | ->equals( 66 | Period::microsecond(0)->asElapsedPeriod(), 67 | ), 68 | ); 69 | $assert->true( 70 | $end 71 | ->elapsedSince($start) 72 | ->longerThan( 73 | Period::microsecond(1)->asElapsedPeriod(), 74 | ), 75 | ); 76 | }, 77 | ); 78 | 79 | yield proof( 80 | 'Elapsed period', 81 | given( 82 | Fixtures\PointInTime::any(), 83 | Set\Integers::above(1), 84 | ), 85 | static function($assert, $start, $microsecond) { 86 | $assert->true( 87 | $start 88 | ->elapsedSince($start) 89 | ->equals( 90 | Period::microsecond(0)->asElapsedPeriod(), 91 | ), 92 | ); 93 | $assert->true( 94 | $start 95 | ->goForward(Period::microsecond($microsecond)) 96 | ->elapsedSince($start) 97 | ->equals( 98 | Period::microsecond($microsecond)->asElapsedPeriod(), 99 | ), 100 | ); 101 | $assert->true( 102 | $start 103 | ->goForward(Period::microsecond($microsecond)) 104 | ->elapsedSince($start) 105 | ->longerThan( 106 | Period::microsecond(0)->asElapsedPeriod(), 107 | ), 108 | ); 109 | }, 110 | ); 111 | 112 | yield proof( 113 | 'Negative elapsed periods throws', 114 | given( 115 | Fixtures\PointInTime::any(), 116 | Set\Integers::above(1), 117 | ), 118 | static function($assert, $start, $microsecond) { 119 | $assert->throws( 120 | static fn() => $start 121 | ->goBack(Period::microsecond($microsecond)) 122 | ->elapsedSince($start), 123 | ); 124 | }, 125 | ); 126 | 127 | yield proof( 128 | 'Negative high resolution elapsed periods throws', 129 | given( 130 | Set\Integers::above(0), 131 | Set\Integers::above(0), 132 | Set\Integers::between(0, 999_999_999), 133 | Set\Integers::between(0, 999_999_999), 134 | )->filter(static fn($start, $end) => $end > $start), 135 | static function( 136 | $assert, 137 | $startSeconds, 138 | $endSeconds, 139 | $startNanoseconds, 140 | $endNanoseconds, 141 | ) { 142 | $start = HighResolution::of($startSeconds, $startNanoseconds); 143 | $end = HighResolution::of($endSeconds, $endNanoseconds); 144 | 145 | $assert->throws( 146 | static fn() => $start->elapsedSince($end), 147 | ); 148 | }, 149 | ); 150 | 151 | yield test( 152 | 'Regression elapsed period', 153 | static function($assert) { 154 | $before = ElapsedPeriod::of(1, 987, 564); 155 | $threshold = ElapsedPeriod::of(2, 0, 0); 156 | $after = ElapsedPeriod::of(2, 40, 375); 157 | 158 | $assert->false($before->longerThan($threshold)); 159 | $assert->true($after->longerThan($threshold)); 160 | 161 | $before = ElapsedPeriod::of(1, 0, 0); 162 | $threshold = ElapsedPeriod::of(2, 0, 0); 163 | $after = ElapsedPeriod::of(2, 0, 1); 164 | 165 | $assert->false($before->longerThan($threshold)); 166 | $assert->true($after->longerThan($threshold)); 167 | 168 | $threshold = ElapsedPeriod::of(2, 2, 0); 169 | $after = ElapsedPeriod::of(2, 1, 1); 170 | 171 | $assert->false($after->longerThan($threshold)); 172 | }, 173 | ); 174 | }; 175 | -------------------------------------------------------------------------------- /proofs/move/endOfDay.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 17 | )), 18 | static function($assert, $point) { 19 | $endOfDay = (new EndOfDay)($point); 20 | 21 | $assert->same( 22 | $point->year()->toInt(), 23 | $endOfDay->year()->toInt(), 24 | ); 25 | $assert->same( 26 | $point->month()->ofYear(), 27 | $endOfDay->month()->ofYear(), 28 | ); 29 | $assert->same( 30 | $point->day()->ofMonth(), 31 | $endOfDay->day()->ofMonth(), 32 | ); 33 | $assert->same( 34 | 23, 35 | $endOfDay->hour()->toInt(), 36 | ); 37 | $assert->same( 38 | 59, 39 | $endOfDay->minute()->toInt(), 40 | ); 41 | $assert->same( 42 | 59, 43 | $endOfDay->second()->toInt(), 44 | ); 45 | $assert->same( 46 | 999, 47 | $endOfDay->millisecond()->toInt(), 48 | ); 49 | $assert->same( 50 | 999, 51 | $endOfDay->microsecond()->toInt(), 52 | ); 53 | }, 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /proofs/move/endOfMonth.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 18 | )), 19 | static function($assert, $point) { 20 | $endOfMonth = (new EndOfMonth)($point); 21 | 22 | $assert->same( 23 | $point->year()->toInt(), 24 | $endOfMonth->year()->toInt(), 25 | ); 26 | $assert->same( 27 | $point->month()->ofYear(), 28 | $endOfMonth->month()->ofYear(), 29 | ); 30 | $assert->same( 31 | $point->month()->numberOfDays(), 32 | $endOfMonth->day()->ofMonth(), 33 | ); 34 | $assert->same( 35 | 23, 36 | $endOfMonth->hour()->toInt(), 37 | ); 38 | $assert->same( 39 | 59, 40 | $endOfMonth->minute()->toInt(), 41 | ); 42 | $assert->same( 43 | 59, 44 | $endOfMonth->second()->toInt(), 45 | ); 46 | $assert->same( 47 | 999, 48 | $endOfMonth->millisecond()->toInt(), 49 | ); 50 | $assert->same( 51 | 999, 52 | $endOfMonth->microsecond()->toInt(), 53 | ); 54 | }, 55 | ); 56 | 57 | yield test( 58 | 'End of month regression', 59 | static function($assert) { 60 | $point = Clock::live() 61 | ->at( 62 | '0100-02-27T18:35:40.134853+01:00', 63 | Format::of('Y-m-d\TH:i:s.uP'), 64 | ) 65 | ->match( 66 | static fn($point) => $point, 67 | static fn() => null, 68 | ); 69 | 70 | $assert->object($point); 71 | 72 | $endOfMonth = (new EndOfMonth)($point); 73 | 74 | $assert->same( 75 | $point->year()->toInt(), 76 | $endOfMonth->year()->toInt(), 77 | ); 78 | $assert->same( 79 | $point->month()->ofYear(), 80 | $endOfMonth->month()->ofYear(), 81 | ); 82 | $assert->same( 83 | $point->month()->numberOfDays(), 84 | $endOfMonth->day()->ofMonth(), 85 | ); 86 | $assert->same( 87 | 23, 88 | $endOfMonth->hour()->toInt(), 89 | ); 90 | $assert->same( 91 | 59, 92 | $endOfMonth->minute()->toInt(), 93 | ); 94 | $assert->same( 95 | 59, 96 | $endOfMonth->second()->toInt(), 97 | ); 98 | $assert->same( 99 | 999, 100 | $endOfMonth->millisecond()->toInt(), 101 | ); 102 | $assert->same( 103 | 999, 104 | $endOfMonth->microsecond()->toInt(), 105 | ); 106 | }, 107 | ); 108 | }; 109 | -------------------------------------------------------------------------------- /proofs/move/endOfYear.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 17 | )), 18 | static function($assert, $point) { 19 | $endOfYear = (new EndOfYear)($point); 20 | 21 | $assert->same( 22 | $point->year()->toInt(), 23 | $endOfYear->year()->toInt(), 24 | ); 25 | $assert->same( 26 | 12, 27 | $endOfYear->month()->ofYear()->toInt(), 28 | ); 29 | $assert->same( 30 | 31, 31 | $endOfYear->day()->ofMonth(), 32 | ); 33 | $assert->same( 34 | 23, 35 | $endOfYear->hour()->toInt(), 36 | ); 37 | $assert->same( 38 | 59, 39 | $endOfYear->minute()->toInt(), 40 | ); 41 | $assert->same( 42 | 59, 43 | $endOfYear->second()->toInt(), 44 | ); 45 | $assert->same( 46 | 999, 47 | $endOfYear->millisecond()->toInt(), 48 | ); 49 | $assert->same( 50 | 999, 51 | $endOfYear->microsecond()->toInt(), 52 | ); 53 | }, 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /proofs/move/startOfDay.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 17 | )), 18 | static function($assert, $point) { 19 | $startOfDay = (new StartOfDay)($point); 20 | 21 | $assert->same( 22 | $point->year()->toInt(), 23 | $startOfDay->year()->toInt(), 24 | ); 25 | $assert->same( 26 | $point->month()->ofYear(), 27 | $startOfDay->month()->ofYear(), 28 | ); 29 | $assert->same( 30 | $point->day()->ofMonth(), 31 | $startOfDay->day()->ofMonth(), 32 | ); 33 | $assert->same( 34 | 0, 35 | $startOfDay->hour()->toInt(), 36 | ); 37 | $assert->same( 38 | 0, 39 | $startOfDay->minute()->toInt(), 40 | ); 41 | $assert->same( 42 | 0, 43 | $startOfDay->second()->toInt(), 44 | ); 45 | $assert->same( 46 | 0, 47 | $startOfDay->millisecond()->toInt(), 48 | ); 49 | $assert->same( 50 | 0, 51 | $startOfDay->microsecond()->toInt(), 52 | ); 53 | }, 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /proofs/move/startOfMonth.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 17 | )), 18 | static function($assert, $point) { 19 | $startOfMonth = (new StartOfMonth)($point); 20 | 21 | $assert->same( 22 | $point->year()->toInt(), 23 | $startOfMonth->year()->toInt(), 24 | ); 25 | $assert->same( 26 | $point->month()->ofYear(), 27 | $startOfMonth->month()->ofYear(), 28 | ); 29 | $assert->same( 30 | 1, 31 | $startOfMonth->day()->ofMonth(), 32 | ); 33 | $assert->same( 34 | 0, 35 | $startOfMonth->hour()->toInt(), 36 | ); 37 | $assert->same( 38 | 0, 39 | $startOfMonth->minute()->toInt(), 40 | ); 41 | $assert->same( 42 | 0, 43 | $startOfMonth->second()->toInt(), 44 | ); 45 | $assert->same( 46 | 0, 47 | $startOfMonth->millisecond()->toInt(), 48 | ); 49 | $assert->same( 50 | 0, 51 | $startOfMonth->microsecond()->toInt(), 52 | ); 53 | }, 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /proofs/move/startOfYear.php: -------------------------------------------------------------------------------- 1 | Clock::live()->now()), 17 | )), 18 | static function($assert, $point) { 19 | $startOfYear = (new StartOfYear)($point); 20 | 21 | $assert->same( 22 | $point->year()->toInt(), 23 | $startOfYear->year()->toInt(), 24 | ); 25 | $assert->same( 26 | 1, 27 | $startOfYear->month()->ofYear()->toInt(), 28 | ); 29 | $assert->same( 30 | 1, 31 | $startOfYear->day()->ofMonth(), 32 | ); 33 | $assert->same( 34 | 0, 35 | $startOfYear->hour()->toInt(), 36 | ); 37 | $assert->same( 38 | 0, 39 | $startOfYear->minute()->toInt(), 40 | ); 41 | $assert->same( 42 | 0, 43 | $startOfYear->second()->toInt(), 44 | ); 45 | $assert->same( 46 | 0, 47 | $startOfYear->millisecond()->toInt(), 48 | ); 49 | $assert->same( 50 | 0, 51 | $startOfYear->microsecond()->toInt(), 52 | ); 53 | }, 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /proofs/period.php: -------------------------------------------------------------------------------- 1 | $p1->add($p2), 26 | $period, 27 | $period, 28 | ), 29 | )), 30 | static function($assert, $period) { 31 | $assert 32 | ->number($period->years()) 33 | ->int() 34 | ->greaterThanOrEqual(0); 35 | $assert 36 | ->number($period->months()) 37 | ->int() 38 | ->greaterThanOrEqual(0) 39 | ->lessThanOrEqual(11); 40 | $assert 41 | ->number($period->days()) 42 | ->int() 43 | ->greaterThanOrEqual(0); 44 | $assert 45 | ->number($period->hours()) 46 | ->int() 47 | ->greaterThanOrEqual(0) 48 | ->lessThanOrEqual(23); 49 | $assert 50 | ->number($period->minutes()) 51 | ->int() 52 | ->greaterThanOrEqual(0) 53 | ->lessThanOrEqual(59); 54 | $assert 55 | ->number($period->seconds()) 56 | ->int() 57 | ->greaterThanOrEqual(0) 58 | ->lessThanOrEqual(59); 59 | $assert 60 | ->number($period->milliseconds()) 61 | ->int() 62 | ->greaterThanOrEqual(0) 63 | ->lessThanOrEqual(999); 64 | $assert 65 | ->number($period->microseconds()) 66 | ->int() 67 | ->greaterThanOrEqual(0) 68 | ->lessThanOrEqual(999); 69 | }, 70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /proofs/pointInTime.php: -------------------------------------------------------------------------------- 1 | map(Period::microsecond(...)), 15 | Set\Integers::above(0)->map(Period::millisecond(...)), 16 | ), 17 | ), 18 | static function($assert, $point, $period) { 19 | $assert->true( 20 | $point 21 | ->goForward($period) 22 | ->goBack($period) 23 | ->equals($point), 24 | ); 25 | $assert->true( 26 | $point 27 | ->goBack($period) 28 | ->goForward($period) 29 | ->equals($point), 30 | ); 31 | $assert->false( 32 | $point 33 | ->goBack($period) 34 | ->equals($point), 35 | ); 36 | $assert->false( 37 | $point 38 | ->goForward($period) 39 | ->equals($point), 40 | ); 41 | }, 42 | ); 43 | 44 | yield proof( 45 | 'PointInTime::aheadOf()', 46 | given( 47 | Fixtures\PointInTime::any(), 48 | Fixtures\Period::any()->filter( 49 | static fn($period) => !$period->equals(Period::microsecond(0)), 50 | ), 51 | ), 52 | static function($assert, $point, $period) { 53 | $assert->true( 54 | $point 55 | ->goForward($period) 56 | ->aheadOf($point), 57 | ); 58 | $assert->false( 59 | $point 60 | ->goBack($period) 61 | ->aheadOf($point), 62 | ); 63 | }, 64 | ); 65 | }; 66 | -------------------------------------------------------------------------------- /proofs/pointInTime/highResolution.php: -------------------------------------------------------------------------------- 1 | filter(static fn($start, $end) => $start < $end), 16 | static function($assert, $start, $end, $startNanoseconds, $endNanoseconds) { 17 | $start = HighResolution::of($start, $startNanoseconds); 18 | $end = HighResolution::of($end, $endNanoseconds); 19 | 20 | $assert->true($end->aheadOf($start)); 21 | $assert->false($start->aheadOf($end)); 22 | }, 23 | ); 24 | 25 | yield proof( 26 | 'HighResolution::aheadOf() in same second', 27 | given( 28 | Set\Integers::above(0), 29 | Set\Integers::between(0, 999_999_999), 30 | Set\Integers::between(0, 999_999_999), 31 | )->filter(static fn($_, $start, $end) => $start < $end), 32 | static function($assert, $second, $start, $end) { 33 | $start = HighResolution::of($second, $start); 34 | $end = HighResolution::of($second, $end); 35 | 36 | $assert->true($end->aheadOf($start)); 37 | $assert->false($start->aheadOf($end)); 38 | }, 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Calendar/Day.php: -------------------------------------------------------------------------------- 1 | self::monday, 27 | 2 => self::tuesday, 28 | 3 => self::wednesday, 29 | 4 => self::thursday, 30 | 5 => self::friday, 31 | 6 => self::saturday, 32 | 0 => self::sunday, 33 | }; 34 | } 35 | 36 | /** 37 | * @return int<0, 6> 38 | */ 39 | public function toInt(): int 40 | { 41 | return match ($this) { 42 | self::monday => 1, 43 | self::tuesday => 2, 44 | self::wednesday => 3, 45 | self::thursday => 4, 46 | self::friday => 5, 47 | self::saturday => 6, 48 | self::sunday => 0, 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Calendar/Month.php: -------------------------------------------------------------------------------- 1 | self::january, 32 | 2 => self::february, 33 | 3 => self::march, 34 | 4 => self::april, 35 | 5 => self::may, 36 | 6 => self::june, 37 | 7 => self::july, 38 | 8 => self::august, 39 | 9 => self::september, 40 | 10 => self::october, 41 | 11 => self::november, 42 | 12 => self::december, 43 | }; 44 | } 45 | 46 | /** 47 | * @return int<1, 12> 48 | */ 49 | public function toInt(): int 50 | { 51 | return match ($this) { 52 | self::january => 1, 53 | self::february => 2, 54 | self::march => 3, 55 | self::april => 4, 56 | self::may => 5, 57 | self::june => 6, 58 | self::july => 7, 59 | self::august => 8, 60 | self::september => 9, 61 | self::october => 10, 62 | self::november => 11, 63 | self::december => 12, 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Clock.php: -------------------------------------------------------------------------------- 1 | implementation->now(); 40 | } 41 | 42 | /** 43 | * @param callable(Timezones): Timezone $changeTimezone 44 | */ 45 | public function switch(callable $changeTimezone): self 46 | { 47 | return new self($this->implementation->switch($changeTimezone)); 48 | } 49 | 50 | /** 51 | * @psalm-mutation-free 52 | * 53 | * @param non-empty-string $date 54 | * 55 | * @return Maybe 56 | */ 57 | public function at(string $date, Format|Format\Custom $format): Maybe 58 | { 59 | if ($format instanceof Format\Custom) { 60 | $format = $format->normalize(); 61 | } 62 | 63 | return $this->implementation->at($date, $format); 64 | } 65 | 66 | /** 67 | * @psalm-mutation-free 68 | */ 69 | public function ofFormat(Format|Format\Custom $format): OfFormat 70 | { 71 | if ($format instanceof Format\Custom) { 72 | $format = $format->normalize(); 73 | } 74 | 75 | return OfFormat::new($this, $format); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Clock/Frozen.php: -------------------------------------------------------------------------------- 1 | now = $now; 25 | $this->concrete = $concrete; 26 | } 27 | 28 | /** 29 | * @param callable(Timezones): Timezone $changeTimezone 30 | */ 31 | public function switch(callable $changeTimezone): self 32 | { 33 | return new self( 34 | $this->now, 35 | $this->concrete->switch($changeTimezone), 36 | ); 37 | } 38 | 39 | public function now(): PointInTime 40 | { 41 | return $this->now; 42 | } 43 | 44 | /** 45 | * @psalm-mutation-free 46 | * 47 | * @param non-empty-string $date 48 | * 49 | * @return Maybe 50 | */ 51 | public function at(string $date, Format $format): Maybe 52 | { 53 | return $this 54 | ->concrete 55 | ->at($date, $format) 56 | ->map(fn($point) => $point->changeOffset($this->now->offset())); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Clock/Live.php: -------------------------------------------------------------------------------- 1 | offset = $offset; 25 | } 26 | 27 | /** 28 | * @param callable(Timezones): Timezone $changeTimezone 29 | */ 30 | public function switch(callable $changeTimezone): self 31 | { 32 | /** @var callable(non-empty-string): Timezone */ 33 | $of = static function(string $zone): Timezone { 34 | /** @var non-empty-string $zone */ 35 | $now = (new \DateTimeImmutable('now'))->setTimezone(new \DateTimeZone($zone)); 36 | 37 | return Timezone::of( 38 | Offset::from($now->format('P')), 39 | (bool) (int) $now->format('I'), 40 | ); 41 | }; 42 | 43 | return new self( 44 | $changeTimezone(Timezones::new($of))->offset(), 45 | ); 46 | } 47 | 48 | public function now(): PointInTime 49 | { 50 | return PointInTime::now()->changeOffset($this->offset); 51 | } 52 | 53 | /** 54 | * @psalm-mutation-free 55 | * 56 | * @param non-empty-string $date 57 | * 58 | * @return Maybe 59 | */ 60 | public function at(string $date, Format $format): Maybe 61 | { 62 | try { 63 | $datetime = \DateTimeImmutable::createFromFormat($format->toString(), $date); 64 | } catch (\Throwable) { 65 | /** @var Maybe */ 66 | return Maybe::nothing(); 67 | } 68 | 69 | if ($datetime === false) { 70 | /** @var Maybe */ 71 | return Maybe::nothing(); 72 | } 73 | 74 | if ($datetime->format($format->toString()) !== $date) { 75 | /** @var Maybe */ 76 | return Maybe::nothing(); 77 | } 78 | 79 | return Maybe::just(PointInTime::at($datetime)->changeOffset($this->offset)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Clock/Logger.php: -------------------------------------------------------------------------------- 1 | clock = $clock; 27 | $this->logger = $logger; 28 | } 29 | 30 | /** 31 | * @param callable(Timezones): Timezone $changeTimezone 32 | */ 33 | public function switch(callable $changeTimezone): self 34 | { 35 | return new self( 36 | $this->clock->switch($changeTimezone), 37 | $this->logger, 38 | ); 39 | } 40 | 41 | public function now(): PointInTime 42 | { 43 | $now = $this->clock->now(); 44 | $this->logger->debug('Current time is {point}', [ 45 | 'point' => $now->format(Format::iso8601()), 46 | ]); 47 | 48 | return $now; 49 | } 50 | 51 | /** 52 | * @psalm-mutation-free 53 | * 54 | * @param non-empty-string $date 55 | * 56 | * @return Maybe 57 | */ 58 | public function at(string $date, Format $format): Maybe 59 | { 60 | return $this 61 | ->clock 62 | ->at($date, $format) 63 | ->map(fn($point) => $this->log($point, $date, $format)); 64 | } 65 | 66 | private function log( 67 | PointInTime $point, 68 | string $date, 69 | Format $format, 70 | ): PointInTime { 71 | /** 72 | * @psalm-suppress ImpureVariable 73 | * @psalm-suppress ImpurePropertyFetch 74 | * @psalm-suppress ImpureMethodCall 75 | */ 76 | $this->logger->debug('Asked time {date} ({format}) resolved to {point}', [ 77 | 'date' => $date, 78 | 'format' => $format->toString(), 79 | 'point' => $point->format(Format::iso8601()), 80 | ]); 81 | 82 | return $point; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Clock/OfFormat.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | public function at(string $date): Maybe 39 | { 40 | return $this->clock->at($date, $this->format); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ElapsedPeriod.php: -------------------------------------------------------------------------------- 1 | */ 12 | private int $seconds; 13 | /** @var int<0, 999> */ 14 | private int $milliseconds; 15 | /** @var int<0, 999> */ 16 | private int $microseconds; 17 | 18 | /** 19 | * @param int<0, max> $seconds 20 | * @param int<0, 999> $milliseconds 21 | * @param int<0, 999> $microseconds 22 | */ 23 | private function __construct( 24 | int $seconds, 25 | int $milliseconds, 26 | int $microseconds, 27 | ) { 28 | $this->seconds = $seconds; 29 | $this->milliseconds = $milliseconds; 30 | $this->microseconds = $microseconds; 31 | } 32 | 33 | /** 34 | * @psalm-pure 35 | * @internal 36 | * 37 | * @param int<0, max> $seconds 38 | * @param int<0, 999> $milliseconds 39 | * @param int<0, 999> $microseconds 40 | */ 41 | public static function of( 42 | int $seconds, 43 | int $milliseconds, 44 | int $microseconds, 45 | ): self { 46 | return new self($seconds, $milliseconds, $microseconds); 47 | } 48 | 49 | public function longerThan(self $period): bool 50 | { 51 | if ($this->seconds > $period->seconds) { 52 | return true; 53 | } 54 | 55 | if ($period->seconds > $this->seconds) { 56 | return false; 57 | } 58 | 59 | if ($this->milliseconds > $period->milliseconds) { 60 | return true; 61 | } 62 | 63 | if ($period->milliseconds > $this->milliseconds) { 64 | return false; 65 | } 66 | 67 | return $this->microseconds > $period->microseconds; 68 | } 69 | 70 | public function equals(self $period): bool 71 | { 72 | return $this->seconds === $period->seconds && 73 | $this->milliseconds === $period->milliseconds && 74 | $this->microseconds === $period->microseconds; 75 | } 76 | 77 | public function asPeriod(): Period 78 | { 79 | return Period::second($this->seconds) 80 | ->add(Period::millisecond($this->milliseconds)) 81 | ->add(Period::microsecond($this->microseconds)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Format.php: -------------------------------------------------------------------------------- 1 | value; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Format/Custom.php: -------------------------------------------------------------------------------- 1 | goForward( 16 | Period::hour(23 - $point->hour()->toInt()) 17 | ->add(Period::minute(59 - $point->minute()->toInt())) 18 | ->add(Period::second(59 - $point->second()->toInt())) 19 | ->add(Period::millisecond(999 - $point->millisecond()->toInt())) 20 | ->add(Period::microsecond(999 - $point->microsecond()->toInt())), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Move/EndOfMonth.php: -------------------------------------------------------------------------------- 1 | goForward( 17 | Period::day($point->month()->numberOfDays() - $point->day()->ofMonth()), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Move/EndOfYear.php: -------------------------------------------------------------------------------- 1 | goForward( 16 | Period::month(12 - $point->month()->ofYear()->toInt()), 17 | ); 18 | 19 | return (new EndOfMonth)($point); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Move/Month.php: -------------------------------------------------------------------------------- 1 | direction = $direction; 19 | $this->months = $months; 20 | } 21 | 22 | public function __invoke(PointInTime $point): PointInTime 23 | { 24 | /** @var PointInTime $newPoint */ 25 | $newPoint = (new StartOfMonth)($point)->{$this->direction}($this->months); 26 | $newPoint = $newPoint->goForward( 27 | Period::hour($point->hour()->toInt()) 28 | ->add(Period::minute($point->minute()->toInt())) 29 | ->add(Period::second($point->second()->toInt())) 30 | ->add(Period::millisecond($point->millisecond()->toInt())) 31 | ->add(Period::microsecond($point->microsecond()->toInt())), 32 | ); 33 | 34 | if ($newPoint->month()->numberOfDays() < $point->day()->ofMonth()) { 35 | return $newPoint->goForward( 36 | Period::day($newPoint->month()->numberOfDays() - 1), 37 | ); 38 | } 39 | 40 | return $newPoint->goForward( 41 | Period::day($point->day()->ofMonth() - 1), 42 | ); 43 | } 44 | 45 | /** 46 | * @param int<1, max> $months 47 | */ 48 | public static function forward(int $months): self 49 | { 50 | return new self('goForward', Period::month($months)); 51 | } 52 | 53 | /** 54 | * @param int<1, max> $months 55 | */ 56 | public static function backward(int $months): self 57 | { 58 | return new self('goBack', Period::month($months)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Move/StartOfDay.php: -------------------------------------------------------------------------------- 1 | goBack( 16 | Period::hour($point->hour()->toInt()) 17 | ->add(Period::minute($point->minute()->toInt())) 18 | ->add(Period::second($point->second()->toInt())) 19 | ->add(Period::millisecond($point->millisecond()->toInt())) 20 | ->add(Period::microsecond($point->microsecond()->toInt())), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Move/StartOfMonth.php: -------------------------------------------------------------------------------- 1 | goBack( 16 | Period::day($point->day()->ofMonth() - 1), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Move/StartOfYear.php: -------------------------------------------------------------------------------- 1 | goBack( 16 | Period::month($point->month()->ofYear()->toInt() - 1), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Offset.php: -------------------------------------------------------------------------------- 1 | $hours 13 | * @param int<0, 59> $minutes 14 | */ 15 | private function __construct( 16 | private int $hours, 17 | private int $minutes, 18 | private bool $plus, 19 | ) { 20 | } 21 | 22 | /** 23 | * @psalm-pure 24 | */ 25 | public static function utc(): self 26 | { 27 | return self::plus(0, 0); 28 | } 29 | 30 | /** 31 | * @psalm-pure 32 | * 33 | * @param int<0, 14> $hours 34 | * @param int<0, 59> $minutes 35 | */ 36 | public static function plus(int $hours, int $minutes = 0): self 37 | { 38 | return new self($hours, $minutes, true); 39 | } 40 | 41 | /** 42 | * @psalm-pure 43 | * 44 | * @param int<0, 12> $hours 45 | * @param int<0, 59> $minutes 46 | */ 47 | public static function minus(int $hours, int $minutes = 0): self 48 | { 49 | return new self(-$hours, $minutes, false); 50 | } 51 | 52 | /** 53 | * @psalm-pure 54 | * @internal 55 | */ 56 | public static function from(string $string): self 57 | { 58 | [$hours, $minutes] = \explode(':', $string); 59 | 60 | /** @psalm-suppress ArgumentTypeCoercion */ 61 | return new self( 62 | (int) $hours, 63 | (int) $minutes, 64 | \str_starts_with($string, '+'), 65 | ); 66 | } 67 | 68 | /** 69 | * @return int<-12, 14> 70 | */ 71 | public function hours(): int 72 | { 73 | return $this->hours; 74 | } 75 | 76 | /** 77 | * @return int<0, 59> 78 | */ 79 | public function minutes(): int 80 | { 81 | return $this->minutes; 82 | } 83 | 84 | /** 85 | * @return non-empty-string 86 | */ 87 | public function toString(): string 88 | { 89 | if ($this->hours === 0 && $this->minutes === 0) { 90 | return 'Z'; 91 | } 92 | 93 | /** @var non-empty-string */ 94 | return \sprintf( 95 | '%s%02d:%02d', 96 | $this->plus ? '+' : '-', 97 | \abs($this->hours), 98 | $this->minutes, 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Period.php: -------------------------------------------------------------------------------- 1 | $year 13 | * @param int<0, 11> $month 14 | * @param int<0, max> $day 15 | * @param int<0, 23> $hour 16 | * @param int<0, 59> $minute 17 | * @param int<0, 59> $second 18 | * @param int<0, 999> $millisecond 19 | * @param int<0, 999> $microsecond 20 | */ 21 | private function __construct( 22 | private int $year, 23 | private int $month, 24 | private int $day, 25 | private int $hour, 26 | private int $minute, 27 | private int $second, 28 | private int $millisecond, 29 | private int $microsecond, 30 | ) { 31 | } 32 | 33 | /** 34 | * @psalm-pure 35 | * 36 | * @param int<0, max> $year 37 | * @param int<0, 11> $month 38 | * @param int<0, max> $day 39 | * @param int<0, 23> $hour 40 | * @param int<0, 59> $minute 41 | * @param int<0, 59> $second 42 | * @param int<0, 999> $millisecond 43 | * @param int<0, 999> $microsecond 44 | */ 45 | public static function of( 46 | int $year, 47 | int $month, 48 | int $day, 49 | int $hour, 50 | int $minute, 51 | int $second, 52 | int $millisecond, 53 | int $microsecond, 54 | ): self { 55 | return new self( 56 | $year, 57 | $month, 58 | $day, 59 | $hour, 60 | $minute, 61 | $second, 62 | $millisecond, 63 | $microsecond, 64 | ); 65 | } 66 | 67 | /** 68 | * @psalm-pure 69 | * 70 | * @param int<0, max> $year 71 | * @param int<0, max> $month 72 | * @param int<0, max> $day 73 | * @param int<0, max> $hour 74 | * @param int<0, max> $minute 75 | * @param int<0, max> $second 76 | * @param int<0, max> $millisecond 77 | * @param int<0, max> $microsecond 78 | */ 79 | public static function composite( 80 | int $year, 81 | int $month, 82 | int $day, 83 | int $hour, 84 | int $minute, 85 | int $second, 86 | int $millisecond, 87 | int $microsecond, 88 | ): self { 89 | return self::microsecond($microsecond) 90 | ->add(self::millisecond($millisecond)) 91 | ->add(self::second($second)) 92 | ->add(self::minute($minute)) 93 | ->add(self::hour($hour)) 94 | ->add(self::day($day)) 95 | ->add(self::month($month)) 96 | ->add(self::year($year)); 97 | } 98 | 99 | /** 100 | * @psalm-pure 101 | * 102 | * @param 0|positive-int $year 103 | */ 104 | public static function year(int $year): self 105 | { 106 | return new self( 107 | $year, 108 | 0, 109 | 0, 110 | 0, 111 | 0, 112 | 0, 113 | 0, 114 | 0, 115 | ); 116 | } 117 | 118 | /** 119 | * @psalm-pure 120 | * 121 | * @param 0|positive-int $month 122 | */ 123 | public static function month(int $month): self 124 | { 125 | if ($month < 12) { 126 | return new self(0, $month, 0, 0, 0, 0, 0, 0); 127 | } 128 | 129 | /** @var int<0, max> */ 130 | $year = (int) ($month / 12); 131 | $month = $month % 12; 132 | 133 | return new self( 134 | $year, 135 | $month, 136 | 0, 137 | 0, 138 | 0, 139 | 0, 140 | 0, 141 | 0, 142 | ); 143 | } 144 | 145 | /** 146 | * @psalm-pure 147 | * 148 | * @param 0|positive-int $day 149 | */ 150 | public static function day(int $day): self 151 | { 152 | return new self( 153 | 0, 154 | 0, 155 | $day, 156 | 0, 157 | 0, 158 | 0, 159 | 0, 160 | 0, 161 | ); 162 | } 163 | 164 | /** 165 | * @psalm-pure 166 | * 167 | * @param 0|positive-int $hour 168 | */ 169 | public static function hour(int $hour): self 170 | { 171 | if ($hour < 24) { 172 | return new self(0, 0, 0, $hour, 0, 0, 0, 0); 173 | } 174 | 175 | /** @var int<0, max> */ 176 | $day = (int) ($hour / 24); 177 | $hour = $hour % 24; 178 | 179 | return new self( 180 | 0, 181 | 0, 182 | $day, 183 | $hour, 184 | 0, 185 | 0, 186 | 0, 187 | 0, 188 | ); 189 | } 190 | 191 | /** 192 | * @psalm-pure 193 | * 194 | * @param 0|positive-int $minute 195 | */ 196 | public static function minute(int $minute): self 197 | { 198 | if ($minute < 60) { 199 | return new self(0, 0, 0, 0, $minute, 0, 0, 0); 200 | } 201 | 202 | /** @var int<0, max> */ 203 | $hour = (int) ($minute / 60); 204 | $hour = self::hour($hour); 205 | $minute = $minute % 60; 206 | 207 | return new self( 208 | $hour->years(), 209 | $hour->months(), 210 | $hour->days(), 211 | $hour->hours(), 212 | $minute, 213 | 0, 214 | 0, 215 | 0, 216 | ); 217 | } 218 | 219 | /** 220 | * @psalm-pure 221 | * 222 | * @param 0|positive-int $second 223 | */ 224 | public static function second(int $second): self 225 | { 226 | if ($second < 60) { 227 | return new self(0, 0, 0, 0, 0, $second, 0, 0); 228 | } 229 | 230 | /** @var int<0, max> */ 231 | $minute = (int) ($second / 60); 232 | $minute = self::minute($minute); 233 | $second = $second % 60; 234 | 235 | return new self( 236 | $minute->years(), 237 | $minute->months(), 238 | $minute->days(), 239 | $minute->hours(), 240 | $minute->minutes(), 241 | $second, 242 | 0, 243 | 0, 244 | ); 245 | } 246 | 247 | /** 248 | * @psalm-pure 249 | * 250 | * @param 0|positive-int $millisecond 251 | */ 252 | public static function millisecond(int $millisecond): self 253 | { 254 | if ($millisecond < 1_000) { 255 | return new self(0, 0, 0, 0, 0, 0, $millisecond, 0); 256 | } 257 | 258 | /** @var int<0, max> */ 259 | $second = (int) ($millisecond / 1000); 260 | $second = self::second($second); 261 | $millisecond = $millisecond % 1000; 262 | 263 | return new self( 264 | $second->years(), 265 | $second->months(), 266 | $second->days(), 267 | $second->hours(), 268 | $second->minutes(), 269 | $second->seconds(), 270 | $millisecond, 271 | 0, 272 | ); 273 | } 274 | 275 | /** 276 | * @psalm-pure 277 | * 278 | * @param 0|positive-int $microsecond 279 | */ 280 | public static function microsecond(int $microsecond): self 281 | { 282 | if ($microsecond < 1_000) { 283 | return new self(0, 0, 0, 0, 0, 0, 0, $microsecond); 284 | } 285 | 286 | /** @var int<0, max> */ 287 | $millisecond = (int) ($microsecond / 1000); 288 | $millisecond = self::millisecond($millisecond); 289 | $microsecond = $microsecond % 1000; 290 | 291 | return new self( 292 | $millisecond->years(), 293 | $millisecond->months(), 294 | $millisecond->days(), 295 | $millisecond->hours(), 296 | $millisecond->minutes(), 297 | $millisecond->seconds(), 298 | $millisecond->milliseconds(), 299 | $microsecond, 300 | ); 301 | } 302 | 303 | /** 304 | * @return int<0, max> 305 | */ 306 | public function years(): int 307 | { 308 | return $this->year; 309 | } 310 | 311 | /** 312 | * @return int<0, 11> 313 | */ 314 | public function months(): int 315 | { 316 | return $this->month; 317 | } 318 | 319 | /** 320 | * @return int<0, max> 321 | */ 322 | public function days(): int 323 | { 324 | return $this->day; 325 | } 326 | 327 | /** 328 | * @return int<0, 23> 329 | */ 330 | public function hours(): int 331 | { 332 | return $this->hour; 333 | } 334 | 335 | /** 336 | * @return int<0, 59> 337 | */ 338 | public function minutes(): int 339 | { 340 | return $this->minute; 341 | } 342 | 343 | /** 344 | * @return int<0, 59> 345 | */ 346 | public function seconds(): int 347 | { 348 | return $this->second; 349 | } 350 | 351 | /** 352 | * @return int<0, 999> 353 | */ 354 | public function milliseconds(): int 355 | { 356 | return $this->millisecond; 357 | } 358 | 359 | /** 360 | * @return int<0, 999> 361 | */ 362 | public function microseconds(): int 363 | { 364 | return $this->microsecond; 365 | } 366 | 367 | public function equals(self $period): bool 368 | { 369 | return $this->year === $period->years() && 370 | $this->month === $period->months() && 371 | $this->day === $period->days() && 372 | $this->hour === $period->hours() && 373 | $this->minute === $period->minutes() && 374 | $this->second === $period->seconds() && 375 | $this->millisecond === $period->milliseconds() && 376 | $this->microsecond === $period->microseconds(); 377 | } 378 | 379 | public function add(self $period): self 380 | { 381 | $microsecond = self::microsecond($this->microsecond + $period->microseconds()); 382 | $millisecond = self::millisecond( 383 | $this->millisecond + 384 | $period->milliseconds() + 385 | $microsecond->milliseconds(), 386 | ); 387 | $second = self::second( 388 | $this->second + 389 | $period->seconds() + 390 | $millisecond->seconds() + 391 | $microsecond->seconds(), 392 | ); 393 | $minute = self::minute( 394 | $this->minute + 395 | $period->minutes() + 396 | $second->minutes() + 397 | $millisecond->minutes() + 398 | $microsecond->minutes(), 399 | ); 400 | $hour = self::hour( 401 | $this->hour + 402 | $period->hours() + 403 | $minute->hours() + 404 | $second->hours() + 405 | $millisecond->hours() + 406 | $microsecond->hours(), 407 | ); 408 | $day = self::day( 409 | $this->day + 410 | $period->days() + 411 | $hour->days() + 412 | $minute->days() + 413 | $second->days() + 414 | $millisecond->days() + 415 | $microsecond->days(), 416 | ); 417 | $month = self::month( 418 | $this->month + 419 | $period->months() + 420 | $day->months() + 421 | $hour->months() + 422 | $minute->months() + 423 | $second->months() + 424 | $millisecond->months() + 425 | $microsecond->months(), 426 | ); 427 | $year = self::year( 428 | $this->year + 429 | $period->years() + 430 | $month->years() + 431 | $day->years() + 432 | $hour->years() + 433 | $minute->years() + 434 | $second->years() + 435 | $millisecond->years() + 436 | $microsecond->years(), 437 | ); 438 | 439 | return new self( 440 | $year->years(), 441 | $month->months(), 442 | $day->days(), 443 | $hour->hours(), 444 | $minute->minutes(), 445 | $second->seconds(), 446 | $millisecond->milliseconds(), 447 | $microsecond->microseconds(), 448 | ); 449 | } 450 | 451 | /** 452 | * @throws \LogicException When using a period containing months or years 453 | */ 454 | public function asElapsedPeriod(): ElapsedPeriod 455 | { 456 | if ($this->months() !== 0 || $this->years() !== 0) { 457 | // a month or a year is not constant 458 | throw new \LogicException('Months and years can not be converted to microseconds'); 459 | } 460 | 461 | $seconds = Period\Value::day->seconds($this->days()) + 462 | Period\Value::hour->seconds($this->hours()) + 463 | Period\Value::minute->seconds($this->minutes()) + 464 | $this->second; 465 | 466 | return ElapsedPeriod::of( 467 | $seconds, 468 | $this->millisecond, 469 | $this->microsecond, 470 | ); 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /src/Period/Value.php: -------------------------------------------------------------------------------- 1 | $number 20 | * 21 | * @return int<0, max> 22 | */ 23 | public function seconds(int $number): int 24 | { 25 | return match ($this) { 26 | self::minute => $number * 60, 27 | self::hour => $number * self::minute->seconds(60), 28 | self::day => $number * self::hour->seconds(24), 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PointInTime.php: -------------------------------------------------------------------------------- 1 | date->format('Y')); 58 | } 59 | 60 | public function month(): Month 61 | { 62 | return Month::of($this->date); 63 | } 64 | 65 | public function day(): Day 66 | { 67 | return Day::of($this->date); 68 | } 69 | 70 | public function hour(): Hour 71 | { 72 | /** @var int<0, 23> */ 73 | $hour = (int) $this->date->format('G'); 74 | 75 | return Hour::of($hour); 76 | } 77 | public function minute(): Minute 78 | { 79 | /** @var int<0, 59> */ 80 | $minute = (int) $this->date->format('i'); 81 | 82 | return Minute::of($minute); 83 | } 84 | 85 | public function second(): Second 86 | { 87 | /** @var int<0, 59> */ 88 | $second = (int) $this->date->format('s'); 89 | 90 | return Second::of($second); 91 | } 92 | 93 | public function millisecond(): Millisecond 94 | { 95 | /** @var int<0, 999> */ 96 | $millisecond = (int) $this->date->format('v'); 97 | 98 | return Millisecond::of($millisecond); 99 | } 100 | 101 | public function microsecond(): Microsecond 102 | { 103 | /** @var int<0, 999> */ 104 | $microsecond = ((int) $this->date->format('u')) % 1000; 105 | 106 | return Microsecond::of($microsecond); 107 | } 108 | 109 | public function format(Format|Format\Custom $format): string 110 | { 111 | if ($format instanceof Format\Custom) { 112 | $format = $format->normalize(); 113 | } 114 | 115 | return $this->date->format($format->toString()); 116 | } 117 | 118 | public function changeOffset(Offset $offset): self 119 | { 120 | return new self( 121 | $this->date->setTimezone( 122 | new \DateTimeZone($offset->toString()), 123 | ), 124 | $this->highResolution, 125 | ); 126 | } 127 | 128 | public function offset(): Offset 129 | { 130 | return Offset::from($this->date->format('P')); 131 | } 132 | 133 | public function elapsedSince(self $point): ElapsedPeriod 134 | { 135 | if (!\is_null($this->highResolution) && !\is_null($point->highResolution)) { 136 | return $this->highResolution->elapsedSince($point->highResolution); 137 | } 138 | 139 | $seconds = ((int) $this->date->format('U')) - ((int) $point->date->format('U')); 140 | $milliseconds = $this->millisecond()->toInt() - $point->millisecond()->toInt(); 141 | $microseconds = $this->microsecond()->toInt() - $point->microsecond()->toInt(); 142 | 143 | if ($milliseconds < 0) { 144 | $seconds -= 1; 145 | $milliseconds += 1_000; 146 | } 147 | 148 | if ($microseconds < 0) { 149 | $milliseconds -= 1; 150 | $microseconds += 1_000; 151 | } 152 | 153 | if ($milliseconds < 0) { 154 | // This handles the case where any second diff is positive, but zero 155 | // milliseconds and any microsecond diff. 156 | // Duplication could be avoided by switching the 2 previous if but 157 | // it would require to compute the number of seconds to subtract. 158 | // The duplication seems more obvious to understand (at least for 159 | // now). 160 | $seconds -= 1; 161 | $milliseconds += 1_000; 162 | } 163 | 164 | if ($seconds < 0) { 165 | throw new \RuntimeException(\sprintf( 166 | 'Negative period : %ss, %smillis, %smicros', 167 | $seconds, 168 | $milliseconds, 169 | $microseconds, 170 | )); 171 | } 172 | 173 | return ElapsedPeriod::of( 174 | $seconds, 175 | $milliseconds, 176 | $microseconds, 177 | ); 178 | } 179 | 180 | public function goBack(Period $period): self 181 | { 182 | $interval = self::dateInterval($period); 183 | 184 | if (\is_null($interval)) { 185 | return $this; 186 | } 187 | 188 | return new self( 189 | $this->date->sub($interval), 190 | null, 191 | ); 192 | } 193 | 194 | public function goForward(Period $period): self 195 | { 196 | $interval = self::dateInterval($period); 197 | 198 | if (\is_null($interval)) { 199 | return $this; 200 | } 201 | 202 | return new self( 203 | $this->date->add($interval), 204 | null, 205 | ); 206 | } 207 | 208 | public function equals(self $point): bool 209 | { 210 | $format = Format::of('Y-m-dTH:i:s.u'); 211 | $self = $this->changeOffset(Offset::utc())->format($format); 212 | $other = $point->changeOffset(Offset::utc())->format($format); 213 | 214 | return $self === $other; 215 | } 216 | 217 | public function aheadOf(self $point): bool 218 | { 219 | if (!\is_null($this->highResolution) && !\is_null($point->highResolution)) { 220 | return $this->highResolution->aheadOf($point->highResolution); 221 | } 222 | 223 | return $this->date > $point->date; 224 | } 225 | 226 | public function toString(): string 227 | { 228 | return $this->date->format('Y-m-d\TH:i:s.uP'); 229 | } 230 | 231 | /** 232 | * @psalm-pure 233 | */ 234 | private static function dateInterval(Period $period): ?\DateInterval 235 | { 236 | /** @var list */ 237 | $parts = []; 238 | 239 | if ($period->years() > 0) { 240 | $parts[] = \sprintf( 241 | '%s years', 242 | $period->years(), 243 | ); 244 | } 245 | 246 | if ($period->months() > 0) { 247 | $parts[] = \sprintf( 248 | '%s months', 249 | $period->months(), 250 | ); 251 | } 252 | 253 | if ($period->days() > 0) { 254 | $parts[] = \sprintf( 255 | '%s days', 256 | $period->days(), 257 | ); 258 | } 259 | 260 | if ($period->hours() > 0) { 261 | $parts[] = \sprintf( 262 | '%s hours', 263 | $period->hours(), 264 | ); 265 | } 266 | 267 | if ($period->minutes() > 0) { 268 | $parts[] = \sprintf( 269 | '%s minutes', 270 | $period->minutes(), 271 | ); 272 | } 273 | 274 | if ($period->seconds() > 0) { 275 | $parts[] = \sprintf( 276 | '%s seconds', 277 | $period->seconds(), 278 | ); 279 | } 280 | 281 | if ($period->milliseconds() > 0) { 282 | $parts[] = \sprintf( 283 | '%s milliseconds', 284 | $period->milliseconds(), 285 | ); 286 | } 287 | 288 | if ($period->microseconds() > 0) { 289 | $parts[] = \sprintf( 290 | '%s microseconds', 291 | $period->microseconds(), 292 | ); 293 | } 294 | 295 | if (\count($parts) === 0) { 296 | return null; 297 | } 298 | 299 | /** @psalm-suppress ImpureMethodCall */ 300 | return \DateInterval::createFromDateString(\implode(' + ', $parts)) ?: null; 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/PointInTime/Day.php: -------------------------------------------------------------------------------- 1 | */ 14 | private int $day; 15 | private Calendar\Day $week; 16 | /** @var int<0, 365> */ 17 | private int $ofYear; 18 | 19 | private function __construct(\DateTimeImmutable $date) 20 | { 21 | /** @var int<1, 31> */ 22 | $day = (int) $date->format('j'); 23 | 24 | $this->day = $day; 25 | $this->week = Calendar\Day::of((int) $date->format('w')); 26 | /** @var int<0, 365> */ 27 | $this->ofYear = (int) $date->format('z'); 28 | } 29 | 30 | /** 31 | * @psalm-pure 32 | * @internal 33 | */ 34 | public static function of(\DateTimeImmutable $date): self 35 | { 36 | return new self($date); 37 | } 38 | 39 | public function ofWeek(): Calendar\Day 40 | { 41 | return $this->week; 42 | } 43 | 44 | /** 45 | * @return int<0, 365> 46 | */ 47 | public function ofYear(): int 48 | { 49 | return $this->ofYear; 50 | } 51 | 52 | /** 53 | * @return 24 54 | */ 55 | public function numberOfHours(): int 56 | { 57 | return 24; 58 | } 59 | 60 | /** 61 | * @return int<1, 31> 62 | */ 63 | public function ofMonth(): int 64 | { 65 | return $this->day; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/PointInTime/HighResolution.php: -------------------------------------------------------------------------------- 1 | $seconds 16 | * @param int<0, 999_999_999> $nanoseconds 17 | */ 18 | private function __construct( 19 | private int $seconds, 20 | private int $nanoseconds, 21 | ) { 22 | } 23 | 24 | /** 25 | * @internal 26 | */ 27 | public static function now(): self 28 | { 29 | /** 30 | * @var int<0, max> $seconds 31 | * @var int<0, 999_999_999> $nanoseconds 32 | */ 33 | [$seconds, $nanoseconds] = \hrtime(); 34 | 35 | return new self($seconds, $nanoseconds); 36 | } 37 | 38 | /** 39 | * @internal 40 | * 41 | * @param int<0, max> $seconds 42 | * @param int<0, 999_999_999> $nanoseconds 43 | */ 44 | public static function of(int $seconds, int $nanoseconds): self 45 | { 46 | return new self($seconds, $nanoseconds); 47 | } 48 | 49 | public function aheadOf(self $other): bool 50 | { 51 | if ($this->seconds > $other->seconds) { 52 | return true; 53 | } 54 | 55 | if ($this->seconds < $other->seconds) { 56 | return false; 57 | } 58 | 59 | return $this->nanoseconds > $other->nanoseconds; 60 | } 61 | 62 | public function elapsedSince(self $other): ElapsedPeriod 63 | { 64 | $seconds = $this->seconds - $other->seconds; 65 | $nanoseconds = $this->nanoseconds - $other->nanoseconds; 66 | 67 | if ($nanoseconds < 0) { 68 | $seconds -= 1; 69 | $nanoseconds += 1_000_000_000; 70 | } 71 | 72 | /** @var int<0, 999> */ 73 | $microseconds = ((int) ($nanoseconds / 1_000)) % 1_000; 74 | /** @var int<0, 999> */ 75 | $milliseconds = ((int) ($nanoseconds / 1_000_000)) % 1_000; 76 | 77 | if ($seconds < 0) { 78 | throw new \RuntimeException(\sprintf( 79 | 'Negative period : %ss, %smillis, %smicros', 80 | $seconds, 81 | $milliseconds, 82 | $microseconds, 83 | )); 84 | } 85 | 86 | return ElapsedPeriod::of( 87 | $seconds, 88 | $milliseconds, 89 | $microseconds, 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/PointInTime/Hour.php: -------------------------------------------------------------------------------- 1 | $hour 13 | */ 14 | private function __construct( 15 | private int $hour, 16 | ) { 17 | } 18 | 19 | /** 20 | * @psalm-pure 21 | * @internal 22 | * 23 | * @param int<0, 23> $hour 24 | */ 25 | public static function of(int $hour): self 26 | { 27 | return new self($hour); 28 | } 29 | 30 | /** 31 | * @return 60 32 | */ 33 | public function numberOfMinutes(): int 34 | { 35 | return 60; 36 | } 37 | 38 | /** 39 | * @return int<0, 23> 40 | */ 41 | public function toInt(): int 42 | { 43 | return $this->hour; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PointInTime/Microsecond.php: -------------------------------------------------------------------------------- 1 | $microsecond 13 | */ 14 | private function __construct( 15 | private int $microsecond, 16 | ) { 17 | } 18 | 19 | /** 20 | * @psalm-pure 21 | * @internal 22 | * 23 | * @param int<0, 999> $microsecond 24 | */ 25 | public static function of(int $microsecond): self 26 | { 27 | return new self($microsecond); 28 | } 29 | 30 | /** 31 | * @return int<0, 999> 32 | */ 33 | public function toInt(): int 34 | { 35 | return $this->microsecond; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PointInTime/Millisecond.php: -------------------------------------------------------------------------------- 1 | $millisecond 13 | */ 14 | private function __construct( 15 | private int $millisecond, 16 | ) { 17 | } 18 | 19 | /** 20 | * @psalm-pure 21 | * @internal 22 | * 23 | * @param int<0, 999> $millisecond 24 | */ 25 | public static function of(int $millisecond): self 26 | { 27 | return new self($millisecond); 28 | } 29 | 30 | /** 31 | * @return int<0, 999> 32 | */ 33 | public function toInt(): int 34 | { 35 | return $this->millisecond; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PointInTime/Minute.php: -------------------------------------------------------------------------------- 1 | $minute 13 | */ 14 | private function __construct( 15 | private int $minute, 16 | ) { 17 | } 18 | 19 | /** 20 | * @psalm-pure 21 | * @internal 22 | * 23 | * @param int<0, 59> $minute 24 | */ 25 | public static function of(int $minute): self 26 | { 27 | return new self($minute); 28 | } 29 | 30 | /** 31 | * @return 60 32 | */ 33 | public function numberOfSeconds(): int 34 | { 35 | return 60; 36 | } 37 | 38 | /** 39 | * @return int<0, 59> 40 | */ 41 | public function toInt(): int 42 | { 43 | return $this->minute; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PointInTime/Month.php: -------------------------------------------------------------------------------- 1 | */ 15 | private int $days; 16 | 17 | private function __construct(\DateTimeImmutable $date) 18 | { 19 | $this->month = Calendar\Month::of((int) $date->format('n')); 20 | /** @var int<28, 31> */ 21 | $this->days = (int) $date->format('t'); 22 | } 23 | 24 | /** 25 | * @psalm-pure 26 | * @internal 27 | */ 28 | public static function of(\DateTimeImmutable $date): self 29 | { 30 | return new self($date); 31 | } 32 | 33 | /** 34 | * @return int<28, 31> 35 | */ 36 | public function numberOfDays(): int 37 | { 38 | return $this->days; 39 | } 40 | 41 | public function ofYear(): Calendar\Month 42 | { 43 | return $this->month; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/PointInTime/Second.php: -------------------------------------------------------------------------------- 1 | $second 13 | */ 14 | private function __construct( 15 | private int $second, 16 | ) { 17 | } 18 | 19 | /** 20 | * @psalm-pure 21 | * @internal 22 | * 23 | * @param int<0, 59> $second 24 | */ 25 | public static function of(int $second): self 26 | { 27 | return new self($second); 28 | } 29 | 30 | /** 31 | * @return int<0, 59> 32 | */ 33 | public function toInt(): int 34 | { 35 | return $this->second; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PointInTime/Year.php: -------------------------------------------------------------------------------- 1 | year = $year; 18 | /** @var 365|366 */ 19 | $this->days = ((int) (new \DateTimeImmutable("{$year}-12-31T00:00:00"))->format('z')) + 1; 20 | } 21 | 22 | /** 23 | * @psalm-pure 24 | * @internal 25 | */ 26 | public static function of(int $year): self 27 | { 28 | return new self($year); 29 | } 30 | 31 | /** 32 | * @return 365|366 33 | */ 34 | public function numberOfDays(): int 35 | { 36 | return $this->days; 37 | } 38 | 39 | /** 40 | * @return 12 41 | */ 42 | public function numberOfMonths(): int 43 | { 44 | return 12; 45 | } 46 | 47 | public function toInt(): int 48 | { 49 | return $this->year; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Timezone.php: -------------------------------------------------------------------------------- 1 | offset; 25 | } 26 | 27 | public function daylightSavingTimeApplied(): bool 28 | { 29 | return $this->dst; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Timezone/Africa.php: -------------------------------------------------------------------------------- 1 | of)('Africa/Lome'); 31 | } 32 | 33 | public function ceuta(): Timezone 34 | { 35 | return ($this->of)('Africa/Ceuta'); 36 | } 37 | 38 | public function elAaiun(): Timezone 39 | { 40 | return ($this->of)('Africa/El_Aaiun'); 41 | } 42 | 43 | public function portoNovo(): Timezone 44 | { 45 | return ($this->of)('Africa/Porto-Novo'); 46 | } 47 | 48 | public function djibouti(): Timezone 49 | { 50 | return ($this->of)('Africa/Djibouti'); 51 | } 52 | 53 | public function windhoek(): Timezone 54 | { 55 | return ($this->of)('Africa/Windhoek'); 56 | } 57 | 58 | public function algiers(): Timezone 59 | { 60 | return ($this->of)('Africa/Algiers'); 61 | } 62 | 63 | public function ouagadougou(): Timezone 64 | { 65 | return ($this->of)('Africa/Ouagadougou'); 66 | } 67 | 68 | public function bamako(): Timezone 69 | { 70 | return ($this->of)('Africa/Bamako'); 71 | } 72 | 73 | public function harare(): Timezone 74 | { 75 | return ($this->of)('Africa/Harare'); 76 | } 77 | 78 | public function bujumbura(): Timezone 79 | { 80 | return ($this->of)('Africa/Bujumbura'); 81 | } 82 | 83 | public function douala(): Timezone 84 | { 85 | return ($this->of)('Africa/Douala'); 86 | } 87 | 88 | public function brazzaville(): Timezone 89 | { 90 | return ($this->of)('Africa/Brazzaville'); 91 | } 92 | 93 | public function tripoli(): Timezone 94 | { 95 | return ($this->of)('Africa/Tripoli'); 96 | } 97 | 98 | public function casablanca(): Timezone 99 | { 100 | return ($this->of)('Africa/Casablanca'); 101 | } 102 | 103 | public function niamey(): Timezone 104 | { 105 | return ($this->of)('Africa/Niamey'); 106 | } 107 | 108 | public function mbabane(): Timezone 109 | { 110 | return ($this->of)('Africa/Mbabane'); 111 | } 112 | 113 | public function blantyre(): Timezone 114 | { 115 | return ($this->of)('Africa/Blantyre'); 116 | } 117 | 118 | public function conakry(): Timezone 119 | { 120 | return ($this->of)('Africa/Conakry'); 121 | } 122 | 123 | public function khartoum(): Timezone 124 | { 125 | return ($this->of)('Africa/Khartoum'); 126 | } 127 | 128 | public function luanda(): Timezone 129 | { 130 | return ($this->of)('Africa/Luanda'); 131 | } 132 | 133 | public function libreville(): Timezone 134 | { 135 | return ($this->of)('Africa/Libreville'); 136 | } 137 | 138 | public function maseru(): Timezone 139 | { 140 | return ($this->of)('Africa/Maseru'); 141 | } 142 | 143 | public function lusaka(): Timezone 144 | { 145 | return ($this->of)('Africa/Lusaka'); 146 | } 147 | 148 | public function darEsSalaam(): Timezone 149 | { 150 | return ($this->of)('Africa/Dar_es_Salaam'); 151 | } 152 | 153 | public function nairobi(): Timezone 154 | { 155 | return ($this->of)('Africa/Nairobi'); 156 | } 157 | 158 | public function banjul(): Timezone 159 | { 160 | return ($this->of)('Africa/Banjul'); 161 | } 162 | 163 | public function bissau(): Timezone 164 | { 165 | return ($this->of)('Africa/Bissau'); 166 | } 167 | 168 | public function nouakchott(): Timezone 169 | { 170 | return ($this->of)('Africa/Nouakchott'); 171 | } 172 | 173 | public function johannesburg(): Timezone 174 | { 175 | return ($this->of)('Africa/Johannesburg'); 176 | } 177 | 178 | public function timbuktu(): Timezone 179 | { 180 | return ($this->of)('Africa/Timbuktu'); 181 | } 182 | 183 | public function saoTome(): Timezone 184 | { 185 | return ($this->of)('Africa/Sao_Tome'); 186 | } 187 | 188 | public function freetown(): Timezone 189 | { 190 | return ($this->of)('Africa/Freetown'); 191 | } 192 | 193 | public function kampala(): Timezone 194 | { 195 | return ($this->of)('Africa/Kampala'); 196 | } 197 | 198 | public function dakar(): Timezone 199 | { 200 | return ($this->of)('Africa/Dakar'); 201 | } 202 | 203 | public function lagos(): Timezone 204 | { 205 | return ($this->of)('Africa/Lagos'); 206 | } 207 | 208 | public function cairo(): Timezone 209 | { 210 | return ($this->of)('Africa/Cairo'); 211 | } 212 | 213 | public function mogadishu(): Timezone 214 | { 215 | return ($this->of)('Africa/Mogadishu'); 216 | } 217 | 218 | public function gaborone(): Timezone 219 | { 220 | return ($this->of)('Africa/Gaborone'); 221 | } 222 | 223 | public function tunis(): Timezone 224 | { 225 | return ($this->of)('Africa/Tunis'); 226 | } 227 | 228 | public function kigali(): Timezone 229 | { 230 | return ($this->of)('Africa/Kigali'); 231 | } 232 | 233 | public function malabo(): Timezone 234 | { 235 | return ($this->of)('Africa/Malabo'); 236 | } 237 | 238 | public function abidjan(): Timezone 239 | { 240 | return ($this->of)('Africa/Abidjan'); 241 | } 242 | 243 | public function accra(): Timezone 244 | { 245 | return ($this->of)('Africa/Accra'); 246 | } 247 | 248 | public function asmera(): Timezone 249 | { 250 | return ($this->of)('Africa/Asmera'); 251 | } 252 | 253 | public function ndjamena(): Timezone 254 | { 255 | return ($this->of)('Africa/Ndjamena'); 256 | } 257 | 258 | public function lubumbashi(): Timezone 259 | { 260 | return ($this->of)('Africa/Lubumbashi'); 261 | } 262 | 263 | public function juba(): Timezone 264 | { 265 | return ($this->of)('Africa/Juba'); 266 | } 267 | 268 | public function monrovia(): Timezone 269 | { 270 | return ($this->of)('Africa/Monrovia'); 271 | } 272 | 273 | public function maputo(): Timezone 274 | { 275 | return ($this->of)('Africa/Maputo'); 276 | } 277 | 278 | public function kinshasa(): Timezone 279 | { 280 | return ($this->of)('Africa/Kinshasa'); 281 | } 282 | 283 | public function asmara(): Timezone 284 | { 285 | return ($this->of)('Africa/Asmara'); 286 | } 287 | 288 | public function bangui(): Timezone 289 | { 290 | return ($this->of)('Africa/Bangui'); 291 | } 292 | 293 | public function addisAbaba(): Timezone 294 | { 295 | return ($this->of)('Africa/Addis_Ababa'); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/Timezone/America.php: -------------------------------------------------------------------------------- 1 | of); 36 | } 37 | 38 | public function indiana(): Indiana 39 | { 40 | return Indiana::new($this->of); 41 | } 42 | 43 | public function northDakota(): NorthDakota 44 | { 45 | return NorthDakota::new($this->of); 46 | } 47 | 48 | public function montreal(): Timezone 49 | { 50 | return ($this->of)('America/Montreal'); 51 | } 52 | 53 | public function guatemala(): Timezone 54 | { 55 | return ($this->of)('America/Guatemala'); 56 | } 57 | 58 | public function boaVista(): Timezone 59 | { 60 | return ($this->of)('America/Boa_Vista'); 61 | } 62 | 63 | public function portoAcre(): Timezone 64 | { 65 | return ($this->of)('America/Porto_Acre'); 66 | } 67 | 68 | public function winnipeg(): Timezone 69 | { 70 | return ($this->of)('America/Winnipeg'); 71 | } 72 | 73 | public function santiago(): Timezone 74 | { 75 | return ($this->of)('America/Santiago'); 76 | } 77 | 78 | public function virgin(): Timezone 79 | { 80 | return ($this->of)('America/Virgin'); 81 | } 82 | 83 | public function moncton(): Timezone 84 | { 85 | return ($this->of)('America/Moncton'); 86 | } 87 | 88 | public function noronha(): Timezone 89 | { 90 | return ($this->of)('America/Noronha'); 91 | } 92 | 93 | public function recife(): Timezone 94 | { 95 | return ($this->of)('America/Recife'); 96 | } 97 | 98 | public function saintKitts(): Timezone 99 | { 100 | return ($this->of)('America/St_Kitts'); 101 | } 102 | 103 | public function rankinInlet(): Timezone 104 | { 105 | return ($this->of)('America/Rankin_Inlet'); 106 | } 107 | 108 | public function jamaica(): Timezone 109 | { 110 | return ($this->of)('America/Jamaica'); 111 | } 112 | 113 | public function lima(): Timezone 114 | { 115 | return ($this->of)('America/Lima'); 116 | } 117 | 118 | public function rosario(): Timezone 119 | { 120 | return ($this->of)('America/Rosario'); 121 | } 122 | 123 | public function cambridgeBay(): Timezone 124 | { 125 | return ($this->of)('America/Cambridge_Bay'); 126 | } 127 | 128 | public function coralHarbour(): Timezone 129 | { 130 | return ($this->of)('America/Coral_Harbour'); 131 | } 132 | 133 | public function fortWayne(): Timezone 134 | { 135 | return ($this->of)('America/Fort_Wayne'); 136 | } 137 | 138 | public function nassau(): Timezone 139 | { 140 | return ($this->of)('America/Nassau'); 141 | } 142 | 143 | public function mazatlan(): Timezone 144 | { 145 | return ($this->of)('America/Mazatlan'); 146 | } 147 | 148 | public function grandTurk(): Timezone 149 | { 150 | return ($this->of)('America/Grand_Turk'); 151 | } 152 | 153 | public function merida(): Timezone 154 | { 155 | return ($this->of)('America/Merida'); 156 | } 157 | 158 | public function ensenada(): Timezone 159 | { 160 | return ($this->of)('America/Ensenada'); 161 | } 162 | 163 | public function rainyRiver(): Timezone 164 | { 165 | return ($this->of)('America/Rainy_River'); 166 | } 167 | 168 | public function bahiaBanderas(): Timezone 169 | { 170 | return ($this->of)('America/Bahia_Banderas'); 171 | } 172 | 173 | public function guadeloupe(): Timezone 174 | { 175 | return ($this->of)('America/Guadeloupe'); 176 | } 177 | 178 | public function cuiaba(): Timezone 179 | { 180 | return ($this->of)('America/Cuiaba'); 181 | } 182 | 183 | public function scoresbysund(): Timezone 184 | { 185 | return ($this->of)('America/Scoresbysund'); 186 | } 187 | 188 | public function maceio(): Timezone 189 | { 190 | return ($this->of)('America/Maceio'); 191 | } 192 | 193 | public function curacao(): Timezone 194 | { 195 | return ($this->of)('America/Curacao'); 196 | } 197 | 198 | public function aruba(): Timezone 199 | { 200 | return ($this->of)('America/Aruba'); 201 | } 202 | 203 | public function monterrey(): Timezone 204 | { 205 | return ($this->of)('America/Monterrey'); 206 | } 207 | 208 | public function hermosillo(): Timezone 209 | { 210 | return ($this->of)('America/Hermosillo'); 211 | } 212 | 213 | public function guayaquil(): Timezone 214 | { 215 | return ($this->of)('America/Guayaquil'); 216 | } 217 | 218 | public function managua(): Timezone 219 | { 220 | return ($this->of)('America/Managua'); 221 | } 222 | 223 | public function matamoros(): Timezone 224 | { 225 | return ($this->of)('America/Matamoros'); 226 | } 227 | 228 | public function losAngeles(): Timezone 229 | { 230 | return ($this->of)('America/Los_Angeles'); 231 | } 232 | 233 | public function tegucigalpa(): Timezone 234 | { 235 | return ($this->of)('America/Tegucigalpa'); 236 | } 237 | 238 | public function monticello(): Timezone 239 | { 240 | return ($this->of)('America/Kentucky/Monticello'); 241 | } 242 | 243 | public function nome(): Timezone 244 | { 245 | return ($this->of)('America/Nome'); 246 | } 247 | 248 | public function montevideo(): Timezone 249 | { 250 | return ($this->of)('America/Montevideo'); 251 | } 252 | 253 | public function gooseBay(): Timezone 254 | { 255 | return ($this->of)('America/Goose_Bay'); 256 | } 257 | 258 | public function boise(): Timezone 259 | { 260 | return ($this->of)('America/Boise'); 261 | } 262 | 263 | public function belem(): Timezone 264 | { 265 | return ($this->of)('America/Belem'); 266 | } 267 | 268 | public function atikokan(): Timezone 269 | { 270 | return ($this->of)('America/Atikokan'); 271 | } 272 | 273 | public function swiftCurrent(): Timezone 274 | { 275 | return ($this->of)('America/Swift_Current'); 276 | } 277 | 278 | public function detroit(): Timezone 279 | { 280 | return ($this->of)('America/Detroit'); 281 | } 282 | 283 | public function laPaz(): Timezone 284 | { 285 | return ($this->of)('America/La_Paz'); 286 | } 287 | 288 | public function chicago(): Timezone 289 | { 290 | return ($this->of)('America/Chicago'); 291 | } 292 | 293 | public function creston(): Timezone 294 | { 295 | return ($this->of)('America/Creston'); 296 | } 297 | 298 | public function nipigon(): Timezone 299 | { 300 | return ($this->of)('America/Nipigon'); 301 | } 302 | 303 | public function costaRica(): Timezone 304 | { 305 | return ($this->of)('America/Costa_Rica'); 306 | } 307 | 308 | public function halifax(): Timezone 309 | { 310 | return ($this->of)('America/Halifax'); 311 | } 312 | 313 | public function yellowknife(): Timezone 314 | { 315 | return ($this->of)('America/Yellowknife'); 316 | } 317 | 318 | public function puertoRico(): Timezone 319 | { 320 | return ($this->of)('America/Puerto_Rico'); 321 | } 322 | 323 | public function edmonton(): Timezone 324 | { 325 | return ($this->of)('America/Edmonton'); 326 | } 327 | 328 | public function mexicoCity(): Timezone 329 | { 330 | return ($this->of)('America/Mexico_City'); 331 | } 332 | 333 | public function saoPaulo(): Timezone 334 | { 335 | return ($this->of)('America/Sao_Paulo'); 336 | } 337 | 338 | public function yakutat(): Timezone 339 | { 340 | return ($this->of)('America/Yakutat'); 341 | } 342 | 343 | public function saintThomas(): Timezone 344 | { 345 | return ($this->of)('America/St_Thomas'); 346 | } 347 | 348 | public function chihuahua(): Timezone 349 | { 350 | return ($this->of)('America/Chihuahua'); 351 | } 352 | 353 | public function grenada(): Timezone 354 | { 355 | return ($this->of)('America/Grenada'); 356 | } 357 | 358 | public function elSalvador(): Timezone 359 | { 360 | return ($this->of)('America/El_Salvador'); 361 | } 362 | 363 | public function santoDomingo(): Timezone 364 | { 365 | return ($this->of)('America/Santo_Domingo'); 366 | } 367 | 368 | public function montserrat(): Timezone 369 | { 370 | return ($this->of)('America/Montserrat'); 371 | } 372 | 373 | public function portoVelho(): Timezone 374 | { 375 | return ($this->of)('America/Porto_Velho'); 376 | } 377 | 378 | public function panama(): Timezone 379 | { 380 | return ($this->of)('America/Panama'); 381 | } 382 | 383 | public function antigua(): Timezone 384 | { 385 | return ($this->of)('America/Antigua'); 386 | } 387 | 388 | public function santarem(): Timezone 389 | { 390 | return ($this->of)('America/Santarem'); 391 | } 392 | 393 | public function dawson(): Timezone 394 | { 395 | return ($this->of)('America/Dawson'); 396 | } 397 | 398 | public function saintBarthelemy(): Timezone 399 | { 400 | return ($this->of)('America/St_Barthelemy'); 401 | } 402 | 403 | public function iqaluit(): Timezone 404 | { 405 | return ($this->of)('America/Iqaluit'); 406 | } 407 | 408 | public function eirunepe(): Timezone 409 | { 410 | return ($this->of)('America/Eirunepe'); 411 | } 412 | 413 | public function inuvik(): Timezone 414 | { 415 | return ($this->of)('America/Inuvik'); 416 | } 417 | 418 | public function anguilla(): Timezone 419 | { 420 | return ($this->of)('America/Anguilla'); 421 | } 422 | 423 | public function portOfSpain(): Timezone 424 | { 425 | return ($this->of)('America/Port_of_Spain'); 426 | } 427 | 428 | public function araguaina(): Timezone 429 | { 430 | return ($this->of)('America/Araguaina'); 431 | } 432 | 433 | public function guyana(): Timezone 434 | { 435 | return ($this->of)('America/Guyana'); 436 | } 437 | 438 | public function fortaleza(): Timezone 439 | { 440 | return ($this->of)('America/Fortaleza'); 441 | } 442 | 443 | public function blancSablon(): Timezone 444 | { 445 | return ($this->of)('America/Blanc-Sablon'); 446 | } 447 | 448 | public function juneau(): Timezone 449 | { 450 | return ($this->of)('America/Juneau'); 451 | } 452 | 453 | public function cayman(): Timezone 454 | { 455 | return ($this->of)('America/Cayman'); 456 | } 457 | 458 | public function menominee(): Timezone 459 | { 460 | return ($this->of)('America/Menominee'); 461 | } 462 | 463 | public function cayenne(): Timezone 464 | { 465 | return ($this->of)('America/Cayenne'); 466 | } 467 | 468 | public function pangnirtung(): Timezone 469 | { 470 | return ($this->of)('America/Pangnirtung'); 471 | } 472 | 473 | public function metlakatla(): Timezone 474 | { 475 | return ($this->of)('America/Metlakatla'); 476 | } 477 | 478 | public function asuncion(): Timezone 479 | { 480 | return ($this->of)('America/Asuncion'); 481 | } 482 | 483 | public function saintLucia(): Timezone 484 | { 485 | return ($this->of)('America/St_Lucia'); 486 | } 487 | 488 | public function saintVincent(): Timezone 489 | { 490 | return ($this->of)('America/St_Vincent'); 491 | } 492 | 493 | public function martinique(): Timezone 494 | { 495 | return ($this->of)('America/Martinique'); 496 | } 497 | 498 | public function kralendijk(): Timezone 499 | { 500 | return ($this->of)('America/Kralendijk'); 501 | } 502 | 503 | public function newYork(): Timezone 504 | { 505 | return ($this->of)('America/New_York'); 506 | } 507 | 508 | public function vancouver(): Timezone 509 | { 510 | return ($this->of)('America/Vancouver'); 511 | } 512 | 513 | public function bogota(): Timezone 514 | { 515 | return ($this->of)('America/Bogota'); 516 | } 517 | 518 | public function dominica(): Timezone 519 | { 520 | return ($this->of)('America/Dominica'); 521 | } 522 | 523 | public function danmarkshavn(): Timezone 524 | { 525 | return ($this->of)('America/Danmarkshavn'); 526 | } 527 | 528 | public function anchorage(): Timezone 529 | { 530 | return ($this->of)('America/Anchorage'); 531 | } 532 | 533 | public function marigot(): Timezone 534 | { 535 | return ($this->of)('America/Marigot'); 536 | } 537 | 538 | public function rioBranco(): Timezone 539 | { 540 | return ($this->of)('America/Rio_Branco'); 541 | } 542 | 543 | public function paramaribo(): Timezone 544 | { 545 | return ($this->of)('America/Paramaribo'); 546 | } 547 | 548 | public function caracas(): Timezone 549 | { 550 | return ($this->of)('America/Caracas'); 551 | } 552 | 553 | public function resolute(): Timezone 554 | { 555 | return ($this->of)('America/Resolute'); 556 | } 557 | 558 | public function godthab(): Timezone 559 | { 560 | return ($this->of)('America/Godthab'); 561 | } 562 | 563 | public function catamarca(): Timezone 564 | { 565 | return ($this->of)('America/Catamarca'); 566 | } 567 | 568 | public function glaceBay(): Timezone 569 | { 570 | return ($this->of)('America/Glace_Bay'); 571 | } 572 | 573 | public function regina(): Timezone 574 | { 575 | return ($this->of)('America/Regina'); 576 | } 577 | 578 | public function toronto(): Timezone 579 | { 580 | return ($this->of)('America/Toronto'); 581 | } 582 | 583 | public function barbados(): Timezone 584 | { 585 | return ($this->of)('America/Barbados'); 586 | } 587 | 588 | public function santaIsabel(): Timezone 589 | { 590 | return ($this->of)('America/Santa_Isabel'); 591 | } 592 | 593 | public function miquelon(): Timezone 594 | { 595 | return ($this->of)('America/Miquelon'); 596 | } 597 | 598 | public function havana(): Timezone 599 | { 600 | return ($this->of)('America/Havana'); 601 | } 602 | 603 | public function ojinaga(): Timezone 604 | { 605 | return ($this->of)('America/Ojinaga'); 606 | } 607 | 608 | public function denver(): Timezone 609 | { 610 | return ($this->of)('America/Denver'); 611 | } 612 | 613 | public function cancun(): Timezone 614 | { 615 | return ($this->of)('America/Cancun'); 616 | } 617 | 618 | public function thunderBay(): Timezone 619 | { 620 | return ($this->of)('America/Thunder_Bay'); 621 | } 622 | 623 | public function adak(): Timezone 624 | { 625 | return ($this->of)('America/Adak'); 626 | } 627 | 628 | public function saintJohns(): Timezone 629 | { 630 | return ($this->of)('America/St_Johns'); 631 | } 632 | 633 | public function portAuPrince(): Timezone 634 | { 635 | return ($this->of)('America/Port-au-Prince'); 636 | } 637 | 638 | public function whitehorse(): Timezone 639 | { 640 | return ($this->of)('America/Whitehorse'); 641 | } 642 | 643 | public function louisville(): Timezone 644 | { 645 | return ($this->of)('America/Louisville'); 646 | } 647 | 648 | public function manaus(): Timezone 649 | { 650 | return ($this->of)('America/Manaus'); 651 | } 652 | 653 | public function lowerPrinces(): Timezone 654 | { 655 | return ($this->of)('America/Lower_Princes'); 656 | } 657 | 658 | public function sitka(): Timezone 659 | { 660 | return ($this->of)('America/Sitka'); 661 | } 662 | 663 | public function thule(): Timezone 664 | { 665 | return ($this->of)('America/Thule'); 666 | } 667 | 668 | public function campoGrande(): Timezone 669 | { 670 | return ($this->of)('America/Campo_Grande'); 671 | } 672 | 673 | public function phoenix(): Timezone 674 | { 675 | return ($this->of)('America/Phoenix'); 676 | } 677 | 678 | public function shiprock(): Timezone 679 | { 680 | return ($this->of)('America/Shiprock'); 681 | } 682 | 683 | public function bahia(): Timezone 684 | { 685 | return ($this->of)('America/Bahia'); 686 | } 687 | 688 | public function tortola(): Timezone 689 | { 690 | return ($this->of)('America/Tortola'); 691 | } 692 | 693 | public function dawsonCreek(): Timezone 694 | { 695 | return ($this->of)('America/Dawson_Creek'); 696 | } 697 | 698 | public function tijuana(): Timezone 699 | { 700 | return ($this->of)('America/Tijuana'); 701 | } 702 | 703 | public function belize(): Timezone 704 | { 705 | return ($this->of)('America/Belize'); 706 | } 707 | 708 | public function atka(): Timezone 709 | { 710 | return ($this->of)('America/Atka'); 711 | } 712 | } 713 | -------------------------------------------------------------------------------- /src/Timezone/America/Argentina.php: -------------------------------------------------------------------------------- 1 | of)('America/Argentina/Rio_Gallegos'); 31 | } 32 | 33 | public function mendoza(): Timezone 34 | { 35 | return ($this->of)('America/Argentina/Mendoza'); 36 | } 37 | 38 | public function buenosAires(): Timezone 39 | { 40 | return ($this->of)('America/Argentina/Buenos_Aires'); 41 | } 42 | 43 | public function ushuaia(): Timezone 44 | { 45 | return ($this->of)('America/Argentina/Ushuaia'); 46 | } 47 | 48 | public function sanJuan(): Timezone 49 | { 50 | return ($this->of)('America/Argentina/San_Juan'); 51 | } 52 | 53 | public function laRioja(): Timezone 54 | { 55 | return ($this->of)('America/Argentina/La_Rioja'); 56 | } 57 | 58 | public function salta(): Timezone 59 | { 60 | return ($this->of)('America/Argentina/Salta'); 61 | } 62 | 63 | public function sanLuis(): Timezone 64 | { 65 | return ($this->of)('America/Argentina/San_Luis'); 66 | } 67 | 68 | public function jujuy(): Timezone 69 | { 70 | return ($this->of)('America/Argentina/Jujuy'); 71 | } 72 | 73 | public function tucuman(): Timezone 74 | { 75 | return ($this->of)('America/Argentina/Tucuman'); 76 | } 77 | 78 | public function comodRivadavia(): Timezone 79 | { 80 | return ($this->of)('America/Argentina/ComodRivadavia'); 81 | } 82 | 83 | public function catamarca(): Timezone 84 | { 85 | return ($this->of)('America/Argentina/Catamarca'); 86 | } 87 | 88 | public function cordoba(): Timezone 89 | { 90 | return ($this->of)('America/Argentina/Cordoba'); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Timezone/America/Indiana.php: -------------------------------------------------------------------------------- 1 | of)('America/Indiana/Vincennes'); 31 | } 32 | 33 | public function marengo(): Timezone 34 | { 35 | return ($this->of)('America/Indiana/Marengo'); 36 | } 37 | 38 | public function tellCity(): Timezone 39 | { 40 | return ($this->of)('America/Indiana/Tell_City'); 41 | } 42 | 43 | public function knox(): Timezone 44 | { 45 | return ($this->of)('America/Indiana/Knox'); 46 | } 47 | 48 | public function vevay(): Timezone 49 | { 50 | return ($this->of)('America/Indiana/Vevay'); 51 | } 52 | 53 | public function indianapolis(): Timezone 54 | { 55 | return ($this->of)('America/Indiana/Indianapolis'); 56 | } 57 | 58 | public function petersburg(): Timezone 59 | { 60 | return ($this->of)('America/Indiana/Petersburg'); 61 | } 62 | 63 | public function winamac(): Timezone 64 | { 65 | return ($this->of)('America/Indiana/Winamac'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Timezone/America/NorthDakota.php: -------------------------------------------------------------------------------- 1 | of)('America/North_Dakota/Beulah'); 31 | } 32 | 33 | public function newSalem(): Timezone 34 | { 35 | return ($this->of)('America/North_Dakota/New_Salem'); 36 | } 37 | 38 | public function center(): Timezone 39 | { 40 | return ($this->of)('America/North_Dakota/Center'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Timezone/Antartica.php: -------------------------------------------------------------------------------- 1 | of)('Antarctica/Davis'); 31 | } 32 | 33 | public function palmer(): Timezone 34 | { 35 | return ($this->of)('Antarctica/Palmer'); 36 | } 37 | 38 | public function syowa(): Timezone 39 | { 40 | return ($this->of)('Antarctica/Syowa'); 41 | } 42 | 43 | public function casey(): Timezone 44 | { 45 | return ($this->of)('Antarctica/Casey'); 46 | } 47 | 48 | public function troll(): Timezone 49 | { 50 | return ($this->of)('Antarctica/Troll'); 51 | } 52 | 53 | public function mcMurdo(): Timezone 54 | { 55 | return ($this->of)('Antarctica/McMurdo'); 56 | } 57 | 58 | public function vostok(): Timezone 59 | { 60 | return ($this->of)('Antarctica/Vostok'); 61 | } 62 | 63 | public function rothera(): Timezone 64 | { 65 | return ($this->of)('Antarctica/Rothera'); 66 | } 67 | 68 | public function mawson(): Timezone 69 | { 70 | return ($this->of)('Antarctica/Mawson'); 71 | } 72 | 73 | public function macquarie(): Timezone 74 | { 75 | return ($this->of)('Antarctica/Macquarie'); 76 | } 77 | 78 | public function southPole(): Timezone 79 | { 80 | return ($this->of)('Antarctica/South_Pole'); 81 | } 82 | 83 | public function dumontDUrville(): Timezone 84 | { 85 | return ($this->of)('Antarctica/DumontDUrville'); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Timezone/Arctic.php: -------------------------------------------------------------------------------- 1 | of)('Arctic/Longyearbyen'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Timezone/Asia.php: -------------------------------------------------------------------------------- 1 | of)('Asia/Manila'); 31 | } 32 | 33 | public function baghdad(): Timezone 34 | { 35 | return ($this->of)('Asia/Baghdad'); 36 | } 37 | 38 | public function ulaanbaatar(): Timezone 39 | { 40 | return ($this->of)('Asia/Ulaanbaatar'); 41 | } 42 | 43 | public function almaty(): Timezone 44 | { 45 | return ($this->of)('Asia/Almaty'); 46 | } 47 | 48 | public function samarkand(): Timezone 49 | { 50 | return ($this->of)('Asia/Samarkand'); 51 | } 52 | 53 | public function ustNera(): Timezone 54 | { 55 | return ($this->of)('Asia/Ust-Nera'); 56 | } 57 | 58 | public function pontianak(): Timezone 59 | { 60 | return ($this->of)('Asia/Pontianak'); 61 | } 62 | 63 | public function tehran(): Timezone 64 | { 65 | return ($this->of)('Asia/Tehran'); 66 | } 67 | 68 | public function saigon(): Timezone 69 | { 70 | return ($this->of)('Asia/Saigon'); 71 | } 72 | 73 | public function krasnoyarsk(): Timezone 74 | { 75 | return ($this->of)('Asia/Krasnoyarsk'); 76 | } 77 | 78 | public function hebron(): Timezone 79 | { 80 | return ($this->of)('Asia/Hebron'); 81 | } 82 | 83 | public function kuching(): Timezone 84 | { 85 | return ($this->of)('Asia/Kuching'); 86 | } 87 | 88 | public function katmandu(): Timezone 89 | { 90 | return ($this->of)('Asia/Katmandu'); 91 | } 92 | 93 | public function shanghai(): Timezone 94 | { 95 | return ($this->of)('Asia/Shanghai'); 96 | } 97 | 98 | public function calcutta(): Timezone 99 | { 100 | return ($this->of)('Asia/Calcutta'); 101 | } 102 | 103 | public function jayapura(): Timezone 104 | { 105 | return ($this->of)('Asia/Jayapura'); 106 | } 107 | 108 | public function muscat(): Timezone 109 | { 110 | return ($this->of)('Asia/Muscat'); 111 | } 112 | 113 | public function omsk(): Timezone 114 | { 115 | return ($this->of)('Asia/Omsk'); 116 | } 117 | 118 | public function aqtau(): Timezone 119 | { 120 | return ($this->of)('Asia/Aqtau'); 121 | } 122 | 123 | public function khandyga(): Timezone 124 | { 125 | return ($this->of)('Asia/Khandyga'); 126 | } 127 | 128 | public function riyadh(): Timezone 129 | { 130 | return ($this->of)('Asia/Riyadh'); 131 | } 132 | 133 | public function bangkok(): Timezone 134 | { 135 | return ($this->of)('Asia/Bangkok'); 136 | } 137 | 138 | public function thimphu(): Timezone 139 | { 140 | return ($this->of)('Asia/Thimphu'); 141 | } 142 | 143 | public function aden(): Timezone 144 | { 145 | return ($this->of)('Asia/Aden'); 146 | } 147 | 148 | public function yekaterinburg(): Timezone 149 | { 150 | return ($this->of)('Asia/Yekaterinburg'); 151 | } 152 | 153 | public function oral(): Timezone 154 | { 155 | return ($this->of)('Asia/Oral'); 156 | } 157 | 158 | public function novokuznetsk(): Timezone 159 | { 160 | return ($this->of)('Asia/Novokuznetsk'); 161 | } 162 | 163 | public function bishkek(): Timezone 164 | { 165 | return ($this->of)('Asia/Bishkek'); 166 | } 167 | 168 | public function macau(): Timezone 169 | { 170 | return ($this->of)('Asia/Macau'); 171 | } 172 | 173 | public function qyzylorda(): Timezone 174 | { 175 | return ($this->of)('Asia/Qyzylorda'); 176 | } 177 | 178 | public function seoul(): Timezone 179 | { 180 | return ($this->of)('Asia/Seoul'); 181 | } 182 | 183 | public function irkutsk(): Timezone 184 | { 185 | return ($this->of)('Asia/Irkutsk'); 186 | } 187 | 188 | public function aqtobe(): Timezone 189 | { 190 | return ($this->of)('Asia/Aqtobe'); 191 | } 192 | 193 | public function chongqing(): Timezone 194 | { 195 | return ($this->of)('Asia/Chongqing'); 196 | } 197 | 198 | public function kabul(): Timezone 199 | { 200 | return ($this->of)('Asia/Kabul'); 201 | } 202 | 203 | public function thimbu(): Timezone 204 | { 205 | return ($this->of)('Asia/Thimbu'); 206 | } 207 | 208 | public function karachi(): Timezone 209 | { 210 | return ($this->of)('Asia/Karachi'); 211 | } 212 | 213 | public function jakarta(): Timezone 214 | { 215 | return ($this->of)('Asia/Jakarta'); 216 | } 217 | 218 | public function harbin(): Timezone 219 | { 220 | return ($this->of)('Asia/Harbin'); 221 | } 222 | 223 | public function novosibirsk(): Timezone 224 | { 225 | return ($this->of)('Asia/Novosibirsk'); 226 | } 227 | 228 | public function dili(): Timezone 229 | { 230 | return ($this->of)('Asia/Dili'); 231 | } 232 | 233 | public function colombo(): Timezone 234 | { 235 | return ($this->of)('Asia/Colombo'); 236 | } 237 | 238 | public function ashkhabad(): Timezone 239 | { 240 | return ($this->of)('Asia/Ashkhabad'); 241 | } 242 | 243 | public function dacca(): Timezone 244 | { 245 | return ($this->of)('Asia/Dacca'); 246 | } 247 | 248 | public function ashgabat(): Timezone 249 | { 250 | return ($this->of)('Asia/Ashgabat'); 251 | } 252 | 253 | public function ujungPandang(): Timezone 254 | { 255 | return ($this->of)('Asia/Ujung_Pandang'); 256 | } 257 | 258 | public function qatar(): Timezone 259 | { 260 | return ($this->of)('Asia/Qatar'); 261 | } 262 | 263 | public function tokyo(): Timezone 264 | { 265 | return ($this->of)('Asia/Tokyo'); 266 | } 267 | 268 | public function macao(): Timezone 269 | { 270 | return ($this->of)('Asia/Macao'); 271 | } 272 | 273 | public function tashkent(): Timezone 274 | { 275 | return ($this->of)('Asia/Tashkent'); 276 | } 277 | 278 | public function baku(): Timezone 279 | { 280 | return ($this->of)('Asia/Baku'); 281 | } 282 | 283 | public function pyongyang(): Timezone 284 | { 285 | return ($this->of)('Asia/Pyongyang'); 286 | } 287 | 288 | public function tbilisi(): Timezone 289 | { 290 | return ($this->of)('Asia/Tbilisi'); 291 | } 292 | 293 | public function amman(): Timezone 294 | { 295 | return ($this->of)('Asia/Amman'); 296 | } 297 | 298 | public function vladivostok(): Timezone 299 | { 300 | return ($this->of)('Asia/Vladivostok'); 301 | } 302 | 303 | public function damascus(): Timezone 304 | { 305 | return ($this->of)('Asia/Damascus'); 306 | } 307 | 308 | public function bahrain(): Timezone 309 | { 310 | return ($this->of)('Asia/Bahrain'); 311 | } 312 | 313 | public function vientiane(): Timezone 314 | { 315 | return ($this->of)('Asia/Vientiane'); 316 | } 317 | 318 | public function hovd(): Timezone 319 | { 320 | return ($this->of)('Asia/Hovd'); 321 | } 322 | 323 | public function kuwait(): Timezone 324 | { 325 | return ($this->of)('Asia/Kuwait'); 326 | } 327 | 328 | public function magadan(): Timezone 329 | { 330 | return ($this->of)('Asia/Magadan'); 331 | } 332 | 333 | public function ulanBator(): Timezone 334 | { 335 | return ($this->of)('Asia/Ulan_Bator'); 336 | } 337 | 338 | public function nicosia(): Timezone 339 | { 340 | return ($this->of)('Asia/Nicosia'); 341 | } 342 | 343 | public function telAviv(): Timezone 344 | { 345 | return ($this->of)('Asia/Tel_Aviv'); 346 | } 347 | 348 | public function choibalsan(): Timezone 349 | { 350 | return ($this->of)('Asia/Choibalsan'); 351 | } 352 | 353 | public function brunei(): Timezone 354 | { 355 | return ($this->of)('Asia/Brunei'); 356 | } 357 | 358 | public function kualaLumpur(): Timezone 359 | { 360 | return ($this->of)('Asia/Kuala_Lumpur'); 361 | } 362 | 363 | public function kathmandu(): Timezone 364 | { 365 | return ($this->of)('Asia/Kathmandu'); 366 | } 367 | 368 | public function srednekolymsk(): Timezone 369 | { 370 | return ($this->of)('Asia/Srednekolymsk'); 371 | } 372 | 373 | public function dubai(): Timezone 374 | { 375 | return ($this->of)('Asia/Dubai'); 376 | } 377 | 378 | public function yakutsk(): Timezone 379 | { 380 | return ($this->of)('Asia/Yakutsk'); 381 | } 382 | 383 | public function beirut(): Timezone 384 | { 385 | return ($this->of)('Asia/Beirut'); 386 | } 387 | 388 | public function gaza(): Timezone 389 | { 390 | return ($this->of)('Asia/Gaza'); 391 | } 392 | 393 | public function singapore(): Timezone 394 | { 395 | return ($this->of)('Asia/Singapore'); 396 | } 397 | 398 | public function rangoon(): Timezone 399 | { 400 | return ($this->of)('Asia/Rangoon'); 401 | } 402 | 403 | public function sakhalin(): Timezone 404 | { 405 | return ($this->of)('Asia/Sakhalin'); 406 | } 407 | 408 | public function phnomPenh(): Timezone 409 | { 410 | return ($this->of)('Asia/Phnom_Penh'); 411 | } 412 | 413 | public function kamchatka(): Timezone 414 | { 415 | return ($this->of)('Asia/Kamchatka'); 416 | } 417 | 418 | public function yerevan(): Timezone 419 | { 420 | return ($this->of)('Asia/Yerevan'); 421 | } 422 | 423 | public function chungking(): Timezone 424 | { 425 | return ($this->of)('Asia/Chungking'); 426 | } 427 | 428 | public function hoChiMinh(): Timezone 429 | { 430 | return ($this->of)('Asia/Ho_Chi_Minh'); 431 | } 432 | 433 | public function chita(): Timezone 434 | { 435 | return ($this->of)('Asia/Chita'); 436 | } 437 | 438 | public function istanbul(): Timezone 439 | { 440 | return ($this->of)('Asia/Istanbul'); 441 | } 442 | 443 | public function hongKong(): Timezone 444 | { 445 | return ($this->of)('Asia/Hong_Kong'); 446 | } 447 | 448 | public function dhaka(): Timezone 449 | { 450 | return ($this->of)('Asia/Dhaka'); 451 | } 452 | 453 | public function jerusalem(): Timezone 454 | { 455 | return ($this->of)('Asia/Jerusalem'); 456 | } 457 | 458 | public function makassar(): Timezone 459 | { 460 | return ($this->of)('Asia/Makassar'); 461 | } 462 | 463 | public function kolkata(): Timezone 464 | { 465 | return ($this->of)('Asia/Kolkata'); 466 | } 467 | 468 | public function taipei(): Timezone 469 | { 470 | return ($this->of)('Asia/Taipei'); 471 | } 472 | 473 | public function dushanbe(): Timezone 474 | { 475 | return ($this->of)('Asia/Dushanbe'); 476 | } 477 | 478 | public function anadyr(): Timezone 479 | { 480 | return ($this->of)('Asia/Anadyr'); 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /src/Timezone/Atlantic.php: -------------------------------------------------------------------------------- 1 | of)('Atlantic/Faroe'); 31 | } 32 | 33 | public function southGeorgia(): Timezone 34 | { 35 | return ($this->of)('Atlantic/South_Georgia'); 36 | } 37 | 38 | public function capeVerde(): Timezone 39 | { 40 | return ($this->of)('Atlantic/Cape_Verde'); 41 | } 42 | 43 | public function faeroe(): Timezone 44 | { 45 | return ($this->of)('Atlantic/Faeroe'); 46 | } 47 | 48 | public function bermuda(): Timezone 49 | { 50 | return ($this->of)('Atlantic/Bermuda'); 51 | } 52 | 53 | public function janMayen(): Timezone 54 | { 55 | return ($this->of)('Atlantic/Jan_Mayen'); 56 | } 57 | 58 | public function reykjavik(): Timezone 59 | { 60 | return ($this->of)('Atlantic/Reykjavik'); 61 | } 62 | 63 | public function saintHelena(): Timezone 64 | { 65 | return ($this->of)('Atlantic/St_Helena'); 66 | } 67 | 68 | public function canary(): Timezone 69 | { 70 | return ($this->of)('Atlantic/Canary'); 71 | } 72 | 73 | public function madeira(): Timezone 74 | { 75 | return ($this->of)('Atlantic/Madeira'); 76 | } 77 | 78 | public function azores(): Timezone 79 | { 80 | return ($this->of)('Atlantic/Azores'); 81 | } 82 | 83 | public function stanley(): Timezone 84 | { 85 | return ($this->of)('Atlantic/Stanley'); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Timezone/Australia.php: -------------------------------------------------------------------------------- 1 | of)('Australia/Lindeman'); 31 | } 32 | 33 | public function currie(): Timezone 34 | { 35 | return ($this->of)('Australia/Currie'); 36 | } 37 | 38 | public function victoria(): Timezone 39 | { 40 | return ($this->of)('Australia/Victoria'); 41 | } 42 | 43 | public function adelaide(): Timezone 44 | { 45 | return ($this->of)('Australia/Adelaide'); 46 | } 47 | 48 | public function perth(): Timezone 49 | { 50 | return ($this->of)('Australia/Perth'); 51 | } 52 | 53 | public function brisbane(): Timezone 54 | { 55 | return ($this->of)('Australia/Brisbane'); 56 | } 57 | 58 | public function west(): Timezone 59 | { 60 | return ($this->of)('Australia/West'); 61 | } 62 | 63 | public function australianCapitalTerritory(): Timezone 64 | { 65 | return ($this->of)('Australia/ACT'); 66 | } 67 | 68 | public function north(): Timezone 69 | { 70 | return ($this->of)('Australia/North'); 71 | } 72 | 73 | public function eucla(): Timezone 74 | { 75 | return ($this->of)('Australia/Eucla'); 76 | } 77 | 78 | public function lordeHoweIsland(): Timezone 79 | { 80 | return ($this->of)('Australia/LHI'); 81 | } 82 | 83 | public function newSouthWales(): Timezone 84 | { 85 | return ($this->of)('Australia/NSW'); 86 | } 87 | 88 | public function queensland(): Timezone 89 | { 90 | return ($this->of)('Australia/Queensland'); 91 | } 92 | 93 | public function south(): Timezone 94 | { 95 | return ($this->of)('Australia/South'); 96 | } 97 | 98 | public function melbourne(): Timezone 99 | { 100 | return ($this->of)('Australia/Melbourne'); 101 | } 102 | 103 | public function yancowinna(): Timezone 104 | { 105 | return ($this->of)('Australia/Yancowinna'); 106 | } 107 | 108 | public function canberra(): Timezone 109 | { 110 | return ($this->of)('Australia/Canberra'); 111 | } 112 | 113 | public function sydney(): Timezone 114 | { 115 | return ($this->of)('Australia/Sydney'); 116 | } 117 | 118 | public function darwin(): Timezone 119 | { 120 | return ($this->of)('Australia/Darwin'); 121 | } 122 | 123 | public function hobart(): Timezone 124 | { 125 | return ($this->of)('Australia/Hobart'); 126 | } 127 | 128 | public function brokenHill(): Timezone 129 | { 130 | return ($this->of)('Australia/Broken_Hill'); 131 | } 132 | 133 | public function tasmania(): Timezone 134 | { 135 | return ($this->of)('Australia/Tasmania'); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Timezone/Europe.php: -------------------------------------------------------------------------------- 1 | of)('Europe/Uzhgorod'); 31 | } 32 | 33 | public function riga(): Timezone 34 | { 35 | return ($this->of)('Europe/Riga'); 36 | } 37 | 38 | public function paris(): Timezone 39 | { 40 | return ($this->of)('Europe/Paris'); 41 | } 42 | 43 | public function guernsey(): Timezone 44 | { 45 | return ($this->of)('Europe/Guernsey'); 46 | } 47 | 48 | public function samara(): Timezone 49 | { 50 | return ($this->of)('Europe/Samara'); 51 | } 52 | 53 | public function athens(): Timezone 54 | { 55 | return ($this->of)('Europe/Athens'); 56 | } 57 | 58 | public function tirane(): Timezone 59 | { 60 | return ($this->of)('Europe/Tirane'); 61 | } 62 | 63 | public function london(): Timezone 64 | { 65 | return ($this->of)('Europe/London'); 66 | } 67 | 68 | public function helsinki(): Timezone 69 | { 70 | return ($this->of)('Europe/Helsinki'); 71 | } 72 | 73 | public function oslo(): Timezone 74 | { 75 | return ($this->of)('Europe/Oslo'); 76 | } 77 | 78 | public function podgorica(): Timezone 79 | { 80 | return ($this->of)('Europe/Podgorica'); 81 | } 82 | 83 | public function minsk(): Timezone 84 | { 85 | return ($this->of)('Europe/Minsk'); 86 | } 87 | 88 | public function monaco(): Timezone 89 | { 90 | return ($this->of)('Europe/Monaco'); 91 | } 92 | 93 | public function lisbon(): Timezone 94 | { 95 | return ($this->of)('Europe/Lisbon'); 96 | } 97 | 98 | public function tallinn(): Timezone 99 | { 100 | return ($this->of)('Europe/Tallinn'); 101 | } 102 | 103 | public function berlin(): Timezone 104 | { 105 | return ($this->of)('Europe/Berlin'); 106 | } 107 | 108 | public function gibraltar(): Timezone 109 | { 110 | return ($this->of)('Europe/Gibraltar'); 111 | } 112 | 113 | public function prague(): Timezone 114 | { 115 | return ($this->of)('Europe/Prague'); 116 | } 117 | 118 | public function stockholm(): Timezone 119 | { 120 | return ($this->of)('Europe/Stockholm'); 121 | } 122 | 123 | public function moscow(): Timezone 124 | { 125 | return ($this->of)('Europe/Moscow'); 126 | } 127 | 128 | public function bucharest(): Timezone 129 | { 130 | return ($this->of)('Europe/Bucharest'); 131 | } 132 | 133 | public function andorra(): Timezone 134 | { 135 | return ($this->of)('Europe/Andorra'); 136 | } 137 | 138 | public function vilnius(): Timezone 139 | { 140 | return ($this->of)('Europe/Vilnius'); 141 | } 142 | 143 | public function rome(): Timezone 144 | { 145 | return ($this->of)('Europe/Rome'); 146 | } 147 | 148 | public function kiev(): Timezone 149 | { 150 | return ($this->of)('Europe/Kiev'); 151 | } 152 | 153 | public function copenhagen(): Timezone 154 | { 155 | return ($this->of)('Europe/Copenhagen'); 156 | } 157 | 158 | public function belgrade(): Timezone 159 | { 160 | return ($this->of)('Europe/Belgrade'); 161 | } 162 | 163 | public function isleOfMan(): Timezone 164 | { 165 | return ($this->of)('Europe/Isle_of_Man'); 166 | } 167 | 168 | public function budapest(): Timezone 169 | { 170 | return ($this->of)('Europe/Budapest'); 171 | } 172 | 173 | public function tiraspol(): Timezone 174 | { 175 | return ($this->of)('Europe/Tiraspol'); 176 | } 177 | 178 | public function vaduz(): Timezone 179 | { 180 | return ($this->of)('Europe/Vaduz'); 181 | } 182 | 183 | public function sarajevo(): Timezone 184 | { 185 | return ($this->of)('Europe/Sarajevo'); 186 | } 187 | 188 | public function amsterdam(): Timezone 189 | { 190 | return ($this->of)('Europe/Amsterdam'); 191 | } 192 | 193 | public function mariehamn(): Timezone 194 | { 195 | return ($this->of)('Europe/Mariehamn'); 196 | } 197 | 198 | public function skopje(): Timezone 199 | { 200 | return ($this->of)('Europe/Skopje'); 201 | } 202 | 203 | public function kaliningrad(): Timezone 204 | { 205 | return ($this->of)('Europe/Kaliningrad'); 206 | } 207 | 208 | public function bratislava(): Timezone 209 | { 210 | return ($this->of)('Europe/Bratislava'); 211 | } 212 | 213 | public function sanMarino(): Timezone 214 | { 215 | return ($this->of)('Europe/San_Marino'); 216 | } 217 | 218 | public function busingen(): Timezone 219 | { 220 | return ($this->of)('Europe/Busingen'); 221 | } 222 | 223 | public function zaporozhye(): Timezone 224 | { 225 | return ($this->of)('Europe/Zaporozhye'); 226 | } 227 | 228 | public function chisinau(): Timezone 229 | { 230 | return ($this->of)('Europe/Chisinau'); 231 | } 232 | 233 | public function brussels(): Timezone 234 | { 235 | return ($this->of)('Europe/Brussels'); 236 | } 237 | 238 | public function luxembourg(): Timezone 239 | { 240 | return ($this->of)('Europe/Luxembourg'); 241 | } 242 | 243 | public function belfast(): Timezone 244 | { 245 | return ($this->of)('Europe/Belfast'); 246 | } 247 | 248 | public function vienna(): Timezone 249 | { 250 | return ($this->of)('Europe/Vienna'); 251 | } 252 | 253 | public function ljubljana(): Timezone 254 | { 255 | return ($this->of)('Europe/Ljubljana'); 256 | } 257 | 258 | public function simferopol(): Timezone 259 | { 260 | return ($this->of)('Europe/Simferopol'); 261 | } 262 | 263 | public function dublin(): Timezone 264 | { 265 | return ($this->of)('Europe/Dublin'); 266 | } 267 | 268 | public function nicosia(): Timezone 269 | { 270 | return ($this->of)('Europe/Nicosia'); 271 | } 272 | 273 | public function zagreb(): Timezone 274 | { 275 | return ($this->of)('Europe/Zagreb'); 276 | } 277 | 278 | public function jersey(): Timezone 279 | { 280 | return ($this->of)('Europe/Jersey'); 281 | } 282 | 283 | public function madrid(): Timezone 284 | { 285 | return ($this->of)('Europe/Madrid'); 286 | } 287 | 288 | public function vatican(): Timezone 289 | { 290 | return ($this->of)('Europe/Vatican'); 291 | } 292 | 293 | public function istanbul(): Timezone 294 | { 295 | return ($this->of)('Europe/Istanbul'); 296 | } 297 | 298 | public function zurich(): Timezone 299 | { 300 | return ($this->of)('Europe/Zurich'); 301 | } 302 | 303 | public function sofia(): Timezone 304 | { 305 | return ($this->of)('Europe/Sofia'); 306 | } 307 | 308 | public function volgograd(): Timezone 309 | { 310 | return ($this->of)('Europe/Volgograd'); 311 | } 312 | 313 | public function malta(): Timezone 314 | { 315 | return ($this->of)('Europe/Malta'); 316 | } 317 | 318 | public function warsaw(): Timezone 319 | { 320 | return ($this->of)('Europe/Warsaw'); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/Timezone/Indian.php: -------------------------------------------------------------------------------- 1 | of)('Indian/Cocos'); 31 | } 32 | 33 | public function antananarivo(): Timezone 34 | { 35 | return ($this->of)('Indian/Antananarivo'); 36 | } 37 | 38 | public function reunion(): Timezone 39 | { 40 | return ($this->of)('Indian/Reunion'); 41 | } 42 | 43 | public function chagos(): Timezone 44 | { 45 | return ($this->of)('Indian/Chagos'); 46 | } 47 | 48 | public function comoro(): Timezone 49 | { 50 | return ($this->of)('Indian/Comoro'); 51 | } 52 | 53 | public function mayotte(): Timezone 54 | { 55 | return ($this->of)('Indian/Mayotte'); 56 | } 57 | 58 | public function maldives(): Timezone 59 | { 60 | return ($this->of)('Indian/Maldives'); 61 | } 62 | 63 | public function mauritius(): Timezone 64 | { 65 | return ($this->of)('Indian/Mauritius'); 66 | } 67 | 68 | public function mahe(): Timezone 69 | { 70 | return ($this->of)('Indian/Mahe'); 71 | } 72 | 73 | public function kerguelen(): Timezone 74 | { 75 | return ($this->of)('Indian/Kerguelen'); 76 | } 77 | 78 | public function christmas(): Timezone 79 | { 80 | return ($this->of)('Indian/Christmas'); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Timezone/Pacific.php: -------------------------------------------------------------------------------- 1 | of)('Pacific/Kosrae'); 31 | } 32 | 33 | public function enderbury(): Timezone 34 | { 35 | return ($this->of)('Pacific/Enderbury'); 36 | } 37 | 38 | public function apia(): Timezone 39 | { 40 | return ($this->of)('Pacific/Apia'); 41 | } 42 | 43 | public function noumea(): Timezone 44 | { 45 | return ($this->of)('Pacific/Noumea'); 46 | } 47 | 48 | public function chatham(): Timezone 49 | { 50 | return ($this->of)('Pacific/Chatham'); 51 | } 52 | 53 | public function wake(): Timezone 54 | { 55 | return ($this->of)('Pacific/Wake'); 56 | } 57 | 58 | public function wallis(): Timezone 59 | { 60 | return ($this->of)('Pacific/Wallis'); 61 | } 62 | 63 | public function johnston(): Timezone 64 | { 65 | return ($this->of)('Pacific/Johnston'); 66 | } 67 | 68 | public function saipan(): Timezone 69 | { 70 | return ($this->of)('Pacific/Saipan'); 71 | } 72 | 73 | public function tarawa(): Timezone 74 | { 75 | return ($this->of)('Pacific/Tarawa'); 76 | } 77 | 78 | public function pitcairn(): Timezone 79 | { 80 | return ($this->of)('Pacific/Pitcairn'); 81 | } 82 | 83 | public function niue(): Timezone 84 | { 85 | return ($this->of)('Pacific/Niue'); 86 | } 87 | 88 | public function ponape(): Timezone 89 | { 90 | return ($this->of)('Pacific/Ponape'); 91 | } 92 | 93 | public function guam(): Timezone 94 | { 95 | return ($this->of)('Pacific/Guam'); 96 | } 97 | 98 | public function auckland(): Timezone 99 | { 100 | return ($this->of)('Pacific/Auckland'); 101 | } 102 | 103 | public function pagoPago(): Timezone 104 | { 105 | return ($this->of)('Pacific/Pago_Pago'); 106 | } 107 | 108 | public function chuuk(): Timezone 109 | { 110 | return ($this->of)('Pacific/Chuuk'); 111 | } 112 | 113 | public function kwajalein(): Timezone 114 | { 115 | return ($this->of)('Pacific/Kwajalein'); 116 | } 117 | 118 | public function fakaofo(): Timezone 119 | { 120 | return ($this->of)('Pacific/Fakaofo'); 121 | } 122 | 123 | public function majuro(): Timezone 124 | { 125 | return ($this->of)('Pacific/Majuro'); 126 | } 127 | 128 | public function guadalcanal(): Timezone 129 | { 130 | return ($this->of)('Pacific/Guadalcanal'); 131 | } 132 | 133 | public function efate(): Timezone 134 | { 135 | return ($this->of)('Pacific/Efate'); 136 | } 137 | 138 | public function tongatapu(): Timezone 139 | { 140 | return ($this->of)('Pacific/Tongatapu'); 141 | } 142 | 143 | public function pohnpei(): Timezone 144 | { 145 | return ($this->of)('Pacific/Pohnpei'); 146 | } 147 | 148 | public function honolulu(): Timezone 149 | { 150 | return ($this->of)('Pacific/Honolulu'); 151 | } 152 | 153 | public function bougainville(): Timezone 154 | { 155 | return ($this->of)('Pacific/Bougainville'); 156 | } 157 | 158 | public function galapagos(): Timezone 159 | { 160 | return ($this->of)('Pacific/Galapagos'); 161 | } 162 | 163 | public function gambier(): Timezone 164 | { 165 | return ($this->of)('Pacific/Gambier'); 166 | } 167 | 168 | public function palau(): Timezone 169 | { 170 | return ($this->of)('Pacific/Palau'); 171 | } 172 | 173 | public function midway(): Timezone 174 | { 175 | return ($this->of)('Pacific/Midway'); 176 | } 177 | 178 | public function marquesas(): Timezone 179 | { 180 | return ($this->of)('Pacific/Marquesas'); 181 | } 182 | 183 | public function funafuti(): Timezone 184 | { 185 | return ($this->of)('Pacific/Funafuti'); 186 | } 187 | 188 | public function norfolk(): Timezone 189 | { 190 | return ($this->of)('Pacific/Norfolk'); 191 | } 192 | 193 | public function portMoresby(): Timezone 194 | { 195 | return ($this->of)('Pacific/Port_Moresby'); 196 | } 197 | 198 | public function tahiti(): Timezone 199 | { 200 | return ($this->of)('Pacific/Tahiti'); 201 | } 202 | 203 | public function fiji(): Timezone 204 | { 205 | return ($this->of)('Pacific/Fiji'); 206 | } 207 | 208 | public function kiritimati(): Timezone 209 | { 210 | return ($this->of)('Pacific/Kiritimati'); 211 | } 212 | 213 | public function truk(): Timezone 214 | { 215 | return ($this->of)('Pacific/Truk'); 216 | } 217 | 218 | public function easter(): Timezone 219 | { 220 | return ($this->of)('Pacific/Easter'); 221 | } 222 | 223 | public function rarotonga(): Timezone 224 | { 225 | return ($this->of)('Pacific/Rarotonga'); 226 | } 227 | 228 | public function yap(): Timezone 229 | { 230 | return ($this->of)('Pacific/Yap'); 231 | } 232 | 233 | public function nauru(): Timezone 234 | { 235 | return ($this->of)('Pacific/Nauru'); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/Timezones.php: -------------------------------------------------------------------------------- 1 | of)('UTC'); 42 | } 43 | 44 | public function africa(): Africa 45 | { 46 | return Africa::new($this->of); 47 | } 48 | 49 | public function america(): America 50 | { 51 | return America::new($this->of); 52 | } 53 | 54 | public function antartica(): Antartica 55 | { 56 | return Antartica::new($this->of); 57 | } 58 | 59 | public function arctic(): Arctic 60 | { 61 | return Arctic::new($this->of); 62 | } 63 | 64 | public function asia(): Asia 65 | { 66 | return Asia::new($this->of); 67 | } 68 | 69 | public function atlantic(): Atlantic 70 | { 71 | return Atlantic::new($this->of); 72 | } 73 | 74 | public function australia(): Australia 75 | { 76 | return Australia::new($this->of); 77 | } 78 | 79 | public function europe(): Europe 80 | { 81 | return Europe::new($this->of); 82 | } 83 | 84 | public function indian(): Indian 85 | { 86 | return Indian::new($this->of); 87 | } 88 | 89 | public function pacific(): Pacific 90 | { 91 | return Pacific::new($this->of); 92 | } 93 | } 94 | --------------------------------------------------------------------------------