blob: 0ddee8dd639a35576cee12623e70be5197056fc9 [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 DateTimeInterface;
16use Throwable;
17
18/**
19 * Trait Options.
20 *
21 * Embed base methods to change settings of Carbon classes.
22 *
23 * Depends on the following methods:
24 *
25 * @method \Carbon\Carbon|\Carbon\CarbonImmutable shiftTimezone($timezone) Set the timezone
26 */
27trait Options
28{
29 use Localization;
30
31 /**
32 * Customizable PHP_INT_SIZE override.
33 *
34 * @var int
35 */
36 public static $PHPIntSize = PHP_INT_SIZE;
37
38 /**
39 * First day of week.
40 *
41 * @var int|string
42 */
43 protected static $weekStartsAt = CarbonInterface::MONDAY;
44
45 /**
46 * Last day of week.
47 *
48 * @var int|string
49 */
50 protected static $weekEndsAt = CarbonInterface::SUNDAY;
51
52 /**
53 * Days of weekend.
54 *
55 * @var array
56 */
57 protected static $weekendDays = [
58 CarbonInterface::SATURDAY,
59 CarbonInterface::SUNDAY,
60 ];
61
62 /**
63 * Format regex patterns.
64 *
65 * @var array<string, string>
66 */
67 protected static $regexFormats = [
68 'd' => '(3[01]|[12][0-9]|0[1-9])',
69 'D' => '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)',
70 'j' => '([123][0-9]|[1-9])',
71 'l' => '([a-zA-Z]{2,})',
72 'N' => '([1-7])',
73 'S' => '(st|nd|rd|th)',
74 'w' => '([0-6])',
75 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])',
76 'W' => '(5[012]|[1-4][0-9]|0?[1-9])',
77 'F' => '([a-zA-Z]{2,})',
78 'm' => '(1[012]|0[1-9])',
79 'M' => '([a-zA-Z]{3})',
80 'n' => '(1[012]|[1-9])',
81 't' => '(2[89]|3[01])',
82 'L' => '(0|1)',
83 'o' => '([1-9][0-9]{0,4})',
84 'Y' => '([1-9]?[0-9]{4})',
85 'y' => '([0-9]{2})',
86 'a' => '(am|pm)',
87 'A' => '(AM|PM)',
88 'B' => '([0-9]{3})',
89 'g' => '(1[012]|[1-9])',
90 'G' => '(2[0-3]|1?[0-9])',
91 'h' => '(1[012]|0[1-9])',
92 'H' => '(2[0-3]|[01][0-9])',
93 'i' => '([0-5][0-9])',
94 's' => '([0-5][0-9])',
95 'u' => '([0-9]{1,6})',
96 'v' => '([0-9]{1,3})',
97 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\\/[a-zA-Z]*)',
98 'I' => '(0|1)',
99 'O' => '([+-](1[012]|0[0-9])[0134][05])',
100 'P' => '([+-](1[012]|0[0-9]):[0134][05])',
101 'p' => '(Z|[+-](1[012]|0[0-9]):[0134][05])',
102 'T' => '([a-zA-Z]{1,5})',
103 'Z' => '(-?[1-5]?[0-9]{1,4})',
104 'U' => '([0-9]*)',
105
106 // The formats below are combinations of the above formats.
107 'c' => '(([1-9]?[0-9]{4})-(1[012]|0[1-9])-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[+-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP
108 'r' => '(([a-zA-Z]{3}), ([123][0-9]|0[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [+-](1[012]|0[0-9])([0134][05]))', // D, d M Y H:i:s O
109 ];
110
111 /**
112 * Format modifiers (such as available in createFromFormat) regex patterns.
113 *
114 * @var array
115 */
116 protected static $regexFormatModifiers = [
117 '*' => '.+',
118 ' ' => '[ ]',
119 '#' => '[;:\\/.,()-]',
120 '?' => '([^a]|[a])',
121 '!' => '',
122 '|' => '',
123 '+' => '',
124 ];
125
126 /**
127 * Indicates if months should be calculated with overflow.
128 * Global setting.
129 *
130 * @var bool
131 */
132 protected static $monthsOverflow = true;
133
134 /**
135 * Indicates if years should be calculated with overflow.
136 * Global setting.
137 *
138 * @var bool
139 */
140 protected static $yearsOverflow = true;
141
142 /**
143 * Indicates if the strict mode is in use.
144 * Global setting.
145 *
146 * @var bool
147 */
148 protected static $strictModeEnabled = true;
149
150 /**
151 * Function to call instead of format.
152 *
153 * @var string|callable|null
154 */
155 protected static $formatFunction;
156
157 /**
158 * Function to call instead of createFromFormat.
159 *
160 * @var string|callable|null
161 */
162 protected static $createFromFormatFunction;
163
164 /**
165 * Function to call instead of parse.
166 *
167 * @var string|callable|null
168 */
169 protected static $parseFunction;
170
171 /**
172 * Indicates if months should be calculated with overflow.
173 * Specific setting.
174 *
175 * @var bool|null
176 */
177 protected $localMonthsOverflow;
178
179 /**
180 * Indicates if years should be calculated with overflow.
181 * Specific setting.
182 *
183 * @var bool|null
184 */
185 protected $localYearsOverflow;
186
187 /**
188 * Indicates if the strict mode is in use.
189 * Specific setting.
190 *
191 * @var bool|null
192 */
193 protected $localStrictModeEnabled;
194
195 /**
196 * Options for diffForHumans and forHumans methods.
197 *
198 * @var bool|null
199 */
200 protected $localHumanDiffOptions;
201
202 /**
203 * Format to use on string cast.
204 *
205 * @var string|null
206 */
207 protected $localToStringFormat;
208
209 /**
210 * Format to use on JSON serialization.
211 *
212 * @var string|null
213 */
214 protected $localSerializer;
215
216 /**
217 * Instance-specific macros.
218 *
219 * @var array|null
220 */
221 protected $localMacros;
222
223 /**
224 * Instance-specific generic macros.
225 *
226 * @var array|null
227 */
228 protected $localGenericMacros;
229
230 /**
231 * Function to call instead of format.
232 *
233 * @var string|callable|null
234 */
235 protected $localFormatFunction;
236
237 /**
238 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
239 * You should rather use the ->settings() method.
240 * @see settings
241 *
242 * Enable the strict mode (or disable with passing false).
243 *
244 * @param bool $strictModeEnabled
245 */
246 public static function useStrictMode($strictModeEnabled = true)
247 {
248 static::$strictModeEnabled = $strictModeEnabled;
249 }
250
251 /**
252 * Returns true if the strict mode is globally in use, false else.
253 * (It can be overridden in specific instances.)
254 *
255 * @return bool
256 */
257 public static function isStrictModeEnabled()
258 {
259 return static::$strictModeEnabled;
260 }
261
262 /**
263 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
264 * You should rather use the ->settings() method.
265 * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
266 * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
267 * @see settings
268 *
269 * Indicates if months should be calculated with overflow.
270 *
271 * @param bool $monthsOverflow
272 *
273 * @return void
274 */
275 public static function useMonthsOverflow($monthsOverflow = true)
276 {
277 static::$monthsOverflow = $monthsOverflow;
278 }
279
280 /**
281 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
282 * You should rather use the ->settings() method.
283 * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
284 * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
285 * @see settings
286 *
287 * Reset the month overflow behavior.
288 *
289 * @return void
290 */
291 public static function resetMonthsOverflow()
292 {
293 static::$monthsOverflow = true;
294 }
295
296 /**
297 * Get the month overflow global behavior (can be overridden in specific instances).
298 *
299 * @return bool
300 */
301 public static function shouldOverflowMonths()
302 {
303 return static::$monthsOverflow;
304 }
305
306 /**
307 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
308 * You should rather use the ->settings() method.
309 * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
310 * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
311 * @see settings
312 *
313 * Indicates if years should be calculated with overflow.
314 *
315 * @param bool $yearsOverflow
316 *
317 * @return void
318 */
319 public static function useYearsOverflow($yearsOverflow = true)
320 {
321 static::$yearsOverflow = $yearsOverflow;
322 }
323
324 /**
325 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
326 * You should rather use the ->settings() method.
327 * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
328 * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
329 * @see settings
330 *
331 * Reset the month overflow behavior.
332 *
333 * @return void
334 */
335 public static function resetYearsOverflow()
336 {
337 static::$yearsOverflow = true;
338 }
339
340 /**
341 * Get the month overflow global behavior (can be overridden in specific instances).
342 *
343 * @return bool
344 */
345 public static function shouldOverflowYears()
346 {
347 return static::$yearsOverflow;
348 }
349
350 /**
351 * Set specific options.
352 * - strictMode: true|false|null
353 * - monthOverflow: true|false|null
354 * - yearOverflow: true|false|null
355 * - humanDiffOptions: int|null
356 * - toStringFormat: string|Closure|null
357 * - toJsonFormat: string|Closure|null
358 * - locale: string|null
359 * - timezone: \DateTimeZone|string|int|null
360 * - macros: array|null
361 * - genericMacros: array|null
362 *
363 * @param array $settings
364 *
365 * @return $this|static
366 */
367 public function settings(array $settings)
368 {
369 $this->localStrictModeEnabled = $settings['strictMode'] ?? null;
370 $this->localMonthsOverflow = $settings['monthOverflow'] ?? null;
371 $this->localYearsOverflow = $settings['yearOverflow'] ?? null;
372 $this->localHumanDiffOptions = $settings['humanDiffOptions'] ?? null;
373 $this->localToStringFormat = $settings['toStringFormat'] ?? null;
374 $this->localSerializer = $settings['toJsonFormat'] ?? null;
375 $this->localMacros = $settings['macros'] ?? null;
376 $this->localGenericMacros = $settings['genericMacros'] ?? null;
377 $this->localFormatFunction = $settings['formatFunction'] ?? null;
378
379 if (isset($settings['locale'])) {
380 $locales = $settings['locale'];
381
382 if (!\is_array($locales)) {
383 $locales = [$locales];
384 }
385
386 $this->locale(...$locales);
387 }
388
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100389 if (isset($settings['innerTimezone'])) {
390 return $this->setTimezone($settings['innerTimezone']);
391 }
392
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200393 if (isset($settings['timezone'])) {
394 return $this->shiftTimezone($settings['timezone']);
395 }
396
397 return $this;
398 }
399
400 /**
401 * Returns current local settings.
402 *
403 * @return array
404 */
405 public function getSettings()
406 {
407 $settings = [];
408 $map = [
409 'localStrictModeEnabled' => 'strictMode',
410 'localMonthsOverflow' => 'monthOverflow',
411 'localYearsOverflow' => 'yearOverflow',
412 'localHumanDiffOptions' => 'humanDiffOptions',
413 'localToStringFormat' => 'toStringFormat',
414 'localSerializer' => 'toJsonFormat',
415 'localMacros' => 'macros',
416 'localGenericMacros' => 'genericMacros',
417 'locale' => 'locale',
418 'tzName' => 'timezone',
419 'localFormatFunction' => 'formatFunction',
420 ];
421
422 foreach ($map as $property => $key) {
423 $value = $this->$property ?? null;
424
425 if ($value !== null) {
426 $settings[$key] = $value;
427 }
428 }
429
430 return $settings;
431 }
432
433 /**
434 * Show truthy properties on var_dump().
435 *
436 * @return array
437 */
438 public function __debugInfo()
439 {
440 $infos = array_filter(get_object_vars($this), function ($var) {
441 return $var;
442 });
443
444 foreach (['dumpProperties', 'constructedObjectId'] as $property) {
445 if (isset($infos[$property])) {
446 unset($infos[$property]);
447 }
448 }
449
450 $this->addExtraDebugInfos($infos);
451
452 return $infos;
453 }
454
455 protected function addExtraDebugInfos(&$infos): void
456 {
457 if ($this instanceof DateTimeInterface) {
458 try {
459 if (!isset($infos['date'])) {
460 $infos['date'] = $this->format(CarbonInterface::MOCK_DATETIME_FORMAT);
461 }
462
463 if (!isset($infos['timezone'])) {
464 $infos['timezone'] = $this->tzName;
465 }
466 } catch (Throwable $exception) {
467 // noop
468 }
469 }
470 }
471}