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