4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Intl\DateFormatter
;
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
;
22 * Replacement for PHP's native {@link \IntlDateFormatter} class.
24 * The only methods currently supported in this class are:
26 * - {@link __construct}
29 * - {@link getCalendar}
30 * - {@link getDateType}
31 * - {@link getErrorCode}
32 * - {@link getErrorMessage}
34 * - {@link getPattern}
35 * - {@link getTimeType}
36 * - {@link getTimeZoneId}
39 * - {@link setLenient}
40 * - {@link setPattern}
41 * - {@link setTimeZoneId}
42 * - {@link setTimeZone}
44 * @author Igor Wiedler <igor@wiedler.ch>
45 * @author Bernhard Schussek <bschussek@gmail.com>
47 class IntlDateFormatter
50 * The error code from the last operation
54 protected $errorCode = IntlGlobals
::U_ZERO_ERROR
;
57 * The error message from the last operation
61 protected $errorMessage = 'U_ZERO_ERROR';
63 /* date/time format types */
70 /* calendar formats */
71 const TRADITIONAL
= 0;
75 * Patterns used to format the date when no pattern is provided
79 private $defaultDateFormats = array(
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',
88 * Patterns used to format the time when no pattern is provided
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',
117 private $dateTimeZone;
122 private $unitializedTimeZoneId = false;
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
140 * @see http://www.php.net/manual/en/intldateformatter.create.php
141 * @see http://userguide.icu-project.org/formatparse/datetime
143 * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed
144 * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
146 public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self
::GREGORIAN
, $pattern = null)
148 if ('en' !== $locale) {
149 throw new MethodArgumentValueNotImplementedException(__METHOD__
, 'locale', $locale, 'Only the locale "en" is supported');
152 if (self
::GREGORIAN
!== $calendar) {
153 throw new MethodArgumentValueNotImplementedException(__METHOD__
, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
156 $this->datetype
= $datetype;
157 $this->timetype
= $timetype;
159 $this->setPattern($pattern);
160 $this->setTimeZoneId($timezone);
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
174 * @return IntlDateFormatter
176 * @see http://www.php.net/manual/en/intldateformatter.create.php
177 * @see http://userguide.icu-project.org/formatparse/datetime
179 * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed
180 * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
182 public static function create($locale, $datetype, $timetype, $timezone = null, $calendar = self
::GREGORIAN
, $pattern = null)
184 return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
188 * Format the date/time value (timestamp) as a string
190 * @param integer|\DateTime $timestamp The timestamp to format. \DateTime objects
191 * are supported as of PHP 5.3.4.
193 * @return string|Boolean The formatted value or false if formatting failed.
195 * @see http://www.php.net/manual/en/intldateformatter.format.php
197 * @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented
199 public function format($timestamp)
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';
207 throw new MethodArgumentValueNotImplementedException(__METHOD__
, 'timestamp', $timestamp, $message);
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);
221 if (null !== $argumentError) {
222 IntlGlobals
::setError(IntlGlobals
::U_ILLEGAL_ARGUMENT_ERROR
, $argumentError);
223 $this->errorCode
= IntlGlobals
::getErrorCode();
224 $this->errorMessage
= IntlGlobals
::getErrorMessage();
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();
234 $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
235 $formatted = $transformer->format($this->createDateTime($timestamp));
237 // behave like the intl extension
238 IntlGlobals
::setError(IntlGlobals
::U_ZERO_ERROR
);
239 $this->errorCode
= IntlGlobals
::getErrorCode();
240 $this->errorMessage
= IntlGlobals
::getErrorMessage();
246 * Not supported. Formats an object
248 * @param object $object
249 * @param mixed $format
250 * @param string $locale
252 * @return string The formatted value
254 * @see http://www.php.net/manual/en/intldateformatter.formatobject.php
256 * @throws MethodNotImplementedException
258 public function formatObject($object, $format = null, $locale = null)
260 throw new MethodNotImplementedException(__METHOD__
);
264 * Returns the formatter's calendar
266 * @return int The calendar being used by the formatter. Currently always returns
267 * IntlDateFormatter::GREGORIAN.
269 * @see http://www.php.net/manual/en/intldateformatter.getcalendar.php
271 public function getCalendar()
273 return self
::GREGORIAN
;
277 * Not supported. Returns the formatter's calendar object
279 * @return object The calendar's object being used by the formatter
281 * @see http://www.php.net/manual/en/intldateformatter.getcalendarobject.php
283 * @throws MethodNotImplementedException
285 public function getCalendarObject()
287 throw new MethodNotImplementedException(__METHOD__
);
291 * Returns the formatter's datetype
293 * @return int The current value of the formatter
295 * @see http://www.php.net/manual/en/intldateformatter.getdatetype.php
297 public function getDateType()
299 return $this->datetype
;
303 * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value
305 * @return int The error code from last formatter call
307 * @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php
309 public function getErrorCode()
311 return $this->errorCode
;
315 * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
317 * @return string The error message from last formatter call
319 * @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php
321 public function getErrorMessage()
323 return $this->errorMessage
;
327 * Returns the formatter's locale
329 * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
331 * @return string The locale used to create the formatter. Currently always
334 * @see http://www.php.net/manual/en/intldateformatter.getlocale.php
336 public function getLocale($type = Locale
::ACTUAL_LOCALE
)
342 * Returns the formatter's pattern
344 * @return string The pattern string used by the formatter
346 * @see http://www.php.net/manual/en/intldateformatter.getpattern.php
348 public function getPattern()
350 return $this->pattern
;
354 * Returns the formatter's time type
356 * @return string The time type used by the formatter
358 * @see http://www.php.net/manual/en/intldateformatter.gettimetype.php
360 public function getTimeType()
362 return $this->timetype
;
366 * Returns the formatter's timezone identifier
368 * @return string The timezone identifier used by the formatter
370 * @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php
372 public function getTimeZoneId()
374 if (!$this->unitializedTimeZoneId
) {
375 return $this->timeZoneId
;
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();
387 * Not supported. Returns the formatter's timezone
389 * @return mixed The timezone used by the formatter
391 * @see http://www.php.net/manual/en/intldateformatter.gettimezone.php
393 * @throws MethodNotImplementedException
395 public function getTimeZone()
397 throw new MethodNotImplementedException(__METHOD__
);
401 * Returns whether the formatter is lenient
403 * @return Boolean Currently always returns false.
405 * @see http://www.php.net/manual/en/intldateformatter.islenient.php
407 * @throws MethodNotImplementedException
409 public function isLenient()
415 * Not supported. Parse string to a field-based time value
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.
423 * @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field
425 * @see http://www.php.net/manual/en/intldateformatter.localtime.php
427 * @throws MethodNotImplementedException
429 public function localtime($value, &$position = 0)
431 throw new MethodNotImplementedException(__METHOD__
);
435 * Parse string to a timestamp value
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.
443 * @return string Parsed value as a timestamp
445 * @see http://www.php.net/manual/en/intldateformatter.parse.php
447 * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented
449 public function parse($value, &$position = null)
451 // We don't calculate the position when parsing the value
452 if (null !== $position) {
453 throw new MethodArgumentNotImplementedException(__METHOD__
, 'position');
456 $dateTime = $this->createDateTime(0);
457 $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
459 $timestamp = $transformer->parse($dateTime, $value);
461 // behave like the intl extension. FullTransformer::parse() set the proper error
462 $this->errorCode
= IntlGlobals
::getErrorCode();
463 $this->errorMessage
= IntlGlobals
::getErrorMessage();
469 * Not supported. Set the formatter's calendar
471 * @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN.
473 * @return Boolean true on success or false on failure
475 * @see http://www.php.net/manual/en/intldateformatter.setcalendar.php
477 * @throws MethodNotImplementedException
479 public function setCalendar($calendar)
481 throw new MethodNotImplementedException(__METHOD__
);
485 * Set the leniency of the parser
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.
492 * @param Boolean $lenient Sets whether the parser is lenient or not. Currently
493 * only false (strict) is supported.
495 * @return Boolean true on success or false on failure
497 * @see http://www.php.net/manual/en/intldateformatter.setlenient.php
499 * @throws MethodArgumentValueNotImplementedException When $lenient is true
501 public function setLenient($lenient)
504 throw new MethodArgumentValueNotImplementedException(__METHOD__
, 'lenient', $lenient, 'Only the strict parser is supported');
511 * Set the formatter's pattern
513 * @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation
515 * @return Boolean true on success or false on failure
517 * @see http://www.php.net/manual/en/intldateformatter.setpattern.php
518 * @see http://userguide.icu-project.org/formatparse/datetime
520 public function setPattern($pattern)
522 if (null === $pattern) {
523 $pattern = $this->getDefaultPattern();
526 $this->pattern
= $pattern;
532 * Set the formatter's timezone identifier
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
538 * @return Boolean true on success or false on failure
540 * @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php
542 public function setTimeZoneId($timeZoneId)
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();
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';
556 $this->unitializedTimeZoneId
= true;
559 // Backup original passed time zone
560 $timeZone = $timeZoneId;
562 // Get an Etc/GMT time zone that is accepted for \DateTimeZone
563 if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) {
565 $timeZoneId = DateFormat\TimeZoneTransformer
::getEtcTimeZoneId($timeZoneId);
566 } catch (\InvalidArgumentException
$e) {
567 // Does nothing, will fallback to UTC
572 $this->dateTimeZone
= new \
DateTimeZone($timeZoneId);
573 } catch (\Exception
$e) {
574 $this->dateTimeZone
= new \
DateTimeZone('UTC');
577 $this->timeZoneId
= $timeZone;
583 * This method was added in PHP 5.5 as replacement for `setTimeZoneId()`
585 * @param mixed $timeZone
587 * @return Boolean true on success or false on failure
589 * @see http://www.php.net/manual/en/intldateformatter.settimezone.php
591 public function setTimeZone($timeZone)
593 return $this->setTimeZoneId($timeZone);
597 * Create and returns a DateTime object with the specified timestamp and with the
600 * @param int $timestamp
604 protected function createDateTime($timestamp)
606 $dateTime = new \
DateTime();
607 $dateTime->setTimestamp($timestamp);
608 $dateTime->setTimezone($this->dateTimeZone
);
614 * Returns a pattern string based in the datetype and timetype values
618 protected function getDefaultPattern()
620 $patternParts = array();
621 if (self
::NONE
!== $this->datetype
) {
622 $patternParts[] = $this->defaultDateFormats
[$this->datetype
];
624 if (self
::NONE
!== $this->timetype
) {
625 $patternParts[] = $this->defaultTimeFormats
[$this->timetype
];
627 $pattern = implode(' ', $patternParts);