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