├── .phive
└── phars.xml
├── Dockerfile
├── LICENSE
├── README.md
├── composer.json
├── docs.Dockerfile
├── docs
├── config
│ ├── __init__.py
│ └── all.py
├── en
│ ├── 3-x-migration-guide.rst
│ ├── conf.py
│ ├── contents.rst
│ └── index.rst
├── fr
│ ├── conf.py
│ ├── contents.rst
│ └── index.rst
├── ja
│ ├── conf.py
│ ├── contents.rst
│ └── index.rst
└── pt
│ ├── conf.py
│ ├── contents.rst
│ └── index.rst
├── psalm-baseline.xml
├── psalm.xml
└── src
├── Chronos.php
├── ChronosDate.php
├── ChronosTime.php
├── ClockFactory.php
├── DifferenceFormatter.php
├── DifferenceFormatterInterface.php
├── FormattingTrait.php
└── Translator.php
/.phive/phars.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Basic docker based environment
2 | # Necessary to trick dokku into building the documentation
3 | # using dockerfile instead of herokuish
4 | FROM php:8.1
5 |
6 | WORKDIR /code
7 |
8 | VOLUME ["/code"]
9 |
10 | CMD [ '/bin/bash' ]
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) Brian Nesbitt
2 | Copyright (C) Cake Software Foundation, Inc. (https://cakefoundation.org)
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP Chronos
2 |
3 | 
4 | [](https://packagist.org/packages/cakephp/chronos)
5 | [](https://packagist.org/packages/cakephp/chronos/stats)
6 | [](https://coveralls.io/r/cakephp/chronos?branch=master)
7 | [](LICENSE)
8 |
9 | Chronos focuses on providing immutable date/datetime objects.
10 | Immutable objects help ensure that datetime objects aren't accidentally
11 | modified, keeping data more predictable.
12 |
13 | # Installation
14 |
15 | Installing with composer:
16 |
17 | ```
18 | $ composer require cakephp/chronos
19 | ```
20 |
21 | For details on the (minimum/maximum) PHP version see [version map](https://github.com/cakephp/chronos/wiki#version-map).
22 |
23 | # Usage
24 |
25 | ```php
26 | modify('+2 hours');
53 |
54 | // This will keep modifications
55 | $date = new Chronos('2015-10-21 16:29:00');
56 | $date = $date->modify('+2 hours');
57 | ```
58 |
59 | # Calendar Dates
60 |
61 | PHP only offers datetime objects as part of the native extensions. Chronos adds
62 | a number of conveniences to the traditional DateTime object and introduces
63 | a `ChronosDate` object. `ChronosDate` instances their time frozen to `00:00:00` and the timezone
64 | set to the server default timezone. This makes them ideal when working with
65 | calendar dates as the time components will always match.
66 |
67 | ```php
68 | use Cake\Chronos\ChronosDate;
69 |
70 | $today = new ChronosDate();
71 | echo $today;
72 | // Outputs '2015-10-21'
73 |
74 | echo $today->modify('+3 hours');
75 | // Outputs '2015-10-21'
76 | ```
77 |
78 | Like instances of `Chronos`, `ChronosDate` objects are also *immutable*.
79 |
80 | # Documentation
81 |
82 | A more descriptive documentation can be found at [book.cakephp.org/chronos/3/en/](https://book.cakephp.org/chronos/3/en/).
83 |
84 | # API Documentation
85 |
86 | API documentation can be found on [api.cakephp.org/chronos](https://api.cakephp.org/chronos).
87 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cakephp/chronos",
3 | "description": "A simple API extension for DateTime.",
4 | "license": "MIT",
5 | "type": "library",
6 | "keywords": [
7 | "date",
8 | "time",
9 | "DateTime"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Brian Nesbitt",
14 | "email": "brian@nesbot.com",
15 | "homepage": "http://nesbot.com"
16 | },
17 | {
18 | "name": "The CakePHP Team",
19 | "homepage": "https://cakephp.org"
20 | }
21 | ],
22 | "homepage": "https://cakephp.org",
23 | "support": {
24 | "issues": "https://github.com/cakephp/chronos/issues",
25 | "source": "https://github.com/cakephp/chronos"
26 | },
27 | "require": {
28 | "php": ">=8.1",
29 | "psr/clock": "^1.0"
30 | },
31 | "require-dev": {
32 | "cakephp/cakephp-codesniffer": "^5.0",
33 | "phpunit/phpunit": "^10.1.0 || ^11.1.3"
34 | },
35 | "provide": {
36 | "psr/clock-implementation": "1.0"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "Cake\\Chronos\\": "src/"
41 | }
42 | },
43 | "autoload-dev": {
44 | "psr-4": {
45 | "Cake\\Chronos\\Test\\": "tests/"
46 | }
47 | },
48 | "config": {
49 | "allow-plugins": {
50 | "dealerdirect/phpcodesniffer-composer-installer": true
51 | }
52 | },
53 | "scripts": {
54 | "check": [
55 | "@test",
56 | "@cs-check",
57 | "@stan"
58 | ],
59 | "cs-check": "phpcs --colors --parallel=16 -p",
60 | "cs-fix": "phpcbf --colors --parallel=16 -p",
61 | "phpstan": "tools/phpstan analyse",
62 | "psalm": "tools/psalm --show-info=false",
63 | "psalm-baseline": "tools/psalm --set-baseline=psalm-baseline.xml",
64 | "stan": [
65 | "@phpstan",
66 | "@psalm"
67 | ],
68 | "stan-baseline": "tools/phpstan --generate-baseline",
69 | "stan-setup": "phive install",
70 | "test": "phpunit"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/docs.Dockerfile:
--------------------------------------------------------------------------------
1 | # Generate the HTML output.
2 | FROM ghcr.io/cakephp/docs-builder as builder
3 |
4 | RUN pip install git+https://github.com/sphinx-contrib/video.git@master
5 |
6 | COPY docs /data/docs
7 | ENV LANGS="en fr ja pt"
8 |
9 | # build docs with sphinx
10 | RUN cd /data/docs-builder && \
11 | make website LANGS="$LANGS" SOURCE=/data/docs DEST=/data/website
12 |
13 | # Build a small nginx container with just the static site in it.
14 | FROM ghcr.io/cakephp/docs-builder:runtime as runtime
15 |
16 | ENV LANGS="en fr ja pt"
17 | ENV SEARCH_SOURCE="/usr/share/nginx/html"
18 | ENV SEARCH_URL_PREFIX="/chronos/3"
19 |
20 | COPY --from=builder /data/docs /data/docs
21 | COPY --from=builder /data/website /data/website
22 | COPY --from=builder /data/docs-builder/nginx.conf /etc/nginx/conf.d/default.conf
23 |
24 | # Move docs into place.
25 | RUN cp -R /data/website/html/* /usr/share/nginx/html \
26 | && rm -rf /data/website
27 |
--------------------------------------------------------------------------------
/docs/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cakephp/chronos/8187733ac77bb0a2e20e1fb5ca57a8696d013fdd/docs/config/__init__.py
--------------------------------------------------------------------------------
/docs/config/all.py:
--------------------------------------------------------------------------------
1 | # Global configuration information used across all the
2 | # translations of documentation.
3 | #
4 | # Import the base theme configuration
5 | from cakephpsphinx.config.all import *
6 |
7 | # The version info for the project you're documenting, acts as replacement for
8 | # |version| and |release|, also used in various other places throughout the
9 | # built documents.
10 | #
11 |
12 | # The full version, including alpha/beta/rc tags.
13 | release = '3.x'
14 |
15 | # The search index version.
16 | search_version = 'chronos-3'
17 |
18 | # The marketing display name for the book.
19 | version_name = ''
20 |
21 | # Project name shown in the black header bar
22 | project = 'Chronos'
23 |
24 | # Other versions that display in the version picker menu.
25 | version_list = [
26 | {'name': '1.x', 'number': '/chronos/1', 'title': '1.x'},
27 | {'name': '2.x', 'number': '/chronos/2', 'title': '2.x'},
28 | {'name': '3.x', 'number': '/chronos/3', 'title': '3.x', 'current': True},
29 | ]
30 |
31 | # Languages available.
32 | languages = ['en', 'fr', 'ja', 'pt']
33 |
34 | # The GitHub branch name for this version of the docs
35 | # for edit links to point at.
36 | branch = '3.x'
37 |
38 | # Current version being built
39 | version = '3.x'
40 |
41 | # Language in use for this directory.
42 | language = 'en'
43 |
44 | show_root_link = True
45 |
46 | repository = 'cakephp/chronos'
47 |
48 | source_path = 'docs/'
49 |
50 | hide_page_contents = ('search', '404', 'contents')
51 |
--------------------------------------------------------------------------------
/docs/en/3-x-migration-guide.rst:
--------------------------------------------------------------------------------
1 | 3.x Migration Guide
2 | ###################
3 |
4 | Chronos 3.x contains breaking changes that could impact your application. This
5 | guide provides an overview of the breaking changes made in 3.x
6 |
7 | Minimum of PHP 8.1
8 | ==================
9 |
10 | Chronos 3.x requires at least PHP 8.1. This allows chronos to provide more
11 | comprehensive typehinting and better performance by leveraging features found in
12 | newer PHP versions.
13 |
14 | MutableDateTime and MutableDate removed
15 | =======================================
16 |
17 | The ``MutableDateTime`` and ``MutableDate`` classes have been removed. Long term
18 | PHP will be deprecating and removing mutable datetime classes in favour of
19 | immutable ones. Chronos has long favoured immutable objects and removing the
20 | mutable variants helps simplify the internals of Chronos and encourages safer
21 | development practices.
22 |
--------------------------------------------------------------------------------
/docs/en/conf.py:
--------------------------------------------------------------------------------
1 | import sys, os
2 |
3 | # Append the top level directory of the docs, so we can import from the config dir.
4 | sys.path.insert(0, os.path.abspath('..'))
5 |
6 | # Pull in all the configuration options defined in the global config file..
7 | from config.all import *
8 |
9 | language = 'en'
10 |
--------------------------------------------------------------------------------
/docs/en/contents.rst:
--------------------------------------------------------------------------------
1 | .. toctree::
2 | :maxdepth: 2
3 | :caption: CakePHP Chronos
4 |
5 | /index
6 |
7 | API
--------------------------------------------------------------------------------
/docs/en/index.rst:
--------------------------------------------------------------------------------
1 | Chronos
2 | #######
3 |
4 | Chronos provides a zero-dependency ``DateTimeImmutable`` extension, Date-only and Time-only classes:
5 |
6 | * ``Cake\Chronos\Chronos`` extends ``DateTimeImmutable`` and provides many helpers.
7 | * ``Cake\Chronos\ChronosDate`` represents calendar dates unaffected by time or time zones.
8 | * ``Cake\Chronos\ChronosTime`` represents clock times independent of date or time zones.
9 | * Only safe, immutable objects.
10 | * A pluggable translation system. Only English translations are included in the
11 | library. However, ``cakephp/i18n`` can be used for full language support.
12 |
13 | The ``Chronos`` class extends ``DateTimeImmutable`` and implements ``DateTimeInterface``
14 | which allows users to use type declarations that support either.
15 |
16 | ``ChronosDate`` and ``ChronosTime`` do not extend ``DateTimeImmutable`` and do not
17 | share an interface. However, they can be converted to a ``DateTimeImmutable`` instance
18 | using ``toDateTimeImmutable()``.
19 |
20 | Installation
21 | ------------
22 |
23 | To install Chronos, you should use ``composer``. From your
24 | application's ROOT directory (where composer.json file is located) run the
25 | following::
26 |
27 | php composer.phar require "cakephp/chronos:^3.0"
28 |
29 | Creating Instances
30 | ------------------
31 |
32 | There are many ways to get an instance of Chronos or Date. There are a number of
33 | factory methods that work with different argument sets::
34 |
35 | use Cake\Chronos\Chronos;
36 |
37 | $now = Chronos::now();
38 | $today = Chronos::today();
39 | $yesterday = Chronos::yesterday();
40 | $tomorrow = Chronos::tomorrow();
41 |
42 | // Parse relative expressions
43 | $date = Chronos::parse('+2 days, +3 hours');
44 |
45 | // Date and time integer values.
46 | $date = Chronos::create(2015, 12, 25, 4, 32, 58);
47 |
48 | // Date or time integer values.
49 | $date = Chronos::createFromDate(2015, 12, 25);
50 | $date = Chronos::createFromTime(11, 45, 10);
51 |
52 | // Parse formatted values.
53 | $date = Chronos::createFromFormat('m/d/Y', '06/15/2015');
54 |
55 | Working with Immutable Objects
56 | ------------------------------
57 |
58 | Chronos provides only *immutable* objects.
59 |
60 | If you've used PHP ``DateTimeImmutable`` and ``DateTime`` classes, then you understand
61 | the difference between *mutable* and *immutable* objects.
62 |
63 | Immutable objects create copies of an object each time a change is made. Because modifier methods
64 | around datetimes are not always easy to identify, data can be modified accidentally
65 | or without the developer knowing. Immutable objects prevent accidental changes
66 | to data, and make code free of order-based dependency issues. Immutability does
67 | mean that you will need to remember to replace variables when using modifiers::
68 |
69 | // This code doesn't work with immutable objects
70 | $chronos->addDay(1);
71 | doSomething($chronos);
72 | return $chronos;
73 |
74 | // This works like you'd expect
75 | $chronos = $chronos->addDay(1);
76 | $chronos = doSomething($chronos);
77 | return $chronos;
78 |
79 | By capturing the return value of each modification your code will work as
80 | expected.
81 |
82 | Date Objects
83 | ------------
84 |
85 | PHP provides only date-time classes that combines both dates and time parts.
86 | Representing calendar dates can be a bit awkward with ``DateTimeImmutable`` as it includes
87 | time and timezones, which aren't part of a 'date'. Chronos provides
88 | ``ChronosDate`` that allows you to represent dates. The time these objects
89 | these objects is always fixed to ``00:00:00`` and not affeced by the server time zone
90 | or modify helpers::
91 |
92 | use Cake\Chronos\ChronosDate;
93 |
94 | $today = ChronosDate::today();
95 |
96 | // Changes to the time/timezone are ignored.
97 | $today->modify('+1 hours');
98 |
99 | // Outputs '2015-12-20'
100 | echo $today;
101 |
102 | Although ``ChronosDate`` uses a fixed time zone internally, you can specify which
103 | time zone to use for current time such as ``now()`` or ``today()``::
104 |
105 | use Cake\Chronos\ChronosDate:
106 |
107 | // Takes the current date from Asia/Tokyo time zone
108 | $today = ChronosDate::today('Asia/Tokyo');
109 |
110 | Modifier Methods
111 | ----------------
112 |
113 | Chronos objects provide modifier methods that let you modify the value in
114 | a granular way::
115 |
116 | // Set components of the datetime value.
117 | $halloween = Chronos::create()
118 | ->year(2015)
119 | ->month(10)
120 | ->day(31)
121 | ->hour(20)
122 | ->minute(30);
123 |
124 | You can also modify parts of the datetime relatively::
125 |
126 | $future = Chronos::create()
127 | ->addYears(1)
128 | ->subMonths(2)
129 | ->addDays(15)
130 | ->addHours(20)
131 | ->subMinutes(2);
132 |
133 | It is also possible to make big jumps to defined points in time::
134 |
135 | $time = Chronos::create();
136 | $time->startOfDay();
137 | $time->endOfDay();
138 | $time->startOfMonth();
139 | $time->endOfMonth();
140 | $time->startOfYear();
141 | $time->endOfYear();
142 | $time->startOfWeek();
143 | $time->endOfWeek();
144 |
145 | Or jump to specific days of the week::
146 |
147 | $time->next(Chronos::TUESDAY);
148 | $time->previous(Chronos::MONDAY);
149 |
150 | When modifying dates/times across :abbr:`DST (Daylight Savings Time)` transitions
151 | your operations may gain/lose an additional hours resulting in hour values that
152 | don't add up. You can avoid these issues by first changing your timezone to
153 | ``UTC``, modifying the time::
154 |
155 | // Additional hour gained.
156 | $time = new Chronos('2014-03-30 00:00:00', 'Europe/London');
157 | debug($time->modify('+24 hours')); // 2014-03-31 01:00:00
158 |
159 | // First switch to UTC, and modify
160 | $time = $time->setTimezone('UTC')
161 | ->modify('+24 hours');
162 |
163 | Once you are done modifying the time you can add the original timezone to get
164 | the localized time.
165 |
166 | Comparison Methods
167 | ------------------
168 |
169 | Once you have 2 instances of Chronos date/time objects you can compare them in
170 | a variety of ways::
171 |
172 | // Full suite of comparators exist
173 | // equals, notEquals, greaterThan, greaterThanOrEquals, lessThan, lessThanOrEquals
174 | $first->equals($second);
175 | $first->greaterThanOrEquals($second);
176 |
177 | // See if the current object is between two others.
178 | $now->between($start, $end);
179 |
180 | // Find which argument is closest or farthest.
181 | $now->closest($june, $november);
182 | $now->farthest($june, $november);
183 |
184 | You can also inquire about where a given value falls on the calendar::
185 |
186 | $now->isToday();
187 | $now->isYesterday();
188 | $now->isFuture();
189 | $now->isPast();
190 |
191 | // Check the day of the week
192 | $now->isWeekend();
193 |
194 | // All other weekday methods exist too.
195 | $now->isMonday();
196 |
197 | You can also find out if a value was within a relative time period::
198 |
199 | $time->wasWithinLast('3 days');
200 | $time->isWithinNext('3 hours');
201 |
202 | Generating Differences
203 | ----------------------
204 |
205 | In addition to comparing datetimes, calculating differences or deltas between
206 | two values is a common task::
207 |
208 | // Get a DateInterval representing the difference
209 | $first->diff($second);
210 |
211 | // Get difference as a count of specific units.
212 | $first->diffInHours($second);
213 | $first->diffInDays($second);
214 | $first->diffInWeeks($second);
215 | $first->diffInYears($second);
216 |
217 | You can generate human readable differences suitable for use in a feed or
218 | timeline::
219 |
220 | // Difference from now.
221 | echo $date->diffForHumans();
222 |
223 | // Difference from another point in time.
224 | echo $date->diffForHumans($other); // 1 hour ago;
225 |
226 | Formatting Strings
227 | ------------------
228 |
229 | Chronos provides a number of methods for displaying our outputting datetime
230 | objects::
231 |
232 | // Uses the format controlled by setToStringFormat()
233 | echo $date;
234 |
235 | // Different standard formats
236 | echo $time->toAtomString(); // 1975-12-25T14:15:16-05:00
237 | echo $time->toCookieString(); // Thursday, 25-Dec-1975 14:15:16 EST
238 | echo $time->toIso8601String(); // 1975-12-25T14:15:16-05:00
239 | echo $time->toRfc822String(); // Thu, 25 Dec 75 14:15:16 -0500
240 | echo $time->toRfc850String(); // Thursday, 25-Dec-75 14:15:16 EST
241 | echo $time->toRfc1036String(); // Thu, 25 Dec 75 14:15:16 -0500
242 | echo $time->toRfc1123String(); // Thu, 25 Dec 1975 14:15:16 -0500
243 | echo $time->toRfc2822String(); // Thu, 25 Dec 1975 14:15:16 -0500
244 | echo $time->toRfc3339String(); // 1975-12-25T14:15:16-05:00
245 | echo $time->toRssString(); // Thu, 25 Dec 1975 14:15:16 -0500
246 | echo $time->toW3cString(); // 1975-12-25T14:15:16-05:00
247 |
248 | // Get the quarter/week
249 | echo $time->toQuarter(); // 4
250 | echo $time->toWeek(); // 52
251 |
252 | // Generic formatting
253 | echo $time->toTimeString(); // 14:15:16
254 | echo $time->toDateString(); // 1975-12-25
255 | echo $time->toDateTimeString(); // 1975-12-25 14:15:16
256 | echo $time->toFormattedDateString(); // Dec 25, 1975
257 | echo $time->toDayDateTimeString(); // Thu, Dec 25, 1975 2:15 PM
258 |
259 | Extracting Date Components
260 | --------------------------
261 |
262 | Getting parts of a date object can be done by directly accessing properties::
263 |
264 | $time = new Chronos('2015-12-31 23:59:58.123');
265 | $time->year; // 2015
266 | $time->month; // 12
267 | $time->day; // 31
268 | $time->hour // 23
269 | $time->minute // 59
270 | $time->second // 58
271 | $time->micro // 123
272 |
273 | Other properties that can be accessed are:
274 |
275 | - timezone
276 | - timezoneName
277 | - dayOfWeek
278 | - dayOfMonth
279 | - dayOfYear
280 | - daysInMonth
281 | - timestamp
282 | - quarter
283 | - half
284 |
285 | Testing Aids
286 | ------------
287 |
288 | When writing unit tests, it is helpful to fixate the current time. Chronos lets
289 | you fix the current time for each class. As part of your test suite's bootstrap
290 | process you can include the following::
291 |
292 | Chronos::setTestNow(Chronos::now());
293 | ChronosDate::setTestNow(ChronosDate::parse(Chronos::now()));
294 |
295 | This will fix the current time of all objects to be the point at which the test
296 | suite started.
297 |
298 | For example, if you fixate the ``Chronos`` to some moment in the past, any new
299 | instance of ``Chronos`` created with ``now`` or a relative time string, will be
300 | returned relative to the fixated time::
301 |
302 | Chronos::setTestNow(new Chronos('1975-12-25 00:00:00'));
303 |
304 | $time = new Chronos(); // 1975-12-25 00:00:00
305 | $time = new Chronos('1 hour ago'); // 1975-12-24 23:00:00
306 |
307 | To reset the fixation, simply call ``setTestNow()`` again with no parameter or
308 | with ``null`` as a parameter.
309 |
--------------------------------------------------------------------------------
/docs/fr/conf.py:
--------------------------------------------------------------------------------
1 | import sys, os
2 |
3 | # Append the top level directory of the docs, so we can import from the config dir.
4 | sys.path.insert(0, os.path.abspath('..'))
5 |
6 | # Pull in all the configuration options defined in the global config file..
7 | from config.all import *
8 |
9 | language = 'fr'
10 |
--------------------------------------------------------------------------------
/docs/fr/contents.rst:
--------------------------------------------------------------------------------
1 | .. toctree::
2 | :maxdepth: 2
3 | :caption: CakePHP Chronos
4 |
5 | /index
6 |
7 | API
--------------------------------------------------------------------------------
/docs/fr/index.rst:
--------------------------------------------------------------------------------
1 | Chronos
2 | #######
3 |
4 | Chronos fournit une collection d'extensions sans aucune dépendance pour l'objet
5 | ``DateTime``. En plus de méthodes pratiques, Chronos fournit:
6 |
7 | * Des objets ``Date`` pour représenter les dates du calendrier.
8 | * Des objets immutables pour les dates et les datetimes.
9 | * Un système de traduction intégrable. Seules les traductions anglaises sont
10 | incluses dans la librairie. Cependant, ``cakephp/i18n`` peut être utilisé
11 | pour un support complet d'autres langues.
12 |
13 | Installation
14 | ------------
15 |
16 | Pour installer Chronos, vous devez utiliser ``composer``. À partir du répertoire
17 | ROOT de votre application (celui où se trouve le fichier composer.json),
18 | exécutez ce qui suit::
19 |
20 | php composer.phar require "cakephp/chronos:^2.0"
21 |
22 | Vue d'Ensemble
23 | --------------
24 |
25 | Chronos fournit un certain nombre d'extensions pour les objets DateTime fournis
26 | par PHP. Chronos fournit 5 classes qui gèrent les variantes mutables et
27 | immutables de date/time et les extensions de ``DateInterval``.
28 |
29 | * ``Cake\Chronos\Chronos`` est un objet de *date et heure* immutable.
30 | * ``Cake\Chronos\ChronosDate`` est un objet de *date* immutable.
31 | * ``Cake\Chronos\MutableDateTime`` est un objet de *date et heure* mutable.
32 | * ``Cake\Chronos\MutableDate`` est un objet de *date* mutable.
33 | * ``Cake\Chronos\ChronosInterval`` est une extension pour l'objet
34 | ``DateInterval``.
35 |
36 | Créer des Instances
37 | -------------------
38 |
39 | Il y a plusieurs façons d'obtenir une instance de Chronos ou de Date. Il y a
40 | un certain nombre de méthodes factory qui fonctionnent avec différents ensembles
41 | d'arguments::
42 |
43 | use Cake\Chronos\Chronos;
44 |
45 | $now = Chronos::now();
46 | $today = Chronos::today();
47 | $yesterday = Chronos::yesterday();
48 | $tomorrow = Chronos::tomorrow();
49 |
50 | // Parse les expressions relatives
51 | $date = Chronos::parse('+2 days, +3 hours');
52 |
53 | // Des entiers indiquant la date et l'heure.
54 | $date = Chronos::create(2015, 12, 25, 4, 32, 58);
55 |
56 | // Des entiers indiquant la date ou l'heure.
57 | $date = Chronos::createFromDate(2015, 12, 25);
58 | $date = Chronos::createFromTime(11, 45, 10);
59 |
60 | // Parse les valeurs formatées.
61 | $date = Chronos::createFromFormat('m/d/Y', '06/15/2015');
62 |
63 | Travailler avec les Objets Immutables
64 | -------------------------------------
65 |
66 | Si vous avez utilisé les objets ``DateTime`` de PHP, vous êtes à l'aise avec
67 | les objets *mutable*. Chronos offre des objets mutables, mais elle fournit
68 | également des objets *immutables*. Les objets Immutables créent des copies des
69 | objets à chaque fois qu'un objet est modifié. Puisque les méthodes de
70 | modification autour des datetimes ne sont pas toujours transparentes, les
71 | données peuvent être modifiées accidentellement ou sans que le développeur ne
72 | le sache. Les objets immutables évitent les changements accidentels des
73 | données et permettent de s'affranchir de tout problème lié à l'ordre d'appel
74 | des fonctions ou des dépendances. L'immutabilité signifie que vous devez vous
75 | souvenir de remplacer les variables quand vous utilisez les modificateurs::
76 |
77 | // Ce code ne fonctionne pas avec les objets immutables
78 | $time->addDay(1);
79 | doSomething($time);
80 | return $time;
81 |
82 | // Ceci fonctionne comme vous le souhaitez
83 | $time = $time->addDay(1);
84 | $time = doSomething($time);
85 | return $time;
86 |
87 | En capturant la valeur de retour pour chaque modification, votre code
88 | fonctionnera comme souhaité. Si vous avez déjà créé un objet immutable, et que
89 | vous souhaitez un objet mutable, vous pouvez utiliser ``toMutable()``::
90 |
91 | $inplace = $time->toMutable();
92 |
93 | Objets Date
94 | -----------
95 |
96 | PHP fournit seulement un unique objet DateTime. Représenter les dates de
97 | calendrier peut être un peu gênant avec cette classe puisqu'elle inclut les
98 | timezones, et les composants de time qui n'appartiennent pas vraiment
99 | au concept d'un 'jour'. Chronos fournit un objet ``Date`` qui vous permet
100 | de représenter les dates. Les time et timezone pour ces objets sont toujours
101 | fixés à ``00:00:00 UTC`` et toutes les méthodes de formatage/différence
102 | fonctionnent au niveau du jour::
103 |
104 | use Cake\Chronos\ChronosDate;
105 |
106 | $today = ChronosDate::today();
107 |
108 | // Les changements selon le time/timezone sont ignorés.
109 | $today->modify('+1 hours');
110 |
111 | // Affiche '2015-12-20'
112 | echo $today;
113 |
114 | Bien que ``Date`` utilise en interne un fuseau horaire fixe, vous pouvez
115 | spécifier le fuseau à utiliser pour l'heure courante telle que ``now()`` ou
116 | ``today()``::
117 |
118 | use Cake\Chronos\ChronosDate:
119 |
120 | // Prend l'heure courante pour le fuseau horaire de Tokyo
121 | $today = ChronosDate::today('Asia/Tokyo');
122 |
123 |
124 | Méthodes de Modification
125 | ------------------------
126 |
127 | Les objets Chronos fournissent des méthodes de modification qui vous laissent
128 | modifier la valeur d'une façon assez précise::
129 |
130 | // Définit les composants de la valeur du datetime.
131 | $halloween = Chronos::create()
132 | ->year(2015)
133 | ->month(10)
134 | ->day(31)
135 | ->hour(20)
136 | ->minute(30);
137 |
138 | Vous pouvez aussi modifier les parties de la date de façon relative::
139 |
140 | $future = Chronos::create()
141 | ->addYear(1)
142 | ->subMonth(2)
143 | ->addDays(15)
144 | ->addHours(20)
145 | ->subMinutes(2);
146 |
147 | Il est également possible de faire des sauts vers des points définis dans le
148 | temps::
149 |
150 | $time = Chronos::create();
151 | $time->startOfDay();
152 | $time->endOfDay();
153 | $time->startOfMonth();
154 | $time->endOfMonth();
155 | $time->startOfYear();
156 | $time->endOfYear();
157 | $time->startOfWeek();
158 | $time->endOfWeek();
159 |
160 | Ou de sauter à un jour spécifique de la semaine::
161 |
162 | $time->next(Chronos::TUESDAY);
163 | $time->previous(Chronos::MONDAY);
164 |
165 | Quand vous modifiez des dates/heures au-delà d'un passage à l'heure d'été ou à
166 | l'heure d'hiver, vous opérations peuvent gagner/perdre une heure de plus, de
167 | sorte que les heures seront incorrectes. Vous pouvez éviter ce problème en
168 | définissant d'abord le timezone à ``UTC``, ce qui change l'heure::
169 |
170 | // Une heure de plus de gagnée.
171 | $time = new Chronos('2014-03-30 00:00:00', 'Europe/London');
172 | debug($time->modify('+24 hours')); // 2014-03-31 01:00:00
173 |
174 | // Passez d'abord à UTC, et modifiez ensuite
175 | $time = $time->setTimezone('UTC')
176 | ->modify('+24 hours');
177 |
178 | Une fois que vous avez modifié l'heure, vous pouvez repasser au timezone
179 | d'origine pour obtenir l'heure locale.
180 |
181 | Méthodes de Comparaison
182 | -----------------------
183 |
184 | Une fois que vous avez 2 instances d'objets date/time de Chronos, vous pouvez
185 | les comparer de plusieurs façons::
186 |
187 | // Il existe une suite complète de comparateurs
188 | // equals, notEquals, greaterThan, greaterThanOrEquals, lessThan, lessThanOrEquals
189 | $first->equals($second);
190 | $first->greaterThanOrEquals($second);
191 |
192 | // Regarder si l'objet courant est entre deux autres.
193 | $now->between($start, $end);
194 |
195 | // Trouver l'argument le plus proche ou le plus éloigné.
196 | $now->closest($june, $november);
197 | $now->farthest($june, $november);
198 |
199 | Vous pouvez aussi vous renseigner sur le moment où une valeur donnée tombe dans
200 | le calendrier::
201 |
202 | $now->isToday();
203 | $now->isYesterday();
204 | $now->isFuture();
205 | $now->isPast();
206 |
207 | // Vérifie le jour de la semaine
208 | $now->isWeekend();
209 |
210 | // Toutes les autres méthodes des jours de la semaine existent aussi.
211 | $now->isMonday();
212 |
213 | Vous pouvez aussi trouver si une valeur était dans une période de temps relative::
214 |
215 | $time->wasWithinLast('3 days');
216 | $time->isWithinNext('3 hours');
217 |
218 | Générer des Différences
219 | -----------------------
220 |
221 | En plus de comparer les datetimes, calculer les différences ou les deltas entre
222 | des valeurs est une tâche courante::
223 |
224 | // Récupère un DateInterval représentant la différence
225 | $first->diff($second);
226 |
227 | // Récupère la différence en tant que nombre d'unités spécifiques.
228 | $first->diffInHours($second);
229 | $first->diffInDays($second);
230 | $first->diffInWeeks($second);
231 | $first->diffInYears($second);
232 |
233 | Vous pouvez générer des différences lisibles qui peuvent vous servir pour
234 | l'utilisation d'un feed ou d'une timeline::
235 |
236 | // Différence à partir de maintenant.
237 | echo $date->diffForHumans();
238 |
239 | // Différence à partir d'un autre point du temps.
240 | echo $date->diffForHumans($other); // 1 hour ago;
241 |
242 | Formater les Chaînes
243 | --------------------
244 |
245 | Chronos fournit un certain nombre de méthodes pour afficher nos sorties d'objets
246 | datetime::
247 |
248 | // Utilise le format contrôlé par setToStringFormat()
249 | echo $date;
250 |
251 | // Différents formats standards
252 | echo $time->toAtomString(); // 1975-12-25T14:15:16-05:00
253 | echo $time->toCookieString(); // Thursday, 25-Dec-1975 14:15:16 EST
254 | echo $time->toIso8601String(); // 1975-12-25T14:15:16-05:00
255 | echo $time->toRfc822String(); // Thu, 25 Dec 75 14:15:16 -0500
256 | echo $time->toRfc850String(); // Thursday, 25-Dec-75 14:15:16 EST
257 | echo $time->toRfc1036String(); // Thu, 25 Dec 75 14:15:16 -0500
258 | echo $time->toRfc1123String(); // Thu, 25 Dec 1975 14:15:16 -0500
259 | echo $time->toRfc2822String(); // Thu, 25 Dec 1975 14:15:16 -0500
260 | echo $time->toRfc3339String(); // 1975-12-25T14:15:16-05:00
261 | echo $time->toRssString(); // Thu, 25 Dec 1975 14:15:16 -0500
262 | echo $time->toW3cString(); // 1975-12-25T14:15:16-05:00
263 |
264 | // Récupère le trimestre
265 | echo $time->toQuarter(); // 4;
266 | // Récupère la semaine
267 | echo $time->toWeek(); // 52;
268 |
269 | // Formatage générique
270 | echo $time->toTimeString(); // 14:15:16
271 | echo $time->toDateString(); // 1975-12-25
272 | echo $time->toDateTimeString(); // 1975-12-25 14:15:16
273 | echo $time->toFormattedDateString(); // Dec 25, 1975
274 | echo $time->toDayDateTimeString(); // Thu, Dec 25, 1975 2:15 PM
275 |
276 | Extraire des Fragments de Date
277 | ------------------------------
278 |
279 | Il est possible de récupérer des parties d'un objet date en accédant directement
280 | à ses propriétés::
281 |
282 | $time = new Chronos('2015-12-31 23:59:58.123');
283 | $time->year; // 2015
284 | $time->month; // 12
285 | $time->day; // 31
286 | $time->hour // 23
287 | $time->minute // 59
288 | $time->second // 58
289 | $time->micro // 123
290 |
291 | Les autres propriétés accessibles sont:
292 |
293 | - timezone
294 | - timezoneName
295 | - dayOfWeek
296 | - dayOfMonth
297 | - dayOfYear
298 | - daysInMonth
299 | - timestamp
300 | - quarter
301 | - half
302 |
303 | Aides aux Tests
304 | ---------------
305 |
306 | Quand vous écrivez des tests unitaires, il peut être utile de fixer le *time*
307 | courant. Chronos vous permet de fixer le time courant pour chaque classe.
308 | Pour l'intégrer dans votre processus de démarrage (bootstrap) de suite de tests,
309 | vous pouvez inclure ce qui suit::
310 |
311 | Chronos::setTestNow(Chronos::now());
312 | MutableDateTime::setTestNow(MutableDateTime::now());
313 | ChronosDate::setTestNow(ChronosDate::parse(Chronos::now()));
314 | MutableDate::setTestNow(MutableDate::now());
315 |
316 | Ceci va fixer le time courant de tous les objets selon le moment où la suite de
317 | tests a démarré.
318 |
319 | Par exemple, si vous fixez le ``Chronos`` à un moment du passé, chaque nouvelle
320 | instance de ``Chronos`` créée avec ``now`` ou une chaine de temps relative, sera
321 | retournée relativement à la date fixée::
322 |
323 | Chronos::setTestNow(new Chronos('1975-12-25 00:00:00'));
324 |
325 | $time = new Chronos(); // 1975-12-25 00:00:00
326 | $time = new Chronos('1 hour ago'); // 1975-12-24 23:00:00
327 |
328 | Pour réinitialiser la "fixation" du temps, appelez simplement ``setTestNow()``
329 | sans paramètre ou avec ``null`` comme paramètre.
330 |
--------------------------------------------------------------------------------
/docs/ja/conf.py:
--------------------------------------------------------------------------------
1 | import sys, os
2 |
3 | # Append the top level directory of the docs, so we can import from the config dir.
4 | sys.path.insert(0, os.path.abspath('..'))
5 |
6 | # Pull in all the configuration options defined in the global config file..
7 | from config.all import *
8 |
9 | language = 'ja'
10 |
--------------------------------------------------------------------------------
/docs/ja/contents.rst:
--------------------------------------------------------------------------------
1 | .. toctree::
2 | :maxdepth: 2
3 | :caption: CakePHP Chronos
4 |
5 | /index
6 |
7 | API
--------------------------------------------------------------------------------
/docs/ja/index.rst:
--------------------------------------------------------------------------------
1 | Chronos
2 | #######
3 |
4 | Chronos (クロノス) は、 ``DateTime`` オブジェクトへの拡張の依存関係の無いコレクションを提供します。
5 | 便利なメソッドに加えて、Chronos は以下を提供します。
6 |
7 | * カレンダー日付のための ``Date`` オブジェクト
8 | * イミュータブルな日付と日時オブジェクト
9 | * プラグインのような翻訳システム。ライブラリーは英語のみの翻訳を含んでいます。
10 | しかし、全ての言語サポートのために、 ``cakephp/i18n`` を使うことができます。
11 |
12 | インストール
13 | ------------
14 |
15 | Chronos をインストールするためには、 ``composer`` を利用することができます。
16 | アプリケーションの ROOT ディレクトリー(composer.json ファイルのある場所)
17 | で以下のように実行します。 ::
18 |
19 | php composer.phar require cakephp/chronos "@stable"
20 |
21 | 概要
22 | ----
23 |
24 | Chronos は PHP が提供する DateTime オブジェクトのいくつかの拡張を提供します。
25 | Chronos は ``DateInterval`` の拡張機能および、ミュータブル(変更可能)と
26 | イミュータブル(変更不可)な 日付/時刻 の派生系をカバーする5つのクラスを提供します。
27 |
28 | * ``Cake\Chronos\Chronos`` はイミュータブルな *日付と時刻* オブジェクト。
29 | * ``Cake\Chronos\ChronosDate`` はイミュータブルな *日付* オブジェクト。
30 | * ``Cake\Chronos\MutableDateTime`` はミュータブルな *日付と時刻* オブジェクト。
31 | * ``Cake\Chronos\MutableDate`` はミュータブルな *日付* オブジェクト。
32 | * ``Cake\Chronos\ChronosInterval`` は ``DateInterval`` の拡張機能。
33 |
34 | インスタンスの作成
35 | ------------------
36 |
37 | Chronos または Date のインスタンスを取得するためには、多くの方法があります。
38 | 異なる引数セットで動作する多くのファクトリーメソッドがあります。 ::
39 |
40 | use Cake\Chronos\Chronos;
41 |
42 | $now = Chronos::now();
43 | $today = Chronos::today();
44 | $yesterday = Chronos::yesterday();
45 | $tomorrow = Chronos::tomorrow();
46 |
47 | // 相対式のパース
48 | $date = Chronos::parse('+2 days, +3 hours');
49 |
50 | // 日付と時間の整数値
51 | $date = Chronos::create(2015, 12, 25, 4, 32, 58);
52 |
53 | // 日付または時間の整数値
54 | $date = Chronos::createFromDate(2015, 12, 25);
55 | $date = Chronos::createFromTime(11, 45, 10);
56 |
57 | // 整形した値にパース
58 | $date = Chronos::createFromFormat('m/d/Y', '06/15/2015');
59 |
60 | イミュータブルオブジェクトの動作
61 | --------------------------------
62 |
63 | もしあなたが、PHP の ``DateTime`` オブジェクトを使用したことがあるなら、
64 | *ミュータブル* オブジェクトは簡単に使用できます。
65 | Chronos はミュータブルオブジェクトを提供しますが、これは *イミュータブル* オブジェクトにもなります。
66 | イミュータブルオブジェクトはオブジェクトが変更されるたびにオブジェクトのコピーを作ります。
67 | なぜなら、日時周りの変更メソッドは必ずしも透明でないため、データが誤って、
68 | または開発者が知らない内に変更してしまうからです。
69 | イミュータブルオブジェクトはデータが誤って変更されることを防止し、
70 | 順序ベースの依存関係の問題の無いコードを作ります。
71 | 不変性は、変更時に忘れずに変数を置き換える必要があることを意味しています。 ::
72 |
73 | // このコードはイミュータブルオブジェクトでは動作しません
74 | $time->addDay(1);
75 | doSomething($time);
76 | return $time
77 |
78 | // このコードは期待通りに動作します
79 | $time = $time->addDay(1);
80 | $time = doSomething($time);
81 | return $time
82 |
83 | 各修正の戻り値をキャプチャーすることによって、コードは期待通りに動作します。
84 | イミュータブルオブジェクトを持っていて、ミュータブルオブジェクトを作りたい場合、
85 | ``toMutable()`` が使用できます。 ::
86 |
87 | $inplace = $time->toMutable();
88 |
89 | 日付オブジェクト
90 | ------------------
91 |
92 | PHP は単純な DateTime オブジェクトだけを提供します。このクラスのカレンダー日付の表現で、
93 | タイムゾーンおよび、本当に「日」の概念に属していないタイムコンポーネントを含むと、
94 | 少し厄介な可能性があります。
95 | Chronos は日時表現のための ``Date`` オブジェクトを提供します。
96 | これらのオブジェクトの時間とタイムゾーンは常に ``00:00:00 UTC`` に固定されており、
97 | 全ての書式/差分のメソッドは一日単位で動作します。 ::
98 |
99 | use Cake\Chronos\ChronosDate;
100 |
101 | $today = ChronosDate::today();
102 |
103 | // 時間/タイムゾーンの変更は無視されます
104 | $today->modify('+1 hours');
105 |
106 | // 出力 '2015-12-20'
107 | echo $today;
108 |
109 | 変更メソッド
110 | ------------
111 |
112 | Chronos オブジェクトは細やかに値を変更できるメソッドを提供します。 ::
113 |
114 | // 日時の値のコンポーネントを設定
115 | $halloween = Chronos::create()
116 | ->year(2015)
117 | ->month(10)
118 | ->day(31)
119 | ->hour(20)
120 | ->minute(30);
121 |
122 | また、日時の部分を相対的に変更することもできます。 ::
123 |
124 | $future = Chronos::create()
125 | ->addYear(1)
126 | ->subMonth(2)
127 | ->addDays(15)
128 | ->addHours(20)
129 | ->subMinutes(2);
130 |
131 | また、ある時間の中で、定義された時点に飛ぶことも可能です。 ::
132 |
133 | $time = Chronos::create();
134 | $time->startOfDay();
135 | $time->endOfDay();
136 | $time->startOfMonth();
137 | $time->endOfMonth();
138 | $time->startOfYear();
139 | $time->endOfYear();
140 | $time->startOfWeek();
141 | $time->endOfWeek();
142 |
143 | また、1週間中の特定の日にも飛べます。 ::
144 |
145 | $time->next(Chronos::TUESDAY);
146 | $time->previous(Chronos::MONDAY);
147 |
148 | :abbr:`DST (夏時間)` の遷移の前後で日付/時間を変更すると、
149 | あなたの操作で時間が増減するかもしれませんが、その結果、意図しない時間の値になります。
150 | これらの問題を回避するには、最初にタイムゾーンを ``UTC`` に変更し、時間を変更します。 ::
151 |
152 | // 余分な時間が追加されました
153 | $time = new Chronos('2014-03-30 00:00:00', 'Europe/London');
154 | debug($time->modify('+24 hours')); // 2014-03-31 01:00:00
155 |
156 | // 最初に UTC に切り替え、そして更新
157 | $time = $time->setTimezone('UTC')
158 | ->modify('+24 hours');
159 |
160 | 時間を変更すると、元のタイムゾーンを追加してローカライズされた時間を取得することができます。
161 |
162 | 比較メソッド
163 | ------------
164 |
165 | Chronos の日付/時間オブジェクトの2つのインスタンスを様々な方法で比較することができます。 ::
166 |
167 | // 比較のフルセットが存在します
168 | // equals, notEquals, greaterThan, greaterThanOrEquals, lessThan, lessThanOrEquals
169 | $first->equals($second);
170 | $first->greaterThanOrEquals($second);
171 |
172 | // カレントオブジェクトが2つのオブジェクトの間にあるかどうかを確認します。
173 | $now->between($start, $end);
174 |
175 | // どちらの引数が最も近い (closest) か、または最も遠い (farthest) かを見つけます。
176 | $now->closest($june, $november);
177 | $now->farthest($june, $november);
178 |
179 | また、与えられた値のカレンダーに当たる場所について問い合わせできます。 ::
180 |
181 | $now->isToday();
182 | $now->isYesterday();
183 | $now->isFuture();
184 | $now->isPast();
185 |
186 | // 曜日をチェック
187 | $now->isWeekend();
188 |
189 | // 他の曜日のメソッドも全て存在します。
190 | $now->isMonday();
191 |
192 | また、値が相対的な期間内にあったかどうかを見つけることができます。 ::
193 |
194 | $time->wasWithinLast('3 days');
195 | $time->isWithinNext('3 hours');
196 |
197 | 差の生成
198 | --------
199 |
200 | 日時比較に加えて、2つの値の差や変化の計算は一般的なタスクです。 ::
201 |
202 | // 差をあらわす DateInterval を取得
203 | $first->diff($second);
204 |
205 | // 特定の単位での差を取得
206 | $first->diffInHours($second);
207 | $first->diffInDays($second);
208 | $first->diffInWeeks($second);
209 | $first->diffInYears($second);
210 |
211 | フィードやタイムラインで使用するのに適した、人が読める形式の差を生成することができます。 ::
212 |
213 | // 現在からの差
214 | echo $date->diffForHumans();
215 |
216 | // 別の時点からの差
217 | echo $date->diffForHumans($other); // 1時間前;
218 |
219 | フォーマットの設定
220 | ------------------
221 |
222 | Chronos は、出力した日時オブジェクトを表示するための多くのメソッドを提供します。 ::
223 |
224 | // setToStringFormat() が制御するフォーマットを使用します
225 | echo $date;
226 |
227 | // 別の標準フォーマット
228 | echo $time->toAtomString(); // 1975-12-25T14:15:16-05:00
229 | echo $time->toCookieString(); // Thursday, 25-Dec-1975 14:15:16 EST
230 | echo $time->toIso8601String(); // 1975-12-25T14:15:16-05:00
231 | echo $time->toRfc822String(); // Thu, 25 Dec 75 14:15:16 -0500
232 | echo $time->toRfc850String(); // Thursday, 25-Dec-75 14:15:16 EST
233 | echo $time->toRfc1036String(); // Thu, 25 Dec 75 14:15:16 -0500
234 | echo $time->toRfc1123String(); // Thu, 25 Dec 1975 14:15:16 -0500
235 | echo $time->toRfc2822String(); // Thu, 25 Dec 1975 14:15:16 -0500
236 | echo $time->toRfc3339String(); // 1975-12-25T14:15:16-05:00
237 | echo $time->toRssString(); // Thu, 25 Dec 1975 14:15:16 -0500
238 | echo $time->toW3cString(); // 1975-12-25T14:15:16-05:00
239 |
240 | // クォーター/週数を取得
241 | echo $time->toQuarter(); // 4;
242 | echo $time->toWeek(); // 52
243 |
244 | // 一般的なフォーマット
245 | echo $time->toTimeString(); // 14:15:16
246 | echo $time->toDateString(); // 1975-12-25
247 | echo $time->toDateTimeString(); // 1975-12-25 14:15:16
248 | echo $time->toFormattedDateString(); // Dec 25, 1975
249 | echo $time->toDayDateTimeString(); // Thu, Dec 25, 1975 2:15 PM
250 |
251 | 日付要素の抽出
252 | --------------
253 |
254 | 日付オブジェクトのプロパティーに直接アクセスして要素を取得することができます。 ::
255 |
256 | $time = new Chronos('2015-12-31 23:59:58');
257 | $time->year; // 2015
258 | $time->month; // 12
259 | $time->day; // 31
260 | $time->hour // 23
261 | $time->minute // 59
262 | $time->second // 58
263 |
264 | 以下のプロパティーにもアクセスできます。 :
265 |
266 | - timezone
267 | - timezoneName
268 | - micro
269 | - dayOfWeek
270 | - dayOfMonth
271 | - dayOfYear
272 | - daysInMonth
273 | - timestamp
274 | - quarter
275 | - half
276 |
277 | テストの支援
278 | ------------
279 |
280 | 単体テストを書いている時、現在時刻を固定すると便利です。Chronos は、
281 | 各クラスの現在時刻を修正することができます。
282 | テストスイートの bootstrap 処理に以下を含めることができます。 ::
283 |
284 | Chronos::setTestNow(Chronos::now());
285 | MutableDateTime::setTestNow(MutableDateTime::now());
286 | ChronosDate::setTestNow(ChronosDate::parse(Chronos::now()));
287 | MutableDate::setTestNow(MutableDate::now());
288 |
289 | これでテストスイートが開始された時点で全てのオブジェクトの現在時刻を修正します。
290 |
291 | 例えば、 ``Chronos`` を過去のある瞬間に固定した場合、新たな ``Chronos``
292 | のインスタンスが生成する ``now`` または相対時刻の文字列は、
293 | 固定された時刻の相対を返却します。 ::
294 |
295 | Chronos::setTestNow(new Chronos('1975-12-25 00:00:00'));
296 |
297 | $time = new Chronos(); // 1975-12-25 00:00:00
298 | $time = new Chronos('1 hour ago'); // 1975-12-24 23:00:00
299 |
300 | 固定をリセットするには、 ``setTestNow()`` をパラメーター無し、または ``null`` を設定して
301 | 再び呼び出してください。
302 |
--------------------------------------------------------------------------------
/docs/pt/conf.py:
--------------------------------------------------------------------------------
1 | import sys, os
2 |
3 | # Append the top level directory of the docs, so we can import from the config dir.
4 | sys.path.insert(0, os.path.abspath('..'))
5 |
6 | # Pull in all the configuration options defined in the global config file..
7 | from config.all import *
8 |
9 | language = 'pt'
10 |
--------------------------------------------------------------------------------
/docs/pt/contents.rst:
--------------------------------------------------------------------------------
1 | .. toctree::
2 | :maxdepth: 2
3 | :caption: CakePHP Chronos
4 |
5 | /index
6 |
7 | API
--------------------------------------------------------------------------------
/docs/pt/index.rst:
--------------------------------------------------------------------------------
1 | Chronos
2 | #######
3 |
4 | O Chronos oferece uma coleção independente de extensões para lidar com o objeto
5 | ``DateTime``. Além de métodos de conveniência, o Chronos oferece:
6 |
7 | * Objetos ``Date`` para representar datas de calendário.
8 | * Objetos *date* e *datetime* imutáveis.
9 | * Um sistema de tradução acoplável. Apenas traduções em inglês estão incluídas
10 | na biblioteca. Todavia, ``cakephp/i18n`` pode ser usado para suporte completo
11 | a idiomas.
12 |
13 | Instalação
14 | ----------
15 |
16 | Para instalar o Chronos, você deve usar o ``composer``. A partir do diretório
17 | *ROOT* de sua aplicação (local onde o arquivo composer.json está localizado)
18 | execute o seguinte comando::
19 |
20 | php composer.phar require cakephp/chronos "@stable"
21 |
22 | Visão geral
23 | -----------
24 |
25 | Chronos oferece extensões para lidar com objetos *DateTime* do PHP. 5 classes
26 | cobrem variantes de data/hora mutáveis e imutáveis e uma extensão do objeto
27 | ``DateInterval``.
28 |
29 | * ``Cake\Chronos\Chronos`` é um objeto *date & time* imutável.
30 | * ``Cake\Chronos\ChronosDate`` é um objeto *date* imutável.
31 | * ``Cake\Chronos\MutableDateTime`` é um objeto *date and time* mutável.
32 | * ``Cake\Chronos\MutableDate`` é um objeto *date* mutável.
33 | * ``Cake\Chronos\ChronosInterval`` é uma extensão do objeto ``DateInterval``.
34 |
35 | Criando instâncias
36 | ------------------
37 |
38 | Existem várias maneiras de criar instâncias do Chronos ou mesmo, do objeto Date.
39 | Um número considerável de métodos padrão que funcionam com conjuntos diferentes
40 | de argumentos::
41 |
42 | use Cake\Chronos\Chronos;
43 |
44 | $now = Chronos::now();
45 | $today = Chronos::today();
46 | $yesterday = Chronos::yesterday();
47 | $tomorrow = Chronos::tomorrow();
48 |
49 | // Interpreta expressões relativas.
50 | $date = Chronos::parse('+2 days, +3 hours');
51 |
52 | // Valores inteiros de Date e Time.
53 | $date = Chronos::create(2015, 12, 25, 4, 32, 58);
54 |
55 | // Valores inteiros de Date ou Time.
56 | $date = Chronos::createFromDate(2015, 12, 25);
57 | $date = Chronos::createFromTime(11, 45, 10);
58 |
59 | // Interpreta valores formatados.
60 | $date = Chronos::createFromFormat('m/d/Y', '06/15/2015');
61 |
62 | Trabalhando com objetos imutáveis
63 | ---------------------------------
64 |
65 | Se você é familiarizado com os objetos ``DateTime`` do PHP, você se sentirá
66 | confortável com objetos *mutáveis*. Além de objetos mutáveis o Chronos também
67 | oferece objetos imutáveis que por sua vez criam cópias de objetos toda vez que
68 | um objeto é modificado. Devido ao fato de que metodos modificadores relativos
69 | a data e hora nem sempre serem transparentes, informações podem ser modificadas
70 | acidentalmente ou sem que o desenvolvedor saiba. Objetos imutáveis previnem
71 | essas alterações acidentais nos dados. Imutabilidade significa que você deverá
72 | lembrar de substituir variáveis usando modificadores::
73 |
74 | // Esse código não funciona com objetos imutáveis
75 | $time->addDay(1);
76 | doSomething($time);
77 | return $time;
78 |
79 | // Esse funciona como o esperado
80 | $time = $time->addDay(1);
81 | $time = doSomething($time);
82 | return $time;
83 |
84 | Ao capturar o valor de retorno de cada modificação, seu código funcionará como o
85 | esperado. Se você tem um objeto imutável e quer criar um mutável a partir do
86 | mesmo, use ``toMutable()``::
87 |
88 | $inplace = $time->toMutable();
89 |
90 | Objetos Date
91 | ------------
92 |
93 | O PHP disponibiliza um único objeto DateTime. Representar datas de calendário
94 | pode ser um pouco desconfortável por essa classe, uma vez que ela inclui
95 | *timezones* e componentes de hora que realmente não se encaixam no conceito de
96 | 'dia'. O Chronos oferece um objeto ``Date`` para representar datas. A hora e a
97 | zona desse objeto é sempre fixado em ``00:00:00 UTC`` e todos os métodos de
98 | formatação/diferença operam sob a resolução de dia::
99 |
100 | use Cake\Chronos\ChronosDate;
101 |
102 | $today = ChronosDate::today();
103 |
104 | // Mudanças na hora/timezone são ignoradas
105 | $today->modify('+1 hours');
106 |
107 | // Exibe '2016-08-15'
108 | echo $today;
109 |
110 | Métodos modificadores
111 | ---------------------
112 |
113 | Objetos Chronos disponibilizam métodos que permitem a modificação de valores de
114 | forma granular::
115 |
116 | // Define componentes do valor datetime
117 | $halloween = Chronos::create()
118 | ->year(2015)
119 | ->month(10)
120 | ->day(31)
121 | ->hour(20)
122 | ->minute(30);
123 |
124 | Você também pode modificar partes da data relativamente::
125 |
126 | $future = Chronos::create()
127 | ->addYear(1)
128 | ->subMonth(2)
129 | ->addDays(15)
130 | ->addHours(20)
131 | ->subMinutes(2);
132 |
133 | Também é possível realizar grandes saltos para períodos definidos no tempo::
134 |
135 | $time = Chronos::create();
136 | $time->startOfDay();
137 | $time->startOfMonth();
138 | $time->endOfMonth();
139 | $time->endOfYear();
140 | $time->startOfWeek();
141 | $time->endOfWeek();
142 |
143 | Ou ainda para dias específicos da semana::
144 |
145 | $time->next(Chronos::TUESDAY);
146 | $time->previous(Chronos::MONDAY);
147 |
148 | Métodos de comparação
149 | ---------------------
150 |
151 | Uma vez que você possui 2 instâncias de objetos data/hora do Chronos, é possível
152 | compará-los de várias maneiras::
153 |
154 | // Coleção completa de comparadores
155 | // equals, notEquals, greaterThan, greaterThanOrEquals, lessThan, lessThanOrEquals
156 | $first->equals($second);
157 | $first->greaterThanOrEquals($second);
158 |
159 | // Veja se o objeto atual está entre outros
160 | $now->between($start, $end);
161 |
162 | // Encontre qual argumento está mais perto ou mais longe
163 | $now->closest($june, $november);
164 | $now->farthest($june, $november);
165 |
166 | Você também pode arguir sobre quando um determinado valor cai no calendário::
167 |
168 | $now->isToday();
169 | $now->isYesterday();
170 | $now->isFuture();
171 | $now->isPast();
172 |
173 | // Verifica se o dia é no final de semana
174 | $now->isWeekend();
175 |
176 | // Todos os métodos para outros dias da semana existem também
177 | $now->isMonday();
178 |
179 | Você também pode verificar se um determinado valor está dentro de um período de
180 | tempo relativo::
181 |
182 | $time->wasWithinLast('3 days');
183 | $time->isWithinNext('3 hours');
184 |
185 | Gerando diferenças
186 | ------------------
187 |
188 | Em adição à comparação de *datetimes*, calcular diferenças ou deltas entre
189 | valores é uma tarefa simples::
190 |
191 | // Recebe um DateInterval representando a diferença
192 | $first->diff($second);
193 |
194 | // Recebe a diferença como um contador de unidades específicas
195 | $first->diffInHours($second);
196 | $first->diffInDays($second);
197 | $first->diffInWeeks($second);
198 | $first->diffInYears($second);
199 |
200 | Você pode gerar diferenças de fácil leitura para humanos para usar em um *feed*
201 | ou *timeline*::
202 |
203 | // Diferença em relação ao momento atual
204 | echo $date->diffForHumans();
205 |
206 | // Diferença em relação a outro período no tempo
207 | echo $date->diffForHumans($other); // 1 hora atrás;
208 |
209 | Formatando strings
210 | ------------------
211 |
212 | O Chronos disponibiliza métodos para exibir nossos objetos *datetime*::
213 |
214 | // Usa o formato controlado por setToStringFormat()
215 | echo $date;
216 |
217 | // Diferentes padrões de formato
218 | echo $time->toAtomString(); // 1975-12-25T14:15:16-05:00
219 | echo $time->toCookieString(); // Thursday, 25-Dec-1975 14:15:16 EST
220 | echo $time->toIso8601String(); // 1975-12-25T14:15:16-05:00
221 | echo $time->toRfc822String(); // Thu, 25 Dec 75 14:15:16 -0500
222 | echo $time->toRfc850String(); // Thursday, 25-Dec-75 14:15:16 EST
223 | echo $time->toRfc1036String(); // Thu, 25 Dec 75 14:15:16 -0500
224 | echo $time->toRfc1123String(); // Thu, 25 Dec 1975 14:15:16 -0500
225 | echo $time->toRfc2822String(); // Thu, 25 Dec 1975 14:15:16 -0500
226 | echo $time->toRfc3339String(); // 1975-12-25T14:15:16-05:00
227 | echo $time->toRssString(); // Thu, 25 Dec 1975 14:15:16 -0500
228 | echo $time->toW3cString(); // 1975-12-25T14:15:16-05:00
229 |
230 | // Recebe o trimestre
231 | echo $time->toQuarter(); // 4;
232 |
233 | Extraindo componentes de data
234 | -----------------------------
235 |
236 | Podemos receber partes de um objeto *date* acessando propriedades::
237 |
238 | $time = new Chronos('2015-12-31 23:59:58');
239 | $time->year; // 2015
240 | $time->month; // 12
241 | $time->day; // 31
242 | $time->hour // 23
243 | $time->minute // 59
244 | $time->second // 58
245 |
246 | Outras propriedades que podem ser acessadas são:
247 |
248 | - timezone
249 | - timezoneName
250 | - micro
251 | - dayOfWeek
252 | - dayOfMonth
253 | - dayOfYear
254 | - daysInMonth
255 | - timestamp
256 | - quarter
257 | - half
258 |
259 | Auxílio para testes
260 | -------------------
261 |
262 | Ao escrever testes unitários, fixar a hora atual é bastante útil. O Chronos
263 | lhe permite fixar a hora atual para cada classe. Como parte das suas ferramentas
264 | de testes, você pode incluir o seguinte::
265 |
266 | Chronos::setTestNow(Chronos::now());
267 | MutableDateTime::setTestNow(MutableDateTime::now());
268 | ChronosDate::setTestNow(ChronosDate::parse(Chronos::now()));
269 | MutableDate::setTestNow(MutableDate::now());
270 |
271 | Isso irá corrigir a hora atual de todos os objetos para o momento em que o
272 | processo de testes foi iniciado.
273 |
274 | Por exemplo, se você fixar o ``Chronos`` em algum momento no passado, qualquer
275 | nova instância do ``Chronos`` criada com ``now`` ou uma *string* de tempo
276 | relativa, teremos um retorno referente ao tempo fixado::
277 |
278 | Chronos::setTestNow(new Chronos('1975-12-25 00:00:00'));
279 |
280 | $time = new Chronos(); // 1975-12-25 00:00:00
281 | $time = new Chronos('1 hour ago'); // 1975-12-24 23:00:00
282 |
283 |
--------------------------------------------------------------------------------
/psalm-baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | array_filter
6 | date_default_timezone_get
7 | date_default_timezone_get
8 | iterator_to_array
9 |
10 |
11 | diffForHumans
12 | diffFormatter
13 | format
14 | format
15 | format
16 | format
17 | format
18 | format
19 | getTimestamp
20 | getTimezone
21 | getWeekendDays
22 | hasTestNow
23 | now
24 | now
25 | now
26 | now
27 | now
28 | now
29 | now
30 | now
31 | now
32 | now
33 | now
34 | now
35 | now
36 | now
37 | now
38 | now
39 | now
40 | now
41 | now
42 | now
43 | now
44 | now
45 | safeCreateDateTimeZone
46 | toDateString
47 | toDateString
48 | toDateString
49 | toDateString
50 | toDateString
51 | toDateString
52 | toDateString
53 | tomorrow
54 | yesterday
55 |
56 |
57 | static::$days
58 | static::$days
59 | static::$days
60 | static::$days
61 | static::$days
62 | static::$days
63 | static::$days
64 | static::$days
65 | static::$days
66 | static::$weekEndsAt
67 | static::$weekEndsAt
68 | static::$weekStartsAt
69 | static::$weekStartsAt
70 |
71 |
72 | 'Y',
74 | 'yearIso' => 'o',
75 | 'month' => 'n',
76 | 'day' => 'j',
77 | 'hour' => 'G',
78 | 'minute' => 'i',
79 | 'second' => 's',
80 | 'micro' => 'u',
81 | 'microsecond' => 'u',
82 | 'dayOfWeek' => 'N',
83 | 'dayOfYear' => 'z',
84 | 'weekOfYear' => 'W',
85 | 'daysInMonth' => 't',
86 | 'timestamp' => 'U',
87 | ];]]>
88 |
89 |
90 | f]]>
91 |
92 |
93 | public static function createFromFormat(
94 |
95 |
96 |
97 |
98 | array_filter
99 | date_default_timezone_get
100 | iterator_to_array
101 |
102 |
103 | diffForHumans
104 | diffFormatter
105 | format
106 | getTestNow
107 | getWeekEndsAt
108 | getWeekEndsAt
109 | getWeekStartsAt
110 | getWeekStartsAt
111 | getWeekendDays
112 | hasTestNow
113 | now
114 | now
115 | now
116 | now
117 | now
118 | now
119 | now
120 | now
121 | now
122 | now
123 | now
124 | now
125 | tomorrow
126 | yesterday
127 |
128 |
129 | static::$days
130 | static::$days
131 | static::$days
132 | static::$days
133 | static::$days
134 | static::$days
135 | static::$days
136 | static::$days
137 | static::$days
138 |
139 |
140 | 'Y',
142 | 'yearIso' => 'o',
143 | 'month' => 'n',
144 | 'day' => 'j',
145 | 'dayOfWeek' => 'N',
146 | 'dayOfYear' => 'z',
147 | 'weekOfYear' => 'W',
148 | 'daysInMonth' => 't',
149 | ];]]>
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/ChronosDate.php:
--------------------------------------------------------------------------------
1 | $month
35 | * @property-read int<1, 31> $day
36 | * @property-read int<1, 7> $dayOfWeek 1 (for Monday) through 7 (for Sunday)
37 | * @property-read int<0, 365> $dayOfYear 0 through 365
38 | * @property-read int<1, 5> $weekOfMonth 1 through 5
39 | * @property-read int<1, 53> $weekOfYear ISO-8601 week number of year, weeks starting on Monday
40 | * @property-read int<1, 31> $daysInMonth number of days in the given month
41 | * @property-read int $age does a diffInYears() with default parameters
42 | * @property-read int<1, 4> $quarter the quarter of this instance, 1 - 4
43 | * @property-read int<1, 2> $half the half of the year, with 1 for months Jan...Jun and 2 for Jul...Dec.
44 | * @psalm-immutable
45 | * @psalm-consistent-constructor
46 | */
47 | class ChronosDate implements Stringable
48 | {
49 | use FormattingTrait;
50 |
51 | /**
52 | * Default format to use for __toString method when type juggling occurs.
53 | *
54 | * @var string
55 | */
56 | public const DEFAULT_TO_STRING_FORMAT = 'Y-m-d';
57 |
58 | /**
59 | * Format to use for __toString method when type juggling occurs.
60 | *
61 | * @var string
62 | */
63 | protected static string $toStringFormat = self::DEFAULT_TO_STRING_FORMAT;
64 |
65 | /**
66 | * Names of days of the week.
67 | *
68 | * @var array
69 | */
70 | protected static array $days = [
71 | Chronos::MONDAY => 'Monday',
72 | Chronos::TUESDAY => 'Tuesday',
73 | Chronos::WEDNESDAY => 'Wednesday',
74 | Chronos::THURSDAY => 'Thursday',
75 | Chronos::FRIDAY => 'Friday',
76 | Chronos::SATURDAY => 'Saturday',
77 | Chronos::SUNDAY => 'Sunday',
78 | ];
79 |
80 | /**
81 | * Instance of the diff formatting object.
82 | *
83 | * @var \Cake\Chronos\DifferenceFormatterInterface|null
84 | */
85 | protected static ?DifferenceFormatterInterface $diffFormatter = null;
86 |
87 | /**
88 | * Errors from last time createFromFormat() was called.
89 | *
90 | * @var array|false
91 | */
92 | protected static array|false $lastErrors = false;
93 |
94 | /**
95 | * @var \DateTimeImmutable
96 | */
97 | protected DateTimeImmutable $native;
98 |
99 | /**
100 | * Create a new Immutable Date instance.
101 | *
102 | * Dates do not have time or timezone components exposed. Internally
103 | * ChronosDate wraps a PHP DateTimeImmutable but limits modifications
104 | * to only those that operate on day values.
105 | *
106 | * By default dates will be calculated from the server's default timezone.
107 | * You can use the `timezone` parameter to use a different timezone. Timezones
108 | * are used when parsing relative date expressions like `today` and `yesterday`
109 | * but do not participate in parsing values like `2022-01-01`.
110 | *
111 | * @param \Cake\Chronos\ChronosDate|\DateTimeInterface|string $time Fixed or relative time
112 | * @param \DateTimeZone|string|null $timezone The time zone used for 'now'
113 | */
114 | public function __construct(
115 | ChronosDate|DateTimeInterface|string $time = 'now',
116 | DateTimeZone|string|null $timezone = null
117 | ) {
118 | $this->native = $this->createNative($time, $timezone);
119 | }
120 |
121 | /**
122 | * Initializes the PHP DateTimeImmutable object.
123 | *
124 | * @param \Cake\Chronos\ChronosDate|\DateTimeInterface|string $time Fixed or relative time
125 | * @param \DateTimeZone|string|null $timezone The time zone used for 'now'
126 | * @return \DateTimeImmutable
127 | */
128 | protected function createNative(
129 | ChronosDate|DateTimeInterface|string $time,
130 | DateTimeZone|string|null $timezone
131 | ): DateTimeImmutable {
132 | if (!is_string($time)) {
133 | return new DateTimeImmutable($time->format('Y-m-d 00:00:00'));
134 | }
135 |
136 | $timezone ??= date_default_timezone_get();
137 | $timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
138 |
139 | $testNow = Chronos::getTestNow();
140 | if ($testNow === null) {
141 | $time = new DateTimeImmutable($time, $timezone);
142 |
143 | return new DateTimeImmutable($time->format('Y-m-d 00:00:00'));
144 | }
145 |
146 | $testNow = $testNow->setTimezone($timezone);
147 | if ($time !== 'now') {
148 | $testNow = $testNow->modify($time);
149 | }
150 |
151 | return new DateTimeImmutable($testNow->format('Y-m-d 00:00:00'));
152 | }
153 |
154 | /**
155 | * Get today's date.
156 | *
157 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
158 | * @return static
159 | */
160 | public static function now(DateTimeZone|string|null $timezone = null): static
161 | {
162 | return new static('now', $timezone);
163 | }
164 |
165 | /**
166 | * Get today's date.
167 | *
168 | * @param \DateTimeZone|string|null $timezone Time zone to use for today.
169 | * @return static
170 | */
171 | public static function today(DateTimeZone|string|null $timezone = null): static
172 | {
173 | return static::now($timezone);
174 | }
175 |
176 | /**
177 | * Get tomorrow's date.
178 | *
179 | * @param \DateTimeZone|string|null $timezone Time zone to use for tomorrow.
180 | * @return static
181 | */
182 | public static function tomorrow(DateTimeZone|string|null $timezone = null): static
183 | {
184 | return new static('tomorrow', $timezone);
185 | }
186 |
187 | /**
188 | * Get yesterday's date.
189 | *
190 | * @param \DateTimeZone|string|null $timezone Time zone to use for yesterday.
191 | * @return static
192 | */
193 | public static function yesterday(DateTimeZone|string|null $timezone = null): static
194 | {
195 | return new static('yesterday', $timezone);
196 | }
197 |
198 | /**
199 | * Create an instance from a string. This is an alias for the
200 | * constructor that allows better fluent syntax as it allows you to do
201 | * Chronos::parse('Monday next week')->fn() rather than
202 | * (new Chronos('Monday next week'))->fn()
203 | *
204 | * @param \Cake\Chronos\ChronosDate|\DateTimeInterface|string $time The strtotime compatible string to parse
205 | * @return static
206 | */
207 | public static function parse(ChronosDate|DateTimeInterface|string $time): static
208 | {
209 | return new static($time);
210 | }
211 |
212 | /**
213 | * Create an instance from a specific date.
214 | *
215 | * @param int $year The year to create an instance with.
216 | * @param int $month The month to create an instance with.
217 | * @param int $day The day to create an instance with.
218 | * @return static
219 | */
220 | public static function create(int $year, int $month, int $day): static
221 | {
222 | $instance = static::createFromFormat(
223 | 'Y-m-d',
224 | sprintf('%s-%s-%s', 0, $month, $day),
225 | );
226 |
227 | return $instance->addYears($year);
228 | }
229 |
230 | /**
231 | * Create an instance from a specific format
232 | *
233 | * @param string $format The date() compatible format string.
234 | * @param string $time The formatted date string to interpret.
235 | * @return static
236 | * @throws \InvalidArgumentException
237 | */
238 | public static function createFromFormat(
239 | string $format,
240 | string $time,
241 | ): static {
242 | $dateTime = DateTimeImmutable::createFromFormat($format, $time);
243 |
244 | static::$lastErrors = DateTimeImmutable::getLastErrors();
245 | if (!$dateTime) {
246 | $message = static::$lastErrors ? implode(PHP_EOL, static::$lastErrors['errors']) : 'Unknown error';
247 |
248 | throw new InvalidArgumentException($message);
249 | }
250 |
251 | return new static($dateTime);
252 | }
253 |
254 | /**
255 | * Returns parse warnings and errors from the last ``createFromFormat()``
256 | * call.
257 | *
258 | * Returns the same data as DateTimeImmutable::getLastErrors().
259 | *
260 | * @return array|false
261 | */
262 | public static function getLastErrors(): array|false
263 | {
264 | return static::$lastErrors;
265 | }
266 |
267 | /**
268 | * Creates an instance from an array of date values.
269 | *
270 | * Allowed values:
271 | * - year
272 | * - month
273 | * - day
274 | *
275 | * @param array $values Array of date and time values.
276 | * @return static
277 | */
278 | public static function createFromArray(array $values): static
279 | {
280 | $formatted = sprintf('%04d-%02d-%02d ', $values['year'], $values['month'], $values['day']);
281 |
282 | return static::parse($formatted);
283 | }
284 |
285 | /**
286 | * Get the difference formatter instance or overwrite the current one.
287 | *
288 | * @param \Cake\Chronos\DifferenceFormatterInterface|null $formatter The formatter instance when setting.
289 | * @return \Cake\Chronos\DifferenceFormatterInterface The formatter instance.
290 | */
291 | public static function diffFormatter(?DifferenceFormatterInterface $formatter = null): DifferenceFormatterInterface
292 | {
293 | if ($formatter === null) {
294 | if (static::$diffFormatter === null) {
295 | static::$diffFormatter = new DifferenceFormatter();
296 | }
297 |
298 | return static::$diffFormatter;
299 | }
300 |
301 | return static::$diffFormatter = $formatter;
302 | }
303 |
304 | /**
305 | * Add an Interval to a Date
306 | *
307 | * Any changes to the time will be ignored and reset to 00:00:00
308 | *
309 | * @param \DateInterval $interval The interval to modify this date by.
310 | * @return static A modified Date instance
311 | */
312 | public function add(DateInterval $interval): static
313 | {
314 | if ($interval->f > 0 || $interval->s > 0 || $interval->i > 0 || $interval->h > 0) {
315 | throw new InvalidArgumentException('Cannot add intervals with time components');
316 | }
317 | $new = clone $this;
318 | $new->native = $new->native->add($interval)->setTime(0, 0, 0);
319 |
320 | return $new;
321 | }
322 |
323 | /**
324 | * Subtract an Interval from a Date.
325 | *
326 | * Any changes to the time will be ignored and reset to 00:00:00
327 | *
328 | * @param \DateInterval $interval The interval to modify this date by.
329 | * @return static A modified Date instance
330 | */
331 | public function sub(DateInterval $interval): static
332 | {
333 | if ($interval->f > 0 || $interval->s > 0 || $interval->i > 0 || $interval->h > 0) {
334 | throw new InvalidArgumentException('Cannot subtract intervals with time components');
335 | }
336 | $new = clone $this;
337 | $new->native = $new->native->sub($interval)->setTime(0, 0, 0);
338 |
339 | return $new;
340 | }
341 |
342 | /**
343 | * Creates a new instance with date modified according to DateTimeImmutable::modifier().
344 | *
345 | * Attempting to change a time component will raise an exception
346 | *
347 | * @param string $modifier Date modifier
348 | * @return static
349 | */
350 | public function modify(string $modifier): static
351 | {
352 | if (preg_match('/hour|minute|second/', $modifier)) {
353 | throw new InvalidArgumentException('Cannot modify date objects by time values');
354 | }
355 |
356 | $new = clone $this;
357 | $new->native = $new->native->modify($modifier);
358 | if ($new->native === false) {
359 | throw new InvalidArgumentException(sprintf('Unable to modify date using `%s`', $modifier));
360 | }
361 |
362 | if ($new->format('H:i:s') !== '00:00:00') {
363 | $new->native = $new->native->setTime(0, 0, 0);
364 | }
365 |
366 | return $new;
367 | }
368 |
369 | /**
370 | * Sets the date.
371 | *
372 | * @param int $year The year to set.
373 | * @param int $month The month to set.
374 | * @param int $day The day to set.
375 | * @return static
376 | */
377 | public function setDate(int $year, int $month, int $day): static
378 | {
379 | $new = clone $this;
380 | $new->native = $new->native->setDate($year, $month, $day);
381 |
382 | return $new;
383 | }
384 |
385 | /**
386 | * Sets the date according to the ISO 8601 standard
387 | *
388 | * @param int $year Year of the date.
389 | * @param int $week Week of the date.
390 | * @param int $dayOfWeek Offset from the first day of the week.
391 | * @return static
392 | */
393 | public function setISODate(int $year, int $week, int $dayOfWeek = 1): static
394 | {
395 | $new = clone $this;
396 | $new->native = $new->native->setISODate($year, $week, $dayOfWeek);
397 |
398 | return $new;
399 | }
400 |
401 | /**
402 | * Returns the difference between this instance and target.
403 | *
404 | * @param \Cake\Chronos\ChronosDate $target Target instance
405 | * @param bool $absolute Whether the interval is forced to be positive
406 | * @return \DateInterval
407 | */
408 | public function diff(ChronosDate $target, bool $absolute = false): DateInterval
409 | {
410 | return $this->native->diff($target->native, $absolute);
411 | }
412 |
413 | /**
414 | * Returns formatted date string according to DateTimeImmutable::format().
415 | *
416 | * @param string $format String format
417 | * @return string
418 | */
419 | public function format(string $format): string
420 | {
421 | return $this->native->format($format);
422 | }
423 |
424 | /**
425 | * Set the instance's year
426 | *
427 | * @param int $value The year value.
428 | * @return static
429 | */
430 | public function year(int $value): static
431 | {
432 | return $this->setDate($value, $this->month, $this->day);
433 | }
434 |
435 | /**
436 | * Set the instance's month
437 | *
438 | * @param int $value The month value.
439 | * @return static
440 | */
441 | public function month(int $value): static
442 | {
443 | return $this->setDate($this->year, $value, $this->day);
444 | }
445 |
446 | /**
447 | * Set the instance's day
448 | *
449 | * @param int $value The day value.
450 | * @return static
451 | */
452 | public function day(int $value): static
453 | {
454 | return $this->setDate($this->year, $this->month, $value);
455 | }
456 |
457 | /**
458 | * Add years to the instance. Positive $value travel forward while
459 | * negative $value travel into the past.
460 | *
461 | * If the new ChronosDate does not exist, the last day of the month is used
462 | * instead instead of overflowing into the next month.
463 | *
464 | * ### Example:
465 | *
466 | * ```
467 | * (new Chronos('2015-01-03'))->addYears(1); // Results in 2016-01-03
468 | *
469 | * (new Chronos('2012-02-29'))->addYears(1); // Results in 2013-02-28
470 | * ```
471 | *
472 | * @param int $value The number of years to add.
473 | * @return static
474 | */
475 | public function addYears(int $value): static
476 | {
477 | $month = $this->month;
478 | $date = $this->modify($value . ' years');
479 |
480 | if ($date->month !== $month) {
481 | return $date->modify('last day of previous month');
482 | }
483 |
484 | return $date;
485 | }
486 |
487 | /**
488 | * Remove years from the instance.
489 | *
490 | * Has the same behavior as `addYears()`.
491 | *
492 | * @param int $value The number of years to remove.
493 | * @return static
494 | */
495 | public function subYears(int $value): static
496 | {
497 | return $this->addYears(-$value);
498 | }
499 |
500 | /**
501 | * Add years with overflowing to the instance. Positive $value
502 | * travels forward while negative $value travels into the past.
503 | *
504 | * If the new ChronosDate does not exist, the days overflow into the next month.
505 | *
506 | * ### Example:
507 | *
508 | * ```
509 | * (new Chronos('2012-02-29'))->addYearsWithOverflow(1); // Results in 2013-03-01
510 | * ```
511 | *
512 | * @param int $value The number of years to add.
513 | * @return static
514 | */
515 | public function addYearsWithOverflow(int $value): static
516 | {
517 | return $this->modify($value . ' year');
518 | }
519 |
520 | /**
521 | * Remove years with overflow from the instance
522 | *
523 | * Has the same behavior as `addYeasrWithOverflow()`.
524 | *
525 | * @param int $value The number of years to remove.
526 | * @return static
527 | */
528 | public function subYearsWithOverflow(int $value): static
529 | {
530 | return $this->addYearsWithOverflow(-1 * $value);
531 | }
532 |
533 | /**
534 | * Add months to the instance. Positive $value travels forward while
535 | * negative $value travels into the past.
536 | *
537 | * When adding or subtracting months, if the resulting time is a date
538 | * that does not exist, the result of this operation will always be the
539 | * last day of the intended month.
540 | *
541 | * ### Example:
542 | *
543 | * ```
544 | * (new Chronos('2015-01-03'))->addMonths(1); // Results in 2015-02-03
545 | *
546 | * (new Chronos('2015-01-31'))->addMonths(1); // Results in 2015-02-28
547 | * ```
548 | *
549 | * @param int $value The number of months to add.
550 | * @return static
551 | */
552 | public function addMonths(int $value): static
553 | {
554 | $day = $this->day;
555 | $date = $this->modify($value . ' months');
556 |
557 | if ($date->day !== $day) {
558 | return $date->modify('last day of previous month');
559 | }
560 |
561 | return $date;
562 | }
563 |
564 | /**
565 | * Remove months from the instance
566 | *
567 | * Has the same behavior as `addMonths()`.
568 | *
569 | * @param int $value The number of months to remove.
570 | * @return static
571 | */
572 | public function subMonths(int $value): static
573 | {
574 | return $this->addMonths(-$value);
575 | }
576 |
577 | /**
578 | * Add months with overflowing to the instance. Positive $value
579 | * travels forward while negative $value travels into the past.
580 | *
581 | * If the new ChronosDate does not exist, the days overflow into the next month.
582 | *
583 | * ### Example:
584 | *
585 | * ```
586 | * (new Chronos('2012-01-30'))->addMonthsWithOverflow(1); // Results in 2013-03-01
587 | * ```
588 | *
589 | * @param int $value The number of months to add.
590 | * @return static
591 | */
592 | public function addMonthsWithOverflow(int $value): static
593 | {
594 | return $this->modify($value . ' months');
595 | }
596 |
597 | /**
598 | * Add months with overflowing to the instance. Positive $value
599 | * travels forward while negative $value travels into the past.
600 | *
601 | * If the new ChronosDate does not exist, the days overflow into the next month.
602 | *
603 | * ### Example:
604 | *
605 | * ```
606 | * (new Chronos('2012-01-30'))->addMonthsWithOverflow(1); // Results in 2013-03-01
607 | * ```
608 | *
609 | * @param int $value The number of months to remove.
610 | * @return static
611 | */
612 | public function subMonthsWithOverflow(int $value): static
613 | {
614 | return $this->addMonthsWithOverflow(-1 * $value);
615 | }
616 |
617 | /**
618 | * Add days to the instance. Positive $value travels forward while
619 | * negative $value travels into the past.
620 | *
621 | * @param int $value The number of days to add.
622 | * @return static
623 | */
624 | public function addDays(int $value): static
625 | {
626 | return $this->modify("$value days");
627 | }
628 |
629 | /**
630 | * Remove days from the instance
631 | *
632 | * @param int $value The number of days to remove.
633 | * @return static
634 | */
635 | public function subDays(int $value): static
636 | {
637 | return $this->addDays(-$value);
638 | }
639 |
640 | /**
641 | * Add weekdays to the instance. Positive $value travels forward while
642 | * negative $value travels into the past.
643 | *
644 | * @param int $value The number of weekdays to add.
645 | * @return static
646 | */
647 | public function addWeekdays(int $value): static
648 | {
649 | return $this->modify($value . ' weekdays, ' . $this->format('H:i:s'));
650 | }
651 |
652 | /**
653 | * Remove weekdays from the instance
654 | *
655 | * @param int $value The number of weekdays to remove.
656 | * @return static
657 | */
658 | public function subWeekdays(int $value): static
659 | {
660 | return $this->addWeekdays(-$value);
661 | }
662 |
663 | /**
664 | * Add weeks to the instance. Positive $value travels forward while
665 | * negative $value travels into the past.
666 | *
667 | * @param int $value The number of weeks to add.
668 | * @return static
669 | */
670 | public function addWeeks(int $value): static
671 | {
672 | return $this->modify("$value week");
673 | }
674 |
675 | /**
676 | * Remove weeks to the instance
677 | *
678 | * @param int $value The number of weeks to remove.
679 | * @return static
680 | */
681 | public function subWeeks(int $value): static
682 | {
683 | return $this->addWeeks(-$value);
684 | }
685 |
686 | /**
687 | * Resets the date to the first day of the month
688 | *
689 | * @return static
690 | */
691 | public function startOfMonth(): static
692 | {
693 | return $this->modify('first day of this month');
694 | }
695 |
696 | /**
697 | * Resets the date to end of the month
698 | *
699 | * @return static
700 | */
701 | public function endOfMonth(): static
702 | {
703 | return $this->modify('last day of this month');
704 | }
705 |
706 | /**
707 | * Resets the date to the first day of the year
708 | *
709 | * @return static
710 | */
711 | public function startOfYear(): static
712 | {
713 | return $this->modify('first day of january');
714 | }
715 |
716 | /**
717 | * Resets the date to end of the year
718 | *
719 | * @return static
720 | */
721 | public function endOfYear(): static
722 | {
723 | return $this->modify('last day of december');
724 | }
725 |
726 | /**
727 | * Resets the date to the first day of the decade
728 | *
729 | * @return static
730 | */
731 | public function startOfDecade(): static
732 | {
733 | $year = $this->year - $this->year % Chronos::YEARS_PER_DECADE;
734 |
735 | return $this->modify("first day of january $year");
736 | }
737 |
738 | /**
739 | * Resets the date to end of the decade
740 | *
741 | * @return static
742 | */
743 | public function endOfDecade(): static
744 | {
745 | $year = $this->year - $this->year % Chronos::YEARS_PER_DECADE + Chronos::YEARS_PER_DECADE - 1;
746 |
747 | return $this->modify("last day of december $year");
748 | }
749 |
750 | /**
751 | * Resets the date to the first day of the century
752 | *
753 | * @return static
754 | */
755 | public function startOfCentury(): static
756 | {
757 | $year = $this->startOfYear()
758 | ->year($this->year - 1 - ($this->year - 1) % Chronos::YEARS_PER_CENTURY + 1)
759 | ->year;
760 |
761 | return $this->modify("first day of january $year");
762 | }
763 |
764 | /**
765 | * Resets the date to end of the century and time to 23:59:59
766 | *
767 | * @return static
768 | */
769 | public function endOfCentury(): static
770 | {
771 | $y = $this->year - 1
772 | - ($this->year - 1)
773 | % Chronos::YEARS_PER_CENTURY
774 | + Chronos::YEARS_PER_CENTURY;
775 |
776 | $year = $this->endOfYear()
777 | ->year($y)
778 | ->year;
779 |
780 | return $this->modify("last day of december $year");
781 | }
782 |
783 | /**
784 | * Resets the date to the first day of week (defined in $weekStartsAt)
785 | *
786 | * @return static
787 | */
788 | public function startOfWeek(): static
789 | {
790 | $dateTime = $this;
791 | if ($dateTime->dayOfWeek !== Chronos::getWeekStartsAt()) {
792 | $dateTime = $dateTime->previous(Chronos::getWeekStartsAt());
793 | }
794 |
795 | return $dateTime;
796 | }
797 |
798 | /**
799 | * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59
800 | *
801 | * @return static
802 | */
803 | public function endOfWeek(): static
804 | {
805 | $dateTime = $this;
806 | if ($dateTime->dayOfWeek !== Chronos::getWeekEndsAt()) {
807 | $dateTime = $dateTime->next(Chronos::getWeekEndsAt());
808 | }
809 |
810 | return $dateTime;
811 | }
812 |
813 | /**
814 | * Modify to the next occurrence of a given day of the week.
815 | * If no dayOfWeek is provided, modify to the next occurrence
816 | * of the current day of the week. Use the supplied consts
817 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
818 | *
819 | * @param int|null $dayOfWeek The day of the week to move to.
820 | * @return static
821 | */
822 | public function next(?int $dayOfWeek = null): static
823 | {
824 | if ($dayOfWeek === null) {
825 | $dayOfWeek = $this->dayOfWeek;
826 | }
827 |
828 | $day = static::$days[$dayOfWeek];
829 |
830 | return $this->modify("next $day");
831 | }
832 |
833 | /**
834 | * Modify to the previous occurrence of a given day of the week.
835 | * If no dayOfWeek is provided, modify to the previous occurrence
836 | * of the current day of the week. Use the supplied consts
837 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
838 | *
839 | * @param int|null $dayOfWeek The day of the week to move to.
840 | * @return static
841 | */
842 | public function previous(?int $dayOfWeek = null): static
843 | {
844 | if ($dayOfWeek === null) {
845 | $dayOfWeek = $this->dayOfWeek;
846 | }
847 |
848 | $day = static::$days[$dayOfWeek];
849 |
850 | return $this->modify("last $day");
851 | }
852 |
853 | /**
854 | * Modify to the first occurrence of a given day of the week
855 | * in the current month. If no dayOfWeek is provided, modify to the
856 | * first day of the current month. Use the supplied consts
857 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
858 | *
859 | * @param int|null $dayOfWeek The day of the week to move to.
860 | * @return static
861 | */
862 | public function firstOfMonth(?int $dayOfWeek = null): static
863 | {
864 | $day = $dayOfWeek === null ? 'day' : static::$days[$dayOfWeek];
865 |
866 | return $this->modify("first $day of this month");
867 | }
868 |
869 | /**
870 | * Modify to the last occurrence of a given day of the week
871 | * in the current month. If no dayOfWeek is provided, modify to the
872 | * last day of the current month. Use the supplied consts
873 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
874 | *
875 | * @param int|null $dayOfWeek The day of the week to move to.
876 | * @return static
877 | */
878 | public function lastOfMonth(?int $dayOfWeek = null): static
879 | {
880 | $day = $dayOfWeek === null ? 'day' : static::$days[$dayOfWeek];
881 |
882 | return $this->modify("last $day of this month");
883 | }
884 |
885 | /**
886 | * Modify to the given occurrence of a given day of the week
887 | * in the current month. If the calculated occurrence is outside the scope
888 | * of the current month, then return false and no modifications are made.
889 | * Use the supplied consts to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
890 | *
891 | * @param int $nth The offset to use.
892 | * @param int $dayOfWeek The day of the week to move to.
893 | * @return static|false
894 | */
895 | public function nthOfMonth(int $nth, int $dayOfWeek): static|false
896 | {
897 | $dateTime = $this->firstOfMonth();
898 | $check = $dateTime->format('Y-m');
899 | $dateTime = $dateTime->modify("+$nth " . static::$days[$dayOfWeek]);
900 |
901 | return $dateTime->format('Y-m') === $check ? $dateTime : false;
902 | }
903 |
904 | /**
905 | * Modify to the first occurrence of a given day of the week
906 | * in the current quarter. If no dayOfWeek is provided, modify to the
907 | * first day of the current quarter. Use the supplied consts
908 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
909 | *
910 | * @param int|null $dayOfWeek The day of the week to move to.
911 | * @return static
912 | */
913 | public function firstOfQuarter(?int $dayOfWeek = null): static
914 | {
915 | return $this
916 | ->day(1)
917 | ->month($this->quarter * Chronos::MONTHS_PER_QUARTER - 2)
918 | ->firstOfMonth($dayOfWeek);
919 | }
920 |
921 | /**
922 | * Modify to the last occurrence of a given day of the week
923 | * in the current quarter. If no dayOfWeek is provided, modify to the
924 | * last day of the current quarter. Use the supplied consts
925 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
926 | *
927 | * @param int|null $dayOfWeek The day of the week to move to.
928 | * @return static
929 | */
930 | public function lastOfQuarter(?int $dayOfWeek = null): static
931 | {
932 | return $this
933 | ->day(1)
934 | ->month($this->quarter * Chronos::MONTHS_PER_QUARTER)
935 | ->lastOfMonth($dayOfWeek);
936 | }
937 |
938 | /**
939 | * Modify to the given occurrence of a given day of the week
940 | * in the current quarter. If the calculated occurrence is outside the scope
941 | * of the current quarter, then return false and no modifications are made.
942 | * Use the supplied consts to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
943 | *
944 | * @param int $nth The offset to use.
945 | * @param int $dayOfWeek The day of the week to move to.
946 | * @return static|false
947 | */
948 | public function nthOfQuarter(int $nth, int $dayOfWeek): static|false
949 | {
950 | $dateTime = $this->day(1)->month($this->quarter * Chronos::MONTHS_PER_QUARTER);
951 | $lastMonth = $dateTime->month;
952 | $year = $dateTime->year;
953 | $dateTime = $dateTime->firstOfQuarter()->modify("+$nth" . static::$days[$dayOfWeek]);
954 |
955 | return $lastMonth < $dateTime->month || $year !== $dateTime->year ? false : $dateTime;
956 | }
957 |
958 | /**
959 | * Modify to the first occurrence of a given day of the week
960 | * in the current year. If no dayOfWeek is provided, modify to the
961 | * first day of the current year. Use the supplied consts
962 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
963 | *
964 | * @param int|null $dayOfWeek The day of the week to move to.
965 | * @return static
966 | */
967 | public function firstOfYear(?int $dayOfWeek = null): static
968 | {
969 | $day = $dayOfWeek === null ? 'day' : static::$days[$dayOfWeek];
970 |
971 | return $this->modify("first $day of january");
972 | }
973 |
974 | /**
975 | * Modify to the last occurrence of a given day of the week
976 | * in the current year. If no dayOfWeek is provided, modify to the
977 | * last day of the current year. Use the supplied consts
978 | * to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
979 | *
980 | * @param int|null $dayOfWeek The day of the week to move to.
981 | * @return static
982 | */
983 | public function lastOfYear(?int $dayOfWeek = null): static
984 | {
985 | $day = $dayOfWeek === null ? 'day' : static::$days[$dayOfWeek];
986 |
987 | return $this->modify("last $day of december");
988 | }
989 |
990 | /**
991 | * Modify to the given occurrence of a given day of the week
992 | * in the current year. If the calculated occurrence is outside the scope
993 | * of the current year, then return false and no modifications are made.
994 | * Use the supplied consts to indicate the desired dayOfWeek, ex. Chronos::MONDAY.
995 | *
996 | * @param int $nth The offset to use.
997 | * @param int $dayOfWeek The day of the week to move to.
998 | * @return static|false
999 | */
1000 | public function nthOfYear(int $nth, int $dayOfWeek): static|false
1001 | {
1002 | $dateTime = $this->firstOfYear()->modify("+$nth " . static::$days[$dayOfWeek]);
1003 |
1004 | return $this->year === $dateTime->year ? $dateTime : false;
1005 | }
1006 |
1007 | /**
1008 | * Determines if the instance is equal to another
1009 | *
1010 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1011 | * @return bool
1012 | */
1013 | public function equals(ChronosDate $other): bool
1014 | {
1015 | return $this->native == $other->native;
1016 | }
1017 |
1018 | /**
1019 | * Determines if the instance is not equal to another
1020 | *
1021 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1022 | * @return bool
1023 | */
1024 | public function notEquals(ChronosDate $other): bool
1025 | {
1026 | return !$this->equals($other);
1027 | }
1028 |
1029 | /**
1030 | * Determines if the instance is greater (after) than another
1031 | *
1032 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1033 | * @return bool
1034 | */
1035 | public function greaterThan(ChronosDate $other): bool
1036 | {
1037 | return $this->native > $other->native;
1038 | }
1039 |
1040 | /**
1041 | * Determines if the instance is greater (after) than or equal to another
1042 | *
1043 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1044 | * @return bool
1045 | */
1046 | public function greaterThanOrEquals(ChronosDate $other): bool
1047 | {
1048 | return $this->native >= $other->native;
1049 | }
1050 |
1051 | /**
1052 | * Determines if the instance is less (before) than another
1053 | *
1054 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1055 | * @return bool
1056 | */
1057 | public function lessThan(ChronosDate $other): bool
1058 | {
1059 | return $this->native < $other->native;
1060 | }
1061 |
1062 | /**
1063 | * Determines if the instance is less (before) or equal to another
1064 | *
1065 | * @param \Cake\Chronos\ChronosDate $other The instance to compare with.
1066 | * @return bool
1067 | */
1068 | public function lessThanOrEquals(ChronosDate $other): bool
1069 | {
1070 | return $this->native <= $other->native;
1071 | }
1072 |
1073 | /**
1074 | * Determines if the instance is between two others
1075 | *
1076 | * @param \Cake\Chronos\ChronosDate $start Start of target range
1077 | * @param \Cake\Chronos\ChronosDate $end End of target range
1078 | * @param bool $equals Whether to include the beginning and end of range
1079 | * @return bool
1080 | */
1081 | public function between(ChronosDate $start, ChronosDate $end, bool $equals = true): bool
1082 | {
1083 | if ($start->greaterThan($end)) {
1084 | [$start, $end] = [$end, $start];
1085 | }
1086 |
1087 | if ($equals) {
1088 | return $this->greaterThanOrEquals($start) && $this->lessThanOrEquals($end);
1089 | }
1090 |
1091 | return $this->greaterThan($start) && $this->lessThan($end);
1092 | }
1093 |
1094 | /**
1095 | * Get the closest date from the instance.
1096 | *
1097 | * @param \Cake\Chronos\ChronosDate $first The instance to compare with.
1098 | * @param \Cake\Chronos\ChronosDate $second The instance to compare with.
1099 | * @param \Cake\Chronos\ChronosDate ...$others Others instance to compare with.
1100 | * @return self
1101 | */
1102 | public function closest(ChronosDate $first, ChronosDate $second, ChronosDate ...$others): ChronosDate
1103 | {
1104 | $closest = $first;
1105 | $closestDiffInDays = $this->diffInDays($first);
1106 | foreach ([$second, ...$others] as $other) {
1107 | $otherDiffInDays = $this->diffInDays($other);
1108 | if ($otherDiffInDays < $closestDiffInDays) {
1109 | $closest = $other;
1110 | $closestDiffInDays = $otherDiffInDays;
1111 | }
1112 | }
1113 |
1114 | return $closest;
1115 | }
1116 |
1117 | /**
1118 | * Get the farthest date from the instance.
1119 | *
1120 | * @param \Cake\Chronos\ChronosDate $first The instance to compare with.
1121 | * @param \Cake\Chronos\ChronosDate $second The instance to compare with.
1122 | * @param \Cake\Chronos\ChronosDate ...$others Others instance to compare with.
1123 | * @return self
1124 | */
1125 | public function farthest(ChronosDate $first, ChronosDate $second, ChronosDate ...$others): ChronosDate
1126 | {
1127 | $farthest = $first;
1128 | $farthestDiffInDays = $this->diffInDays($first);
1129 | foreach ([$second, ...$others] as $other) {
1130 | $otherDiffInDays = $this->diffInDays($other);
1131 | if ($otherDiffInDays > $farthestDiffInDays) {
1132 | $farthest = $other;
1133 | $farthestDiffInDays = $otherDiffInDays;
1134 | }
1135 | }
1136 |
1137 | return $farthest;
1138 | }
1139 |
1140 | /**
1141 | * Determines if the instance is a weekday
1142 | *
1143 | * @return bool
1144 | */
1145 | public function isWeekday(): bool
1146 | {
1147 | return !$this->isWeekend();
1148 | }
1149 |
1150 | /**
1151 | * Determines if the instance is a weekend day
1152 | *
1153 | * @return bool
1154 | */
1155 | public function isWeekend(): bool
1156 | {
1157 | return in_array($this->dayOfWeek, Chronos::getWeekendDays(), true);
1158 | }
1159 |
1160 | /**
1161 | * Determines if the instance is yesterday
1162 | *
1163 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1164 | * @return bool
1165 | */
1166 | public function isYesterday(DateTimeZone|string|null $timezone = null): bool
1167 | {
1168 | return $this->equals(static::yesterday($timezone));
1169 | }
1170 |
1171 | /**
1172 | * Determines if the instance is today
1173 | *
1174 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1175 | * @return bool
1176 | */
1177 | public function isToday(DateTimeZone|string|null $timezone = null): bool
1178 | {
1179 | return $this->equals(static::now($timezone));
1180 | }
1181 |
1182 | /**
1183 | * Determines if the instance is tomorrow
1184 | *
1185 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1186 | * @return bool
1187 | */
1188 | public function isTomorrow(DateTimeZone|string|null $timezone = null): bool
1189 | {
1190 | return $this->equals(static::tomorrow($timezone));
1191 | }
1192 |
1193 | /**
1194 | * Determines if the instance is within the next week
1195 | *
1196 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1197 | * @return bool
1198 | */
1199 | public function isNextWeek(DateTimeZone|string|null $timezone = null): bool
1200 | {
1201 | return $this->format('W o') === static::now($timezone)->addWeeks(1)->format('W o');
1202 | }
1203 |
1204 | /**
1205 | * Determines if the instance is within the last week
1206 | *
1207 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1208 | * @return bool
1209 | */
1210 | public function isLastWeek(DateTimeZone|string|null $timezone = null): bool
1211 | {
1212 | return $this->format('W o') === static::now($timezone)->subWeeks(1)->format('W o');
1213 | }
1214 |
1215 | /**
1216 | * Determines if the instance is within the next month
1217 | *
1218 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1219 | * @return bool
1220 | */
1221 | public function isNextMonth(DateTimeZone|string|null $timezone = null): bool
1222 | {
1223 | return $this->format('m Y') === static::now($timezone)->addMonths(1)->format('m Y');
1224 | }
1225 |
1226 | /**
1227 | * Determines if the instance is within the last month
1228 | *
1229 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1230 | * @return bool
1231 | */
1232 | public function isLastMonth(DateTimeZone|string|null $timezone = null): bool
1233 | {
1234 | return $this->format('m Y') === static::now($timezone)->subMonths(1)->format('m Y');
1235 | }
1236 |
1237 | /**
1238 | * Determines if the instance is within the next year
1239 | *
1240 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1241 | * @return bool
1242 | */
1243 | public function isNextYear(DateTimeZone|string|null $timezone = null): bool
1244 | {
1245 | return $this->year === static::now($timezone)->addYears(1)->year;
1246 | }
1247 |
1248 | /**
1249 | * Determines if the instance is within the last year
1250 | *
1251 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1252 | * @return bool
1253 | */
1254 | public function isLastYear(DateTimeZone|string|null $timezone = null): bool
1255 | {
1256 | return $this->year === static::now($timezone)->subYears(1)->year;
1257 | }
1258 |
1259 | /**
1260 | * Determines if the instance is within the first half of year
1261 | *
1262 | * @return bool
1263 | */
1264 | public function isFirstHalf(): bool
1265 | {
1266 | return $this->half === 1;
1267 | }
1268 |
1269 | /**
1270 | * Determines if the instance is within the second half of year
1271 | *
1272 | * @return bool
1273 | */
1274 | public function isSecondHalf(): bool
1275 | {
1276 | return $this->half === 2;
1277 | }
1278 |
1279 | /**
1280 | * Determines if the instance is in the future, ie. greater (after) than now
1281 | *
1282 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1283 | * @return bool
1284 | */
1285 | public function isFuture(DateTimeZone|string|null $timezone = null): bool
1286 | {
1287 | return $this->greaterThan(static::now($timezone));
1288 | }
1289 |
1290 | /**
1291 | * Determines if the instance is in the past, ie. less (before) than now
1292 | *
1293 | * @param \DateTimeZone|string|null $timezone Time zone to use for now.
1294 | * @return bool
1295 | */
1296 | public function isPast(DateTimeZone|string|null $timezone = null): bool
1297 | {
1298 | return $this->lessThan(static::now($timezone));
1299 | }
1300 |
1301 | /**
1302 | * Determines if the instance is a leap year
1303 | *
1304 | * @return bool
1305 | */
1306 | public function isLeapYear(): bool
1307 | {
1308 | return $this->format('L') === '1';
1309 | }
1310 |
1311 | /**
1312 | * Checks if this day is a Sunday.
1313 | *
1314 | * @return bool
1315 | */
1316 | public function isSunday(): bool
1317 | {
1318 | return $this->dayOfWeek === Chronos::SUNDAY;
1319 | }
1320 |
1321 | /**
1322 | * Checks if this day is a Monday.
1323 | *
1324 | * @return bool
1325 | */
1326 | public function isMonday(): bool
1327 | {
1328 | return $this->dayOfWeek === Chronos::MONDAY;
1329 | }
1330 |
1331 | /**
1332 | * Checks if this day is a Tuesday.
1333 | *
1334 | * @return bool
1335 | */
1336 | public function isTuesday(): bool
1337 | {
1338 | return $this->dayOfWeek === Chronos::TUESDAY;
1339 | }
1340 |
1341 | /**
1342 | * Checks if this day is a Wednesday.
1343 | *
1344 | * @return bool
1345 | */
1346 | public function isWednesday(): bool
1347 | {
1348 | return $this->dayOfWeek === Chronos::WEDNESDAY;
1349 | }
1350 |
1351 | /**
1352 | * Checks if this day is a Thursday.
1353 | *
1354 | * @return bool
1355 | */
1356 | public function isThursday(): bool
1357 | {
1358 | return $this->dayOfWeek === Chronos::THURSDAY;
1359 | }
1360 |
1361 | /**
1362 | * Checks if this day is a Friday.
1363 | *
1364 | * @return bool
1365 | */
1366 | public function isFriday(): bool
1367 | {
1368 | return $this->dayOfWeek === Chronos::FRIDAY;
1369 | }
1370 |
1371 | /**
1372 | * Checks if this day is a Saturday.
1373 | *
1374 | * @return bool
1375 | */
1376 | public function isSaturday(): bool
1377 | {
1378 | return $this->dayOfWeek === Chronos::SATURDAY;
1379 | }
1380 |
1381 | /**
1382 | * Returns true this instance happened within the specified interval
1383 | *
1384 | * @param string|int $timeInterval the numeric value with space then time type.
1385 | * Example of valid types: 6 hours, 2 days, 1 minute.
1386 | * @return bool
1387 | */
1388 | public function wasWithinLast(string|int $timeInterval): bool
1389 | {
1390 | $now = new static(new Chronos());
1391 | $interval = $now->modify('-' . $timeInterval);
1392 | $thisTime = $this->format('U');
1393 |
1394 | return $thisTime >= $interval->format('U') && $thisTime <= $now->format('U');
1395 | }
1396 |
1397 | /**
1398 | * Returns true this instance will happen within the specified interval
1399 | *
1400 | * @param string|int $timeInterval the numeric value with space then time type.
1401 | * Example of valid types: 6 hours, 2 days, 1 minute.
1402 | * @return bool
1403 | */
1404 | public function isWithinNext(string|int $timeInterval): bool
1405 | {
1406 | $now = new static(new Chronos());
1407 | $interval = $now->modify('+' . $timeInterval);
1408 | $thisTime = $this->format('U');
1409 |
1410 | return $thisTime <= $interval->format('U') && $thisTime >= $now->format('U');
1411 | }
1412 |
1413 | /**
1414 | * Get the difference by the given interval using a filter callable
1415 | *
1416 | * @param \DateInterval $interval An interval to traverse by
1417 | * @param callable $callback The callback to use for filtering.
1418 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1419 | * @param bool $absolute Get the absolute of the difference
1420 | * @param int $options DatePeriod options, {@see https://www.php.net/manual/en/class.dateperiod.php}
1421 | * @return int
1422 | */
1423 | public function diffFiltered(
1424 | DateInterval $interval,
1425 | callable $callback,
1426 | ?ChronosDate $other = null,
1427 | bool $absolute = true,
1428 | int $options = 0
1429 | ): int {
1430 | $start = $this;
1431 | $end = $other ?? new ChronosDate(Chronos::now());
1432 | $inverse = false;
1433 |
1434 | if ($end < $start) {
1435 | $start = $end;
1436 | $end = $this;
1437 | $inverse = true;
1438 | }
1439 | // Hack around PHP's DatePeriod not counting equal dates at midnight as
1440 | // within the range. Sadly INCLUDE_END_DATE doesn't land until 8.2
1441 | $endTime = $end->native->modify('+1 second');
1442 |
1443 | $period = new DatePeriod($start->native, $interval, $endTime, $options);
1444 | $vals = array_filter(iterator_to_array($period), function (DateTimeInterface $date) use ($callback) {
1445 | return $callback(static::parse($date));
1446 | });
1447 |
1448 | $diff = count($vals);
1449 |
1450 | return $inverse && !$absolute ? -$diff : $diff;
1451 | }
1452 |
1453 | /**
1454 | * Get the difference in years
1455 | *
1456 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1457 | * @param bool $absolute Get the absolute of the difference
1458 | * @return int
1459 | */
1460 | public function diffInYears(?ChronosDate $other = null, bool $absolute = true): int
1461 | {
1462 | $diff = $this->diff($other ?? new static(new Chronos()), $absolute);
1463 |
1464 | return $diff->invert ? -$diff->y : $diff->y;
1465 | }
1466 |
1467 | /**
1468 | * Get the difference in months
1469 | *
1470 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1471 | * @param bool $absolute Get the absolute of the difference
1472 | * @return int
1473 | */
1474 | public function diffInMonths(?ChronosDate $other = null, bool $absolute = true): int
1475 | {
1476 | $diff = $this->diff($other ?? new static(Chronos::now()), $absolute);
1477 | $months = $diff->y * Chronos::MONTHS_PER_YEAR + $diff->m;
1478 |
1479 | return $diff->invert ? -$months : $months;
1480 | }
1481 |
1482 | /**
1483 | * Get the difference in weeks
1484 | *
1485 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1486 | * @param bool $absolute Get the absolute of the difference
1487 | * @return int
1488 | */
1489 | public function diffInWeeks(?ChronosDate $other = null, bool $absolute = true): int
1490 | {
1491 | return (int)($this->diffInDays($other, $absolute) / Chronos::DAYS_PER_WEEK);
1492 | }
1493 |
1494 | /**
1495 | * Get the difference in days
1496 | *
1497 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1498 | * @param bool $absolute Get the absolute of the difference
1499 | * @return int
1500 | */
1501 | public function diffInDays(?ChronosDate $other = null, bool $absolute = true): int
1502 | {
1503 | $diff = $this->diff($other ?? new static(Chronos::now()), $absolute);
1504 |
1505 | return $diff->invert ? -(int)$diff->days : (int)$diff->days;
1506 | }
1507 |
1508 | /**
1509 | * Get the difference in days using a filter callable
1510 | *
1511 | * @param callable $callback The callback to use for filtering.
1512 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1513 | * @param bool $absolute Get the absolute of the difference
1514 | * @param int $options DatePeriod options, {@see https://www.php.net/manual/en/class.dateperiod.php}
1515 | * @return int
1516 | */
1517 | public function diffInDaysFiltered(
1518 | callable $callback,
1519 | ?ChronosDate $other = null,
1520 | bool $absolute = true,
1521 | int $options = 0
1522 | ): int {
1523 | return $this->diffFiltered(new DateInterval('P1D'), $callback, $other, $absolute, $options);
1524 | }
1525 |
1526 | /**
1527 | * Get the difference in weekdays
1528 | *
1529 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1530 | * @param bool $absolute Get the absolute of the difference
1531 | * @param int $options DatePeriod options, {@see https://www.php.net/manual/en/class.dateperiod.php}
1532 | * @return int
1533 | */
1534 | public function diffInWeekdays(?ChronosDate $other = null, bool $absolute = true, int $options = 0): int
1535 | {
1536 | return $this->diffInDaysFiltered(function (ChronosDate $date) {
1537 | return $date->isWeekday();
1538 | }, $other, $absolute, $options);
1539 | }
1540 |
1541 | /**
1542 | * Get the difference in weekend days using a filter
1543 | *
1544 | * @param \Cake\Chronos\ChronosDate|null $other The instance to difference from.
1545 | * @param bool $absolute Get the absolute of the difference
1546 | * @param int $options DatePeriod options, {@see https://www.php.net/manual/en/class.dateperiod.php}
1547 | * @return int
1548 | */
1549 | public function diffInWeekendDays(?ChronosDate $other = null, bool $absolute = true, int $options = 0): int
1550 | {
1551 | return $this->diffInDaysFiltered(function (ChronosDate $date) {
1552 | return $date->isWeekend();
1553 | }, $other, $absolute, $options);
1554 | }
1555 |
1556 | /**
1557 | * Get the difference in a human readable format.
1558 | *
1559 | * When comparing a value in the past to default now:
1560 | * 5 months ago
1561 | *
1562 | * When comparing a value in the future to default now:
1563 | * 5 months from now
1564 | *
1565 | * When comparing a value in the past to another value:
1566 | * 5 months before
1567 | *
1568 | * When comparing a value in the future to another value:
1569 | * 5 months after
1570 | *
1571 | * @param \Cake\Chronos\ChronosDate|null $other The datetime to compare with.
1572 | * @param bool $absolute removes difference modifiers ago, after, etc
1573 | * @return string
1574 | */
1575 | public function diffForHumans(?ChronosDate $other = null, bool $absolute = false): string
1576 | {
1577 | return static::diffFormatter()->diffForHumans($this, $other, $absolute);
1578 | }
1579 |
1580 | /**
1581 | * Returns the date as a `DateTimeImmutable` instance at midnight.
1582 | *
1583 | * @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
1584 | * @return \DateTimeImmutable
1585 | */
1586 | public function toDateTimeImmutable(DateTimeZone|string|null $timezone = null): DateTimeImmutable
1587 | {
1588 | if ($timezone === null) {
1589 | return $this->native;
1590 | }
1591 |
1592 | $timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone;
1593 |
1594 | return new DateTimeImmutable($this->native->format('Y-m-d H:i:s.u'), $timezone);
1595 | }
1596 |
1597 | /**
1598 | * Returns the date as a `DateTimeImmutable` instance at midnight.
1599 | *
1600 | * Alias of `toDateTimeImmutable()`.
1601 | *
1602 | * @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
1603 | * @return \DateTimeImmutable
1604 | */
1605 | public function toNative(DateTimeZone|string|null $timezone = null): DateTimeImmutable
1606 | {
1607 | return $this->toDateTimeImmutable($timezone);
1608 | }
1609 |
1610 | /**
1611 | * Get a part of the object
1612 | *
1613 | * @param string $name The property name to read.
1614 | * @return string|float|int|bool The property value.
1615 | * @throws \InvalidArgumentException
1616 | */
1617 | public function __get(string $name): string|float|int|bool
1618 | {
1619 | static $formats = [
1620 | 'year' => 'Y',
1621 | 'yearIso' => 'o',
1622 | 'month' => 'n',
1623 | 'day' => 'j',
1624 | 'dayOfWeek' => 'N',
1625 | 'dayOfYear' => 'z',
1626 | 'weekOfYear' => 'W',
1627 | 'daysInMonth' => 't',
1628 | ];
1629 |
1630 | switch (true) {
1631 | case isset($formats[$name]):
1632 | return (int)$this->format($formats[$name]);
1633 |
1634 | case $name === 'dayOfWeekName':
1635 | return $this->format('l');
1636 |
1637 | case $name === 'weekOfMonth':
1638 | return (int)ceil($this->day / Chronos::DAYS_PER_WEEK);
1639 |
1640 | case $name === 'age':
1641 | return $this->diffInYears();
1642 |
1643 | case $name === 'quarter':
1644 | return (int)ceil($this->month / 3);
1645 |
1646 | case $name === 'half':
1647 | return $this->month <= 6 ? 1 : 2;
1648 |
1649 | default:
1650 | throw new InvalidArgumentException(sprintf('Unknown getter `%s`', $name));
1651 | }
1652 | }
1653 |
1654 | /**
1655 | * Check if an attribute exists on the object
1656 | *
1657 | * @param string $name The property name to check.
1658 | * @return bool Whether the property exists.
1659 | */
1660 | public function __isset(string $name): bool
1661 | {
1662 | try {
1663 | $this->__get($name);
1664 | } catch (InvalidArgumentException $e) {
1665 | return false;
1666 | }
1667 |
1668 | return true;
1669 | }
1670 |
1671 | /**
1672 | * Return properties for debugging.
1673 | *
1674 | * @return array
1675 | */
1676 | public function __debugInfo(): array
1677 | {
1678 | $properties = [
1679 | 'hasFixedNow' => Chronos::hasTestNow(),
1680 | 'date' => $this->format('Y-m-d'),
1681 | ];
1682 |
1683 | return $properties;
1684 | }
1685 | }
1686 |
--------------------------------------------------------------------------------
/src/ChronosTime.php:
--------------------------------------------------------------------------------
1 | setTimezone($timezone);
88 | }
89 | $this->ticks = static::parseString($time->format('H:i:s.u'));
90 | } elseif (is_string($time)) {
91 | $this->ticks = static::parseString($time);
92 | } elseif ($time instanceof ChronosTime) {
93 | $this->ticks = $time->ticks;
94 | } else {
95 | $this->ticks = static::parseString($time->format('H:i:s.u'));
96 | }
97 | }
98 |
99 | /**
100 | * Copies time from onther instance or from string in the format HH[:.]mm or HH[:.]mm[:.]ss.u
101 | *
102 | * Defaults to server time.
103 | *
104 | * @param \Cake\Chronos\ChronosTime|\DateTimeInterface|string $time Time
105 | * @param \DateTimeZone|string|null $timezone The timezone to use for now
106 | * @return static
107 | */
108 | public static function parse(
109 | ChronosTime|DateTimeInterface|string|null $time = null,
110 | DateTimeZone|string|null $timezone = null
111 | ): static {
112 | return new static($time, $timezone);
113 | }
114 |
115 | /**
116 | * @param string $time Time string in the format HH[:.]mm or HH[:.]mm[:.]ss.u
117 | * @return int
118 | */
119 | protected static function parseString(string $time): int
120 | {
121 | if (!preg_match('/^\s*(\d{1,2})[:.](\d{1,2})(?|[:.](\d{1,2})[.](\d+)|[:.](\d{1,2}))?\s*$/', $time, $matches)) {
122 | throw new InvalidArgumentException(
123 | sprintf('Time string `%s` is not in expected format `HH[:.]mm` or `HH[:.]mm[:.]ss.u`.', $time)
124 | );
125 | }
126 |
127 | $hours = (int)$matches[1];
128 | $minutes = (int)$matches[2];
129 | $seconds = (int)($matches[3] ?? 0);
130 | $microseconds = (int)substr($matches[4] ?? '', 0, 6);
131 |
132 | if ($hours > 24 || $minutes > 59 || $seconds > 59 || $microseconds > 999_999) {
133 | throw new InvalidArgumentException(sprintf('Time string `%s` contains invalid values.', $time));
134 | }
135 |
136 | $ticks = $hours * self::TICKS_PER_HOUR;
137 | $ticks += $minutes * self::TICKS_PER_MINUTE;
138 | $ticks += $seconds * self::TICKS_PER_SECOND;
139 | $ticks += $microseconds * self::TICKS_PER_MICROSECOND;
140 |
141 | return $ticks % self::TICKS_PER_DAY;
142 | }
143 |
144 | /**
145 | * Returns instance set to server time.
146 | *
147 | * @param \DateTimeZone|string|null $timezone The timezone to use for now
148 | * @return static
149 | */
150 | public static function now(DateTimeZone|string|null $timezone = null): static
151 | {
152 | return new static(null, $timezone);
153 | }
154 |
155 | /**
156 | * Returns instance set to midnight.
157 | *
158 | * @return static
159 | */
160 | public static function midnight(): static
161 | {
162 | return new static('00:00:00');
163 | }
164 |
165 | /**
166 | * Returns instance set to noon.
167 | *
168 | * @return static
169 | */
170 | public static function noon(): static
171 | {
172 | return new static('12:00:00');
173 | }
174 |
175 | /**
176 | * Returns instance set to end of day - either
177 | * 23:59:59 or 23:59:59.999999 if `$microseconds` is true
178 | *
179 | * @param bool $microseconds Whether to set microseconds or not
180 | * @return static
181 | */
182 | public static function endOfDay(bool $microseconds = false): static
183 | {
184 | if ($microseconds) {
185 | return new static('23:59:59.999999');
186 | }
187 |
188 | return new static('23:59:59');
189 | }
190 |
191 | /**
192 | * Returns clock microseconds.
193 | *
194 | * @return int
195 | */
196 | public function getMicroseconds(): int
197 | {
198 | return intdiv($this->ticks % self::TICKS_PER_SECOND, self::TICKS_PER_MICROSECOND);
199 | }
200 |
201 | /**
202 | * Sets clock microseconds.
203 | *
204 | * @param int $microseconds Clock microseconds
205 | * @return static
206 | */
207 | public function setMicroseconds(int $microseconds): static
208 | {
209 | $baseTicks = $this->ticks - $this->ticks % self::TICKS_PER_SECOND;
210 | $newTicks = static::mod($baseTicks + $microseconds * self::TICKS_PER_MICROSECOND, self::TICKS_PER_DAY);
211 |
212 | $clone = clone $this;
213 | $clone->ticks = $newTicks;
214 |
215 | return $clone;
216 | }
217 |
218 | /**
219 | * Return clock seconds.
220 | *
221 | * @return int
222 | */
223 | public function getSeconds(): int
224 | {
225 | $secondsTicks = $this->ticks % self::TICKS_PER_MINUTE - $this->ticks % self::TICKS_PER_SECOND;
226 |
227 | return intdiv($secondsTicks, self::TICKS_PER_SECOND);
228 | }
229 |
230 | /**
231 | * Set clock seconds.
232 | *
233 | * @param int $seconds Clock seconds
234 | * @return static
235 | */
236 | public function setSeconds(int $seconds): static
237 | {
238 | $baseTicks = $this->ticks - ($this->ticks % self::TICKS_PER_MINUTE - $this->ticks % self::TICKS_PER_SECOND);
239 | $newTicks = static::mod($baseTicks + $seconds * self::TICKS_PER_SECOND, self::TICKS_PER_DAY);
240 |
241 | $clone = clone $this;
242 | $clone->ticks = $newTicks;
243 |
244 | return $clone;
245 | }
246 |
247 | /**
248 | * Returns clock minutes.
249 | *
250 | * @return int
251 | */
252 | public function getMinutes(): int
253 | {
254 | $minutesTicks = $this->ticks % self::TICKS_PER_HOUR - $this->ticks % self::TICKS_PER_MINUTE;
255 |
256 | return intdiv($minutesTicks, self::TICKS_PER_MINUTE);
257 | }
258 |
259 | /**
260 | * Set clock minutes.
261 | *
262 | * @param int $minutes Clock minutes
263 | * @return static
264 | */
265 | public function setMinutes(int $minutes): static
266 | {
267 | $baseTicks = $this->ticks - ($this->ticks % self::TICKS_PER_HOUR - $this->ticks % self::TICKS_PER_MINUTE);
268 | $newTicks = static::mod($baseTicks + $minutes * self::TICKS_PER_MINUTE, self::TICKS_PER_DAY);
269 |
270 | $clone = clone $this;
271 | $clone->ticks = $newTicks;
272 |
273 | return $clone;
274 | }
275 |
276 | /**
277 | * Returns clock hours.
278 | *
279 | * @return int
280 | */
281 | public function getHours(): int
282 | {
283 | $hoursInTicks = $this->ticks - $this->ticks % self::TICKS_PER_HOUR;
284 |
285 | return intdiv($hoursInTicks, self::TICKS_PER_HOUR);
286 | }
287 |
288 | /**
289 | * Set clock hours.
290 | *
291 | * @param int $hours Clock hours
292 | * @return static
293 | */
294 | public function setHours(int $hours): static
295 | {
296 | $baseTicks = $this->ticks - ($this->ticks - $this->ticks % self::TICKS_PER_HOUR);
297 | $newTicks = static::mod($baseTicks + $hours * self::TICKS_PER_HOUR, self::TICKS_PER_DAY);
298 |
299 | $clone = clone $this;
300 | $clone->ticks = $newTicks;
301 |
302 | return $clone;
303 | }
304 |
305 | /**
306 | * Sets clock time.
307 | *
308 | * @param int $hours Clock hours
309 | * @param int $minutes Clock minutes
310 | * @param int $seconds Clock seconds
311 | * @param int $microseconds Clock microseconds
312 | * @return static
313 | */
314 | public function setTime(int $hours = 0, int $minutes = 0, int $seconds = 0, int $microseconds = 0): static
315 | {
316 | $ticks = $hours * self::TICKS_PER_HOUR +
317 | $minutes * self::TICKS_PER_MINUTE +
318 | $seconds * self::TICKS_PER_SECOND +
319 | $microseconds * self::TICKS_PER_MICROSECOND;
320 | $ticks = static::mod($ticks, self::TICKS_PER_DAY);
321 |
322 | $clone = clone $this;
323 | $clone->ticks = $ticks;
324 |
325 | return $clone;
326 | }
327 |
328 | /**
329 | * @param int $a Left side
330 | * @param int $a Right side
331 | * @return int
332 | */
333 | protected static function mod(int $a, int $b): int
334 | {
335 | if ($a < 0) {
336 | return $a % $b + $b;
337 | }
338 |
339 | return $a % $b;
340 | }
341 |
342 | /**
343 | * Formats string using the same syntax as `DateTimeImmutable::format()`.
344 | *
345 | * As this uses DateTimeImmutable::format() to format the string, non-time formatters
346 | * will still be interpreted. Be sure to escape those characters first.
347 | *
348 | * @param string $format Format string
349 | * @return string
350 | */
351 | public function format(string $format): string
352 | {
353 | return $this->toDateTimeImmutable()->format($format);
354 | }
355 |
356 | /**
357 | * Reset the format used to the default when converting to a string
358 | *
359 | * @return void
360 | */
361 | public static function resetToStringFormat(): void
362 | {
363 | static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT);
364 | }
365 |
366 | /**
367 | * Set the default format used when converting to a string
368 | *
369 | * @param string $format The format to use in future __toString() calls.
370 | * @return void
371 | */
372 | public static function setToStringFormat(string $format): void
373 | {
374 | static::$toStringFormat = $format;
375 | }
376 |
377 | /**
378 | * Format the instance as a string using the set format
379 | *
380 | * @return string
381 | */
382 | public function __toString(): string
383 | {
384 | return $this->format(static::$toStringFormat);
385 | }
386 |
387 | /**
388 | * Returns whether time is equal to target time.
389 | *
390 | * @param \Cake\Chronos\ChronosTime $target Target time
391 | * @return bool
392 | */
393 | public function equals(ChronosTime $target): bool
394 | {
395 | return $this->ticks === $target->ticks;
396 | }
397 |
398 | /**
399 | * Returns whether time is greater than target time.
400 | *
401 | * @param \Cake\Chronos\ChronosTime $target Target time
402 | * @return bool
403 | */
404 | public function greaterThan(ChronosTime $target): bool
405 | {
406 | return $this->ticks > $target->ticks;
407 | }
408 |
409 | /**
410 | * Returns whether time is greater than or equal to target time.
411 | *
412 | * @param \Cake\Chronos\ChronosTime $target Target time
413 | * @return bool
414 | */
415 | public function greaterThanOrEquals(ChronosTime $target): bool
416 | {
417 | return $this->ticks >= $target->ticks;
418 | }
419 |
420 | /**
421 | * Returns whether time is less than target time.
422 | *
423 | * @param \Cake\Chronos\ChronosTime $target Target time
424 | * @return bool
425 | */
426 | public function lessThan(ChronosTime $target): bool
427 | {
428 | return $this->ticks < $target->ticks;
429 | }
430 |
431 | /**
432 | * Returns whether time is less than or equal to target time.
433 | *
434 | * @param \Cake\Chronos\ChronosTime $target Target time
435 | * @return bool
436 | */
437 | public function lessThanOrEquals(ChronosTime $target): bool
438 | {
439 | return $this->ticks <= $target->ticks;
440 | }
441 |
442 | /**
443 | * Returns whether time is between time range.
444 | *
445 | * @param \Cake\Chronos\ChronosTime $start Start of target range
446 | * @param \Cake\Chronos\ChronosTime $end End of target range
447 | * @param bool $equals Whether to include the beginning and end of range
448 | * @return bool
449 | */
450 | public function between(ChronosTime $start, ChronosTime $end, bool $equals = true): bool
451 | {
452 | if ($start->greaterThan($end)) {
453 | [$start, $end] = [$end, $start];
454 | }
455 |
456 | if ($equals) {
457 | return $this->greaterThanOrEquals($start) && $this->lessThanOrEquals($end);
458 | }
459 |
460 | return $this->greaterThan($start) && $this->lessThan($end);
461 | }
462 |
463 | /**
464 | * Returns an `DateTimeImmutable` instance set to this clock time.
465 | *
466 | * @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
467 | * @return \DateTimeImmutable
468 | */
469 | public function toDateTimeImmutable(DateTimeZone|string|null $timezone = null): DateTimeImmutable
470 | {
471 | $timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone;
472 |
473 | return (new DateTimeImmutable(timezone: $timezone))->setTime(
474 | $this->getHours(),
475 | $this->getMinutes(),
476 | $this->getSeconds(),
477 | $this->getMicroseconds()
478 | );
479 | }
480 |
481 | /**
482 | * Returns an `DateTimeImmutable` instance set to this clock time.
483 | *
484 | * Alias of `toDateTimeImmutable()`.
485 | *
486 | * @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
487 | * @return \DateTimeImmutable
488 | */
489 | public function toNative(DateTimeZone|string|null $timezone = null): DateTimeImmutable
490 | {
491 | return $this->toDateTimeImmutable($timezone);
492 | }
493 | }
494 |
--------------------------------------------------------------------------------
/src/ClockFactory.php:
--------------------------------------------------------------------------------
1 | timezone = $timezone;
36 | }
37 |
38 | /**
39 | * Returns the current time object.
40 | *
41 | * @return \Cake\Chronos\Chronos The current time
42 | */
43 | public function now(): DateTimeImmutable
44 | {
45 | return Chronos::now($this->timezone);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/DifferenceFormatter.php:
--------------------------------------------------------------------------------
1 |
12 | * @link https://cakephp.org CakePHP(tm) Project
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | namespace Cake\Chronos;
16 |
17 | use DateTimeInterface;
18 |
19 | /**
20 | * Handles formatting differences in text.
21 | *
22 | * Provides a swappable component for other libraries to leverage.
23 | * when localizing or customizing the difference output.
24 | *
25 | * @internal
26 | */
27 | class DifferenceFormatter implements DifferenceFormatterInterface
28 | {
29 | /**
30 | * The text translator object
31 | *
32 | * @var \Cake\Chronos\Translator
33 | */
34 | protected Translator $translate;
35 |
36 | /**
37 | * Constructor.
38 | *
39 | * @param \Cake\Chronos\Translator|null $translate The text translator object.
40 | */
41 | public function __construct(?Translator $translate = null)
42 | {
43 | $this->translate = $translate ?: new Translator();
44 | }
45 |
46 | /**
47 | * @inheritDoc
48 | */
49 | public function diffForHumans(
50 | ChronosDate|DateTimeInterface $first,
51 | ChronosDate|DateTimeInterface|null $second = null,
52 | bool $absolute = false
53 | ): string {
54 | $isNow = $second === null;
55 | if ($second === null) {
56 | if ($first instanceof ChronosDate) {
57 | $second = new ChronosDate(Chronos::now());
58 | } else {
59 | $second = Chronos::now($first->getTimezone());
60 | }
61 | }
62 | assert(
63 | ($first instanceof ChronosDate && $second instanceof ChronosDate) ||
64 | ($first instanceof DateTimeInterface && $second instanceof DateTimeInterface)
65 | );
66 |
67 | $diffInterval = $first->diff($second);
68 |
69 | switch (true) {
70 | case $diffInterval->y > 0:
71 | $unit = 'year';
72 | $count = $diffInterval->y;
73 | break;
74 | case $diffInterval->m >= 2:
75 | $unit = 'month';
76 | $count = $diffInterval->m;
77 | break;
78 | case $diffInterval->days >= Chronos::DAYS_PER_WEEK * 3:
79 | $unit = 'week';
80 | $count = (int)($diffInterval->days / Chronos::DAYS_PER_WEEK);
81 | break;
82 | case $diffInterval->d > 0:
83 | $unit = 'day';
84 | $count = $diffInterval->d;
85 | break;
86 | case $diffInterval->h > 0:
87 | $unit = 'hour';
88 | $count = $diffInterval->h;
89 | break;
90 | case $diffInterval->i > 0:
91 | $unit = 'minute';
92 | $count = $diffInterval->i;
93 | break;
94 | default:
95 | $count = $diffInterval->s;
96 | $unit = 'second';
97 | break;
98 | }
99 | $time = $this->translate->plural($unit, $count, ['count' => $count]);
100 | if ($absolute) {
101 | return $time;
102 | }
103 | $isFuture = $diffInterval->invert === 1;
104 | $transId = $isNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
105 |
106 | // Some langs have special pluralization for past and future tense.
107 | $tryKeyExists = $unit . '_' . $transId;
108 | if ($this->translate->exists($tryKeyExists)) {
109 | $time = $this->translate->plural($tryKeyExists, $count, ['count' => $count]);
110 | }
111 |
112 | return $this->translate->singular($transId, ['time' => $time]);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/DifferenceFormatterInterface.php:
--------------------------------------------------------------------------------
1 |
12 | * @link https://cakephp.org CakePHP(tm) Project
13 | * @license https://www.opensource.org/licenses/mit-license.php MIT License
14 | */
15 | namespace Cake\Chronos;
16 |
17 | use DateTime;
18 |
19 | /**
20 | * Provides string formatting methods for datetime instances.
21 | *
22 | * Expects implementing classes to define static::$toStringFormat
23 | *
24 | * @internal
25 | */
26 | trait FormattingTrait
27 | {
28 | /**
29 | * Resets the __toString() format to ``DEFAULT_TO_STRING_FORMAT``.
30 | *
31 | * @return void
32 | */
33 | public static function resetToStringFormat(): void
34 | {
35 | static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT);
36 | }
37 |
38 | /**
39 | * Sets the __toString() format.
40 | *
41 | * @param string $format See ``format()`` for accepted specifiers.
42 | * @return void
43 | */
44 | public static function setToStringFormat(string $format): void
45 | {
46 | static::$toStringFormat = $format;
47 | }
48 |
49 | /**
50 | * Returns a formatted string specified by ``setToStringFormat()``
51 | * or the default ``DEFAULT_TO_STRING_FORMAT`` format.
52 | *
53 | * @return string
54 | */
55 | public function __toString(): string
56 | {
57 | return $this->format(static::$toStringFormat);
58 | }
59 |
60 | /**
61 | * Format the instance as date
62 | *
63 | * @return string
64 | */
65 | public function toDateString(): string
66 | {
67 | return $this->format('Y-m-d');
68 | }
69 |
70 | /**
71 | * Format the instance as a readable date
72 | *
73 | * @return string
74 | */
75 | public function toFormattedDateString(): string
76 | {
77 | return $this->format('M j, Y');
78 | }
79 |
80 | /**
81 | * Format the instance as time
82 | *
83 | * @return string
84 | */
85 | public function toTimeString(): string
86 | {
87 | return $this->format('H:i:s');
88 | }
89 |
90 | /**
91 | * Format the instance as date and time
92 | *
93 | * @return string
94 | */
95 | public function toDateTimeString(): string
96 | {
97 | return $this->format('Y-m-d H:i:s');
98 | }
99 |
100 | /**
101 | * Format the instance with day, date and time
102 | *
103 | * @return string
104 | */
105 | public function toDayDateTimeString(): string
106 | {
107 | return $this->format('D, M j, Y g:i A');
108 | }
109 |
110 | /**
111 | * Format the instance as ATOM
112 | *
113 | * @return string
114 | */
115 | public function toAtomString(): string
116 | {
117 | return $this->format(DateTime::ATOM);
118 | }
119 |
120 | /**
121 | * Format the instance as COOKIE
122 | *
123 | * @return string
124 | */
125 | public function toCookieString(): string
126 | {
127 | return $this->format(DateTime::COOKIE);
128 | }
129 |
130 | /**
131 | * Format the instance as ISO8601
132 | *
133 | * @return string
134 | */
135 | public function toIso8601String(): string
136 | {
137 | return $this->format(DateTime::ATOM);
138 | }
139 |
140 | /**
141 | * Format the instance as RFC822
142 | *
143 | * @return string
144 | * @link https://tools.ietf.org/html/rfc822
145 | */
146 | public function toRfc822String(): string
147 | {
148 | return $this->format(DateTime::RFC822);
149 | }
150 |
151 | /**
152 | * Format the instance as RFC850
153 | *
154 | * @return string
155 | * @link https://tools.ietf.org/html/rfc850
156 | */
157 | public function toRfc850String(): string
158 | {
159 | return $this->format(DateTime::RFC850);
160 | }
161 |
162 | /**
163 | * Format the instance as RFC1036
164 | *
165 | * @return string
166 | * @link https://tools.ietf.org/html/rfc1036
167 | */
168 | public function toRfc1036String(): string
169 | {
170 | return $this->format(DateTime::RFC1036);
171 | }
172 |
173 | /**
174 | * Format the instance as RFC1123
175 | *
176 | * @return string
177 | * @link https://tools.ietf.org/html/rfc1123
178 | */
179 | public function toRfc1123String(): string
180 | {
181 | return $this->format(DateTime::RFC1123);
182 | }
183 |
184 | /**
185 | * Format the instance as RFC2822
186 | *
187 | * @return string
188 | * @link https://tools.ietf.org/html/rfc2822
189 | */
190 | public function toRfc2822String(): string
191 | {
192 | return $this->format(DateTime::RFC2822);
193 | }
194 |
195 | /**
196 | * Format the instance as RFC3339
197 | *
198 | * @return string
199 | * @link https://tools.ietf.org/html/rfc3339
200 | */
201 | public function toRfc3339String(): string
202 | {
203 | return $this->format(DateTime::RFC3339);
204 | }
205 |
206 | /**
207 | * Format the instance as RSS
208 | *
209 | * @return string
210 | */
211 | public function toRssString(): string
212 | {
213 | return $this->format(DateTime::RSS);
214 | }
215 |
216 | /**
217 | * Format the instance as W3C
218 | *
219 | * @return string
220 | */
221 | public function toW3cString(): string
222 | {
223 | return $this->format(DateTime::W3C);
224 | }
225 |
226 | /**
227 | * Returns a UNIX timestamp.
228 | *
229 | * @return string UNIX timestamp
230 | */
231 | public function toUnixString(): string
232 | {
233 | return $this->format('U');
234 | }
235 |
236 | /**
237 | * Returns the quarter
238 | *
239 | * @param bool $range Range.
240 | * @return array|int 1, 2, 3, or 4 quarter of year or array if $range true
241 | */
242 | public function toQuarter(bool $range = false): int|array
243 | {
244 | $quarter = (int)ceil((int)$this->format('m') / 3);
245 | if ($range === false) {
246 | return $quarter;
247 | }
248 |
249 | $year = $this->format('Y');
250 | switch ($quarter) {
251 | case 1:
252 | return [$year . '-01-01', $year . '-03-31'];
253 | case 2:
254 | return [$year . '-04-01', $year . '-06-30'];
255 | case 3:
256 | return [$year . '-07-01', $year . '-09-30'];
257 | default:
258 | return [$year . '-10-01', $year . '-12-31'];
259 | }
260 | }
261 |
262 | /**
263 | * Returns ISO 8601 week number of year, weeks starting on Monday
264 | *
265 | * @return int ISO 8601 week number of year
266 | */
267 | public function toWeek(): int
268 | {
269 | return (int)$this->format('W');
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/src/Translator.php:
--------------------------------------------------------------------------------
1 | '1 year',
30 | 'year_plural' => '{count} years',
31 | 'month' => '1 month',
32 | 'month_plural' => '{count} months',
33 | 'week' => '1 week',
34 | 'week_plural' => '{count} weeks',
35 | 'day' => '1 day',
36 | 'day_plural' => '{count} days',
37 | 'hour' => '1 hour',
38 | 'hour_plural' => '{count} hours',
39 | 'minute' => '1 minute',
40 | 'minute_plural' => '{count} minutes',
41 | 'second' => '1 second',
42 | 'second_plural' => '{count} seconds',
43 | 'ago' => '{time} ago',
44 | 'from_now' => '{time} from now',
45 | 'after' => '{time} after',
46 | 'before' => '{time} before',
47 | ];
48 |
49 | /**
50 | * Check if a translation key exists.
51 | *
52 | * @param string $key The key to check.
53 | * @return bool Whether the key exists.
54 | */
55 | public function exists(string $key): bool
56 | {
57 | return isset(static::$strings[$key]);
58 | }
59 |
60 | /**
61 | * Get a plural message.
62 | *
63 | * @param string $key The key to use.
64 | * @param int $count The number of items in the translation.
65 | * @param array $vars Additional context variables.
66 | * @return string The translated message or ''.
67 | */
68 | public function plural(string $key, int $count, array $vars = []): string
69 | {
70 | if ($count === 1) {
71 | return $this->singular($key, $vars);
72 | }
73 |
74 | return $this->singular($key . '_plural', ['count' => $count] + $vars);
75 | }
76 |
77 | /**
78 | * Get a singular message.
79 | *
80 | * @param string $key The key to use.
81 | * @param array $vars Additional context variables.
82 | * @return string The translated message or ''.
83 | */
84 | public function singular(string $key, array $vars = []): string
85 | {
86 | if (isset(static::$strings[$key])) {
87 | $varKeys = array_keys($vars);
88 | foreach ($varKeys as $i => $k) {
89 | $varKeys[$i] = '{' . $k . '}';
90 | }
91 |
92 | return str_replace($varKeys, $vars, static::$strings[$key]);
93 | }
94 |
95 | return '';
96 | }
97 | }
98 |
--------------------------------------------------------------------------------