blob: 164dbbd105d972304a492266f70df40c0ccfe801 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3/**
4 * This file is part of the Carbon package.
5 *
6 * (c) Brian Nesbitt <brian@nesbot.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010011
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020012namespace Carbon\Traits;
13
14use Carbon\CarbonInterface;
15use ReturnTypeWillChange;
16
17/**
18 * Trait Modifiers.
19 *
20 * Returns dates relative to current date using modifier short-hand.
21 */
22trait Modifiers
23{
24 /**
25 * Midday/noon hour.
26 *
27 * @var int
28 */
29 protected static $midDayAt = 12;
30
31 /**
32 * get midday/noon hour
33 *
34 * @return int
35 */
36 public static function getMidDayAt()
37 {
38 return static::$midDayAt;
39 }
40
41 /**
42 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
43 * You should rather consider mid-day is always 12pm, then if you need to test if it's an other
44 * hour, test it explicitly:
45 * $date->format('G') == 13
46 * or to set explicitly to a given hour:
47 * $date->setTime(13, 0, 0, 0)
48 *
49 * Set midday/noon hour
50 *
51 * @param int $hour midday hour
52 *
53 * @return void
54 */
55 public static function setMidDayAt($hour)
56 {
57 static::$midDayAt = $hour;
58 }
59
60 /**
61 * Modify to midday, default to self::$midDayAt
62 *
63 * @return static
64 */
65 public function midDay()
66 {
67 return $this->setTime(static::$midDayAt, 0, 0, 0);
68 }
69
70 /**
71 * Modify to the next occurrence of a given modifier such as a day of
72 * the week. If no modifier is provided, modify to the next occurrence
73 * of the current day of the week. Use the supplied constants
74 * to indicate the desired dayOfWeek, ex. static::MONDAY.
75 *
76 * @param string|int|null $modifier
77 *
78 * @return static
79 */
80 public function next($modifier = null)
81 {
82 if ($modifier === null) {
83 $modifier = $this->dayOfWeek;
84 }
85
86 return $this->change(
87 'next '.(\is_string($modifier) ? $modifier : static::$days[$modifier])
88 );
89 }
90
91 /**
92 * Go forward or backward to the next week- or weekend-day.
93 *
94 * @param bool $weekday
95 * @param bool $forward
96 *
97 * @return static
98 */
99 private function nextOrPreviousDay($weekday = true, $forward = true)
100 {
101 /** @var CarbonInterface $date */
102 $date = $this;
103 $step = $forward ? 1 : -1;
104
105 do {
106 $date = $date->addDays($step);
107 } while ($weekday ? $date->isWeekend() : $date->isWeekday());
108
109 return $date;
110 }
111
112 /**
113 * Go forward to the next weekday.
114 *
115 * @return static
116 */
117 public function nextWeekday()
118 {
119 return $this->nextOrPreviousDay();
120 }
121
122 /**
123 * Go backward to the previous weekday.
124 *
125 * @return static
126 */
127 public function previousWeekday()
128 {
129 return $this->nextOrPreviousDay(true, false);
130 }
131
132 /**
133 * Go forward to the next weekend day.
134 *
135 * @return static
136 */
137 public function nextWeekendDay()
138 {
139 return $this->nextOrPreviousDay(false);
140 }
141
142 /**
143 * Go backward to the previous weekend day.
144 *
145 * @return static
146 */
147 public function previousWeekendDay()
148 {
149 return $this->nextOrPreviousDay(false, false);
150 }
151
152 /**
153 * Modify to the previous occurrence of a given modifier such as a day of
154 * the week. If no dayOfWeek is provided, modify to the previous occurrence
155 * of the current day of the week. Use the supplied constants
156 * to indicate the desired dayOfWeek, ex. static::MONDAY.
157 *
158 * @param string|int|null $modifier
159 *
160 * @return static
161 */
162 public function previous($modifier = null)
163 {
164 if ($modifier === null) {
165 $modifier = $this->dayOfWeek;
166 }
167
168 return $this->change(
169 'last '.(\is_string($modifier) ? $modifier : static::$days[$modifier])
170 );
171 }
172
173 /**
174 * Modify to the first occurrence of a given day of the week
175 * in the current month. If no dayOfWeek is provided, modify to the
176 * first day of the current month. Use the supplied constants
177 * to indicate the desired dayOfWeek, ex. static::MONDAY.
178 *
179 * @param int|null $dayOfWeek
180 *
181 * @return static
182 */
183 public function firstOfMonth($dayOfWeek = null)
184 {
185 $date = $this->startOfDay();
186
187 if ($dayOfWeek === null) {
188 return $date->day(1);
189 }
190
191 return $date->modify('first '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year);
192 }
193
194 /**
195 * Modify to the last occurrence of a given day of the week
196 * in the current month. If no dayOfWeek is provided, modify to the
197 * last day of the current month. Use the supplied constants
198 * to indicate the desired dayOfWeek, ex. static::MONDAY.
199 *
200 * @param int|null $dayOfWeek
201 *
202 * @return static
203 */
204 public function lastOfMonth($dayOfWeek = null)
205 {
206 $date = $this->startOfDay();
207
208 if ($dayOfWeek === null) {
209 return $date->day($date->daysInMonth);
210 }
211
212 return $date->modify('last '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year);
213 }
214
215 /**
216 * Modify to the given occurrence of a given day of the week
217 * in the current month. If the calculated occurrence is outside the scope
218 * of the current month, then return false and no modifications are made.
219 * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
220 *
221 * @param int $nth
222 * @param int $dayOfWeek
223 *
224 * @return mixed
225 */
226 public function nthOfMonth($nth, $dayOfWeek)
227 {
228 $date = $this->avoidMutation()->firstOfMonth();
229 $check = $date->rawFormat('Y-m');
230 $date = $date->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
231
232 return $date->rawFormat('Y-m') === $check ? $this->modify((string) $date) : false;
233 }
234
235 /**
236 * Modify to the first occurrence of a given day of the week
237 * in the current quarter. If no dayOfWeek is provided, modify to the
238 * first day of the current quarter. Use the supplied constants
239 * to indicate the desired dayOfWeek, ex. static::MONDAY.
240 *
241 * @param int|null $dayOfWeek day of the week default null
242 *
243 * @return static
244 */
245 public function firstOfQuarter($dayOfWeek = null)
246 {
247 return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek);
248 }
249
250 /**
251 * Modify to the last occurrence of a given day of the week
252 * in the current quarter. If no dayOfWeek is provided, modify to the
253 * last day of the current quarter. Use the supplied constants
254 * to indicate the desired dayOfWeek, ex. static::MONDAY.
255 *
256 * @param int|null $dayOfWeek day of the week default null
257 *
258 * @return static
259 */
260 public function lastOfQuarter($dayOfWeek = null)
261 {
262 return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek);
263 }
264
265 /**
266 * Modify to the given occurrence of a given day of the week
267 * in the current quarter. If the calculated occurrence is outside the scope
268 * of the current quarter, then return false and no modifications are made.
269 * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
270 *
271 * @param int $nth
272 * @param int $dayOfWeek
273 *
274 * @return mixed
275 */
276 public function nthOfQuarter($nth, $dayOfWeek)
277 {
278 $date = $this->avoidMutation()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER);
279 $lastMonth = $date->month;
280 $year = $date->year;
281 $date = $date->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
282
283 return ($lastMonth < $date->month || $year !== $date->year) ? false : $this->modify((string) $date);
284 }
285
286 /**
287 * Modify to the first occurrence of a given day of the week
288 * in the current year. If no dayOfWeek is provided, modify to the
289 * first day of the current year. Use the supplied constants
290 * to indicate the desired dayOfWeek, ex. static::MONDAY.
291 *
292 * @param int|null $dayOfWeek day of the week default null
293 *
294 * @return static
295 */
296 public function firstOfYear($dayOfWeek = null)
297 {
298 return $this->month(1)->firstOfMonth($dayOfWeek);
299 }
300
301 /**
302 * Modify to the last occurrence of a given day of the week
303 * in the current year. If no dayOfWeek is provided, modify to the
304 * last day of the current year. Use the supplied constants
305 * to indicate the desired dayOfWeek, ex. static::MONDAY.
306 *
307 * @param int|null $dayOfWeek day of the week default null
308 *
309 * @return static
310 */
311 public function lastOfYear($dayOfWeek = null)
312 {
313 return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek);
314 }
315
316 /**
317 * Modify to the given occurrence of a given day of the week
318 * in the current year. If the calculated occurrence is outside the scope
319 * of the current year, then return false and no modifications are made.
320 * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
321 *
322 * @param int $nth
323 * @param int $dayOfWeek
324 *
325 * @return mixed
326 */
327 public function nthOfYear($nth, $dayOfWeek)
328 {
329 $date = $this->avoidMutation()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
330
331 return $this->year === $date->year ? $this->modify((string) $date) : false;
332 }
333
334 /**
335 * Modify the current instance to the average of a given instance (default now) and the current instance
336 * (second-precision).
337 *
338 * @param \Carbon\Carbon|\DateTimeInterface|null $date
339 *
340 * @return static
341 */
342 public function average($date = null)
343 {
344 return $this->addRealMicroseconds((int) ($this->diffInRealMicroseconds($this->resolveCarbon($date), false) / 2));
345 }
346
347 /**
348 * Get the closest date from the instance (second-precision).
349 *
350 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
351 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
352 *
353 * @return static
354 */
355 public function closest($date1, $date2)
356 {
357 return $this->diffInRealMicroseconds($date1) < $this->diffInRealMicroseconds($date2) ? $date1 : $date2;
358 }
359
360 /**
361 * Get the farthest date from the instance (second-precision).
362 *
363 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
364 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
365 *
366 * @return static
367 */
368 public function farthest($date1, $date2)
369 {
370 return $this->diffInRealMicroseconds($date1) > $this->diffInRealMicroseconds($date2) ? $date1 : $date2;
371 }
372
373 /**
374 * Get the minimum instance between a given instance (default now) and the current instance.
375 *
376 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
377 *
378 * @return static
379 */
380 public function min($date = null)
381 {
382 $date = $this->resolveCarbon($date);
383
384 return $this->lt($date) ? $this : $date;
385 }
386
387 /**
388 * Get the minimum instance between a given instance (default now) and the current instance.
389 *
390 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
391 *
392 * @see min()
393 *
394 * @return static
395 */
396 public function minimum($date = null)
397 {
398 return $this->min($date);
399 }
400
401 /**
402 * Get the maximum instance between a given instance (default now) and the current instance.
403 *
404 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
405 *
406 * @return static
407 */
408 public function max($date = null)
409 {
410 $date = $this->resolveCarbon($date);
411
412 return $this->gt($date) ? $this : $date;
413 }
414
415 /**
416 * Get the maximum instance between a given instance (default now) and the current instance.
417 *
418 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
419 *
420 * @see max()
421 *
422 * @return static
423 */
424 public function maximum($date = null)
425 {
426 return $this->max($date);
427 }
428
429 /**
430 * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else.
431 *
432 * @see https://php.net/manual/en/datetime.modify.php
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100433 *
434 * @return static|false
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200435 */
436 #[ReturnTypeWillChange]
437 public function modify($modify)
438 {
439 return parent::modify((string) $modify);
440 }
441
442 /**
443 * Similar to native modify() method of DateTime but can handle more grammars.
444 *
445 * @example
446 * ```
447 * echo Carbon::now()->change('next 2pm');
448 * ```
449 *
450 * @link https://php.net/manual/en/datetime.modify.php
451 *
452 * @param string $modifier
453 *
454 * @return static
455 */
456 public function change($modifier)
457 {
458 return $this->modify(preg_replace_callback('/^(next|previous|last)\s+(\d{1,2}(h|am|pm|:\d{1,2}(:\d{1,2})?))$/i', function ($match) {
459 $match[2] = str_replace('h', ':00', $match[2]);
460 $test = $this->avoidMutation()->modify($match[2]);
461 $method = $match[1] === 'next' ? 'lt' : 'gt';
462 $match[1] = $test->$method($this) ? $match[1].' day' : 'today';
463
464 return $match[1].' '.$match[2];
465 }, strtr(trim($modifier), [
466 ' at ' => ' ',
467 'just now' => 'now',
468 'after tomorrow' => 'tomorrow +1 day',
469 'before yesterday' => 'yesterday -1 day',
470 ])));
471 }
472}