StubIntlDateFormatter.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Locale\Stub;
  11. use Symfony\Component\Locale\Stub\StubLocale;
  12. use Symfony\Component\Locale\Stub\DateFormat\FullTransformer;
  13. use Symfony\Component\Locale\Exception\NotImplementedException;
  14. use Symfony\Component\Locale\Exception\MethodNotImplementedException;
  15. use Symfony\Component\Locale\Exception\MethodArgumentNotImplementedException;
  16. use Symfony\Component\Locale\Exception\MethodArgumentValueNotImplementedException;
  17. /**
  18. * Provides a stub IntlDateFormatter for the 'en' locale.
  19. *
  20. * @author Igor Wiedler <igor@wiedler.ch>
  21. */
  22. class StubIntlDateFormatter
  23. {
  24. /**
  25. * The error code from the last operation
  26. *
  27. * @var integer
  28. */
  29. protected $errorCode = StubIntl::U_ZERO_ERROR;
  30. /**
  31. * The error message from the last operation
  32. *
  33. * @var string
  34. */
  35. protected $errorMessage = 'U_ZERO_ERROR';
  36. /* date/time format types */
  37. const NONE = -1;
  38. const FULL = 0;
  39. const LONG = 1;
  40. const MEDIUM = 2;
  41. const SHORT = 3;
  42. /* calendar formats */
  43. const TRADITIONAL = 0;
  44. const GREGORIAN = 1;
  45. /**
  46. * Patterns used to format the date when no pattern is provided
  47. *
  48. * @var array
  49. */
  50. private $defaultDateFormats = array(
  51. self::NONE => '',
  52. self::FULL => 'EEEE, LLLL d, y',
  53. self::LONG => 'LLLL d, y',
  54. self::MEDIUM => 'LLL d, y',
  55. self::SHORT => 'M/d/yy',
  56. );
  57. /**
  58. * Patterns used to format the time when no pattern is provided
  59. *
  60. * @var array
  61. */
  62. private $defaultTimeFormats = array(
  63. self::FULL => 'h:mm:ss a zzzz',
  64. self::LONG => 'h:mm:ss a z',
  65. self::MEDIUM => 'h:mm:ss a',
  66. self::SHORT => 'h:mm a',
  67. );
  68. /**
  69. * @var int
  70. */
  71. private $datetype;
  72. /**
  73. * @var int
  74. */
  75. private $timetype;
  76. /**
  77. * @var string
  78. */
  79. private $pattern;
  80. /**
  81. * @var DateTimeZone
  82. */
  83. private $dateTimeZone;
  84. /**
  85. * @var Boolean
  86. */
  87. private $unitializedTimeZoneId = false;
  88. /**
  89. * @var string
  90. */
  91. private $timeZoneId;
  92. /**
  93. * Constructor
  94. *
  95. * @param string $locale The locale code
  96. * @param int $datetype Type of date formatting, one of the format type constants
  97. * @param int $timetype Type of time formatting, one of the format type constants
  98. * @param string $timezone Timezone identifier
  99. * @param int $calendar Calendar to use for formatting or parsing; default is Gregorian.
  100. * One of the calendar constants.
  101. * @param string $pattern Optional pattern to use when formatting
  102. *
  103. * @see http://www.php.net/manual/en/intldateformatter.create.php
  104. * @see http://userguide.icu-project.org/formatparse/datetime
  105. *
  106. * @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
  107. * @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
  108. */
  109. public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
  110. {
  111. if ('en' != $locale) {
  112. throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the \'en\' locale is supported');
  113. }
  114. if (self::GREGORIAN != $calendar) {
  115. throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
  116. }
  117. $this->datetype = $datetype;
  118. $this->timetype = $timetype;
  119. $this->setPattern($pattern);
  120. $this->setTimeZoneId($timezone);
  121. }
  122. /**
  123. * Static constructor
  124. *
  125. * @param string $locale The locale code
  126. * @param int $datetype Type of date formatting, one of the format type constants
  127. * @param int $timetype Type of time formatting, one of the format type constants
  128. * @param string $timezone Timezone identifier
  129. * @param int $calendar Calendar to use for formatting or parsing; default is Gregorian.
  130. * One of the calendar constants.
  131. * @param string $pattern Optional pattern to use when formatting
  132. *
  133. * @see http://www.php.net/manual/en/intldateformatter.create.php
  134. * @see http://userguide.icu-project.org/formatparse/datetime
  135. *
  136. * @throws MethodArgumentValueNotImplementedException When $locale different than 'en' is passed
  137. */
  138. static public function create($locale, $datetype, $timetype, $timezone = null, $calendar = self::GREGORIAN, $pattern = null)
  139. {
  140. return new self($locale, $datetype, $timetype, $timezone, $calendar, $pattern);
  141. }
  142. /**
  143. * Format the date/time value (timestamp) as a string
  144. *
  145. * @param mixed $timestamp Unix timestamp to format
  146. *
  147. * @return string The formatted value
  148. *
  149. * @see http://www.php.net/manual/en/intldateformatter.format.php
  150. *
  151. * @throws NotImplementedException If one of the formatting characters is not implemented
  152. */
  153. public function format($timestamp)
  154. {
  155. // intl allows timestamps to be passed as arrays - we don't
  156. if (is_array($timestamp)) {
  157. $message = version_compare(\PHP_VERSION, '5.3.4', '>=') ?
  158. 'Only integer unix timestamps and DateTime objects are supported' :
  159. 'Only integer unix timestamps are supported';
  160. throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, $message);
  161. }
  162. // behave like the intl extension
  163. $argumentError = null;
  164. if (version_compare(\PHP_VERSION, '5.3.4', '<') && !is_int($timestamp)) {
  165. $argumentError = 'datefmt_format: takes either an array or an integer timestamp value ';
  166. } elseif (version_compare(\PHP_VERSION, '5.3.4', '>=') && !is_int($timestamp) && !$timestamp instanceOf \DateTime) {
  167. $argumentError = 'datefmt_format: takes either an array or an integer timestamp value or a DateTime object';
  168. }
  169. if (null !== $argumentError) {
  170. StubIntl::setError(StubIntl::U_ILLEGAL_ARGUMENT_ERROR, $argumentError);
  171. $this->errorCode = StubIntl::getErrorCode();
  172. $this->errorMessage = StubIntl::getErrorMessage();
  173. return false;
  174. }
  175. // As of PHP 5.3.4, IntlDateFormatter::format() accepts DateTime instances
  176. if (version_compare(\PHP_VERSION, '5.3.4', '>=') && $timestamp instanceOf \DateTime) {
  177. $timestamp = $timestamp->getTimestamp();
  178. }
  179. $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
  180. $formatted = $transformer->format($this->createDateTime($timestamp));
  181. // behave like the intl extension
  182. StubIntl::setError(StubIntl::U_ZERO_ERROR);
  183. $this->errorCode = StubIntl::getErrorCode();
  184. $this->errorMessage = StubIntl::getErrorMessage();
  185. return $formatted;
  186. }
  187. /**
  188. * Returns the formatter's calendar
  189. *
  190. * @return int The calendar being used by the formatter
  191. *
  192. * @see http://www.php.net/manual/en/intldateformatter.getcalendar.php
  193. */
  194. public function getCalendar()
  195. {
  196. return self::GREGORIAN;
  197. }
  198. /**
  199. * Returns the formatter's datetype
  200. *
  201. * @return int The current value of the formatter
  202. *
  203. * @see http://www.php.net/manual/en/intldateformatter.getdatetype.php
  204. */
  205. public function getDateType()
  206. {
  207. return $this->datetype;
  208. }
  209. /**
  210. * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value
  211. *
  212. * @return int The error code from last formatter call
  213. *
  214. * @see http://www.php.net/manual/en/intldateformatter.geterrorcode.php
  215. */
  216. public function getErrorCode()
  217. {
  218. return $this->errorCode;
  219. }
  220. /**
  221. * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value
  222. *
  223. * @return string The error message from last formatter call
  224. *
  225. * @see http://www.php.net/manual/en/intldateformatter.geterrormessage.php
  226. */
  227. public function getErrorMessage()
  228. {
  229. return $this->errorMessage;
  230. }
  231. /**
  232. * Returns the formatter's locale
  233. *
  234. * @param int $type The locale name type to return between valid or actual (StubLocale::VALID_LOCALE or StubLocale::ACTUAL_LOCALE, respectively)
  235. *
  236. * @return string The locale name used to create the formatter
  237. *
  238. * @see http://www.php.net/manual/en/intldateformatter.getlocale.php
  239. */
  240. public function getLocale($type = StubLocale::ACTUAL_LOCALE)
  241. {
  242. return 'en';
  243. }
  244. /**
  245. * Returns the formatter's pattern
  246. *
  247. * @return string The pattern string used by the formatter
  248. *
  249. * @see http://www.php.net/manual/en/intldateformatter.getpattern.php
  250. */
  251. public function getPattern()
  252. {
  253. return $this->pattern;
  254. }
  255. /**
  256. * Returns the formatter's time type
  257. *
  258. * @return string The time type used by the formatter
  259. *
  260. * @see http://www.php.net/manual/en/intldateformatter.gettimetype.php
  261. */
  262. public function getTimeType()
  263. {
  264. return $this->timetype;
  265. }
  266. /**
  267. * Returns the formatter's timezone identifier
  268. *
  269. * @return string The timezone identifier used by the formatter
  270. *
  271. * @see http://www.php.net/manual/en/intldateformatter.gettimezoneid.php
  272. */
  273. public function getTimeZoneId()
  274. {
  275. if (!$this->unitializedTimeZoneId) {
  276. return $this->timeZoneId;
  277. }
  278. return null;
  279. }
  280. /**
  281. * Returns whether the formatter is lenient
  282. *
  283. * @return string The timezone identifier used by the formatter
  284. *
  285. * @see http://www.php.net/manual/en/intldateformatter.islenient.php
  286. *
  287. * @throws MethodNotImplementedException
  288. */
  289. public function isLenient()
  290. {
  291. throw new MethodNotImplementedException(__METHOD__);
  292. }
  293. /**
  294. * Parse string to a field-based time value
  295. *
  296. * @param string $value String to convert to a time value
  297. * @param int $position Position at which to start the parsing in $value (zero-based).
  298. * If no error occurs before $value is consumed, $parse_pos will
  299. * contain -1 otherwise it will contain the position at which parsing
  300. * ended. If $parse_pos > strlen($value), the parse fails immediately.
  301. *
  302. * @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field
  303. *
  304. * @see http://www.php.net/manual/en/intldateformatter.localtime.php
  305. *
  306. * @throws MethodNotImplementedException
  307. */
  308. public function localtime($value, &$position = 0)
  309. {
  310. throw new MethodNotImplementedException(__METHOD__);
  311. }
  312. /**
  313. * Parse string to a timestamp value
  314. *
  315. * @param string $value String to convert to a time value
  316. * @param int $position Position at which to start the parsing in $value (zero-based).
  317. * If no error occurs before $value is consumed, $parse_pos will
  318. * contain -1 otherwise it will contain the position at which parsing
  319. * ended. If $parse_pos > strlen($value), the parse fails immediately.
  320. *
  321. * @return string Parsed value as a timestamp
  322. *
  323. * @see http://www.php.net/manual/en/intldateformatter.parse.php
  324. *
  325. * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented
  326. */
  327. public function parse($value, &$position = null)
  328. {
  329. // We don't calculate the position when parsing the value
  330. if (null !== $position) {
  331. throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
  332. }
  333. $dateTime = $this->createDateTime(0);
  334. $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
  335. $timestamp = $transformer->parse($dateTime, $value);
  336. // behave like the intl extension. FullTransformer::parse() set the proper error
  337. if (false === $timestamp) {
  338. $this->errorCode = StubIntl::getErrorCode();
  339. $this->errorMessage = StubIntl::getErrorMessage();
  340. }
  341. return $timestamp;
  342. }
  343. /**
  344. * Set the formatter's calendar
  345. *
  346. * @param string $calendar The calendar to use. Default is IntlDateFormatter::GREGORIAN.
  347. *
  348. * @return Boolean true on success or false on failure
  349. *
  350. * @see http://www.php.net/manual/en/intldateformatter.setcalendar.php
  351. *
  352. * @throws MethodNotImplementedException
  353. */
  354. public function setCalendar($calendar)
  355. {
  356. throw new MethodNotImplementedException(__METHOD__);
  357. }
  358. /**
  359. * Set the leniency of the parser
  360. *
  361. * Define if the parser is strict or lenient in interpreting inputs that do not match the pattern
  362. * exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time
  363. * patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or
  364. * invalid values ("February 30th") are not accepted.
  365. *
  366. * @param Boolean $lenient Sets whether the parser is lenient or not, default is false (strict)
  367. *
  368. * @return Boolean true on success or false on failure
  369. *
  370. * @see http://www.php.net/manual/en/intldateformatter.setlenient.php
  371. *
  372. * @throws MethodNotImplementedException
  373. */
  374. public function setLenient($lenient)
  375. {
  376. throw new MethodNotImplementedException(__METHOD__);
  377. }
  378. /**
  379. * Set the formatter's pattern
  380. *
  381. * @param string $pattern A pattern string in conformance with the ICU IntlDateFormatter documentation
  382. *
  383. * @return Boolean true on success or false on failure
  384. *
  385. * @see http://www.php.net/manual/en/intldateformatter.setpattern.php
  386. * @see http://userguide.icu-project.org/formatparse/datetime
  387. */
  388. public function setPattern($pattern)
  389. {
  390. if (null === $pattern) {
  391. $pattern = $this->getDefaultPattern();
  392. }
  393. $this->pattern = $pattern;
  394. }
  395. /**
  396. * Set the formatter's timezone identifier
  397. *
  398. * @param string $timeZoneId The time zone ID string of the time zone to use.
  399. * If NULL or the empty string, the default time zone for the
  400. * runtime is used.
  401. *
  402. * @return Boolean true on success or false on failure
  403. *
  404. * @see http://www.php.net/manual/en/intldateformatter.settimezoneid.php
  405. */
  406. public function setTimeZoneId($timeZoneId)
  407. {
  408. if (null === $timeZoneId) {
  409. $timeZoneId = date_default_timezone_get();
  410. $this->unitializedTimeZoneId = true;
  411. }
  412. // Backup original passed time zone
  413. $timeZone = $timeZoneId;
  414. // Get an Etc/GMT time zone that is accepted for \DateTimeZone
  415. if ('GMT' !== $timeZoneId && 0 === strpos($timeZoneId, 'GMT')) {
  416. try {
  417. $timeZoneId = DateFormat\TimeZoneTransformer::getEtcTimeZoneId($timeZoneId);
  418. } catch (\InvalidArgumentException $e) {
  419. // Does nothing, will fallback to UTC
  420. }
  421. }
  422. try {
  423. $this->dateTimeZone = new \DateTimeZone($timeZoneId);
  424. } catch (\Exception $e) {
  425. $this->dateTimeZone = new \DateTimeZone('UTC');
  426. }
  427. $this->timeZoneId = $timeZone;
  428. return true;
  429. }
  430. /**
  431. * Create and returns a DateTime object with the specified timestamp and with the
  432. * current time zone
  433. *
  434. * @param int $timestamp
  435. *
  436. * @return DateTime
  437. */
  438. protected function createDateTime($timestamp)
  439. {
  440. $dateTime = new \DateTime();
  441. $dateTime->setTimestamp($timestamp);
  442. $dateTime->setTimezone($this->dateTimeZone);
  443. return $dateTime;
  444. }
  445. /**
  446. * Returns a pattern string based in the datetype and timetype values
  447. *
  448. * @return string
  449. */
  450. protected function getDefaultPattern()
  451. {
  452. $patternParts = array();
  453. if (self::NONE !== $this->datetype) {
  454. $patternParts[] = $this->defaultDateFormats[$this->datetype];
  455. }
  456. if (self::NONE !== $this->timetype) {
  457. $patternParts[] = $this->defaultTimeFormats[$this->timetype];
  458. }
  459. $pattern = implode(' ', $patternParts);
  460. return $pattern;
  461. }
  462. }