]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / intl / Symfony / Component / Intl / DateFormatter / IntlDateFormatter.php
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\Intl\DateFormatter;
13
14 use Symfony\Component\Intl\Globals\IntlGlobals;
15 use Symfony\Component\Intl\DateFormatter\DateFormat\FullTransformer;
16 use Symfony\Component\Intl\Exception\MethodNotImplementedException;
17 use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException;
18 use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
19 use Symfony\Component\Intl\Locale\Locale;
20
21 /**
22 * Replacement for PHP's native {@link \IntlDateFormatter} class.
23 *
24 * The only methods currently supported in this class are:
25 *
26 * - {@link __construct}
27 * - {@link create}
28 * - {@link format}
29 * - {@link getCalendar}
30 * - {@link getDateType}
31 * - {@link getErrorCode}
32 * - {@link getErrorMessage}
33 * - {@link getLocale}
34 * - {@link getPattern}
35 * - {@link getTimeType}
36 * - {@link getTimeZoneId}
37 * - {@link isLenient}
38 * - {@link parse}
39 * - {@link setLenient}
40 * - {@link setPattern}
41 * - {@link setTimeZoneId}
42 * - {@link setTimeZone}
43 *
44 * @author Igor Wiedler <igor@wiedler.ch>
45 * @author Bernhard Schussek <bschussek@gmail.com>
46 */
47 class IntlDateFormatter
48 {
49 /**
50 * The error code from the last operation
51 *
52 * @var integer
53 */
54 protected $errorCode = IntlGlobals::U_ZERO_ERROR;
55
56 /**
57 * The error message from the last operation
58 *
59 * @var string
60 */
61 protected $errorMessage = 'U_ZERO_ERROR';
62
63 /* date/time format types */
64 const NONE = -1;
65 const FULL = 0;
66 const LONG = 1;
67 const MEDIUM = 2;
68 const SHORT = 3;
69
70 /* calendar formats */
71 const TRADITIONAL = 0;
72 const GREGORIAN = 1;
73
74 /**
75 * Patterns used to format the date when no pattern is provided
76 *
77 * @var array
78 */
79 private $defaultDateFormats = array(
80 self::NONE => '',
81 self::FULL => 'EEEE, LLLL d, y',
82 self::LONG => 'LLLL d, y',
83 self::MEDIUM => 'LLL d, y',
84 self::SHORT => 'M/d/yy',
85 );
86
87 /**
88 * Patterns used to format the time when no pattern is provided
89 *
90 * @var array
91 */
92 private $defaultTimeFormats = array(
93 self::FULL => 'h:mm:ss a zzzz',
94 self::LONG => 'h:mm:ss a z',
95 self::MEDIUM => 'h:mm:ss a',
96 self::SHORT => 'h:mm a',
97 );
98
99 /**
100 * @var int
101 */
102 private $datetype;
103
104 /**
105 * @var int
106 */
107 private $timetype;
108
109 /**
110 * @var string
111 */
112 private $pattern;
113
114 /**
115 * @var \DateTimeZone
116 */
117 private $dateTimeZone;
118
119 /**
120 * @var Boolean
121 */
122 private $unitializedTimeZoneId = false;
123
124 /**
125 * @var string
126 */
127 private $timeZoneId;
128
129 /**
130 * Constructor
131 *
132 * @param string $locale The locale code. The only currently supported locale is "en".
133 * @param int $datetype Type of date formatting, one of the format type constants
134 * @param int $timetype Type of time formatting, one of the format type constants
135 * @param string $timezone Timezone identifier
136 * @param int $calendar Calendar to use for formatting or parsing. The only currently
137 * supported value is IntlDateFormatter::GREGORIAN.
138 * @param string $pattern Optional pattern to use when formatting
139 *
140 * @see http://www.php.net/manual/en/intldateformatter.create.php
141 * @see http://userguide.icu-project.org/formatparse/datetime
142 *
143 * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed
144 * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
145 */
146 public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
147 {
148 if ('en' !== $locale) {
149 throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
150 }
151
152 if (self::GREGORIAN !== $calendar) {
153 throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
154 }
155
156 $this->datetype = $datetype;
157 $this->timetype = $timetype;
158
159 $this->setPattern($pattern);
160 $this->setTimeZoneId($timezone);
161 }
162
163 /**
164 * Static constructor
165 *
166 * @param string $locale The locale code. The only currently supported locale is "en".
167 * @param int $datetype Type of date formatting, one of the format type constants
168 * @param int $timetype Type of time formatting, one of the format type constants
169 * @param string $timezone Timezone identifier
170 * @param int $calendar Calendar to use for formatting or parsing; default is Gregorian.
171 * One of the calendar constants.
172 * @param string $pattern Optional pattern to use when formatting
173 *
174 * @return IntlDateFormatter
175 *
176 * @see http://www.php.net/manual/en/intldateformatter.create.php
177 * @see http://userguide.icu-project.org/formatparse/datetime
178 *
179 * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed
180 * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
181 */
182 public static function create($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
183 {
184 return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
185 }
186
187 /**
188 * Format the date/time value (timestamp) as a string
189 *
190 * @param integer|\DateTime $timestamp The timestamp to format. \DateTime objects
191 * are supported as of PHP 5.3.4.
192 *
193 * @return string|Boolean The formatted value or false if formatting failed.
194 *
195 * @see http://www.php.net/manual/en/intldateformatter.format.php
196 *
197 * @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented
198 */
199 public function format($timestamp)
200 {
201 // intl allows timestamps to be passed as arrays - we don't
202 if (is_array($timestamp)) {
203 $message = version_compare(PHP_VERSION, '5.3.4', '>=') ?
204 'Only integer unix timestamps and DateTime objects are supported' :
205 'Only integer unix timestamps are supported';
206
207 throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message);
208 }
209
210 // behave like the intl extension
211 $argumentError = null;
212 if (version_compare(PHP_VERSION, '5.3.4', '<') && !is_int($timestamp)) {
213 $argumentError = 'datefmt_format: takes either an array or an integer timestamp value ';
214 } elseif (version_compare(PHP_VERSION, '5.3.4', '>=') && !is_int($timestamp) && !$timestamp instanceof \DateTime) {
215 $argumentError = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object';
216 if (version_compare(PHP_VERSION, '5.5.0-dev', '>=') && !is_int($timestamp)) {
217 $argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $timestamp);
218 }
219 }
220
221 if (null !== $argumentError) {
222 IntlGlobals::setError(IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR, $argumentError);
223 $this->errorCode = IntlGlobals::getErrorCode();
224 $this->errorMessage = IntlGlobals::getErrorMessage();
225
226 return false;
227 }
228
229 // As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances
230 if (version_compare(PHP_VERSION, '5.3.4', '>=') && $timestamp instanceof \DateTime) {
231 $timestamp = $timestamp->getTimestamp();
232 }
233
234 $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
235 $formatted = $transformer->format($this->createDateTime($timestamp));
236
237 // behave like the intl extension
238 IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR);
239 $this->errorCode = IntlGlobals::getErrorCode();
240 $this->errorMessage = IntlGlobals::getErrorMessage();
241
242 return $formatted;
243 }
244
245 /**
246 * Not supported. Formats an object
247 *
248 * @param object $object
249 * @param mixed $format
250 * @param string $locale
251 *
252 * @return string The formatted value
253 *
254 * @see http://www.php.net/manual/en/intldateformatter.formatobject.php
255 *
256 * @throws MethodNotImplementedException
257 */
258 public function formatObject($object, $format = null, $locale = null)
259 {
260 throw new MethodNotImplementedException(__METHOD__);
261 }
262
263 /**
264 * Returns the formatter's calendar
265 *
266 * @return int The calendar being used by the formatter. Currently always returns
267 * IntlDateFormatter::GREGORIAN.
268 *
269 * @see http://www.php.net/manual/en/intldateformatter.getcalendar.php
270 */
271 public function getCalendar()
272 {
273 return self::GREGORIAN;
274 }
275
276 /**
277 * Not supported. Returns the formatter's calendar object
278 *
279 * @return object The calendar's object being used by the formatter
280 *
281 * @see http://www.php.net/manual/en/intldateformatter.getcalendarobject.php
282 *
283 * @throws MethodNotImplementedException
284 */
285 public function getCalendarObject()
286 {
287 throw new MethodNotImplementedException(__METHOD__);
288 }
289
290 /**
291 * Returns the formatter's datetype
292 *
293 * @return int The current value of the formatter
294 *
295 * @see http://www.php.net/manual/en/intldateformatter.getdatetype.php
296 */
297 public function getDateType()
298 {
299 return $this->datetype;
300 }
301
302 /**
303 * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value
304 *
305 * @return int The error code from last formatter call
306 *
307 * @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php
308 */
309 public function getErrorCode()
310 {
311 return $this->errorCode;
312 }
313
314 /**
315 * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
316 *
317 * @return string The error message from last formatter call
318 *
319 * @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php
320 */
321 public function getErrorMessage()
322 {
323 return $this->errorMessage;
324 }
325
326 /**
327 * Returns the formatter's locale
328 *
329 * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
330 *
331 * @return string The locale used to create the formatter. Currently always
332 * returns "en".
333 *
334 * @see http://www.php.net/manual/en/intldateformatter.getlocale.php
335 */
336 public function getLocale($type = Locale::ACTUAL_LOCALE)
337 {
338 return 'en';
339 }
340
341 /**
342 * Returns the formatter's pattern
343 *
344 * @return string The pattern string used by the formatter
345 *
346 * @see http://www.php.net/manual/en/intldateformatter.getpattern.php
347 */
348 public function getPattern()
349 {
350 return $this->pattern;
351 }
352
353 /**
354 * Returns the formatter's time type
355 *
356 * @return string The time type used by the formatter
357 *
358 * @see http://www.php.net/manual/en/intldateformatter.gettimetype.php
359 */
360 public function getTimeType()
361 {
362 return $this->timetype;
363 }
364
365 /**
366 * Returns the formatter's timezone identifier
367 *
368 * @return string The timezone identifier used by the formatter
369 *
370 * @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php
371 */
372 public function getTimeZoneId()
373 {
374 if (!$this->unitializedTimeZoneId) {
375 return $this->timeZoneId;
376 }
377
378 // In PHP 5.5 default timezone depends on `date_default_timezone_get()` method
379 if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
380 return date_default_timezone_get();
381 }
382
383 return null;
384 }
385
386 /**
387 * Not supported. Returns the formatter's timezone
388 *
389 * @return mixed The timezone used by the formatter
390 *
391 * @see http://www.php.net/manual/en/intldateformatter.gettimezone.php
392 *
393 * @throws MethodNotImplementedException
394 */
395 public function getTimeZone()
396 {
397 throw new MethodNotImplementedException(__METHOD__);
398 }
399
400 /**
401 * Returns whether the formatter is lenient
402 *
403 * @return Boolean Currently always returns false.
404 *
405 * @see http://www.php.net/manual/en/intldateformatter.islenient.php
406 *
407 * @throws MethodNotImplementedException
408 */
409 public function isLenient()
410 {
411 return false;
412 }
413
414 /**
415 * Not supported. Parse string to a field-based time value
416 *
417 * @param string $value String to convert to a time value
418 * @param int $position Position at which to start the parsing in $value (zero-based).
419 * If no error occurs before $value is consumed, $parse_pos will
420 * contain -1 otherwise it will contain the position at which parsing
421 * ended. If $parse_pos > strlen($value), the parse fails immediately.
422 *
423 * @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field
424 *
425 * @see http://www.php.net/manual/en/intldateformatter.localtime.php
426 *
427 * @throws MethodNotImplementedException
428 */
429 public function localtime($value, &$position = 0)
430 {
431 throw new MethodNotImplementedException(__METHOD__);
432 }
433
434 /**
435 * Parse string to a timestamp value
436 *
437 * @param string $value String to convert to a time value
438 * @param int $position Not supported. Position at which to start the parsing in $value (zero-based).
439 * If no error occurs before $value is consumed, $parse_pos will
440 * contain -1 otherwise it will contain the position at which parsing
441 * ended. If $parse_pos > strlen($value), the parse fails immediately.
442 *
443 * @return string Parsed value as a timestamp
444 *
445 * @see http://www.php.net/manual/en/intldateformatter.parse.php
446 *
447 * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented
448 */
449 public function parse($value, &$position = null)
450 {
451 // We don't calculate the position when parsing the value
452 if (null !== $position) {
453 throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
454 }
455
456 $dateTime = $this->createDateTime(0);
457 $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
458
459 $timestamp = $transformer->parse($dateTime, $value);
460
461 // behave like the intl extension. FullTransformer::parse() set the proper error
462 $this->errorCode = IntlGlobals::getErrorCode();
463 $this->errorMessage = IntlGlobals::getErrorMessage();
464
465 return $timestamp;
466 }
467
468 /**
469 * Not supported. Set the formatter's calendar
470 *
471 * @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN.
472 *
473 * @return Boolean true on success or false on failure
474 *
475 * @see http://www.php.net/manual/en/intldateformatter.setcalendar.php
476 *
477 * @throws MethodNotImplementedException
478 */
479 public function setCalendar($calendar)
480 {
481 throw new MethodNotImplementedException(__METHOD__);
482 }
483
484 /**
485 * Set the leniency of the parser
486 *
487 * Define if the parser is strict or lenient in interpreting inputs that do not match the pattern
488 * exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time
489 * patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or
490 * invalid values ("February 30th") are not accepted.
491 *
492 * @param Boolean $lenient Sets whether the parser is lenient or not. Currently
493 * only false (strict) is supported.
494 *
495 * @return Boolean true on success or false on failure
496 *
497 * @see http://www.php.net/manual/en/intldateformatter.setlenient.php
498 *
499 * @throws MethodArgumentValueNotImplementedException When $lenient is true
500 */
501 public function setLenient($lenient)
502 {
503 if ($lenient) {
504 throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported');
505 }
506
507 return true;
508 }
509
510 /**
511 * Set the formatter's pattern
512 *
513 * @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation
514 *
515 * @return Boolean true on success or false on failure
516 *
517 * @see http://www.php.net/manual/en/intldateformatter.setpattern.php
518 * @see http://userguide.icu-project.org/formatparse/datetime
519 */
520 public function setPattern($pattern)
521 {
522 if (null === $pattern) {
523 $pattern = $this->getDefaultPattern();
524 }
525
526 $this->pattern = $pattern;
527
528 return true;
529 }
530
531 /**
532 * Set the formatter's timezone identifier
533 *
534 * @param string $timeZoneId The time zone ID string of the time zone to use.
535 * If NULL or the empty string, the default time zone for the
536 * runtime is used.
537 *
538 * @return Boolean true on success or false on failure
539 *
540 * @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php
541 */
542 public function setTimeZoneId($timeZoneId)
543 {
544 if (null === $timeZoneId) {
545 // In PHP 5.5 if $timeZoneId is null it fallbacks to `date_default_timezone_get()` method
546 if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
547 $timeZoneId = date_default_timezone_get();
548 } else {
549 // TODO: changes were made to ext/intl in PHP 5.4.4 release that need to be investigated since it will
550 // use ini's date.timezone when the time zone is not provided. As a not well tested workaround, uses UTC.
551 // See the first two items of the commit message for more information:
552 // https://github.com/php/php-src/commit/eb346ef0f419b90739aadfb6cc7b7436c5b521d9
553 $timeZoneId = getenv('TZ') ?: 'UTC';
554 }
555
556 $this->unitializedTimeZoneId = true;
557 }
558
559 // Backup original passed time zone
560 $timeZone = $timeZoneId;
561
562 // Get an Etc/GMT time zone that is accepted for \DateTimeZone
563 if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) {
564 try {
565 $timeZoneId = DateFormat\TimeZoneTransformer::getEtcTimeZoneId($timeZoneId);
566 } catch (\InvalidArgumentException $e) {
567 // Does nothing, will fallback to UTC
568 }
569 }
570
571 try {
572 $this->dateTimeZone = new \DateTimeZone($timeZoneId);
573 } catch (\Exception $e) {
574 $this->dateTimeZone = new \DateTimeZone('UTC');
575 }
576
577 $this->timeZoneId = $timeZone;
578
579 return true;
580 }
581
582 /**
583 * This method was added in PHP 5.5 as replacement for `setTimeZoneId()`
584 *
585 * @param mixed $timeZone
586 *
587 * @return Boolean true on success or false on failure
588 *
589 * @see http://www.php.net/manual/en/intldateformatter.settimezone.php
590 */
591 public function setTimeZone($timeZone)
592 {
593 return $this->setTimeZoneId($timeZone);
594 }
595
596 /**
597 * Create and returns a DateTime object with the specified timestamp and with the
598 * current time zone
599 *
600 * @param int $timestamp
601 *
602 * @return \DateTime
603 */
604 protected function createDateTime($timestamp)
605 {
606 $dateTime = new \DateTime();
607 $dateTime->setTimestamp($timestamp);
608 $dateTime->setTimezone($this->dateTimeZone);
609
610 return $dateTime;
611 }
612
613 /**
614 * Returns a pattern string based in the datetype and timetype values
615 *
616 * @return string
617 */
618 protected function getDefaultPattern()
619 {
620 $patternParts = array();
621 if (self::NONE !== $this->datetype) {
622 $patternParts[] = $this->defaultDateFormats[$this->datetype];
623 }
624 if (self::NONE !== $this->timetype) {
625 $patternParts[] = $this->defaultTimeFormats[$this->timetype];
626 }
627 $pattern = implode(' ', $patternParts);
628
629 return $pattern;
630 }
631 }