PageRenderTime 34ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/html/PEAR/Date.php

https://bitbucket.org/kuy/ecom
PHP | 5863 lines | 4655 code | 145 blank | 1063 comment | 132 complexity | abdae536eec33a194346063607246c06 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3. // {{{ Header
  4. /**
  5. * Generic date handling class for PEAR
  6. *
  7. * Handles time zones and changes from local standard to local Summer
  8. * time (daylight-saving time) through the Date_TimeZone class.
  9. * Supports several operations from Date_Calc on Date objects.
  10. *
  11. * PHP versions 4 and 5
  12. *
  13. * LICENSE:
  14. *
  15. * Copyright (c) 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman
  16. * Wandayandi, C.A. Woodcock
  17. * All rights reserved.
  18. *
  19. * Redistribution and use in source and binary forms, with or without
  20. * modification, are permitted under the terms of the BSD License.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. * @category Date and Time
  36. * @package Date
  37. * @author Baba Buehler <baba@babaz.com>
  38. * @author Pierre-Alain Joye <pajoye@php.net>
  39. * @author Firman Wandayandi <firman@php.net>
  40. * @author C.A. Woodcock <c01234@netcomuk.co.uk>
  41. * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock
  42. * @license http://www.opensource.org/licenses/bsd-license.php
  43. * BSD License
  44. * @version CVS: $Id: Date.php,v 1.89 2008/03/23 18:34:16 c01234 Exp $
  45. * @link http://pear.php.net/package/Date
  46. */
  47. // }}}
  48. // {{{ Error constants
  49. define('DATE_ERROR_INVALIDDATE', 1);
  50. define('DATE_ERROR_INVALIDTIME', 2);
  51. define('DATE_ERROR_INVALIDTIMEZONE', 3);
  52. define('DATE_ERROR_INVALIDDATEFORMAT', 4);
  53. define('DATE_ERROR_INVALIDFORMATSTRING', 5);
  54. // }}}
  55. // {{{ Includes
  56. require_once 'PEAR.php';
  57. /**
  58. * Load Date_TimeZone
  59. */
  60. require_once 'Date/TimeZone.php';
  61. /**
  62. * Load Date_Calc
  63. */
  64. require_once 'Date/Calc.php';
  65. /**
  66. * Load Date_Span
  67. */
  68. require_once 'Date/Span.php';
  69. // }}}
  70. // {{{ General constants
  71. /**
  72. * Whether to capture the micro-time (in microseconds) by default
  73. * in calls to 'Date::setNow()'. Note that this makes a call to
  74. * 'gettimeofday()', which may not work on all systems.
  75. *
  76. * @since Constant available since Release 1.5.0
  77. */
  78. define('DATE_CAPTURE_MICROTIME_BY_DEFAULT', false);
  79. /**
  80. * Whether to correct, by adding the local Summer time offset, the
  81. * specified time if it falls in the 'skipped hour' (encountered
  82. * when the clocks go forward).
  83. *
  84. * N.B. if specified as 'false', and if a time zone that adjusts
  85. * for Summer time is specified, then an object of this class will
  86. * be set to a semi-invalid state if an invalid time is set. That
  87. * is, an error will not be returned, unless the user then calls
  88. * a function, directly or indirectly, that accesses the time
  89. * part of the object. So, for example, if the user calls:
  90. *
  91. * <code>$date_object->format2('HH.MI.SS')</code> or:
  92. * <code>$date->object->addSeconds(30)</code>,
  93. *
  94. * an error will be returned if the time is invalid. However,
  95. * if the user calls:
  96. *
  97. * <code>$date->object->addDays(1)</code>,
  98. *
  99. * for example, such that the time is no longer invalid, then the
  100. * object will no longer be in this invalid state. This behaviour
  101. * is intended to minimize unexpected errors when a user uses the
  102. * class to do addition with days only, and does not intend to
  103. * access the time.
  104. *
  105. * Of course, this constant will be unused if the user chooses to
  106. * work in UTC or a time zone without Summer time, in which case
  107. * this situation will never arise.
  108. *
  109. * This constant is set to 'true' by default for backwards-compatibility
  110. * reasons, however, you are recommended to set it to 'false'. Note that the
  111. * behaviour is not intended to match that of previous versions of the class
  112. * in terms of ignoring the Summer time offset when making calculations which
  113. * involve dates in both standard and Summer time - this was recognized as a
  114. * bug - but in terms of returning a PEAR error object when the user sets the
  115. * object to an invalid date (i.e. a time in the hour which is skipped when
  116. * the clocks go forwards, which in Europe would be a time such as 01.30).
  117. * Backwards compatibility here means that the behaviour is the same as it
  118. * used to be, less the bug.
  119. *
  120. * Note that this problem is not an issue for the user if:
  121. *
  122. * (a) the user uses a time zone that does not observe Summer time, e.g. UTC
  123. * (b) the user never accesses the time, that is, he never makes a call to
  124. * Date::getHour() or Date::format("%H"), for example, even if he sets
  125. * the time to something invalid
  126. * (c) the user sets DATE_CORRECTINVALIDTIME_DEFAULT to true
  127. *
  128. * @since Constant available since Release 1.5.0
  129. */
  130. define('DATE_CORRECTINVALIDTIME_DEFAULT', true);
  131. /**
  132. * Whether to validate dates (i.e. day-month-year, ignoring the time) by
  133. * disallowing invalid dates (e.g. 31st February) being set by the following
  134. * functions:
  135. *
  136. * Date::setYear()
  137. * Date::setMonth()
  138. * Date::setDay()
  139. *
  140. * If the constant is set to 'true', then the date will be checked (by
  141. * default), and if invalid, an error will be returned with the Date object
  142. * left unmodified.
  143. *
  144. * This constant is set to 'false' by default for backwards-compatibility
  145. * reasons, however, you are recommended to set it to 'true'.
  146. *
  147. * Note that setHour(), setMinute(), setSecond() and setPartSecond()
  148. * allow an invalid date/time to be set regardless of the value of this
  149. * constant.
  150. *
  151. * @since Constant available since Release 1.5.0
  152. */
  153. define('DATE_VALIDATE_DATE_BY_DEFAULT', false);
  154. /**
  155. * Whether, by default, to accept times including leap seconds (i.e. '23.59.60')
  156. * when setting the date/time, and whether to count leap seconds in the
  157. * following functions:
  158. *
  159. * Date::addSeconds()
  160. * Date::subtractSeconds()
  161. * Date_Calc::addSeconds()
  162. * Date::round()
  163. * Date::roundSeconds()
  164. *
  165. * This constant is set to 'false' by default for backwards-compatibility
  166. * reasons, however, you are recommended to set it to 'true'.
  167. *
  168. * Note that this constant does not affect Date::addSpan() and
  169. * Date::subtractSpan() which will not count leap seconds in any case.
  170. *
  171. * @since Constant available since Release 1.5.0
  172. */
  173. define('DATE_COUNT_LEAP_SECONDS', false);
  174. // }}}
  175. // {{{ Output format constants (used in 'Date::getDate()')
  176. /**
  177. * "YYYY-MM-DD HH:MM:SS"
  178. */
  179. define('DATE_FORMAT_ISO', 1);
  180. /**
  181. * "YYYYMMSSTHHMMSS(Z|(+/-)HHMM)?"
  182. */
  183. define('DATE_FORMAT_ISO_BASIC', 2);
  184. /**
  185. * "YYYY-MM-SSTHH:MM:SS(Z|(+/-)HH:MM)?"
  186. */
  187. define('DATE_FORMAT_ISO_EXTENDED', 3);
  188. /**
  189. * "YYYY-MM-SSTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?"
  190. */
  191. define('DATE_FORMAT_ISO_EXTENDED_MICROTIME', 6);
  192. /**
  193. * "YYYYMMDDHHMMSS"
  194. */
  195. define('DATE_FORMAT_TIMESTAMP', 4);
  196. /**
  197. * long int, seconds since the unix epoch
  198. */
  199. define('DATE_FORMAT_UNIXTIME', 5);
  200. // }}}
  201. // {{{ Class: Date
  202. /**
  203. * Generic date handling class for PEAR
  204. *
  205. * Supports time zones with the Date_TimeZone class. Supports several
  206. * operations from Date_Calc on Date objects.
  207. *
  208. * Note to developers: the class stores the local time and date in the
  209. * local standard time. That is, it does not store the time as the
  210. * local Summer time when and if the time zone is in Summer time. It
  211. * is much easier to store local standard time and remember to offset
  212. * it when the user requests it.
  213. *
  214. * @category Date and Time
  215. * @package Date
  216. * @author Baba Buehler <baba@babaz.com>
  217. * @author Pierre-Alain Joye <pajoye@php.net>
  218. * @author Firman Wandayandi <firman@php.net>
  219. * @author C.A. Woodcock <c01234@netcomuk.co.uk>
  220. * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock
  221. * @license http://www.opensource.org/licenses/bsd-license.php
  222. * BSD License
  223. * @version Release: 1.5.0a1
  224. * @link http://pear.php.net/package/Date
  225. */
  226. class Date
  227. {
  228. // {{{ Properties
  229. /**
  230. * The year
  231. *
  232. * @var int
  233. * @access private
  234. * @since Property available since Release 1.0
  235. */
  236. var $year;
  237. /**
  238. * The month
  239. *
  240. * @var int
  241. * @access private
  242. * @since Property available since Release 1.0
  243. */
  244. var $month;
  245. /**
  246. * The day
  247. *
  248. * @var int
  249. * @access private
  250. * @since Property available since Release 1.0
  251. */
  252. var $day;
  253. /**
  254. * The hour
  255. *
  256. * @var int
  257. * @access private
  258. * @since Property available since Release 1.0
  259. */
  260. var $hour;
  261. /**
  262. * The minute
  263. *
  264. * @var int
  265. * @access private
  266. * @since Property available since Release 1.0
  267. */
  268. var $minute;
  269. /**
  270. * The second
  271. *
  272. * @var int
  273. * @access private
  274. * @since Property available since Release 1.0
  275. */
  276. var $second;
  277. /**
  278. * The parts of a second
  279. *
  280. * @var float
  281. * @access private
  282. * @since Property available since Release 1.4.3
  283. */
  284. var $partsecond;
  285. /**
  286. * The year in local standard time
  287. *
  288. * @var int
  289. * @access private
  290. * @since Property available since Release 1.5.0
  291. */
  292. var $on_standardyear;
  293. /**
  294. * The month in local standard time
  295. *
  296. * @var int
  297. * @access private
  298. * @since Property available since Release 1.5.0
  299. */
  300. var $on_standardmonth;
  301. /**
  302. * The day in local standard time
  303. *
  304. * @var int
  305. * @access private
  306. * @since Property available since Release 1.5.0
  307. */
  308. var $on_standardday;
  309. /**
  310. * The hour in local standard time
  311. *
  312. * @var int
  313. * @access private
  314. * @since Property available since Release 1.5.0
  315. */
  316. var $on_standardhour;
  317. /**
  318. * The minute in local standard time
  319. *
  320. * @var int
  321. * @access private
  322. * @since Property available since Release 1.5.0
  323. */
  324. var $on_standardminute;
  325. /**
  326. * The second in local standard time
  327. *
  328. * @var int
  329. * @access private
  330. * @since Property available since Release 1.5.0
  331. */
  332. var $on_standardsecond;
  333. /**
  334. * The part-second in local standard time
  335. *
  336. * @var float
  337. * @access private
  338. * @since Property available since Release 1.5.0
  339. */
  340. var $on_standardpartsecond;
  341. /**
  342. * Whether the object should accept and count leap seconds
  343. *
  344. * @var bool
  345. * @access private
  346. * @since Property available since Release 1.5.0
  347. */
  348. var $ob_countleapseconds;
  349. /**
  350. * Whether the time is valid as a local time (an invalid time
  351. * is one that lies in the 'skipped hour' at the point that
  352. * the clocks go forward)
  353. *
  354. * @var bool
  355. * @access private
  356. * @see Date::isTimeValid()
  357. * @since Property available since Release 1.5.0
  358. */
  359. var $ob_invalidtime = null;
  360. /**
  361. * Date_TimeZone object for this date
  362. *
  363. * @var object Date_TimeZone object
  364. * @access private
  365. * @since Property available since Release 1.0
  366. */
  367. var $tz;
  368. /**
  369. * Defines the default weekday abbreviation length
  370. *
  371. * Formerly used by Date::format(), but now redundant - the abbreviation
  372. * for the current locale of the machine is used.
  373. *
  374. * @var int
  375. * @access private
  376. * @since Property available since Release 1.4.4
  377. */
  378. var $getWeekdayAbbrnameLength = 3;
  379. // }}}
  380. // {{{ Constructor
  381. /**
  382. * Constructor
  383. *
  384. * Creates a new Date Object initialized to the current date/time in the
  385. * system-default timezone by default. A date optionally
  386. * passed in may be in the ISO 8601, TIMESTAMP or UNIXTIME format,
  387. * or another Date object. If no date is passed, the current date/time
  388. * is used.
  389. *
  390. * If a date is passed and an exception is returned by 'setDate()'
  391. * there is nothing that this function can do, so for this reason, it
  392. * is advisable to pass no parameter and to make a separate call to
  393. * 'setDate()'. A date/time should only be passed if known to be a
  394. * valid ISO 8601 string or a valid Unix timestamp.
  395. *
  396. * @param mixed $date optional ISO 8601 date/time to initialize;
  397. * or, a Unix time stamp
  398. * @param bool $pb_countleapseconds whether to count leap seconds
  399. * (defaults to DATE_COUNT_LEAP_SECONDS)
  400. *
  401. * @return void
  402. * @access public
  403. * @see Date::setDate()
  404. */
  405. function Date($date = null,
  406. $pb_countleapseconds = DATE_COUNT_LEAP_SECONDS)
  407. {
  408. $this->ob_countleapseconds = $pb_countleapseconds;
  409. if (is_a($date, 'Date')) {
  410. $this->copy($date);
  411. } else {
  412. if (!is_null($date)) {
  413. // 'setDate()' expects a time zone to be already set:
  414. //
  415. $this->_setTZToDefault();
  416. $this->setDate($date);
  417. } else {
  418. $this->setNow();
  419. }
  420. }
  421. }
  422. // }}}
  423. // {{{ copy()
  424. /**
  425. * Copy values from another Date object
  426. *
  427. * Makes this Date a copy of another Date object. This is a
  428. * PHP4-compatible implementation of '__clone()' in PHP5.
  429. *
  430. * @param object $date Date object to copy
  431. *
  432. * @return void
  433. * @access public
  434. */
  435. function copy($date)
  436. {
  437. $this->year = $date->year;
  438. $this->month = $date->month;
  439. $this->day = $date->day;
  440. $this->hour = $date->hour;
  441. $this->minute = $date->minute;
  442. $this->second = $date->second;
  443. $this->partsecond = $date->partsecond;
  444. $this->on_standardyear = $date->on_standardyear;
  445. $this->on_standardmonth = $date->on_standardmonth;
  446. $this->on_standardday = $date->on_standardday;
  447. $this->on_standardhour = $date->on_standardhour;
  448. $this->on_standardminute = $date->on_standardminute;
  449. $this->on_standardsecond = $date->on_standardsecond;
  450. $this->on_standardpartsecond = $date->on_standardpartsecond;
  451. $this->ob_countleapseconds = $date->ob_countleapseconds;
  452. $this->ob_invalidtime = $date->ob_invalidtime;
  453. $this->tz = new Date_TimeZone($date->getTZID());
  454. $this->getWeekdayAbbrnameLength = $date->getWeekdayAbbrnameLength;
  455. }
  456. // }}}
  457. // {{{ __clone()
  458. /**
  459. * Copy values from another Date object
  460. *
  461. * Makes this Date a copy of another Date object. For PHP5
  462. * only.
  463. *
  464. * @return void
  465. * @access public
  466. * @see Date::copy()
  467. */
  468. function __clone()
  469. {
  470. // This line of code would be preferable, but will only
  471. // compile in PHP5:
  472. //
  473. // $this->tz = clone $this->tz;
  474. $this->tz = new Date_TimeZone($this->getTZID());
  475. }
  476. // }}}
  477. // {{{ setDate()
  478. /**
  479. * Sets the fields of a Date object based on the input date and format
  480. *
  481. * Format parameter should be one of the specified DATE_FORMAT_* constants:
  482. *
  483. * <code>DATE_FORMAT_ISO</code>
  484. * - 'YYYY-MM-DD HH:MI:SS'
  485. * <code>DATE_FORMAT_ISO_BASIC</code>
  486. * - 'YYYYMMSSTHHMMSS(Z|(+/-)HHMM)?'
  487. * <code>DATE_FORMAT_ISO_EXTENDED</code>
  488. * - 'YYYY-MM-SSTHH:MM:SS(Z|(+/-)HH:MM)?'
  489. * <code>DATE_FORMAT_ISO_EXTENDED_MICROTIME</code>
  490. * - 'YYYY-MM-SSTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?'
  491. * <code>DATE_FORMAT_TIMESTAMP</code>
  492. * - 'YYYYMMDDHHMMSS'
  493. * <code>DATE_FORMAT_UNIXTIME'</code>
  494. * - long integer of the no of seconds since
  495. * the Unix Epoch
  496. * (1st January 1970 00.00.00 GMT)
  497. *
  498. * @param string $date input date
  499. * @param int $format optional format constant
  500. * (DATE_FORMAT_*) of the input date.
  501. * This parameter is not needed,
  502. * except to force the setting of the
  503. * date from a Unix time-stamp
  504. * (DATE_FORMAT_UNIXTIME).
  505. * @param bool $pb_repeatedhourdefault value to return if repeated
  506. * hour is specified (defaults
  507. * to false)
  508. *
  509. * @return void
  510. * @access public
  511. */
  512. function setDate($date,
  513. $format = DATE_FORMAT_ISO,
  514. $pb_repeatedhourdefault = false)
  515. {
  516. if (preg_match('/^([0-9]{4,4})-?(0[1-9]|1[0-2])-?(0[1-9]|[12][0-9]|3[01])' .
  517. '([T\s]?([01][0-9]|2[0-3]):?' . // [hh]
  518. '([0-5][0-9]):?([0-5][0-9]|60)(\.\d+)?' . // [mi]:[ss]
  519. '(Z|[+\-][0-9]{2,2}(:?[0-5][0-9])?)?)?$/i', // offset
  520. $date, $regs) &&
  521. $format != DATE_FORMAT_UNIXTIME
  522. ) {
  523. // DATE_FORMAT_ISO, ISO_BASIC, ISO_EXTENDED, and TIMESTAMP
  524. // These formats are extremely close to each other. This regex
  525. // is very loose and accepts almost any butchered format you could
  526. // throw at it. e.g. 2003-10-07 19:45:15 and 2003-10071945:15
  527. // are the same thing in the eyes of this regex, even though the
  528. // latter is not a valid ISO 8601 date.
  529. if (!Date_Calc::isValidDate($regs[3], $regs[2], $regs[1])) {
  530. return PEAR::raiseError("'" .
  531. Date_Calc::dateFormat($regs[1],
  532. $regs[2],
  533. $regs[3],
  534. "%Y-%m-%d") .
  535. "' is invalid calendar date",
  536. DATE_ERROR_INVALIDDATE);
  537. }
  538. if (isset($regs[9])) {
  539. if ($regs[9] == "Z") {
  540. $this->tz = new Date_TimeZone("UTC");
  541. } else {
  542. $this->tz = new Date_TimeZone("UTC" . $regs[9]);
  543. }
  544. }
  545. $this->setLocalTime($regs[3],
  546. $regs[2],
  547. $regs[1],
  548. isset($regs[5]) ? $regs[5] : 0,
  549. isset($regs[6]) ? $regs[6] : 0,
  550. isset($regs[7]) ? $regs[7] : 0,
  551. isset($regs[8]) ? $regs[8] : 0.0,
  552. $pb_repeatedhourdefault);
  553. } else if (is_numeric($date)) {
  554. // Unix Time; N.B. Unix Time is defined relative to GMT,
  555. // so it needs to be adjusted for the current time zone;
  556. // however we do not know if it is in Summer time until
  557. // we have converted it from Unix time:
  558. //
  559. // Get current time zone details:
  560. //
  561. $hs_id = $this->getTZID();
  562. // Input Unix time as UTC:
  563. //
  564. $this->tz = new Date_TimeZone("UTC");
  565. $this->setDate(gmdate("Y-m-d H:i:s", $date));
  566. // Convert back to correct time zone:
  567. //
  568. $this->convertTZByID($hs_id);
  569. } else {
  570. return PEAR::raiseError("Date not in ISO 8601 format",
  571. DATE_ERROR_INVALIDDATEFORMAT);
  572. }
  573. }
  574. // }}}
  575. // {{{ setNow()
  576. /**
  577. * Sets to local current time and time zone
  578. *
  579. * @param bool $pb_setmicrotime whether to set micro-time (defaults to the
  580. * value of the constant
  581. * DATE_CAPTURE_MICROTIME_BY_DEFAULT)
  582. *
  583. * @return void
  584. * @access public
  585. * @since Method available since Release 1.5.0
  586. */
  587. function setNow($pb_setmicrotime = DATE_CAPTURE_MICROTIME_BY_DEFAULT)
  588. {
  589. $this->_setTZToDefault();
  590. if ($pb_setmicrotime) {
  591. $ha_unixtime = gettimeofday();
  592. } else {
  593. $ha_unixtime = array("sec" => time());
  594. }
  595. $this->setDate(date("Y-m-d H:i:s", $ha_unixtime["sec"]) .
  596. (isset($ha_unixtime["usec"]) ?
  597. "." . sprintf("%06d", $ha_unixtime["usec"]) :
  598. ""));
  599. }
  600. // }}}
  601. // {{{ round()
  602. /**
  603. * Rounds the date according to the specified precision (defaults
  604. * to nearest day)
  605. *
  606. * The precision parameter must be one of the following constants:
  607. *
  608. * <code>DATE_PRECISION_YEAR</code>
  609. * <code>DATE_PRECISION_MONTH</code>
  610. * <code>DATE_PRECISION_DAY</code>
  611. * <code>DATE_PRECISION_HOUR</code>
  612. * <code>DATE_PRECISION_10MINUTES</code>
  613. * <code>DATE_PRECISION_MINUTE</code>
  614. * <code>DATE_PRECISION_10SECONDS</code>
  615. * <code>DATE_PRECISION_SECOND</code>
  616. *
  617. * N.B. the default is DATE_PRECISION_DAY
  618. *
  619. * The precision can also be specified as an integral offset from
  620. * one of these constants, where the offset reflects a precision
  621. * of 10 to the power of the offset greater than the constant.
  622. * For example:
  623. *
  624. * <code>DATE_PRECISION_YEAR - 1</code> rounds the date to the nearest 10
  625. * years
  626. * <code>DATE_PRECISION_YEAR - 3</code> rounds the date to the nearest 1000
  627. * years
  628. * <code>DATE_PRECISION_SECOND + 1</code> rounds the date to 1 decimal
  629. * point of a second
  630. * <code>DATE_PRECISION_SECOND + 3</code> rounds the date to 3 decimal
  631. * points of a second
  632. * <code>DATE_PRECISION_SECOND - 1</code> rounds the date to the nearest 10
  633. * seconds (thus it is equivalent to
  634. * DATE_PRECISION_10SECONDS)
  635. *
  636. * @param int $pn_precision a 'DATE_PRECISION_*' constant
  637. * @param bool $pb_correctinvalidtime whether to correct, by adding the
  638. * local Summer time offset, the rounded
  639. * time if it falls in the skipped hour
  640. * (defaults to
  641. * DATE_CORRECTINVALIDTIME_DEFAULT)
  642. *
  643. * @return void
  644. * @access public
  645. * @since Method available since Release 1.5.0
  646. */
  647. function round($pn_precision = DATE_PRECISION_DAY,
  648. $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT)
  649. {
  650. if ($pn_precision <= DATE_PRECISION_DAY) {
  651. list($hn_year,
  652. $hn_month,
  653. $hn_day,
  654. $hn_hour,
  655. $hn_minute,
  656. $hn_secondraw) =
  657. Date_Calc::round($pn_precision,
  658. $this->day,
  659. $this->month,
  660. $this->year,
  661. $this->hour,
  662. $this->minute,
  663. $this->partsecond == 0.0 ?
  664. $this->second :
  665. $this->second + $this->partsecond,
  666. $this->ob_countleapseconds);
  667. if (is_float($hn_secondraw)) {
  668. $hn_second = intval($hn_secondraw);
  669. $hn_partsecond = $hn_secondraw - $hn_second;
  670. } else {
  671. $hn_second = $hn_secondraw;
  672. $hn_partsecond = 0.0;
  673. }
  674. $this->setLocalTime($hn_day,
  675. $hn_month,
  676. $hn_year,
  677. $hn_hour,
  678. $hn_minute,
  679. $hn_second,
  680. $hn_partsecond,
  681. true, // This is unlikely anyway, but the
  682. // day starts with the repeated hour
  683. // the first time around
  684. $pb_correctinvalidtime);
  685. return;
  686. }
  687. // ($pn_precision >= DATE_PRECISION_HOUR)
  688. //
  689. if ($this->tz->getDSTSavings() % 3600000 == 0 ||
  690. ($this->tz->getDSTSavings() % 60000 == 0 &&
  691. $pn_precision >= DATE_PRECISION_MINUTE)
  692. ) {
  693. list($hn_year,
  694. $hn_month,
  695. $hn_day,
  696. $hn_hour,
  697. $hn_minute,
  698. $hn_secondraw) =
  699. Date_Calc::round($pn_precision,
  700. $this->on_standardday,
  701. $this->on_standardmonth,
  702. $this->on_standardyear,
  703. $this->on_standardhour,
  704. $this->on_standardminute,
  705. $this->on_standardpartsecond == 0.0 ?
  706. $this->on_standardsecond :
  707. $this->on_standardsecond +
  708. $this->on_standardpartsecond,
  709. $this->ob_countleapseconds);
  710. if (is_float($hn_secondraw)) {
  711. $hn_second = intval($hn_secondraw);
  712. $hn_partsecond = $hn_secondraw - $hn_second;
  713. } else {
  714. $hn_second = $hn_secondraw;
  715. $hn_partsecond = 0.0;
  716. }
  717. $this->setStandardTime($hn_day,
  718. $hn_month,
  719. $hn_year,
  720. $hn_hour,
  721. $hn_minute,
  722. $hn_second,
  723. $hn_partsecond);
  724. return;
  725. }
  726. // Very unlikely anyway (as I write, the only time zone like this
  727. // is Lord Howe Island in Australia (offset of half an hour)):
  728. //
  729. // (This algorithm could be better)
  730. //
  731. list($hn_year,
  732. $hn_month,
  733. $hn_day,
  734. $hn_hour,
  735. $hn_minute,
  736. $hn_secondraw) =
  737. Date_Calc::round($pn_precision,
  738. $this->day,
  739. $this->month,
  740. $this->year,
  741. $this->hour,
  742. $this->minute,
  743. $this->partsecond == 0.0 ?
  744. $this->second :
  745. $this->second + $this->partsecond,
  746. $this->ob_countleapseconds);
  747. if (is_float($hn_secondraw)) {
  748. $hn_second = intval($hn_secondraw);
  749. $hn_partsecond = $hn_secondraw - $hn_second;
  750. } else {
  751. $hn_second = $hn_secondraw;
  752. $hn_partsecond = 0.0;
  753. }
  754. $this->setLocalTime($hn_day,
  755. $hn_month,
  756. $hn_year,
  757. $hn_hour,
  758. $hn_minute,
  759. $hn_second,
  760. $hn_partsecond,
  761. false, // This will be right half the time
  762. $pb_correctinvalidtime); // This will be right
  763. // some of the time
  764. // (depends on Summer
  765. // time offset)
  766. }
  767. // }}}
  768. // {{{ roundSeconds()
  769. /**
  770. * Rounds seconds up or down to the nearest specified unit
  771. *
  772. * N.B. this function is equivalent to calling:
  773. * <code>'round(DATE_PRECISION_SECOND + $pn_precision)'</code>
  774. *
  775. * @param int $pn_precision number of digits after the decimal point
  776. * @param bool $pb_correctinvalidtime whether to correct, by adding the
  777. * local Summer time offset, the rounded
  778. * time if it falls in the skipped hour
  779. * (defaults to
  780. * DATE_CORRECTINVALIDTIME_DEFAULT)
  781. *
  782. * @return void
  783. * @access public
  784. * @since Method available since Release 1.5.0
  785. */
  786. function roundSeconds($pn_precision = 0,
  787. $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT)
  788. {
  789. $this->round(DATE_PRECISION_SECOND + $pn_precision,
  790. $pb_correctinvalidtime);
  791. }
  792. // }}}
  793. // {{{ trunc()
  794. /**
  795. * Truncates the date according to the specified precision (by
  796. * default, it truncates the time part of the date)
  797. *
  798. * The precision parameter must be one of the following constants:
  799. *
  800. * <code>DATE_PRECISION_YEAR</code>
  801. * <code>DATE_PRECISION_MONTH</code>
  802. * <code>DATE_PRECISION_DAY</code>
  803. * <code>DATE_PRECISION_HOUR</code>
  804. * <code>DATE_PRECISION_10MINUTES</code>
  805. * <code>DATE_PRECISION_MINUTE</code>
  806. * <code>DATE_PRECISION_10SECONDS</code>
  807. * <code>DATE_PRECISION_SECOND</code>
  808. *
  809. * N.B. the default is DATE_PRECISION_DAY
  810. *
  811. * The precision can also be specified as an integral offset from
  812. * one of these constants, where the offset reflects a precision
  813. * of 10 to the power of the offset greater than the constant.
  814. * For example:
  815. *
  816. * <code>DATE_PRECISION_YEAR</code> truncates the month, day and time
  817. * part of the year
  818. * <code>DATE_PRECISION_YEAR - 1</code> truncates the unit part of the
  819. * year, e.g. 1987 becomes 1980
  820. * <code>DATE_PRECISION_YEAR - 3</code> truncates the hundreds part of the
  821. * year, e.g. 1987 becomes 1000
  822. * <code>DATE_PRECISION_SECOND + 1</code> truncates the part of the second
  823. * less than 0.1 of a second, e.g.
  824. * 3.26301 becomes 3.2 seconds
  825. * <code>DATE_PRECISION_SECOND + 3</code> truncates the part of the second
  826. * less than 0.001 of a second, e.g.
  827. * 3.26301 becomes 3.263 seconds
  828. * <code>DATE_PRECISION_SECOND - 1</code> truncates the unit part of the
  829. * seconds (thus it is equivalent to
  830. * DATE_PRECISION_10SECONDS)
  831. *
  832. * @param int $pn_precision a 'DATE_PRECISION_*' constant
  833. * @param bool $pb_correctinvalidtime whether to correct, by adding the
  834. * local Summer time offset, the
  835. * truncated time if it falls in the
  836. * skipped hour (defaults to
  837. * DATE_CORRECTINVALIDTIME_DEFAULT)
  838. *
  839. * @return void
  840. * @access public
  841. * @since Method available since Release 1.5.0
  842. */
  843. function trunc($pn_precision = DATE_PRECISION_DAY,
  844. $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT)
  845. {
  846. if ($pn_precision <= DATE_PRECISION_DAY) {
  847. if ($pn_precision <= DATE_PRECISION_YEAR) {
  848. $hn_month = 0;
  849. $hn_day = 0;
  850. $hn_hour = 0;
  851. $hn_minute = 0;
  852. $hn_second = 0;
  853. $hn_partsecond = 0.0;
  854. $hn_invprecision = DATE_PRECISION_YEAR - $pn_precision;
  855. if ($hn_invprecision > 0) {
  856. $hn_year = intval($this->year / pow(10, $hn_invprecision)) *
  857. pow(10, $hn_invprecision);
  858. //
  859. // (Conversion to int necessary for PHP <= 4.0.6)
  860. } else {
  861. $hn_year = $this->year;
  862. }
  863. } else if ($pn_precision == DATE_PRECISION_MONTH) {
  864. $hn_year = $this->year;
  865. $hn_month = $this->month;
  866. $hn_day = 0;
  867. $hn_hour = 0;
  868. $hn_minute = 0;
  869. $hn_second = 0;
  870. $hn_partsecond = 0.0;
  871. } else if ($pn_precision == DATE_PRECISION_DAY) {
  872. $hn_year = $this->year;
  873. $hn_month = $this->month;
  874. $hn_day = $this->day;
  875. $hn_hour = 0;
  876. $hn_minute = 0;
  877. $hn_second = 0;
  878. $hn_partsecond = 0.0;
  879. }
  880. $this->setLocalTime($hn_day,
  881. $hn_month,
  882. $hn_year,
  883. $hn_hour,
  884. $hn_minute,
  885. $hn_second,
  886. $hn_partsecond,
  887. true, // This is unlikely anyway, but the
  888. // day starts with the repeated
  889. // hour the first time around
  890. $pb_correctinvalidtime);
  891. return;
  892. }
  893. // Precision is at least equal to DATE_PRECISION_HOUR
  894. //
  895. if ($pn_precision == DATE_PRECISION_HOUR) {
  896. $this->addSeconds($this->partsecond == 0.0 ?
  897. -$this->second :
  898. -$this->second - $this->partsecond);
  899. //
  900. // (leap seconds irrelevant)
  901. $this->addMinutes(-$this->minute);
  902. } else if ($pn_precision <= DATE_PRECISION_MINUTE) {
  903. if ($pn_precision == DATE_PRECISION_10MINUTES) {
  904. $this->addMinutes(-$this->minute % 10);
  905. }
  906. $this->addSeconds($this->partsecond == 0.0 ?
  907. -$this->second :
  908. -$this->second - $this->partsecond);
  909. //
  910. // (leap seconds irrelevant)
  911. } else if ($pn_precision == DATE_PRECISION_10SECONDS) {
  912. $this->addSeconds($this->partsecond == 0.0 ?
  913. -$this->second % 10 :
  914. (-$this->second % 10) - $this->partsecond);
  915. //
  916. // (leap seconds irrelevant)
  917. } else {
  918. // Assume Summer time offset cannot be composed of part-seconds:
  919. //
  920. $hn_precision = $pn_precision - DATE_PRECISION_SECOND;
  921. $hn_partsecond = intval($this->on_standardpartsecond *
  922. pow(10, $hn_precision)) /
  923. pow(10, $hn_precision);
  924. $this->setStandardTime($this->on_standardday,
  925. $this->on_standardmonth,
  926. $this->on_standardyear,
  927. $this->on_standardhour,
  928. $this->on_standardminute,
  929. $this->on_standardsecond,
  930. $hn_partsecond);
  931. }
  932. }
  933. // }}}
  934. // {{{ truncSeconds()
  935. /**
  936. * Truncates seconds according to the specified precision
  937. *
  938. * N.B. this function is equivalent to calling:
  939. * <code>'Date::trunc(DATE_PRECISION_SECOND + $pn_precision)'</code>
  940. *
  941. * @param int $pn_precision number of digits after the decimal point
  942. * @param bool $pb_correctinvalidtime whether to correct, by adding the
  943. * local Summer time offset, the
  944. * truncated time if it falls in the
  945. * skipped hour (defaults to
  946. * DATE_CORRECTINVALIDTIME_DEFAULT)
  947. *
  948. * @return void
  949. * @access public
  950. * @since Method available since Release 1.5.0
  951. */
  952. function truncSeconds($pn_precision = 0,
  953. $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT)
  954. {
  955. $this->trunc(DATE_PRECISION_SECOND + $pn_precision,
  956. $pb_correctinvalidtime);
  957. }
  958. // }}}
  959. // {{{ getDate()
  960. /**
  961. * Gets a string (or other) representation of this date
  962. *
  963. * Returns a date in the format specified by the DATE_FORMAT_* constants.
  964. *
  965. * @param int $format format constant (DATE_FORMAT_*) of the output date
  966. *
  967. * @return string the date in the requested format
  968. * @access public
  969. */
  970. function getDate($format = DATE_FORMAT_ISO)
  971. {
  972. switch ($format) {
  973. case DATE_FORMAT_ISO:
  974. return $this->format("%Y-%m-%d %T");
  975. break;
  976. case DATE_FORMAT_ISO_BASIC:
  977. $format = "%Y%m%dT%H%M%S";
  978. if ($this->getTZID() == 'UTC') {
  979. $format .= "Z";
  980. }
  981. return $this->format($format);
  982. break;
  983. case DATE_FORMAT_ISO_EXTENDED:
  984. $format = "%Y-%m-%dT%H:%M:%S";
  985. if ($this->getTZID() == 'UTC') {
  986. $format .= "Z";
  987. }
  988. return $this->format($format);
  989. break;
  990. case DATE_FORMAT_ISO_EXTENDED_MICROTIME:
  991. $format = "%Y-%m-%dT%H:%M:%s";
  992. if ($this->getTZID() == 'UTC') {
  993. $format .= "Z";
  994. }
  995. return $this->format($format);
  996. break;
  997. case DATE_FORMAT_TIMESTAMP:
  998. return $this->format("%Y%m%d%H%M%S");
  999. break;
  1000. case DATE_FORMAT_UNIXTIME:
  1001. // Enter a time in UTC, so use 'gmmktime()' (the alternative
  1002. // is to offset additionally by the local time, but the object
  1003. // is not necessarily using local time):
  1004. //
  1005. if ($this->ob_invalidtime)
  1006. return $this->_getErrorInvalidTime();
  1007. return gmmktime($this->on_standardhour,
  1008. $this->on_standardminute,
  1009. $this->on_standardsecond,
  1010. $this->on_standardmonth,
  1011. $this->on_standardday,
  1012. $this->on_standardyear) -
  1013. $this->tz->getRawOffset() / 1000; // N.B. Unix-time excludes
  1014. // leap seconds by
  1015. // definition
  1016. break;
  1017. }
  1018. }
  1019. // }}}
  1020. // {{{ format()
  1021. /**
  1022. * Date pretty printing, similar to strftime()
  1023. *
  1024. * Formats the date in the given format, much like
  1025. * strftime(). Most strftime() options are supported.<br><br>
  1026. *
  1027. * Formatting options:<br><br>
  1028. *
  1029. * <code>%a </code> abbreviated weekday name (Sun, Mon, Tue) <br>
  1030. * <code>%A </code> full weekday name (Sunday, Monday, Tuesday) <br>
  1031. * <code>%b </code> abbreviated month name (Jan, Feb, Mar) <br>
  1032. * <code>%B </code> full month name (January, February, March) <br>
  1033. * <code>%C </code> century number (the year divided by 100 and truncated
  1034. * to an integer, range 00 to 99) <br>
  1035. * <code>%d </code> day of month (range 00 to 31) <br>
  1036. * <code>%D </code> equivalent to "%m/%d/%y" <br>
  1037. * <code>%e </code> day of month without leading noughts (range 0 to 31) <br>
  1038. * <code>%E </code> Julian day - no of days since Monday, 24th November,
  1039. * 4714 B.C. (in the proleptic Gregorian calendar) <br>
  1040. * <code>%g </code> like %G, but without the century <br>
  1041. * <code>%G </code> the 4-digit year corresponding to the ISO week
  1042. * number (see %V). This has the same format and value
  1043. * as %Y, except that if the ISO week number belongs
  1044. * to the previous or next year, that year is used
  1045. * instead. <br>
  1046. * <code>%h </code> hour as decimal number without leading noughts (0
  1047. * to 23) <br>
  1048. * <code>%H </code> hour as decimal number (00 to 23) <br>
  1049. * <code>%i </code> hour as decimal number on 12-hour clock without
  1050. * leading noughts (1 to 12) <br>
  1051. * <code>%I </code> hour as decimal number on 12-hour clock (01 to 12) <br>
  1052. * <code>%j </code> day of year (range 001 to 366) <br>
  1053. * <code>%m </code> month as decimal number (range 01 to 12) <br>
  1054. * <code>%M </code> minute as a decimal number (00 to 59) <br>
  1055. * <code>%n </code> newline character ("\n") <br>
  1056. * <code>%o </code> raw timezone offset expressed as '+/-HH:MM' <br>
  1057. * <code>%O </code> dst-corrected timezone offset expressed as '+/-HH:MM' <br>
  1058. * <code>%p </code> either 'am' or 'pm' depending on the time <br>
  1059. * <code>%P </code> either 'AM' or 'PM' depending on the time <br>
  1060. * <code>%r </code> time in am/pm notation; equivalent to "%I:%M:%S %p" <br>
  1061. * <code>%R </code> time in 24-hour notation; equivalent to "%H:%M" <br>
  1062. * <code>%s </code> seconds including the micro-time (the decimal
  1063. * representation less than one second to six decimal
  1064. * places<br>
  1065. * <code>%S </code> seconds as a decimal number (00 to 59) <br>
  1066. * <code>%t </code> tab character ("\t") <br>
  1067. * <code>%T </code> current time; equivalent to "%H:%M:%S" <br>
  1068. * <code>%u </code> day of week as decimal (1 to 7; where 1 = Monday) <br>
  1069. * <code>%U </code> week number of the current year as a decimal
  1070. * number, starting with the first Sunday as the first
  1071. * day of the first week (i.e. the first full week of
  1072. * the year, and the week that contains 7th January)
  1073. * (00 to 53) <br>
  1074. * <code>%V </code> the ISO 8601:1988 week number of the current year
  1075. * as a decimal number, range 01 to 53, where week 1
  1076. * is the first week that has at least 4 days in the
  1077. * current year, and with Monday as the first day of
  1078. * the week. (Use %G or %g for the year component
  1079. * that corresponds to the week number for the
  1080. * specified timestamp.)
  1081. * <code>%w </code> day of week as decimal (0 to 6; where 0 = Sunday) <br>
  1082. * <code>%W </code> week number of the current year as a decimal
  1083. * number, starting with the first Monday as the first
  1084. * day of the first week (i.e. the first full week of
  1085. * the year, and the week that contains 7th January)
  1086. * (00 to 53) <br>
  1087. * <code>%y </code> year as decimal (range 00 to 99) <br>
  1088. * <code>%Y </code> year as decimal including century (range 0000 to
  1089. * 9999) <br>
  1090. * <code>%Z </code> Abbreviated form of time zone name, e.g. 'GMT', or
  1091. * the abbreviation for Summer time if the date falls
  1092. * in Summer time, e.g. 'BST'. <br>
  1093. * <code>%% </code> literal '%' <br>
  1094. * <br>
  1095. *
  1096. * The following codes render a different output to that of 'strftime()':
  1097. *
  1098. * <code>%e</code> in 'strftime()' a single digit is preceded by a space
  1099. * <code>%h</code> in 'strftime()' is equivalent to '%b'
  1100. * <code>%U</code> '%U' and '%W' are different in 'strftime()' in that
  1101. * if week 1 does not start on 1st January, '00' is
  1102. * returned, whereas this function returns '53', that is,
  1103. * the week is counted as the last of the previous year.
  1104. * <code>%W</code>
  1105. *
  1106. * @param string $format the format string for returned date/time
  1107. *
  1108. * @return string date/time in given format
  1109. * @access public
  1110. */
  1111. function format($format)
  1112. {
  1113. $output = "";
  1114. $hn_isoyear = null;
  1115. $hn_isoweek = null;
  1116. $hn_isoday = null;
  1117. for ($strpos = 0; $strpos < strlen($format); $strpos++) {
  1118. $char = substr($format, $strpos, 1);
  1119. if ($char == "%") {
  1120. $nextchar = substr($format, $strpos + 1, 1);
  1121. switch ($nextchar) {
  1122. case "a":
  1123. $output .= Date_Calc::getWeekdayAbbrname($this->day,
  1124. $this->month, $this->year,
  1125. $this->getWeekdayAbbrnameLength);
  1126. break;
  1127. case "A":
  1128. $output .= Date_Calc::getWeekdayFullname($this->day,
  1129. $this->month, $this->year);
  1130. break;
  1131. case "b":
  1132. $output .= Date_Calc::getMonthAbbrname($this->month);
  1133. break;
  1134. case "B":
  1135. $output .= Date_Calc::getMonthFullname($this->month);
  1136. break;
  1137. case "C":
  1138. $output .= sprintf("%02d", intval($this->year / 100));
  1139. break;
  1140. case "d":
  1141. $output .= sprintf("%02d", $this->day);
  1142. break;
  1143. case "D":
  1144. $output .= sprintf("%02d/%02d/%02d", $this->month,
  1145. $this->day, $this->year);
  1146. break;
  1147. case "e":
  1148. $output .= $this->day;
  1149. break;
  1150. case "E":
  1151. $output .= Date_Calc::dateToDays($this->day, $this->month,
  1152. $this->year);
  1153. break;
  1154. case "g":
  1155. if (is_null($hn_isoyear))
  1156. list($hn_isoyear, $hn_isoweek, $hn_isoday) =
  1157. Date_Calc::isoWeekDate($this->day,
  1158. $this->month,
  1159. $this->year);
  1160. $output .= sprintf("%02d", $hn_isoyear % 100);
  1161. break;
  1162. case "G":
  1163. if (is_null($hn_isoyear))
  1164. list($hn_isoyear, $hn_isoweek, $hn_isoday) =
  1165. Date_Calc::isoWeekDate($this->day,
  1166. $this->month,
  1167. $this->year);
  1168. $output .= sprintf("%04d", $hn_isoyear);
  1169. break;
  1170. case 'h':
  1171. if ($this->ob_invalidtime)
  1172. return $this->_getErrorInvalidTime();
  1173. $output .= sprintf("%d", $this->hour);
  1174. break;
  1175. case "H":
  1176. if ($this->ob_invalidtime)
  1177. return $this->_getErrorInvalidTime();
  1178. $output .= sprintf("%02d", $this->hour);
  1179. break;
  1180. case "i":
  1181. case "I":
  1182. if ($this->ob_invalidtime)
  1183. return $this->_getErrorInvalidTime();
  1184. $hour = $this->hour + 1 > 12 ?
  1185. $this->hour - 12 :
  1186. $this->hour;
  1187. $output .= $hour == 0 ?
  1188. 12 :
  1189. ($nextchar == "i" ?
  1190. $hour :
  1191. sprintf('%02d', $hour));
  1192. break;
  1193. case "j":
  1194. $output .= sprintf("%03d",
  1195. Date_Calc::dayOfYear($this->day,
  1196. $this->month,
  1197. $this->year));
  1198. break;
  1199. case "m":
  1200. $output .= sprintf("%02d", $this->month);
  1201. break;
  1202. case "M":
  1203. $output .= sprintf("%02d", $this->minute);
  1204. break;
  1205. case "n":
  1206. $output .= "\n";
  1207. break;
  1208. case "O":
  1209. if ($this->ob_invalidtime)
  1210. return $this->_getErrorInvalidTime();
  1211. $offms = $this->getTZOffset();
  1212. $direction = $offms >= 0 ? "+" : "-";
  1213. $offmins = abs($offms) / 1000 / 60;
  1214. $hours = $offmins / 60;
  1215. $minutes = $offmins % 60;
  1216. $output .= sprintf("%s%02d:%02d", $direction, $hours, $minutes);
  1217. break;
  1218. case "o":
  1219. $offms = $this->tz->getRawOffset($this);
  1220. $direction = $offms >= 0 ? "+" : "-";
  1221. $offmins = abs($offms) / 1000 / 60;
  1222. $hours = $offmins / 60;
  1223. $minutes = $offmins % 60;
  1224. $output .= sprintf("%s%02d:%02d", $direction, $hours, $minutes);
  1225. break;
  1226. case "p":
  1227. if ($this->ob_invalidtime)
  1228. return $this->_getErrorInvalidTime();
  1229. $output .= $this->hour >= 12 ? "pm" : "am";
  1230. break;
  1231. case "P":
  1232. if ($this->ob_invalidtime)
  1233. return $this->_getErrorInvalidTime();
  1234. $output .= $this->hour >= 12 ? "PM" : "AM";
  1235. break;
  1236. case "r":
  1237. if ($this->ob_invalidtime)
  1238. return $this->_getErrorInvalidTime();
  1239. $hour = $this->hour + 1 > 12 ?
  1240. $this->hour - 12 :
  1241. $this->hour;
  1242. $output .= sprintf("%02d:%02d:%02d %s",
  1243. $hour == 0 ? 12 : $hour,
  1244. $this->minute,
  1245. $this->second,
  1246. $this->hour >= 12 ? "PM" : "AM");
  1247. break;
  1248. case "R":
  1249. if ($this->ob_invalidtime)
  1250. return $this->_getErrorInvalidTime();
  1251. $output .= sprintf("%02d:%02d", $this->hour, $this->minute);
  1252. break;
  1253. case "s":
  1254. $output .= str_replace(',',
  1255. '.',
  1256. sprintf("%09f",
  1257. (float)((float) $this->second +
  1258. $this->partsecond)));
  1259. break;
  1260. case "S":
  1261. $output .= sprintf("%02d", $this->second);
  1262. break;
  1263. case "t":
  1264. $output .= "\t";
  1265. break;
  1266. case "T":
  1267. if ($this->ob_invalidtime)
  1268. return $this->_getErrorInvalidTime();
  1269. $output .= sprintf("%02d:%02d:%02d",
  1270. $this->hour,
  1271. $this->minute,
  1272. $this->second);
  1273. break;
  1274. case "u":
  1275. $hn_dayofweek = $this->getDayOfWeek();
  1276. $output .= $hn_dayofweek == 0 ? 7 : $hn_dayofweek;
  1277. break;
  1278. case "U":
  1279. $ha_week = Date_Calc::weekOfYear7th($this->day,
  1280. $this->month,
  1281. $this->year,
  1282. 0);
  1283. $output .= sprintf("%02d", $ha_week[1]);
  1284. break;
  1285. case "V":
  1286. if (is_null($hn_isoyear))
  1287. list($hn_isoyear, $hn_isoweek, $hn_isoday) =
  1288. Date_Calc::isoWeekDate($this->day,
  1289. $this->month,
  1290. $this->year);
  1291. $output .= $hn_isoweek;
  1292. break;
  1293. case "w":
  1294. $output .= $this->getDayOfWeek();
  1295. break;
  1296. case "W":
  1297. $ha_week = Date_Calc::weekOfYear7th($this->day,
  1298. $this->month,
  1299. $this->year,
  1300. 1);
  1301. $output .= sprintf("%02d", $ha_week[1]);
  1302. break;
  1303. case 'y':
  1304. $output .= sprintf('%0' .
  1305. ($this->year < 0 ? '3' : '2') .
  1306. 'd',
  1307. $this->year % 100);
  1308. break;
  1309. case "Y":
  1310. $output .= sprintf('%0' .
  1311. ($this->year < 0 ? '5' : '4') .
  1312. 'd',
  1313. $this->year);
  1314. break;
  1315. case "Z":
  1316. if ($this->ob_invalidtime)
  1317. return $this->_getErrorInvalidTime();
  1318. $output .= $this->getTZShortName();
  1319. break;
  1320. case "%":
  1321. $output .= "%";
  1322. break;
  1323. default:
  1324. $output .= $char.$nextchar;
  1325. }
  1326. $strpos++;
  1327. } else {
  1328. $output .= $char;
  1329. }
  1330. }
  1331. return $output;
  1332. }
  1333. // }}}
  1334. // {{{ _getOrdinalSuffix()
  1335. /**
  1336. * Returns appropriate ordinal suffix (i.e. 'th', 'st', 'nd' or 'rd')
  1337. *
  1338. * @param int $pn_num number with which to determine suffix
  1339. * @param bool $pb_uppercase boolean specifying if the suffix should be
  1340. * capitalized
  1341. *
  1342. * @return string
  1343. * @access private
  1344. * @since Method available since Release 1.5.0
  1345. */
  1346. function _getOrdinalSuffix($pn_num, $pb_uppercase = true)
  1347. {
  1348. switch (($pn_numabs = abs($pn_num)) % 100) {
  1349. case 11:
  1350. case 12:
  1351. case 13:
  1352. $hs_suffix = "th";
  1353. break;
  1354. default:
  1355. switch ($pn_numabs % 10) {
  1356. case 1:
  1357. $hs_suffix = "st";
  1358. break;
  1359. case 2:
  1360. $hs_suffix = "nd";
  1361. break;
  1362. case 3:
  1363. $hs_suffix = "rd";
  1364. break;
  1365. default:
  1366. $hs_suffix = "th";
  1367. }
  1368. }
  1369. return $pb_uppercase ? strtoupper($hs_suffix) : $hs_suffix;
  1370. }
  1371. // }}}
  1372. // {{{ _spellNumber()
  1373. /**
  1374. * Converts a number to its word representation
  1375. *
  1376. * Private helper function, particularly for 'format2()'. N.B. The
  1377. * second argument is the 'SP' code which can be specified in the
  1378. * format string for 'format2()' and is interpreted as follows:
  1379. * 'SP' - returns upper-case spelling, e.g. 'FOUR HUNDRED'
  1380. * 'Sp' - returns spelling with first character of each word
  1381. * capitalized, e.g. 'Four Hundred'
  1382. * 'sp' - returns lower-case spelling, e.g. 'four hundred'
  1383. *
  1384. * @param int $pn_num number to be converted to words
  1385. * @param bool $pb_ordinal boolean specifying if the number should
  1386. * be ordinal
  1387. * @param string $ps_capitalization string for specifying capitalization
  1388. * options
  1389. * @param string $ps_locale language name abbreviation used for
  1390. * formatting numbers as spelled-out words
  1391. *
  1392. * @return string
  1393. * @access private
  1394. * @since Method available since Release 1.5.0
  1395. */
  1396. function _spellNumber($pn_num,
  1397. $pb_ordinal = false,
  1398. $ps_capitalization = "SP",
  1399. $ps_locale = "en_GB")
  1400. {
  1401. include_once "Numbers/Words.php";
  1402. $hs_words = Numbers_Words::toWords($pn_num, $ps_locale);
  1403. if (Pear::isError($hs_words)) {
  1404. return $hs_words;
  1405. }
  1406. if ($pb_ordinal && substr($ps_locale, 0, 2) == "en") {
  1407. if (($pn_rem = ($pn_numabs = abs($pn_num)) % 100) == 12) {
  1408. $hs_words = substr($hs_words, 0, -2) . "fth";
  1409. } else if ($pn_rem >= 11 && $pn_rem <= 15) {
  1410. $hs_words .= "th";
  1411. } else {
  1412. switch ($pn_numabs % 10) {
  1413. case 1:
  1414. $hs_words = substr($hs_words, 0, -3) . "first";
  1415. break;
  1416. case 2:
  1417. $hs_words = substr($hs_words, 0, -3) . "second";
  1418. break;
  1419. case 3:
  1420. $hs_words = substr($hs_words, 0, -3) . "ird";
  1421. break;
  1422. case 5:
  1423. $hs_words = substr($hs_words, 0, -2) . "fth";
  1424. break;
  1425. default:
  1426. switch (substr($hs_words, -1)) {
  1427. case "e":
  1428. $hs_words = substr($hs_words, 0, -1) . "th";
  1429. break;
  1430. case "t":
  1431. $hs_words .= "h";
  1432. break;
  1433. case "y":
  1434. $hs_words = substr($hs_words, 0, -1) . "ieth";
  1435. break;
  1436. default:
  1437. $hs_words .= "th";
  1438. }
  1439. }
  1440. }
  1441. }
  1442. if (($hs_char = substr($ps_capitalization, 0, 1)) ==
  1443. strtolower($hs_char)) {
  1444. $hb_upper = false;
  1445. $hs_words = strtolower($hs_words);
  1446. } else if (($hs_char = substr($ps_capitalization, 1, 1)) ==
  1447. strtolower($hs_char)) {
  1448. $hb_upper = false;
  1449. $hs_words = ucwords($hs_words);
  1450. } else {
  1451. $hb_upper = true;
  1452. $hs_words = strtoupper($hs_words);
  1453. }
  1454. return $hs_words;
  1455. }
  1456. // }}}
  1457. // {{{ _formatNumber()
  1458. /**
  1459. * Formats a number according to the specified format string
  1460. *
  1461. * Private helper function, for 'format2()', which interprets the
  1462. * codes 'SP' and 'TH' and the combination of the two as follows:
  1463. *
  1464. * <code>TH</code> Ordinal number
  1465. * <code>SP</code> Spelled cardinal number
  1466. * <code>SPTH</code> Spelled ordinal number (combination of 'SP' and 'TH'
  1467. * in any order)
  1468. * <code>THSP</code>
  1469. *
  1470. * Code 'SP' can have the following three variations (which can also be used
  1471. * in combination with 'TH'):
  1472. *
  1473. * <code>SP</code> returns upper-case spelling, e.g. 'FOUR HUNDRED'
  1474. * <code>Sp</code> returns spelling with first character of each word
  1475. * capitalized, e.g. 'Four Hundred'
  1476. * <code>sp</code> returns lower-case spelling, e.g. 'four hundred'
  1477. *
  1478. * Code 'TH' can have the following two variations (although in combination
  1479. * with code 'SP', the case specification of 'SP' takes precedence):
  1480. *
  1481. * <code>TH</code> returns upper-case ordinal suffix, e.g. 400TH
  1482. * <code>th</code> returns lower-case ordinal suffix, e.g. 400th
  1483. *
  1484. * N.B. The format string is passed by reference, in order to pass back
  1485. * the part of the format string that matches the valid codes 'SP' and
  1486. * 'TH'. If none of these are found, then it is set to an empty string;
  1487. * If both codes are found then a string is returned with code 'SP'
  1488. * preceding code 'TH' (i.e. 'SPTH', 'Spth' or 'spth').
  1489. *
  1490. * @param int $pn_num integer to be converted to words
  1491. * @param string &$ps_format string of formatting codes (max. length 4)
  1492. * @param int $pn_numofdigits no of digits to display if displayed as
  1493. * numeral (i.e. not spelled out), not
  1494. * including the sign (if negative); to
  1495. * allow all digits specify 0
  1496. * @param bool $pb_nopad boolean specifying whether to suppress
  1497. * padding with leading noughts (if displayed
  1498. * as numeral)
  1499. * @param bool $pb_nosign boolean specifying whether to suppress the
  1500. * display of the sign (if negative)
  1501. * @param string $ps_locale language name abbreviation used for
  1502. * formatting
  1503. * @param string $ps_thousandsep optional thousand-separator (e.g. a comma)
  1504. * numbers as spelled-out words
  1505. * @param int $pn_padtype optional integer to specify padding (if
  1506. * displayed as numeral) - can be
  1507. * STR_PAD_LEFT or STR_PAD_RIGHT
  1508. *
  1509. * @return string
  1510. * @access private
  1511. * @since Method available since Release 1.5.0
  1512. */
  1513. function _formatNumber($pn_num,
  1514. &$ps_format,
  1515. $pn_numofdigits,
  1516. $pb_nopad = false,
  1517. $pb_nosign = false,
  1518. $ps_locale = "en_GB",
  1519. $ps_thousandsep = null,
  1520. $pn_padtype = STR_PAD_LEFT)
  1521. {
  1522. $hs_code1 = substr($ps_format, 0, 2);
  1523. $hs_code2 = substr($ps_format, 2, 2);
  1524. $hs_sp = null;
  1525. $hs_th = null;
  1526. if (strtoupper($hs_code1) == "SP") {
  1527. $hs_sp = $hs_code1;
  1528. if (strtoupper($hs_code2) == "TH") {
  1529. $hs_th = $hs_code2;
  1530. }
  1531. } else if (strtoupper($hs_code1) == "TH") {
  1532. $hs_th = $hs_code1;
  1533. if (strtoupper($hs_code2) == "SP") {
  1534. $hs_sp = $hs_code2;
  1535. }
  1536. }
  1537. $hn_absnum = abs($pn_num);
  1538. if ($pn_numofdigits > 0 && strlen($hn_absnum) > $pn_numofdigits) {
  1539. $hn_absnum = intval(substr($hn_absnum, -$pn_numofdigits));
  1540. }
  1541. $hs_num = $hn_absnum;
  1542. if (!is_null($hs_sp)) {
  1543. // Spell out number:
  1544. //
  1545. $ps_format = $hs_sp .
  1546. (is_null($hs_th) ? "" : ($hs_sp == "SP" ? "TH" : "th"));
  1547. return $this->_spellNumber(!$pb_nosign && $pn_num < 0 ?
  1548. $hn_absnum * -1 :
  1549. $hn_absnum,
  1550. !is_null($hs_th),
  1551. $hs_sp,
  1552. $ps_locale);
  1553. } else {
  1554. // Display number as Arabic numeral:
  1555. //
  1556. if (!$pb_nopad) {
  1557. $hs_num = str_pad($hs_num, $pn_numofdigits, "0", $pn_padtype);
  1558. }
  1559. if (!is_null($ps_thousandsep)) {
  1560. for ($i = strlen($hs_num) - 3; $i > 0; $i -= 3) {
  1561. $hs_num = substr($hs_num, 0, $i) .
  1562. $ps_thousandsep .
  1563. substr($hs_num, $i);
  1564. }
  1565. }
  1566. if (!$pb_nosign) {
  1567. if ($pn_num < 0)
  1568. $hs_num = "-" . $hs_num;
  1569. else if (!$pb_nopad)
  1570. $hs_num = " " . $hs_num;
  1571. }
  1572. if (!is_null($hs_th)) {
  1573. $ps_format = $hs_th;
  1574. return $hs_num .
  1575. $this->_getOrdinalSuffix($pn_num,
  1576. substr($hs_th, 0, 1) == "T");
  1577. } else {
  1578. $ps_format = "";
  1579. return $hs_num;
  1580. }
  1581. }
  1582. }
  1583. // }}}
  1584. // {{{ format2()
  1585. /**
  1586. * Extended version of 'format()' with variable-length formatting codes
  1587. *
  1588. * Most codes reproduce the no of digits equal to the length of the code,
  1589. * for example, 'YYY' will return the last 3 digits of the year, and so
  1590. * the year 2007 will produce '007', and the year 89 will produce '089',
  1591. * unless the no-padding code is used as in 'NPYYY', which will return
  1592. * '89'.
  1593. *
  1594. * For negative values, the sign will be discarded, unless the 'S' code
  1595. * is used in combination, but note that for positive values the value
  1596. * will be padded with a leading space unless it is suppressed with
  1597. * the no-padding modifier, for example for 2007:
  1598. *
  1599. * <code>YYYY</code> returns '2007'
  1600. * <code>SYYYY</code> returns ' 2007'
  1601. * <code>NPSYYYY</code> returns '2007'
  1602. *
  1603. * The no-padding modifier 'NP' can be used with numeric codes to
  1604. * suppress leading (or trailing in the case of code 'F') noughts, and
  1605. * with character-returning codes such as 'DAY' to suppress trailing
  1606. * spaces, which will otherwise be padded to the maximum possible length
  1607. * of the return-value of the code; for example, for Monday:
  1608. *
  1609. * <code>Day</code> returns 'Monday ' because the maximum length of
  1610. * this code is 'Wednesday';
  1611. * <code>NPDay</code> returns 'Monday'
  1612. *
  1613. * N.B. this code affects the code immediately following only, and
  1614. * without this code the default is always to apply padding.
  1615. *
  1616. * Most character-returning codes, such as 'MONTH', will
  1617. * set the capitalization according to the code, so for example:
  1618. *
  1619. * <code>MONTH</code> returns upper-case spelling, e.g. 'JANUARY'
  1620. * <code>Month</code> returns spelling with first character of each word
  1621. * capitalized, e.g. 'January'
  1622. * <code>month</code> returns lower-case spelling, e.g. 'january'
  1623. *
  1624. * Where it makes sense, numeric codes can be combined with a following
  1625. * 'SP' code which spells out the number, or with a 'TH' code, which
  1626. * renders the code as an ordinal ('TH' only works in English), for
  1627. * example, for 31st December:
  1628. *
  1629. * <code>DD</code> returns '31'
  1630. * <code>DDTH</code> returns '31ST'
  1631. * <code>DDth</code> returns '31st'
  1632. * <code>DDSP</code> returns 'THIRTY-ONE'
  1633. * <code>DDSp</code> returns 'Thirty-one'
  1634. * <code>DDsp</code> returns 'thirty-one'
  1635. * <code>DDSPTH</code> returns 'THIRTY-FIRST'
  1636. * <code>DDSpth</code> returns 'Thirty-first'
  1637. * <code>DDspth</code> returns 'thirty-first'
  1638. *
  1639. *
  1640. * All formatting options:
  1641. *
  1642. * <code>-</code> All punctuation and white-space is reproduced unchanged
  1643. * <code>/</code>
  1644. * <code>,</code>
  1645. * <code>.</code>
  1646. * <code>;</code>
  1647. * <code>:</code>
  1648. * <code> </code>
  1649. * <code>"text"</code> Quoted text is reproduced unchanged (escape using
  1650. * '\')
  1651. * <code>AD</code> AD indicator with or without full stops; N.B. if you
  1652. * are using 'Astronomical' year numbering then 'A.D./B.C.'
  1653. * indicators will be out for negative years
  1654. * <code>A.D.</code>
  1655. * <code>AM</code> Meridian indicator with or without full stops
  1656. * <code>A.M.</code>
  1657. * <code>BC</code> BC indicator with or without full stops
  1658. * <code>B.C.</code>
  1659. * <code>BCE</code> BCE indicator with or without full stops
  1660. * <code>B.C.E.</code>
  1661. * <code>CC</code> Century, i.e. the year divided by 100, discarding the
  1662. * remainder; 'S' prefixes negative years with a minus sign
  1663. * <code>SCC</code>
  1664. * <code>CE</code> CE indicator with or without full stops
  1665. * <code>C.E.</code>
  1666. * <code>D</code> Day of week (0-6), where 0 represents Sunday
  1667. * <code>DAY</code> Name of day, padded with blanks to display width of the
  1668. * widest name of day in the locale of the machine
  1669. * <code>DD</code> Day of month (1-31)
  1670. * <code>DDD</code> Day of year (1-366)
  1671. * <code>DY</code> Abbreviated name of day
  1672. * <code>FFF</code> Fractional seconds; no radix character is printed. The
  1673. * no of 'F's determines the no of digits of the
  1674. * part-second to return; e.g. 'HH:MI:SS.FF'
  1675. * <code>F[integer]</code> The integer after 'F' specifies the number of
  1676. * digits of the part-second to return. This is an
  1677. * alternative to using F[integer], and 'F3' is thus
  1678. * equivalent to using 'FFF'.
  1679. * <code>HH</code> Hour of day (0-23)
  1680. * <code>HH12</code> Hour of day (1-12)
  1681. * <code>HH24</code> Hour of day (0-23)
  1682. * <code>ID</code> Day of week (1-7) based on the ISO standard
  1683. * <code>IW</code> Week of year (1-52 or 1-53) based on the ISO standard
  1684. * <code>IYYY</code> 4-digit year based on the ISO 8601 standard; 'S'
  1685. * prefixes negative years with a minus sign
  1686. * <code>SIYYY</code>
  1687. * <code>IYY</code> Last 3, 2, or 1 digit(s) of ISO year
  1688. * <code>IY</code>
  1689. * <code>I</code>
  1690. * <code>J</code> Julian day - the number of days since Monday, 24th
  1691. * November, 4714 B.C. (proleptic Gregorian calendar)
  1692. * <code>MI</code> Minute (0-59)
  1693. * <code>MM</code> Month (01-12; January = 01)
  1694. * <code>MON</code> Abbreviated name of month
  1695. * <code>MONTH</code> Name of month, padded with blanks to display width of
  1696. * the widest name of month in the date language used for
  1697. * <code>PM</code> Meridian indicator with or without full stops
  1698. * <code>P.M.</code>
  1699. * <code>Q</code> Quarter of year (1, 2, 3, 4; January - March = 1)
  1700. * <code>RM</code> Roman numeral month (I-XII; January = I); N.B. padded
  1701. * with leading spaces.
  1702. * <code>SS</code> Second (0-59)
  1703. * <code>SSSSS</code> Seconds past midnight (0-86399)
  1704. * <code>TZC</code> Abbreviated form of time zone name, e.g. 'GMT', or the
  1705. * abbreviation for Summer time if the date falls in Summer
  1706. * time, e.g. 'BST'.
  1707. * N.B. this is not a unique identifier - for this purpose
  1708. * use the time zone region (code 'TZR').
  1709. * <code>TZH</code> Time zone hour; 'S' prefixes the hour with the correct
  1710. * sign, (+/-), which otherwise is not displayed. Note
  1711. * that the leading nought can be suppressed with the
  1712. * no-padding code 'NP'). Also note that if you combine
  1713. * with the 'SP' code, the sign will not be spelled out.
  1714. * (I.e. 'STZHSp' will produce '+One', for example, and
  1715. * not 'Plus One'.
  1716. * 'TZH:TZM' will produce, for example, '+05:30'. (Also
  1717. * see 'TZM' format code)
  1718. * <code>STZH</code>
  1719. * <code>TZI</code> Whether or not the date is in Summer time (daylight
  1720. * saving time). Returns '1' if Summer time, else '0'.
  1721. * <code>TZM</code> Time zone minute, without any +/- sign. (Also see
  1722. * 'TZH' format element)
  1723. * <code>TZN</code> Long form of time zone name, e.g.
  1724. * 'Greenwich Mean Time', or the name of the Summer time if
  1725. * the date falls in Summer time, e.g.
  1726. * 'British Summer Time'. N.B. this is not a unique
  1727. * identifier - for this purpose use the time zone region
  1728. * (code 'TZR').
  1729. * <code>TZO</code> Time zone offset in ISO 8601 form - that is, 'Z' if
  1730. * UTC, else [+/-][hh]:[mm] (which would be equivalent
  1731. * to 'STZH:TZM'). Note that this result is right padded
  1732. * with spaces by default, (i.e. if 'Z').
  1733. * <code>TZS</code> Time zone offset in seconds; 'S' prefixes negative
  1734. * sign with minus sign '-' if negative, and no sign if
  1735. * positive (i.e. -43200 to 50400).
  1736. * <code>STZS</code>
  1737. * <code>TZR</code> Time zone region, that is, the name or ID of the time
  1738. * zone e.g. 'Europe/London'. This value is unique for
  1739. * each time zone.
  1740. * <code>U</code> Seconds since the Unix Epoch -
  1741. * January 1 1970 00:00:00 GMT
  1742. * <code>W</code> 'Absolute' week of month (1-5), counting week 1 as
  1743. * 1st-7th of the year, regardless of the day
  1744. * <code>W1</code> Week of year (1-54), counting week 1 as the week that
  1745. * contains 1st January
  1746. * <code>W4</code> Week of year (1-53), counting week 1 as the week that
  1747. * contains 4th January (i.e. first week with at least 4
  1748. * days)
  1749. * <code>W7</code> Week of year (1-53), counting week 1 as the week that
  1750. * contains 7th January (i.e. first full week)
  1751. * <code>WW</code> 'Absolute' week of year (1-53), counting week 1 as
  1752. * 1st-7th of the year, regardless of the day
  1753. * <code>YEAR</code> Year, spelled out; 'S' prefixes negative years with
  1754. * 'MINUS'; N.B. 'YEAR' differs from 'YYYYSP' in that the
  1755. * first will render 1923, for example, as 'NINETEEN
  1756. * TWENTY-THREE, and the second as 'ONE THOUSAND NINE
  1757. * HUNDRED TWENTY-THREE'
  1758. * <code>SYEAR</code>
  1759. * <code>YYYY</code> 4-digit year; 'S' prefixes negative years with a minus
  1760. * sign
  1761. * <code>SYYYY</code>
  1762. * <code>YYY</code> Last 3, 2, or 1 digit(s) of year
  1763. * <code>YY</code>
  1764. * <code>Y</code>
  1765. * <code>Y,YYY</code> Year with thousands-separator in this position; five
  1766. * possible separators
  1767. * <code>Y.YYY</code>
  1768. * <code>YˇYYY</code> N.B. space-dot (mid-dot, interpunct) is valid only in
  1769. * ISO 8859-1 (so take care when using UTF-8 in
  1770. * particular)
  1771. * <code>Y'YYY</code>
  1772. * <code>Y YYY</code>
  1773. *
  1774. * In addition the following codes can be used in combination with other
  1775. * codes;
  1776. * Codes that modify the next code in the format string:
  1777. *
  1778. * <code>NP</code> 'No Padding' - Returns a value with no trailing blanks
  1779. * and no leading or trailing noughts; N.B. that the
  1780. * default is to include this padding in the return string.
  1781. * N.B. affects the code immediately following only.
  1782. *
  1783. * Codes that modify the previous code in the format string (can only
  1784. * be used with integral codes such as 'MM'):
  1785. *
  1786. * <code>TH</code> Ordinal number
  1787. * <code>SP</code> Spelled cardinal number
  1788. * <code>SPTH</code> Spelled ordinal number (combination of 'SP' and 'TH'
  1789. * in any order)
  1790. * <code>THSP</code>
  1791. *
  1792. * Code 'SP' can have the following three variations (which can also be used
  1793. * in combination with 'TH'):
  1794. *
  1795. * <code>SP</code> returns upper-case spelling, e.g. 'FOUR HUNDRED'
  1796. * <code>Sp</code> returns spelling with first character of each word
  1797. * capitalized, e.g. 'Four Hundred'
  1798. * <code>sp</code> returns lower-case spelling, e.g. 'four hundred'
  1799. *
  1800. * Code 'TH' can have the following two variations (although in combination
  1801. * with code 'SP', the case specification of 'SP' takes precedence):
  1802. *
  1803. * <code>TH</code> returns upper-case ordinal suffix, e.g. 400TH
  1804. * <code>th</code> returns lower-case ordinal suffix, e.g. 400th
  1805. *
  1806. * @param string $ps_format format string for returned date/time
  1807. * @param string $ps_locale language name abbreviation used for formatting
  1808. * numbers as spelled-out words
  1809. *
  1810. * @return string date/time in given format
  1811. * @access public
  1812. * @since Method available since Release 1.5.0
  1813. */
  1814. function format2($ps_format, $ps_locale = "en_GB")
  1815. {
  1816. if (!preg_match('/^("([^"\\\\]|\\\\\\\\|\\\\")*"|(D{1,3}|S?C+|' .
  1817. 'HH(12|24)?|I[DW]|S?IY*|J|M[IM]|Q|SS(SSS)?|S?TZ[HS]|' .
  1818. 'TZM|U|W[W147]?|S?Y{1,3}([,.ˇ\' ]?YYY)*)(SP(TH)?|' .
  1819. 'TH(SP)?)?|AD|A\.D\.|AM|A\.M\.|BCE?|B\.C\.(E\.)?|CE|' .
  1820. 'C\.E\.|DAY|DY|F(F*|[1-9][0-9]*)|MON(TH)?|NP|PM|' .
  1821. 'P\.M\.|RM|TZ[CINOR]|S?YEAR|[^A-Z0-9"])*$/i',
  1822. $ps_format)) {
  1823. return PEAR::raiseError("Invalid date format '$ps_format'",
  1824. DATE_ERROR_INVALIDFORMATSTRING);
  1825. }
  1826. $ret = "";
  1827. $i = 0;
  1828. $hb_nopadflag = false;
  1829. $hb_showsignflag = false;
  1830. $hn_weekdaypad = null;
  1831. $hn_monthpad = null;
  1832. $hn_isoyear = null;
  1833. $hn_isoweek = null;
  1834. $hn_isoday = null;
  1835. $hn_tzoffset = null;
  1836. while ($i < strlen($ps_format)) {
  1837. $hb_lower = false;
  1838. if ($hb_nopadflag) {
  1839. $hb_nopad = true;
  1840. } else {
  1841. $hb_nopad = false;
  1842. }
  1843. if ($hb_showsignflag) {
  1844. $hb_nosign = false;
  1845. } else {
  1846. $hb_nosign = true;
  1847. }
  1848. $hb_nopadflag = false;
  1849. $hb_showsignflag = false;
  1850. switch ($hs_char = substr($ps_format, $i, 1)) {
  1851. case "-":
  1852. case "/":
  1853. case ",":
  1854. case ".":
  1855. case ";":
  1856. case ":":
  1857. case " ":
  1858. $ret .= $hs_char;
  1859. $i += 1;
  1860. break;
  1861. case "\"":
  1862. preg_match('/(([^"\\\\]|\\\\\\\\|\\\\")*)"/',
  1863. $ps_format,
  1864. $ha_matches,
  1865. PREG_OFFSET_CAPTURE,
  1866. $i + 1);
  1867. $ret .= str_replace(array('\\\\', '\\"'),
  1868. array('\\', '"'),
  1869. $ha_matches[1][0]);
  1870. $i += strlen($ha_matches[0][0]) + 1;
  1871. break;
  1872. case "a":
  1873. $hb_lower = true;
  1874. case "A":
  1875. if (strtoupper(substr($ps_format, $i, 4)) == "A.D.") {
  1876. $ret .= $this->year >= 0 ?
  1877. ($hb_lower ? "a.d." : "A.D.") :
  1878. ($hb_lower ? "b.c." : "B.C.");
  1879. $i += 4;
  1880. } else if (strtoupper(substr($ps_format, $i, 2)) == "AD") {
  1881. $ret .= $this->year >= 0 ?
  1882. ($hb_lower ? "ad" : "AD") :
  1883. ($hb_lower ? "bc" : "BC");
  1884. $i += 2;
  1885. } else {
  1886. if ($this->ob_invalidtime)
  1887. return $this->_getErrorInvalidTime();
  1888. if (strtoupper(substr($ps_format, $i, 4)) == "A.M.") {
  1889. $ret .= $this->hour < 12 ?
  1890. ($hb_lower ? "a.m." : "A.M.") :
  1891. ($hb_lower ? "p.m." : "P.M.");
  1892. $i += 4;
  1893. } else if (strtoupper(substr($ps_format, $i, 2)) == "AM") {
  1894. $ret .= $this->hour < 12 ?
  1895. ($hb_lower ? "am" : "AM") :
  1896. ($hb_lower ? "pm" : "PM");
  1897. $i += 2;
  1898. }
  1899. }
  1900. break;
  1901. case "b":
  1902. $hb_lower = true;
  1903. case "B":
  1904. // Check for 'B.C.E.' first:
  1905. //
  1906. if (strtoupper(substr($ps_format, $i, 6)) == "B.C.E.") {
  1907. if ($this->year >= 0) {
  1908. $hs_era = $hb_lower ? "c.e." : "C.E.";
  1909. $ret .= $hb_nopad ?
  1910. $hs_era :
  1911. str_pad($hs_era, 6, " ", STR_PAD_RIGHT);
  1912. } else {
  1913. $ret .= $hb_lower ? "b.c.e." : "B.C.E.";
  1914. }
  1915. $i += 6;
  1916. } else if (strtoupper(substr($ps_format, $i, 3)) == "BCE") {
  1917. if ($this->year >= 0) {
  1918. $hs_era = $hb_lower ? "ce" : "CE";
  1919. $ret .= $hb_nopad ?
  1920. $hs_era :
  1921. str_pad($hs_era, 3, " ", STR_PAD_RIGHT);
  1922. } else {
  1923. $ret .= $hb_lower ? "bce" : "BCE";
  1924. }
  1925. $i += 3;
  1926. } else if (strtoupper(substr($ps_format, $i, 4)) == "B.C.") {
  1927. $ret .= $this->year >= 0 ?
  1928. ($hb_lower ? "a.d." : "A.D.") :
  1929. ($hb_lower ? "b.c." : "B.C.");
  1930. $i += 4;
  1931. } else if (strtoupper(substr($ps_format, $i, 2)) == "BC") {
  1932. $ret .= $this->year >= 0 ?
  1933. ($hb_lower ? "ad" : "AD") :
  1934. ($hb_lower ? "bc" : "BC");
  1935. $i += 2;
  1936. }
  1937. break;
  1938. case "c":
  1939. $hb_lower = true;
  1940. case "C":
  1941. if (strtoupper(substr($ps_format, $i, 4)) == "C.E.") {
  1942. if ($this->year >= 0) {
  1943. $hs_era = $hb_lower ? "c.e." : "C.E.";
  1944. $ret .= $hb_nopad ?
  1945. $hs_era :
  1946. str_pad($hs_era, 6, " ", STR_PAD_RIGHT);
  1947. } else {
  1948. $ret .= $hb_lower ? "b.c.e." : "B.C.E.";
  1949. }
  1950. $i += 4;
  1951. } else if (strtoupper(substr($ps_format, $i, 2)) == "CE") {
  1952. if ($this->year >= 0) {
  1953. $hs_era = $hb_lower ? "ce" : "CE";
  1954. $ret .= $hb_nopad ?
  1955. $hs_era :
  1956. str_pad($hs_era, 3, " ", STR_PAD_RIGHT);
  1957. } else {
  1958. $ret .= $hb_lower ? "bce" : "BCE";
  1959. }
  1960. $i += 2;
  1961. } else {
  1962. // Code C(CCC...):
  1963. //
  1964. $hn_codelen = 1;
  1965. while (strtoupper(substr($ps_format,
  1966. $i + $hn_codelen,
  1967. 1)) == "C")
  1968. ++$hn_codelen;
  1969. // Check next code is not 'CE' or 'C.E.'
  1970. //
  1971. if ($hn_codelen > 1 &&
  1972. (strtoupper(substr($ps_format,
  1973. $i + $hn_codelen - 1,
  1974. 4)) == "C.E." ||
  1975. strtoupper(substr($ps_format,
  1976. $i + $hn_codelen - 1,
  1977. 2)) == "CE"
  1978. ))
  1979. --$hn_codelen;
  1980. $hn_century = intval($this->year / 100);
  1981. $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
  1982. $hs_century = $this->_formatNumber($hn_century,
  1983. $hs_numberformat,
  1984. $hn_codelen,
  1985. $hb_nopad,
  1986. $hb_nosign,
  1987. $ps_locale);
  1988. if (Pear::isError($hs_century))
  1989. return $hs_century;
  1990. $ret .= $hs_century;
  1991. $i += $hn_codelen + strlen($hs_numberformat);
  1992. }
  1993. break;
  1994. case "d":
  1995. $hb_lower = true;
  1996. case "D":
  1997. if (strtoupper(substr($ps_format, $i, 3)) == "DAY") {
  1998. $hs_day = Date_Calc::getWeekdayFullname($this->day,
  1999. $this->month,
  2000. $this->year);
  2001. if (!$hb_nopad) {
  2002. if (is_null($hn_weekdaypad)) {
  2003. // Set week-day padding variable:
  2004. //
  2005. $hn_weekdaypad = 0;
  2006. foreach (Date_Calc::getWeekDays() as $hs_weekday)
  2007. $hn_weekdaypad = max($hn_weekdaypad,
  2008. strlen($hs_weekday));
  2009. }
  2010. $hs_day = str_pad($hs_day,
  2011. $hn_weekdaypad,
  2012. " ",
  2013. STR_PAD_RIGHT);
  2014. }
  2015. $ret .= $hb_lower ?
  2016. strtolower($hs_day) :
  2017. (substr($ps_format, $i + 1, 1) == "A" ?
  2018. strtoupper($hs_day) :
  2019. $hs_day);
  2020. $i += 3;
  2021. } else if (strtoupper(substr($ps_format, $i, 2)) == "DY") {
  2022. $hs_day = Date_Calc::getWeekdayAbbrname($this->day,
  2023. $this->month,
  2024. $this->year);
  2025. $ret .= $hb_lower ?
  2026. strtolower($hs_day) :
  2027. (substr($ps_format, $i + 1, 1) == "Y" ?
  2028. strtoupper($hs_day) :
  2029. $hs_day);
  2030. $i += 2;
  2031. } else if (strtoupper(substr($ps_format, $i, 3)) == "DDD" &&
  2032. strtoupper(substr($ps_format, $i + 2, 3)) != "DAY" &&
  2033. strtoupper(substr($ps_format, $i + 2, 2)) != "DY"
  2034. ) {
  2035. $hn_day = Date_Calc::dayOfYear($this->day,
  2036. $this->month,
  2037. $this->year);
  2038. $hs_numberformat = substr($ps_format, $i + 3, 4);
  2039. $hs_day = $this->_formatNumber($hn_day,
  2040. $hs_numberformat,
  2041. 3,
  2042. $hb_nopad,
  2043. true,
  2044. $ps_locale);
  2045. if (Pear::isError($hs_day))
  2046. return $hs_day;
  2047. $ret .= $hs_day;
  2048. $i += 3 + strlen($hs_numberformat);
  2049. } else if (strtoupper(substr($ps_format, $i, 2)) == "DD" &&
  2050. strtoupper(substr($ps_format, $i + 1, 3)) != "DAY" &&
  2051. strtoupper(substr($ps_format, $i + 1, 2)) != "DY"
  2052. ) {
  2053. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2054. $hs_day = $this->_formatNumber($this->day,
  2055. $hs_numberformat,
  2056. 2,
  2057. $hb_nopad,
  2058. true,
  2059. $ps_locale);
  2060. if (Pear::isError($hs_day))
  2061. return $hs_day;
  2062. $ret .= $hs_day;
  2063. $i += 2 + strlen($hs_numberformat);
  2064. } else {
  2065. // Code 'D':
  2066. //
  2067. $hn_day = Date_Calc::dayOfWeek($this->day,
  2068. $this->month,
  2069. $this->year);
  2070. $hs_numberformat = substr($ps_format, $i + 1, 4);
  2071. $hs_day = $this->_formatNumber($hn_day,
  2072. $hs_numberformat,
  2073. 1,
  2074. $hb_nopad,
  2075. true,
  2076. $ps_locale);
  2077. if (Pear::isError($hs_day))
  2078. return $hs_day;
  2079. $ret .= $hs_day;
  2080. $i += 1 + strlen($hs_numberformat);
  2081. }
  2082. break;
  2083. case "f":
  2084. case "F":
  2085. if ($this->ob_invalidtime)
  2086. return $this->_getErrorInvalidTime();
  2087. $hn_codelen = 1;
  2088. if (is_numeric(substr($ps_format, $i + $hn_codelen, 1))) {
  2089. ++$hn_codelen;
  2090. while (is_numeric(substr($ps_format, $i + $hn_codelen, 1)))
  2091. ++$hn_codelen;
  2092. $hn_partsecdigits = substr($ps_format, $i + 1, $hn_codelen - 1);
  2093. } else {
  2094. while (strtoupper(substr($ps_format,
  2095. $i + $hn_codelen,
  2096. 1)) == "F")
  2097. ++$hn_codelen;
  2098. // Check next code is not F[numeric]:
  2099. //
  2100. if ($hn_codelen > 1 &&
  2101. is_numeric(substr($ps_format, $i + $hn_codelen, 1)))
  2102. --$hn_codelen;
  2103. $hn_partsecdigits = $hn_codelen;
  2104. }
  2105. $hs_partsec = (string) $this->partsecond;
  2106. if (preg_match('/^([0-9]+)(\.([0-9]+))?E-([0-9]+)$/i',
  2107. $hs_partsec,
  2108. $ha_matches)) {
  2109. $hs_partsec =
  2110. str_repeat("0", $ha_matches[4] - strlen($ha_matches[1])) .
  2111. $ha_matches[1] .
  2112. $ha_matches[3];
  2113. } else {
  2114. $hs_partsec = substr($hs_partsec, 2);
  2115. }
  2116. $hs_partsec = substr($hs_partsec, 0, $hn_partsecdigits);
  2117. // '_formatNumber() will not work for this because the
  2118. // part-second is an int, and we want it to behave like a float:
  2119. //
  2120. if ($hb_nopad) {
  2121. $hs_partsec = rtrim($hs_partsec, "0");
  2122. if ($hs_partsec == "")
  2123. $hs_partsec = "0";
  2124. } else {
  2125. $hs_partsec = str_pad($hs_partsec,
  2126. $hn_partsecdigits,
  2127. "0",
  2128. STR_PAD_RIGHT);
  2129. }
  2130. $ret .= $hs_partsec;
  2131. $i += $hn_codelen;
  2132. break;
  2133. case "h":
  2134. case "H":
  2135. if ($this->ob_invalidtime)
  2136. return $this->_getErrorInvalidTime();
  2137. if (strtoupper(substr($ps_format, $i, 4)) == "HH12") {
  2138. $hn_hour = $this->hour % 12;
  2139. if ($hn_hour == 0)
  2140. $hn_hour = 12;
  2141. $hn_codelen = 4;
  2142. } else {
  2143. // Code 'HH' or 'HH24':
  2144. //
  2145. $hn_hour = $this->hour;
  2146. $hn_codelen = strtoupper(substr($ps_format,
  2147. $i,
  2148. 4)) == "HH24" ? 4 : 2;
  2149. }
  2150. $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
  2151. $hs_hour = $this->_formatNumber($hn_hour,
  2152. $hs_numberformat,
  2153. 2,
  2154. $hb_nopad,
  2155. true,
  2156. $ps_locale);
  2157. if (Pear::isError($hs_hour))
  2158. return $hs_hour;
  2159. $ret .= $hs_hour;
  2160. $i += $hn_codelen + strlen($hs_numberformat);
  2161. break;
  2162. case "i":
  2163. case "I":
  2164. if (is_null($hn_isoyear))
  2165. list($hn_isoyear, $hn_isoweek, $hn_isoday) =
  2166. Date_Calc::isoWeekDate($this->day,
  2167. $this->month,
  2168. $this->year);
  2169. if (strtoupper(substr($ps_format, $i, 2)) == "ID" &&
  2170. strtoupper(substr($ps_format, $i + 1, 3)) != "DAY"
  2171. ) {
  2172. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2173. $hs_isoday = $this->_formatNumber($hn_isoday,
  2174. $hs_numberformat,
  2175. 1,
  2176. $hb_nopad,
  2177. true,
  2178. $ps_locale);
  2179. if (Pear::isError($hs_isoday))
  2180. return $hs_isoday;
  2181. $ret .= $hs_isoday;
  2182. $i += 2 + strlen($hs_numberformat);
  2183. } else if (strtoupper(substr($ps_format, $i, 2)) == "IW") {
  2184. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2185. $hs_isoweek = $this->_formatNumber($hn_isoweek,
  2186. $hs_numberformat,
  2187. 2,
  2188. $hb_nopad,
  2189. true,
  2190. $ps_locale);
  2191. if (Pear::isError($hs_isoweek))
  2192. return $hs_isoweek;
  2193. $ret .= $hs_isoweek;
  2194. $i += 2 + strlen($hs_numberformat);
  2195. } else {
  2196. // Code I(YYY...):
  2197. //
  2198. $hn_codelen = 1;
  2199. while (strtoupper(substr($ps_format,
  2200. $i + $hn_codelen,
  2201. 1)) == "Y")
  2202. ++$hn_codelen;
  2203. $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
  2204. $hs_isoyear = $this->_formatNumber($hn_isoyear,
  2205. $hs_numberformat,
  2206. $hn_codelen,
  2207. $hb_nopad,
  2208. $hb_nosign,
  2209. $ps_locale);
  2210. if (Pear::isError($hs_isoyear))
  2211. return $hs_isoyear;
  2212. $ret .= $hs_isoyear;
  2213. $i += $hn_codelen + strlen($hs_numberformat);
  2214. }
  2215. break;
  2216. case "j":
  2217. case "J":
  2218. $hn_jd = Date_Calc::dateToDays($this->day,
  2219. $this->month,
  2220. $this->year);
  2221. $hs_numberformat = substr($ps_format, $i + 1, 4);
  2222. // Allow sign if negative; allow all digits (specify nought);
  2223. // suppress padding:
  2224. //
  2225. $hs_jd = $this->_formatNumber($hn_jd,
  2226. $hs_numberformat,
  2227. 0,
  2228. true,
  2229. false,
  2230. $ps_locale);
  2231. if (Pear::isError($hs_jd))
  2232. return $hs_jd;
  2233. $ret .= $hs_jd;
  2234. $i += 1 + strlen($hs_numberformat);
  2235. break;
  2236. case "m":
  2237. $hb_lower = true;
  2238. case "M":
  2239. if (strtoupper(substr($ps_format, $i, 2)) == "MI") {
  2240. if ($this->ob_invalidtime)
  2241. return $this->_getErrorInvalidTime();
  2242. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2243. $hs_minute = $this->_formatNumber($this->minute,
  2244. $hs_numberformat,
  2245. 2,
  2246. $hb_nopad,
  2247. true,
  2248. $ps_locale);
  2249. if (Pear::isError($hs_minute))
  2250. return $hs_minute;
  2251. $ret .= $hs_minute;
  2252. $i += 2 + strlen($hs_numberformat);
  2253. } else if (strtoupper(substr($ps_format, $i, 2)) == "MM") {
  2254. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2255. $hs_month = $this->_formatNumber($this->month,
  2256. $hs_numberformat,
  2257. 2,
  2258. $hb_nopad,
  2259. true,
  2260. $ps_locale);
  2261. if (Pear::isError($hs_month))
  2262. return $hs_month;
  2263. $ret .= $hs_month;
  2264. $i += 2 + strlen($hs_numberformat);
  2265. } else if (strtoupper(substr($ps_format, $i, 5)) == "MONTH") {
  2266. $hs_month = Date_Calc::getMonthFullname($this->month);
  2267. if (!$hb_nopad) {
  2268. if (is_null($hn_monthpad)) {
  2269. // Set month padding variable:
  2270. //
  2271. $hn_monthpad = 0;
  2272. foreach (Date_Calc::getMonthNames() as $hs_monthofyear)
  2273. $hn_monthpad = max($hn_monthpad,
  2274. strlen($hs_monthofyear));
  2275. }
  2276. $hs_month = str_pad($hs_month,
  2277. $hn_monthpad,
  2278. " ",
  2279. STR_PAD_RIGHT);
  2280. }
  2281. $ret .= $hb_lower ?
  2282. strtolower($hs_month) :
  2283. (substr($ps_format, $i + 1, 1) == "O" ?
  2284. strtoupper($hs_month) :
  2285. $hs_month);
  2286. $i += 5;
  2287. } else if (strtoupper(substr($ps_format, $i, 3)) == "MON") {
  2288. $hs_month = Date_Calc::getMonthAbbrname($this->month);
  2289. $ret .= $hb_lower ?
  2290. strtolower($hs_month) :
  2291. (substr($ps_format, $i + 1, 1) == "O" ?
  2292. strtoupper($hs_month) :
  2293. $hs_month);
  2294. $i += 3;
  2295. }
  2296. break;
  2297. case "n":
  2298. case "N":
  2299. // No-Padding rule 'NP' applies to the next code (either trailing
  2300. // spaces or leading/trailing noughts):
  2301. //
  2302. $hb_nopadflag = true;
  2303. $i += 2;
  2304. break;
  2305. case "p":
  2306. $hb_lower = true;
  2307. case "P":
  2308. if ($this->ob_invalidtime)
  2309. return $this->_getErrorInvalidTime();
  2310. if (strtoupper(substr($ps_format, $i, 4)) == "P.M.") {
  2311. $ret .= $this->hour < 12 ?
  2312. ($hb_lower ? "a.m." : "A.M.") :
  2313. ($hb_lower ? "p.m." : "P.M.");
  2314. $i += 4;
  2315. } else if (strtoupper(substr($ps_format, $i, 2)) == "PM") {
  2316. $ret .= $this->hour < 12 ?
  2317. ($hb_lower ? "am" : "AM") :
  2318. ($hb_lower ? "pm" : "PM");
  2319. $i += 2;
  2320. }
  2321. break;
  2322. case "q":
  2323. case "Q":
  2324. // N.B. Current implementation ignores the day and year, but
  2325. // it is possible that a different implementation might be
  2326. // desired, so pass these parameters anyway:
  2327. //
  2328. $hn_quarter = Date_Calc::quarterOfYear($this->day,
  2329. $this->month,
  2330. $this->year);
  2331. $hs_numberformat = substr($ps_format, $i + 1, 4);
  2332. $hs_quarter = $this->_formatNumber($hn_quarter,
  2333. $hs_numberformat,
  2334. 1,
  2335. $hb_nopad,
  2336. true,
  2337. $ps_locale);
  2338. if (Pear::isError($hs_quarter))
  2339. return $hs_quarter;
  2340. $ret .= $hs_quarter;
  2341. $i += 1 + strlen($hs_numberformat);
  2342. break;
  2343. case "r":
  2344. $hb_lower = true;
  2345. case "R":
  2346. // Code 'RM':
  2347. //
  2348. switch ($this->month) {
  2349. case 1:
  2350. $hs_monthroman = "i";
  2351. break;
  2352. case 2:
  2353. $hs_monthroman = "ii";
  2354. break;
  2355. case 3:
  2356. $hs_monthroman = "iii";
  2357. break;
  2358. case 4:
  2359. $hs_monthroman = "iv";
  2360. break;
  2361. case 5:
  2362. $hs_monthroman = "v";
  2363. break;
  2364. case 6:
  2365. $hs_monthroman = "vi";
  2366. break;
  2367. case 7:
  2368. $hs_monthroman = "vii";
  2369. break;
  2370. case 8:
  2371. $hs_monthroman = "viii";
  2372. break;
  2373. case 9:
  2374. $hs_monthroman = "ix";
  2375. break;
  2376. case 10:
  2377. $hs_monthroman = "x";
  2378. break;
  2379. case 11:
  2380. $hs_monthroman = "xi";
  2381. break;
  2382. case 12:
  2383. $hs_monthroman = "xii";
  2384. break;
  2385. }
  2386. $hs_monthroman = $hb_lower ?
  2387. $hs_monthroman :
  2388. strtoupper($hs_monthroman);
  2389. $ret .= $hb_nopad ?
  2390. $hs_monthroman :
  2391. str_pad($hs_monthroman, 4, " ", STR_PAD_LEFT);
  2392. $i += 2;
  2393. break;
  2394. case "s":
  2395. case "S":
  2396. // Check for 'SSSSS' before 'SS':
  2397. //
  2398. if (strtoupper(substr($ps_format, $i, 5)) == "SSSSS") {
  2399. if ($this->ob_invalidtime)
  2400. return $this->_getErrorInvalidTime();
  2401. $hs_numberformat = substr($ps_format, $i + 5, 4);
  2402. $hn_second = Date_Calc::secondsPastMidnight($this->hour,
  2403. $this->minute,
  2404. $this->second);
  2405. $hs_second = $this->_formatNumber($hn_second,
  2406. $hs_numberformat,
  2407. 5,
  2408. $hb_nopad,
  2409. true,
  2410. $ps_locale);
  2411. if (Pear::isError($hs_second))
  2412. return $hs_second;
  2413. $ret .= $hs_second;
  2414. $i += 5 + strlen($hs_numberformat);
  2415. } else if (strtoupper(substr($ps_format, $i, 2)) == "SS") {
  2416. if ($this->ob_invalidtime)
  2417. return $this->_getErrorInvalidTime();
  2418. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2419. $hs_second = $this->_formatNumber($this->second,
  2420. $hs_numberformat,
  2421. 2,
  2422. $hb_nopad,
  2423. true,
  2424. $ps_locale);
  2425. if (Pear::isError($hs_second))
  2426. return $hs_second;
  2427. $ret .= $hs_second;
  2428. $i += 2 + strlen($hs_numberformat);
  2429. } else {
  2430. // One of the following codes:
  2431. // 'SC(CCC...)'
  2432. // 'SY(YYY...)'
  2433. // 'SIY(YYY...)'
  2434. // 'STZH'
  2435. // 'STZS'
  2436. // 'SYEAR'
  2437. //
  2438. $hb_showsignflag = true;
  2439. if ($hb_nopad)
  2440. $hb_nopadflag = true;
  2441. ++$i;
  2442. }
  2443. break;
  2444. case "t":
  2445. case "T":
  2446. // Code TZ[...]:
  2447. //
  2448. if (strtoupper(substr($ps_format, $i, 3)) == "TZR") {
  2449. // This time-zone-related code can be called when the time is
  2450. // invalid, but the others should return an error:
  2451. //
  2452. $ret .= $this->getTZID();
  2453. $i += 3;
  2454. } else {
  2455. if ($this->ob_invalidtime)
  2456. return $this->_getErrorInvalidTime();
  2457. if (strtoupper(substr($ps_format, $i, 3)) == "TZC") {
  2458. $ret .= $this->getTZShortName();
  2459. $i += 3;
  2460. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZH") {
  2461. if (is_null($hn_tzoffset))
  2462. $hn_tzoffset = $this->getTZOffset();
  2463. $hs_numberformat = substr($ps_format, $i + 3, 4);
  2464. $hn_tzh = intval($hn_tzoffset / 3600000);
  2465. // Suppress sign here (it is added later):
  2466. //
  2467. $hs_tzh = $this->_formatNumber($hn_tzh,
  2468. $hs_numberformat,
  2469. 2,
  2470. $hb_nopad,
  2471. true,
  2472. $ps_locale);
  2473. if (Pear::isError($hs_tzh))
  2474. return $hs_tzh;
  2475. // Display sign, even if positive:
  2476. //
  2477. $ret .= ($hb_nosign ? "" : ($hn_tzh >= 0 ? '+' : '-')) .
  2478. $hs_tzh;
  2479. $i += 3 + strlen($hs_numberformat);
  2480. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZI") {
  2481. $ret .= ($this->inDaylightTime() ? '1' : '0');
  2482. $i += 3;
  2483. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZM") {
  2484. if (is_null($hn_tzoffset))
  2485. $hn_tzoffset = $this->getTZOffset();
  2486. $hs_numberformat = substr($ps_format, $i + 3, 4);
  2487. $hn_tzm = intval(($hn_tzoffset % 3600000) / 60000);
  2488. // Suppress sign:
  2489. //
  2490. $hs_tzm = $this->_formatNumber($hn_tzm,
  2491. $hs_numberformat,
  2492. 2,
  2493. $hb_nopad,
  2494. true,
  2495. $ps_locale);
  2496. if (Pear::isError($hs_tzm))
  2497. return $hs_tzm;
  2498. $ret .= $hs_tzm;
  2499. $i += 3 + strlen($hs_numberformat);
  2500. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZN") {
  2501. $ret .= $this->getTZLongName();
  2502. $i += 3;
  2503. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZO") {
  2504. if (is_null($hn_tzoffset))
  2505. $hn_tzoffset = $this->getTZOffset();
  2506. $hn_tzh = intval(abs($hn_tzoffset) / 3600000);
  2507. $hn_tzm = intval((abs($hn_tzoffset) % 3600000) / 60000);
  2508. if ($hn_tzoffset == 0) {
  2509. $ret .= $hb_nopad ? "Z" : "Z ";
  2510. } else {
  2511. // Display sign, even if positive:
  2512. //
  2513. $ret .= ($hn_tzoffset >= 0 ? '+' : '-') .
  2514. sprintf("%02d", $hn_tzh) .
  2515. ":" .
  2516. sprintf("%02d", $hn_tzm);
  2517. }
  2518. $i += 3;
  2519. } else if (strtoupper(substr($ps_format, $i, 3)) == "TZS") {
  2520. if (is_null($hn_tzoffset))
  2521. $hn_tzoffset = $this->getTZOffset();
  2522. $hs_numberformat = substr($ps_format, $i + 3, 4);
  2523. $hn_tzs = intval($hn_tzoffset / 1000);
  2524. $hs_tzs = $this->_formatNumber($hn_tzs,
  2525. $hs_numberformat,
  2526. 5,
  2527. $hb_nopad,
  2528. $hb_nosign,
  2529. $ps_locale);
  2530. if (Pear::isError($hs_tzs))
  2531. return $hs_tzs;
  2532. $ret .= $hs_tzs;
  2533. $i += 3 + strlen($hs_numberformat);
  2534. }
  2535. }
  2536. break;
  2537. case "u":
  2538. case "U":
  2539. if ($this->ob_invalidtime)
  2540. return $this->_getErrorInvalidTime();
  2541. $hn_unixtime = $this->getTime();
  2542. $hs_numberformat = substr($ps_format, $i + 1, 4);
  2543. // Allow sign if negative; allow all digits (specify nought);
  2544. // suppress padding:
  2545. //
  2546. $hs_unixtime = $this->_formatNumber($hn_unixtime,
  2547. $hs_numberformat,
  2548. 0,
  2549. true,
  2550. false,
  2551. $ps_locale);
  2552. if (Pear::isError($hs_unixtime))
  2553. return $hs_unixtime;
  2554. $ret .= $hs_unixtime;
  2555. $i += 1 + strlen($hs_numberformat);
  2556. break;
  2557. case "w":
  2558. case "W":
  2559. // Check for 'WW' before 'W':
  2560. //
  2561. if (strtoupper(substr($ps_format, $i, 2)) == "WW") {
  2562. $hn_week = Date_Calc::weekOfYearAbsolute($this->day,
  2563. $this->month,
  2564. $this->year);
  2565. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2566. $hs_week = $this->_formatNumber($hn_week,
  2567. $hs_numberformat,
  2568. 2,
  2569. $hb_nopad,
  2570. true,
  2571. $ps_locale);
  2572. if (Pear::isError($hs_week))
  2573. return $hs_week;
  2574. $ret .= $hs_week;
  2575. $i += 2 + strlen($hs_numberformat);
  2576. } else if (strtoupper(substr($ps_format, $i, 2)) == "W1") {
  2577. $hn_week = Date_Calc::weekOfYear1st($this->day,
  2578. $this->month,
  2579. $this->year);
  2580. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2581. $hs_week = $this->_formatNumber($hn_week,
  2582. $hs_numberformat,
  2583. 2,
  2584. $hb_nopad,
  2585. true,
  2586. $ps_locale);
  2587. if (Pear::isError($hs_week))
  2588. return $hs_week;
  2589. $ret .= $hs_week;
  2590. $i += 2 + strlen($hs_numberformat);
  2591. } else if (strtoupper(substr($ps_format, $i, 2)) == "W4") {
  2592. $ha_week = Date_Calc::weekOfYear4th($this->day,
  2593. $this->month,
  2594. $this->year);
  2595. $hn_week = $ha_week[1];
  2596. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2597. $hs_week = $this->_formatNumber($hn_week,
  2598. $hs_numberformat,
  2599. 2,
  2600. $hb_nopad,
  2601. true,
  2602. $ps_locale);
  2603. if (Pear::isError($hs_week))
  2604. return $hs_week;
  2605. $ret .= $hs_week;
  2606. $i += 2 + strlen($hs_numberformat);
  2607. } else if (strtoupper(substr($ps_format, $i, 2)) == "W7") {
  2608. $ha_week = Date_Calc::weekOfYear7th($this->day,
  2609. $this->month,
  2610. $this->year);
  2611. $hn_week = $ha_week[1];
  2612. $hs_numberformat = substr($ps_format, $i + 2, 4);
  2613. $hs_week = $this->_formatNumber($hn_week,
  2614. $hs_numberformat,
  2615. 2,
  2616. $hb_nopad,
  2617. true,
  2618. $ps_locale);
  2619. if (Pear::isError($hs_week))
  2620. return $hs_week;
  2621. $ret .= $hs_week;
  2622. $i += 2 + strlen($hs_numberformat);
  2623. } else {
  2624. // Code 'W':
  2625. //
  2626. $hn_week = Date_Calc::weekOfMonthAbsolute($this->day,
  2627. $this->month,
  2628. $this->year);
  2629. $hs_numberformat = substr($ps_format, $i + 1, 4);
  2630. $hs_week = $this->_formatNumber($hn_week,
  2631. $hs_numberformat,
  2632. 1,
  2633. $hb_nopad,
  2634. true,
  2635. $ps_locale);
  2636. if (Pear::isError($hs_week))
  2637. return $hs_week;
  2638. $ret .= $hs_week;
  2639. $i += 1 + strlen($hs_numberformat);
  2640. }
  2641. break;
  2642. case "y":
  2643. case "Y":
  2644. // Check for 'YEAR' first:
  2645. //
  2646. if (strtoupper(substr($ps_format, $i, 4)) == "YEAR") {
  2647. switch (substr($ps_format, $i, 2)) {
  2648. case "YE":
  2649. $hs_spformat = "SP";
  2650. break;
  2651. case "Ye":
  2652. $hs_spformat = "Sp";
  2653. break;
  2654. default:
  2655. $hs_spformat = "sp";
  2656. }
  2657. if (($hn_yearabs = abs($this->year)) < 100 ||
  2658. $hn_yearabs % 100 < 10) {
  2659. $hs_numberformat = $hs_spformat;
  2660. // Allow all digits (specify nought); padding irrelevant:
  2661. //
  2662. $hs_year = $this->_formatNumber($this->year,
  2663. $hs_numberformat,
  2664. 0,
  2665. true,
  2666. $hb_nosign,
  2667. $ps_locale);
  2668. if (Pear::isError($hs_year))
  2669. return $hs_year;
  2670. $ret .= $hs_year;
  2671. } else {
  2672. // Year is spelled 'Nineteen Twelve' rather than
  2673. // 'One thousand Nine Hundred Twelve':
  2674. //
  2675. $hn_century = intval($this->year / 100);
  2676. $hs_numberformat = $hs_spformat;
  2677. // Allow all digits (specify nought); padding irrelevant:
  2678. //
  2679. $hs_century = $this->_formatNumber($hn_century,
  2680. $hs_numberformat,
  2681. 0,
  2682. true,
  2683. $hb_nosign,
  2684. $ps_locale);
  2685. if (Pear::isError($hs_century))
  2686. return $hs_century;
  2687. $ret .= $hs_century . " ";
  2688. $hs_numberformat = $hs_spformat;
  2689. // Discard sign; padding irrelevant:
  2690. //
  2691. $hs_year = $this->_formatNumber($this->year,
  2692. $hs_numberformat,
  2693. 2,
  2694. false,
  2695. true,
  2696. $ps_locale);
  2697. if (Pear::isError($hs_year))
  2698. return $hs_year;
  2699. $ret .= $hs_year;
  2700. }
  2701. $i += 4;
  2702. } else {
  2703. // Code Y(YYY...):
  2704. //
  2705. $hn_codelen = 1;
  2706. while (strtoupper(substr($ps_format,
  2707. $i + $hn_codelen,
  2708. 1)) == "Y")
  2709. ++$hn_codelen;
  2710. $hs_thousandsep = null;
  2711. $hn_thousandseps = 0;
  2712. if ($hn_codelen <= 3) {
  2713. while (preg_match('/([,.ˇ\' ])YYY/i',
  2714. substr($ps_format,
  2715. $i + $hn_codelen,
  2716. 4),
  2717. $ha_matches)) {
  2718. $hn_codelen += 4;
  2719. $hs_thousandsep = $ha_matches[1];
  2720. ++$hn_thousandseps;
  2721. }
  2722. }
  2723. // Check next code is not 'YEAR'
  2724. //
  2725. if ($hn_codelen > 1 &&
  2726. strtoupper(substr($ps_format,
  2727. $i + $hn_codelen - 1,
  2728. 4)) == "YEAR")
  2729. --$hn_codelen;
  2730. $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
  2731. $hs_year = $this->_formatNumber($this->year,
  2732. $hs_numberformat,
  2733. $hn_codelen -
  2734. $hn_thousandseps,
  2735. $hb_nopad,
  2736. $hb_nosign,
  2737. $ps_locale,
  2738. $hs_thousandsep);
  2739. if (Pear::isError($hs_year))
  2740. return $hs_year;
  2741. $ret .= $hs_year;
  2742. $i += $hn_codelen + strlen($hs_numberformat);
  2743. }
  2744. break;
  2745. default:
  2746. $ret .= $hs_char;
  2747. ++$i;
  2748. break;
  2749. }
  2750. }
  2751. return $ret;
  2752. }
  2753. // }}}
  2754. // {{{ format3()
  2755. /**
  2756. * Formats the date in the same way as 'format()', but using the
  2757. * formatting codes used by the PHP function 'date()'
  2758. *
  2759. * All 'date()' formatting options are supported except 'B'. This
  2760. * function also responds to the DATE_* constants, such as DATE_COOKIE,
  2761. * which are specified at:
  2762. *
  2763. * http://www.php.net/manual/en/ref.datetime.php#datetime.constants
  2764. *
  2765. *
  2766. * Formatting options:
  2767. *
  2768. * (Day)
  2769. *
  2770. * <code>d</code> Day of the month, 2 digits with leading zeros (01 to 31)
  2771. * <code>D</code> A textual representation of a day, three letters ('Mon'
  2772. * to 'Sun')
  2773. * <code>j</code> Day of the month without leading zeros (1 to 31)
  2774. * <code>l</code> [lowercase 'L'] A full textual representation of the day
  2775. * of the week ('Sunday' to 'Saturday')
  2776. * <code>N</code> ISO-8601 numeric representation of the day of the week
  2777. * (1 (for Monday) to 7 (for Sunday))
  2778. * <code>S</code> English ordinal suffix for the day of the month, 2
  2779. * characters ('st', 'nd', 'rd' or 'th')
  2780. * <code>w</code> Numeric representation of the day of the week (0 (for
  2781. * Sunday) to 6 (for Saturday))
  2782. * <code>z</code> The day of the year, starting from 0 (0 to 365)
  2783. *
  2784. * (Week)
  2785. *
  2786. * <code>W</code> ISO-8601 week number of year, weeks starting on Monday
  2787. * (00 to 53)
  2788. *
  2789. * (Month)
  2790. *
  2791. * <code>F</code> A full textual representation of a month ('January' to
  2792. * 'December')
  2793. * <code>m</code> Numeric representation of a month, with leading zeros
  2794. * (01 to 12)
  2795. * <code>M</code> A short textual representation of a month, three letters
  2796. * ('Jan' to 'Dec')
  2797. * <code>n</code> Numeric representation of a month, without leading zeros
  2798. * (1 to 12)
  2799. * <code>t</code> Number of days in the given month (28 to 31)
  2800. *
  2801. * (Year)
  2802. *
  2803. * <code>L</code> Whether it is a leap year (1 if it is a leap year, 0
  2804. * otherwise)
  2805. * <code>o</code> ISO-8601 year number. This has the same value as Y,
  2806. * except that if the ISO week number (W) belongs to the
  2807. * previous or next year, that year is used instead.
  2808. * <code>Y</code> A full numeric representation of a year, 4 digits (0000
  2809. * to 9999)
  2810. * <code>y</code> A two digit representation of a year (00 to 99)
  2811. *
  2812. * (Time)
  2813. *
  2814. * <code>a</code> Lowercase Ante meridiem and Post meridiem ('am' or
  2815. * 'pm')
  2816. * <code>A</code> Uppercase Ante meridiem and Post meridiem ('AM' or
  2817. * 'PM')
  2818. * <code>g</code> 12-hour format of an hour without leading zeros (1 to 12)
  2819. * <code>G</code> 24-hour format of an hour without leading zeros (0 to 23)
  2820. * <code>h</code> 12-hour format of an hour with leading zeros (01 to 12)
  2821. * <code>H</code> 24-hour format of an hour with leading zeros (00 to 23)
  2822. * <code>i</code> Minutes with leading zeros (00 to 59)
  2823. * <code>s</code> Seconds, with leading zeros (00 to 59)
  2824. * <code>u</code> Milliseconds, e.g. '54321'
  2825. *
  2826. * (Time Zone)
  2827. *
  2828. * <code>e</code> Timezone identifier, e.g. Europe/London
  2829. * <code>I</code> Whether or not the date is in Summer time (1 if Summer
  2830. * time, 0 otherwise)
  2831. * <code>O</code> Difference to Greenwich time (GMT) in hours, e.g. '+0200'
  2832. * <code>P</code> Difference to Greenwich time (GMT) with colon between
  2833. * hours and minutes, e.g. '+02:00'
  2834. * <code>T</code> Timezone abbreviation, e.g. 'GMT', 'EST'
  2835. * <code>Z</code> Timezone offset in seconds. The offset for timezones west
  2836. * of UTC is always negative, and for those east of UTC is
  2837. * always positive. (-43200 to 50400)
  2838. *
  2839. * (Full Date/Time)
  2840. *
  2841. * <code>c</code> ISO 8601 date, e.g. '2004-02-12T15:19:21+00:00'
  2842. * <code>r</code> RFC 2822 formatted date, e.g.
  2843. * 'Thu, 21 Dec 2000 16:01:07 +0200'
  2844. * <code>U</code> Seconds since the Unix Epoch
  2845. * (January 1 1970 00:00:00 GMT)
  2846. *
  2847. * @param string $ps_format the format string for returned date/time
  2848. *
  2849. * @return string date/time in given format
  2850. * @access public
  2851. * @since Method available since Release 1.5.0
  2852. */
  2853. function format3($ps_format)
  2854. {
  2855. $hs_format2str = "";
  2856. for ($i = 0; $i < strlen($ps_format); ++$i) {
  2857. switch ($hs_char = substr($ps_format, $i, 1)) {
  2858. case 'd':
  2859. $hs_format2str .= 'DD';
  2860. break;
  2861. case 'D':
  2862. $hs_format2str .= 'NPDy';
  2863. break;
  2864. case 'j':
  2865. $hs_format2str .= 'NPDD';
  2866. break;
  2867. case 'l':
  2868. $hs_format2str .= 'NPDay';
  2869. break;
  2870. case 'N':
  2871. $hs_format2str .= 'ID';
  2872. break;
  2873. case 'S':
  2874. $hs_format2str .= 'th';
  2875. break;
  2876. case 'w':
  2877. $hs_format2str .= 'D';
  2878. break;
  2879. case 'z':
  2880. $hs_format2str .= '"' . ($this->getDayOfYear() - 1) . '"';
  2881. break;
  2882. case 'W':
  2883. $hs_format2str .= 'IW';
  2884. break;
  2885. case 'F':
  2886. $hs_format2str .= 'NPMonth';
  2887. break;
  2888. case 'm':
  2889. $hs_format2str .= 'MM';
  2890. break;
  2891. case 'M':
  2892. $hs_format2str .= 'NPMon';
  2893. break;
  2894. case 'n':
  2895. $hs_format2str .= 'NPMM';
  2896. break;
  2897. case 't':
  2898. $hs_format2str .= '"' . $this->getDaysInMonth() . '"';
  2899. break;
  2900. case 'L':
  2901. $hs_format2str .= '"' . ($this->isLeapYear() ? 1 : 0) . '"';
  2902. break;
  2903. case 'o':
  2904. $hs_format2str .= 'IYYY';
  2905. break;
  2906. case 'Y':
  2907. $hs_format2str .= 'YYYY';
  2908. break;
  2909. case 'y':
  2910. $hs_format2str .= 'YY';
  2911. break;
  2912. case 'a':
  2913. $hs_format2str .= 'am';
  2914. break;
  2915. case 'A':
  2916. $hs_format2str .= 'AM';
  2917. break;
  2918. case 'g':
  2919. $hs_format2str .= 'NPHH12';
  2920. break;
  2921. case 'G':
  2922. $hs_format2str .= 'NPHH24';
  2923. break;
  2924. case 'h':
  2925. $hs_format2str .= 'HH12';
  2926. break;
  2927. case 'H':
  2928. $hs_format2str .= 'HH24';
  2929. break;
  2930. case 'i':
  2931. $hs_format2str .= 'MI';
  2932. break;
  2933. case 's':
  2934. $hs_format2str .= 'SS';
  2935. break;
  2936. case 'u':
  2937. $hs_format2str .= 'SSFFF';
  2938. break;
  2939. case 'e':
  2940. $hs_format2str .= 'TZR';
  2941. break;
  2942. case 'I':
  2943. $hs_format2str .= 'TZI';
  2944. break;
  2945. case 'O':
  2946. $hs_format2str .= 'STZHTZM';
  2947. break;
  2948. case 'P':
  2949. $hs_format2str .= 'STZH:TZM';
  2950. break;
  2951. case 'T':
  2952. $hs_format2str .= 'TZC';
  2953. break;
  2954. case 'Z':
  2955. $hs_format2str .= 'TZS';
  2956. break;
  2957. case 'c':
  2958. $hs_format2str .= 'YYYY-MM-DD"T"HH24:MI:SSSTZH:TZM';
  2959. break;
  2960. case 'r':
  2961. $hs_format2str .= 'Dy, DD Mon YYYY HH24:MI:SS STZHTZM';
  2962. break;
  2963. case 'U':
  2964. $hs_format2str .= 'U';
  2965. break;
  2966. case '\\':
  2967. $hs_char = substr($ps_format, ++$i, 1);
  2968. $hs_format2str .= '"' . ($hs_char == '\\' ? '\\\\' : $hs_char) . '"';
  2969. break;
  2970. case '"':
  2971. $hs_format2str .= '"\\""';
  2972. break;
  2973. default:
  2974. $hs_format2str .= '"' . $hs_char . '"';
  2975. }
  2976. }
  2977. $ret = $this->format2($hs_format2str);
  2978. if (PEAR::isError($ret) &&
  2979. $ret->getCode() == DATE_ERROR_INVALIDFORMATSTRING) {
  2980. return PEAR::raiseError("Invalid date format '$ps_format'",
  2981. DATE_ERROR_INVALIDFORMATSTRING);
  2982. }
  2983. return $ret;
  2984. }
  2985. // }}}
  2986. // {{{ getTime()
  2987. /**
  2988. * Returns the date/time in Unix time() format
  2989. *
  2990. * Returns a representation of this date in Unix time() format. This may
  2991. * only be valid for dates from 1970 to ~2038.
  2992. *
  2993. * @return int number of seconds since the unix epoch
  2994. * @access public
  2995. */
  2996. function getTime()
  2997. {
  2998. return $this->getDate(DATE_FORMAT_UNIXTIME);
  2999. }
  3000. // }}}
  3001. // {{{ getTZID()
  3002. /**
  3003. * Returns the unique ID of the time zone, e.g. 'America/Chicago'
  3004. *
  3005. * @return string the time zone ID
  3006. * @access public
  3007. * @since Method available since Release 1.5.0
  3008. */
  3009. function getTZID()
  3010. {
  3011. return $this->tz->getID();
  3012. }
  3013. // }}}
  3014. // {{{ _setTZToDefault()
  3015. /**
  3016. * sets time zone to the default time zone
  3017. *
  3018. * If PHP version >= 5.1.0, uses the php.ini configuration directive
  3019. * 'date.timezone' if set and valid, else the value returned by
  3020. * 'date("e")' if valid, else the default specified if the global
  3021. * constant '$GLOBALS["_DATE_TIMEZONE_DEFAULT"]', which if itself
  3022. * left unset, defaults to "UTC".
  3023. *
  3024. * N.B. this is a private method; to set the time zone to the
  3025. * default publicly you should call 'setTZByID()', that is, with no
  3026. * parameter (or a parameter of null).
  3027. *
  3028. * @return void
  3029. * @access private
  3030. * @since Method available since Release 1.5.0
  3031. */
  3032. function _setTZToDefault()
  3033. {
  3034. if (function_exists('version_compare') &&
  3035. version_compare(phpversion(), "5.1.0", ">=") &&
  3036. (Date_TimeZone::isValidID($hs_id = ini_get("date.timezone")) ||
  3037. Date_TimeZone::isValidID($hs_id = date("e"))
  3038. )
  3039. ) {
  3040. $this->tz = new Date_TimeZone($hs_id);
  3041. } else {
  3042. $this->tz = Date_TimeZone::getDefault();
  3043. }
  3044. }
  3045. // }}}
  3046. // {{{ setTZ()
  3047. /**
  3048. * Sets the time zone of this Date
  3049. *
  3050. * Sets the time zone of this date with the given
  3051. * Date_TimeZone object. Does not alter the date/time,
  3052. * only assigns a new time zone. For conversion, use
  3053. * convertTZ().
  3054. *
  3055. * @param object $tz the Date_TimeZone object to use. If called with a
  3056. * parameter that is not a Date_TimeZone object, will
  3057. * fall through to setTZByID().
  3058. *
  3059. * @return void
  3060. * @access public
  3061. * @see Date::setTZByID()
  3062. */
  3063. function setTZ($tz)
  3064. {
  3065. if (is_a($tz, 'Date_Timezone')) {
  3066. $this->setTZByID($tz->getID());
  3067. } else {
  3068. $res = $this->setTZByID($tz);
  3069. if (PEAR::isError($res))
  3070. return $res;
  3071. }
  3072. }
  3073. // }}}
  3074. // {{{ setTZByID()
  3075. /**
  3076. * Sets the time zone of this date with the given time zone ID
  3077. *
  3078. * The time zone IDs are drawn from the 'tz data-base' (see
  3079. * http://en.wikipedia.org/wiki/Zoneinfo), which is the de facto
  3080. * internet and IT standard. (There is no official standard, and
  3081. * the tz data-base is not intended to be a regulating body
  3082. * anyway.) Lists of valid IDs are maintained at:
  3083. *
  3084. * http://en.wikipedia.org/wiki/List_of_zoneinfo_timezones
  3085. * http://www.php.net/manual/en/timezones.php
  3086. *
  3087. * If no time-zone is specified and PHP version >= 5.1.0, the time
  3088. * zone is set automatically to the php.ini configuration directive
  3089. * 'date.timezone' if set and valid, else the value returned by
  3090. * 'date("e")' if valid, else the default specified if the global
  3091. * constant '$GLOBALS["_DATE_TIMEZONE_DEFAULT"]', which if itself
  3092. * left unset, defaults to "UTC".
  3093. *
  3094. * N.B. this function preserves the local date and time, that is,
  3095. * whether in local Summer time or local standard time. For example,
  3096. * if the time is set to 11.00 Summer time, and the time zone is then
  3097. * set to another time zone, using this function, in which the date
  3098. * falls in standard time, then the time will remain set to 11.00 UTC,
  3099. * and not 10.00. You can convert a date to another time zone by
  3100. * calling 'convertTZ()'.
  3101. *
  3102. * The ID can also be specified as a UTC offset in one of the following
  3103. * forms, i.e. an offset with no geographical or political base:
  3104. *
  3105. * UTC[+/-][h] - e.g. UTC-1 (the preferred form)
  3106. * UTC[+/-][hh] - e.g. UTC+03
  3107. * UTC[+/-][hh][mm] - e.g. UTC-0530
  3108. * UTC[+/-][hh]:[mm] - e.g. UTC+03:00
  3109. *
  3110. * N.B. 'UTC' seems to be technically preferred over 'GMT'. GMT-based
  3111. * IDs still exist in the tz data-base, but beware of POSIX-style
  3112. * offsets which are the opposite way round to what people normally
  3113. * expect.
  3114. *
  3115. * @param string $ps_id a valid time zone id, e.g. 'Europe/London'
  3116. *
  3117. * @return void
  3118. * @access public
  3119. * @see Date::convertTZByID(), Date_TimeZone::isValidID(),
  3120. * Date_TimeZone::Date_TimeZone()
  3121. */
  3122. function setTZByID($ps_id = null)
  3123. {
  3124. // Whether the date is in Summer time forms the default for
  3125. // the new time zone (if needed, which is very unlikely anyway).
  3126. // This is mainly to prevent unexpected (defaulting) behaviour
  3127. // if the user is in the repeated hour, and switches to a time
  3128. // zone that is also in the repeated hour (e.g. 'Europe/London'
  3129. // and 'Europe/Lisbon').
  3130. //
  3131. $hb_insummertime = $this->inDaylightTime();
  3132. if (PEAR::isError($hb_insummertime)) {
  3133. if ($hb_insummertime->getCode() == DATE_ERROR_INVALIDTIME) {
  3134. $hb_insummertime = false;
  3135. } else {
  3136. return $hb_insummertime;
  3137. }
  3138. }
  3139. if (is_null($ps_id)) {
  3140. $this->_setTZToDefault();
  3141. } else if (Date_TimeZone::isValidID($ps_id)) {
  3142. $this->tz = new Date_TimeZone($ps_id);
  3143. } else {
  3144. return PEAR::raiseError("Invalid time zone ID '$ps_id'",
  3145. DATE_ERROR_INVALIDTIMEZONE);
  3146. }
  3147. $this->setLocalTime($this->day,
  3148. $this->month,
  3149. $this->year,
  3150. $this->hour,
  3151. $this->minute,
  3152. $this->second,
  3153. $this->partsecond,
  3154. $hb_insummertime);
  3155. }
  3156. // }}}
  3157. // {{{ getTZLongName()
  3158. /**
  3159. * Returns the long name of the time zone
  3160. *
  3161. * Returns long form of time zone name, e.g. 'Greenwich Mean Time'.
  3162. * N.B. if the date falls in Summer time, the Summer time name will be
  3163. * returned instead, e.g. 'British Summer Time'.
  3164. *
  3165. * N.B. this is not a unique identifier for the time zone - for this
  3166. * purpose use the time zone ID.
  3167. *
  3168. * @return string the long name of the time zone
  3169. * @access public
  3170. * @since Method available since Release 1.5.0
  3171. */
  3172. function getTZLongName()
  3173. {
  3174. if ($this->ob_invalidtime)
  3175. return $this->_getErrorInvalidTime();
  3176. return $this->tz->getLongName($this->inDaylightTime());
  3177. }
  3178. // }}}
  3179. // {{{ getTZShortName()
  3180. /**
  3181. * Returns the short name of the time zone
  3182. *
  3183. * Returns abbreviated form of time zone name, e.g. 'GMT'. N.B. if the
  3184. * date falls in Summer time, the Summer time name will be returned
  3185. * instead, e.g. 'BST'.
  3186. *
  3187. * N.B. this is not a unique identifier - for this purpose use the
  3188. * time zone ID.
  3189. *
  3190. * @return string the short name of the time zone
  3191. * @access public
  3192. * @since Method available since Release 1.5.0
  3193. */
  3194. function getTZShortName()
  3195. {
  3196. if ($this->ob_invalidtime)
  3197. return $this->_getErrorInvalidTime();
  3198. return $this->tz->getShortName($this->inDaylightTime());
  3199. }
  3200. // }}}
  3201. // {{{ getTZOffset()
  3202. /**
  3203. * Returns the DST-corrected offset from UTC for the given date
  3204. *
  3205. * Gets the offset to UTC for a given date/time, taking into
  3206. * account daylight savings time, if the time zone observes it and if
  3207. * it is in effect.
  3208. *
  3209. * N.B. that the offset is calculated historically
  3210. * and in the future according to the current Summer time rules,
  3211. * and so this function is proleptically correct, but not necessarily
  3212. * historically correct. (Although if you want to be correct about
  3213. * times in the distant past, this class is probably not for you
  3214. * because the whole notion of time zones does not apply, and
  3215. * historically there are so many time zone changes, Summer time
  3216. * rule changes, name changes, calendar changes, that calculating
  3217. * this sort of information is beyond the scope of this package
  3218. * altogether.)
  3219. *
  3220. * @return int the corrected offset to UTC in milliseconds
  3221. * @access public
  3222. * @since Method available since Release 1.5.0
  3223. */
  3224. function getTZOffset()
  3225. {
  3226. if ($this->ob_invalidtime)
  3227. return $this->_getErrorInvalidTime();
  3228. return $this->tz->getOffset($this->inDaylightTime());
  3229. }
  3230. // }}}
  3231. // {{{ inDaylightTime()
  3232. /**
  3233. * Tests if this date/time is in DST
  3234. *
  3235. * Returns true if daylight savings time is in effect for
  3236. * this date in this date's time zone.
  3237. *
  3238. * @param bool $pb_repeatedhourdefault value to return if repeated hour is
  3239. * specified (defaults to false)
  3240. *
  3241. * @return boolean true if DST is in effect for this date
  3242. * @access public
  3243. */
  3244. function inDaylightTime($pb_repeatedhourdefault = false)
  3245. {
  3246. if (!$this->tz->hasDaylightTime())
  3247. return false;
  3248. if ($this->ob_invalidtime)
  3249. return $this->_getErrorInvalidTime();
  3250. // The return value is 'cached' whenever the date/time is set:
  3251. //
  3252. return $this->hour != $this->on_standardhour ||
  3253. $this->minute != $this->on_standardminute ||
  3254. $this->second != $this->on_standardsecond ||
  3255. $this->partsecond != $this->on_standardpartsecond ||
  3256. $this->day != $this->on_standardday ||
  3257. $this->month != $this->on_standardmonth ||
  3258. $this->year != $this->on_standardyear;
  3259. //
  3260. // (these last 3 conditions are theoretical
  3261. // possibilities but normally will never occur)
  3262. }
  3263. // }}}
  3264. // {{{ convertTZ()
  3265. /**
  3266. * Converts this date to a new time zone
  3267. *
  3268. * Previously this might not have worked correctly if your system did
  3269. * not allow putenv() or if localtime() did not work in your
  3270. * environment, but this implementation is no longer used.
  3271. *
  3272. * @param object $tz Date_TimeZone object to convert to
  3273. *
  3274. * @return void
  3275. * @access public
  3276. * @see Date::convertTZByID()
  3277. */
  3278. function convertTZ($tz)
  3279. {
  3280. if ($this->getTZID() == $tz->getID())
  3281. return;
  3282. if ($this->ob_invalidtime)
  3283. return $this->_getErrorInvalidTime();
  3284. $hn_rawoffset = $tz->getRawOffset() - $this->tz->getRawOffset();
  3285. $this->tz = new Date_TimeZone($tz->getID());
  3286. list($hn_standardyear,
  3287. $hn_standardmonth,
  3288. $hn_standardday,
  3289. $hn_standardhour,
  3290. $hn_standardminute,
  3291. $hn_standardsecond,
  3292. $hn_standardpartsecond) =
  3293. $this->_addOffset($hn_rawoffset,
  3294. $this->on_standardday,
  3295. $this->on_standardmonth,
  3296. $this->on_standardyear,
  3297. $this->on_standardhour,
  3298. $this->on_standardminute,
  3299. $this->on_standardsecond,
  3300. $this->on_standardpartsecond);
  3301. $this->setStandardTime($hn_standardday,
  3302. $hn_standardmonth,
  3303. $hn_standardyear,
  3304. $hn_standardhour,
  3305. $hn_standardminute,
  3306. $hn_standardsecond,
  3307. $hn_standardpartsecond);
  3308. }
  3309. // }}}
  3310. // {{{ toUTC()
  3311. /**
  3312. * Converts this date to UTC and sets this date's timezone to UTC
  3313. *
  3314. * @return void
  3315. * @access public
  3316. */
  3317. function toUTC()
  3318. {
  3319. if ($this->getTZID() == "UTC")
  3320. return;
  3321. if ($this->ob_invalidtime)
  3322. return $this->_getErrorInvalidTime();
  3323. $res = $this->convertTZ(new Date_TimeZone("UTC"));
  3324. if (PEAR::isError($res))
  3325. return $res;
  3326. }
  3327. // }}}
  3328. // {{{ convertTZByID()
  3329. /**
  3330. * Converts this date to a new time zone, given a valid time zone ID
  3331. *
  3332. * Previously this might not have worked correctly if your system did
  3333. * not allow putenv() or if localtime() does not work in your
  3334. * environment, but this implementation is no longer used.
  3335. *
  3336. * @param string $ps_id a valid time zone id, e.g. 'Europe/London'
  3337. *
  3338. * @return void
  3339. * @access public
  3340. * @see Date::setTZByID(), Date_TimeZone::isValidID(),
  3341. * Date_TimeZone::Date_TimeZone()
  3342. */
  3343. function convertTZByID($ps_id)
  3344. {
  3345. if (!Date_TimeZone::isValidID($ps_id)) {
  3346. return PEAR::raiseError("Invalid time zone ID '$ps_id'",
  3347. DATE_ERROR_INVALIDTIMEZONE);
  3348. }
  3349. $res = $this->convertTZ(new Date_TimeZone($ps_id));
  3350. if (PEAR::isError($res))
  3351. return $res;
  3352. }
  3353. // }}}
  3354. // {{{ toUTCbyOffset()
  3355. /**
  3356. * Converts the date/time to UTC by the offset specified
  3357. *
  3358. * This function is no longer called from within the Date class
  3359. * itself because a time zone can be set using a pure offset
  3360. * (e.g. UTC+1), i.e. not a geographical time zone. However
  3361. * it is retained for backwards compaibility.
  3362. *
  3363. * @param string $ps_offset offset of the form '[+/-][hh]:[mm]',
  3364. * '[+/-][hh][mm]', or 'Z'
  3365. *
  3366. * @return bool
  3367. * @access private
  3368. */
  3369. function toUTCbyOffset($ps_offset)
  3370. {
  3371. if ($ps_offset == "Z" ||
  3372. preg_match('/^[+\-](00:?00|0{1,2})$/', $ps_offset)) {
  3373. $hs_tzid = "UTC";
  3374. } else if (preg_match('/^[+\-]([0-9]{2,2}:?[0-5][0-9]|[0-9]{1,2})$/',
  3375. $ps_offset)) {
  3376. $hs_tzid = "UTC" . $ps_offset;
  3377. } else {
  3378. return PEAR::raiseError("Invalid offset '$ps_offset'");
  3379. }
  3380. // If the time is invalid, it does not matter here:
  3381. //
  3382. $this->setTZByID($hs_tzid);
  3383. // Now the time will be valid because it is a time zone that
  3384. // does not observe Summer time:
  3385. //
  3386. $this->toUTC();
  3387. }
  3388. // }}}
  3389. // {{{ addYears()
  3390. /**
  3391. * Converts the date to the specified no of years from the given date
  3392. *
  3393. * To subtract years use a negative value for the '$pn_years'
  3394. * parameter
  3395. *
  3396. * @param int $pn_years years to add
  3397. *
  3398. * @return void
  3399. * @access public
  3400. * @since Method available since Release 1.5.0
  3401. */
  3402. function addYears($pn_years)
  3403. {
  3404. list($hs_year, $hs_month, $hs_day) =
  3405. explode(" ", Date_Calc::addYears($pn_years,
  3406. $this->day,
  3407. $this->month,
  3408. $this->year,
  3409. "%Y %m %d"));
  3410. $this->setLocalTime($hs_day,
  3411. $hs_month,
  3412. $hs_year,
  3413. $this->hour,
  3414. $this->minute,
  3415. $this->second,
  3416. $this->partsecond);
  3417. }
  3418. // }}}
  3419. // {{{ addMonths()
  3420. /**
  3421. * Converts the date to the specified no of months from the given date
  3422. *
  3423. * To subtract months use a negative value for the '$pn_months'
  3424. * parameter
  3425. *
  3426. * @param int $pn_months months to add
  3427. *
  3428. * @return void
  3429. * @access public
  3430. * @since Method available since Release 1.5.0
  3431. */
  3432. function addMonths($pn_months)
  3433. {
  3434. list($hs_year, $hs_month, $hs_day) =
  3435. explode(" ", Date_Calc::addMonths($pn_months,
  3436. $this->day,
  3437. $this->month,
  3438. $this->year,
  3439. "%Y %m %d"));
  3440. $this->setLocalTime($hs_day,
  3441. $hs_month,
  3442. $hs_year,
  3443. $this->hour,
  3444. $this->minute,
  3445. $this->second,
  3446. $this->partsecond);
  3447. }
  3448. // }}}
  3449. // {{{ addDays()
  3450. /**
  3451. * Converts the date to the specified no of days from the given date
  3452. *
  3453. * To subtract days use a negative value for the '$pn_days' parameter
  3454. *
  3455. * @param int $pn_days days to add
  3456. *
  3457. * @return void
  3458. * @access public
  3459. * @since Method available since Release 1.5.0
  3460. */
  3461. function addDays($pn_days)
  3462. {
  3463. list($hs_year, $hs_month, $hs_day) =
  3464. explode(" ", Date_Calc::addDays($pn_days,
  3465. $this->day,
  3466. $this->month,
  3467. $this->year,
  3468. "%Y %m %d"));
  3469. $this->setLocalTime($hs_day,
  3470. $hs_month,
  3471. $hs_year,
  3472. $this->hour,
  3473. $this->minute,
  3474. $this->second,
  3475. $this->partsecond);
  3476. }
  3477. // }}}
  3478. // {{{ addHours()
  3479. /**
  3480. * Converts the date to the specified no of hours from the given date
  3481. *
  3482. * To subtract hours use a negative value for the '$pn_hours' parameter
  3483. *
  3484. * @param int $pn_hours hours to add
  3485. *
  3486. * @return void
  3487. * @access public
  3488. * @since Method available since Release 1.5.0
  3489. */
  3490. function addHours($pn_hours)
  3491. {
  3492. if ($this->ob_invalidtime)
  3493. return $this->_getErrorInvalidTime();
  3494. list($hn_standardyear,
  3495. $hn_standardmonth,
  3496. $hn_standardday,
  3497. $hn_standardhour) =
  3498. Date_Calc::addHours($pn_hours,
  3499. $this->on_standardday,
  3500. $this->on_standardmonth,
  3501. $this->on_standardyear,
  3502. $this->on_standardhour);
  3503. $this->setStandardTime($hn_standardday,
  3504. $hn_standardmonth,
  3505. $hn_standardyear,
  3506. $hn_standardhour,
  3507. $this->on_standardminute,
  3508. $this->on_standardsecond,
  3509. $this->on_standardpartsecond);
  3510. }
  3511. // }}}
  3512. // {{{ addMinutes()
  3513. /**
  3514. * Converts the date to the specified no of minutes from the given date
  3515. *
  3516. * To subtract minutes use a negative value for the '$pn_minutes' parameter
  3517. *
  3518. * @param int $pn_minutes minutes to add
  3519. *
  3520. * @return void
  3521. * @access public
  3522. * @since Method available since Release 1.5.0
  3523. */
  3524. function addMinutes($pn_minutes)
  3525. {
  3526. if ($this->ob_invalidtime)
  3527. return $this->_getErrorInvalidTime();
  3528. list($hn_standardyear,
  3529. $hn_standardmonth,
  3530. $hn_standardday,
  3531. $hn_standardhour,
  3532. $hn_standardminute) =
  3533. Date_Calc::addMinutes($pn_minutes,
  3534. $this->on_standardday,
  3535. $this->on_standardmonth,
  3536. $this->on_standardyear,
  3537. $this->on_standardhour,
  3538. $this->on_standardminute);
  3539. $this->setStandardTime($hn_standardday,
  3540. $hn_standardmonth,
  3541. $hn_standardyear,
  3542. $hn_standardhour,
  3543. $hn_standardminute,
  3544. $this->on_standardsecond,
  3545. $this->on_standardpartsecond);
  3546. }
  3547. // }}}
  3548. // {{{ addSeconds()
  3549. /**
  3550. * Adds a given number of seconds to the date
  3551. *
  3552. * @param mixed $sec the no of seconds to add as integer or float
  3553. * @param bool $pb_countleap whether to count leap seconds (defaults to
  3554. * value of count-leap-second object property)
  3555. *
  3556. * @return void
  3557. * @access public
  3558. */
  3559. function addSeconds($sec, $pb_countleap = null)
  3560. {
  3561. if ($this->ob_invalidtime)
  3562. return $this->_getErrorInvalidTime();
  3563. if (!is_int($sec) && !is_float($sec))
  3564. settype($sec, 'int');
  3565. if (!is_null($pb_countleap))
  3566. $pb_countleap = $this->ob_countleapseconds;
  3567. if ($pb_countleap) {
  3568. // Convert to UTC:
  3569. //
  3570. list($hn_standardyear,
  3571. $hn_standardmonth,
  3572. $hn_standardday,
  3573. $hn_standardhour,
  3574. $hn_standardminute,
  3575. $hn_standardsecond,
  3576. $hn_standardpartsecond) =
  3577. $this->_addOffset($this->tz->getRawOffset() * -1,
  3578. $this->on_standardday,
  3579. $this->on_standardmonth,
  3580. $this->on_standardyear,
  3581. $this->on_standardhour,
  3582. $this->on_standardminute,
  3583. $this->on_standardsecond,
  3584. $this->on_standardpartsecond);
  3585. list($hn_standardyear,
  3586. $hn_standardmonth,
  3587. $hn_standardday,
  3588. $hn_standardhour,
  3589. $hn_standardminute,
  3590. $hn_secondraw) =
  3591. Date_Calc::addSeconds($sec,
  3592. $hn_standardday,
  3593. $hn_standardmonth,
  3594. $hn_standardyear,
  3595. $hn_standardhour,
  3596. $hn_standardminute,
  3597. $hn_standardpartsecond == 0.0 ?
  3598. $hn_standardsecond :
  3599. $hn_standardsecond +
  3600. $hn_standardpartsecond,
  3601. $pb_countleap);
  3602. if (is_float($hn_secondraw)) {
  3603. $hn_standardsecond = intval($hn_secondraw);
  3604. $hn_standardpartsecond = $hn_secondraw - $hn_standardsecond;
  3605. } else {
  3606. $hn_standardsecond = $hn_secondraw;
  3607. $hn_standardpartsecond = 0.0;
  3608. }
  3609. list($hn_standardyear,
  3610. $hn_standardmonth,
  3611. $hn_standardday,
  3612. $hn_standardhour,
  3613. $hn_standardminute,
  3614. $hn_standardsecond,
  3615. $hn_standardpartsecond) =
  3616. $this->_addOffset($this->tz->getRawOffset(),
  3617. $hn_standardday,
  3618. $hn_standardmonth,
  3619. $hn_standardyear,
  3620. $hn_standardhour,
  3621. $hn_standardminute,
  3622. $hn_standardsecond,
  3623. $hn_standardpartsecond);
  3624. } else {
  3625. // Use local standard time:
  3626. //
  3627. list($hn_standardyear,
  3628. $hn_standardmonth,
  3629. $hn_standardday,
  3630. $hn_standardhour,
  3631. $hn_standardminute,
  3632. $hn_secondraw) =
  3633. Date_Calc::addSeconds($sec,
  3634. $this->on_standardday,
  3635. $this->on_standardmonth,
  3636. $this->on_standardyear,
  3637. $this->on_standardhour,
  3638. $this->on_standardminute,
  3639. $this->on_standardpartsecond == 0.0 ?
  3640. $this->on_standardsecond :
  3641. $this->on_standardsecond +
  3642. $this->on_standardpartsecond,
  3643. false);
  3644. if (is_float($hn_secondraw)) {
  3645. $hn_standardsecond = intval($hn_secondraw);
  3646. $hn_standardpartsecond = $hn_secondraw - $hn_standardsecond;
  3647. } else {
  3648. $hn_standardsecond = $hn_secondraw;
  3649. $hn_standardpartsecond = 0.0;
  3650. }
  3651. }
  3652. $this->setStandardTime($hn_standardday,
  3653. $hn_standardmonth,
  3654. $hn_standardyear,
  3655. $hn_standardhour,
  3656. $hn_standardminute,
  3657. $hn_standardsecond,
  3658. $hn_standardpartsecond);
  3659. }
  3660. // }}}
  3661. // {{{ subtractSeconds()
  3662. /**
  3663. * Subtracts a given number of seconds from the date
  3664. *
  3665. * @param mixed $sec the no of seconds to subtract as integer or
  3666. * float
  3667. * @param bool $pb_countleap whether to count leap seconds (defaults to
  3668. * value of count-leap-second object property)
  3669. *
  3670. * @return void
  3671. * @access public
  3672. */
  3673. function subtractSeconds($sec, $pb_countleap = null)
  3674. {
  3675. if (is_null($pb_countleap))
  3676. $pb_countleap = $this->ob_countleapseconds;
  3677. $res = $this->addSeconds(-$sec, $pb_countleap);
  3678. if (PEAR::isError($res))
  3679. return $res;
  3680. }
  3681. // }}}
  3682. // {{{ addSpan()
  3683. /**
  3684. * Adds a time span to the date
  3685. *
  3686. * A time span is defined as a unsigned no of days, hours, minutes
  3687. * and seconds, where the no of minutes and seconds must be less than
  3688. * 60, and the no of hours must be less than 24.
  3689. *
  3690. * A span is added (and subtracted) according to the following logic:
  3691. *
  3692. * Hours, minutes and seconds are added such that if they fall over
  3693. * a leap second, the leap second is ignored, and not counted.
  3694. * For example, if a leap second occurred at 23.59.60, the
  3695. * following calculations:
  3696. *
  3697. * 23.59.59 + one second
  3698. * 23.59.00 + one minute
  3699. * 23.00.00 + one hour
  3700. *
  3701. * would all produce 00.00.00 the next day.
  3702. *
  3703. * A day is treated as equivalent to 24 hours, so if the clocks
  3704. * went backwards at 01.00, and one day was added to the time
  3705. * 00.30, the result would be 23.30 the same day.
  3706. *
  3707. * This is the implementation which is thought to yield the behaviour
  3708. * that the user is most likely to expect, or in another way of
  3709. * looking at it, it is the implementation that produces the least
  3710. * unexpected behaviour. It basically works in hours, that is, a day
  3711. * is treated as exactly equivalent to 24 hours, and minutes and
  3712. * seconds are treated as equivalent to 1/60th and 1/3600th of an
  3713. * hour. It should be obvious that working in days is impractical;
  3714. * working in seconds is problematic when it comes to adding days
  3715. * that fall over leap seconds, where it would appear to most users
  3716. * that the function adds only 23 hours, 59 minutes and 59 seconds.
  3717. * It is also problematic to work in any kind of mixture of days,
  3718. * hours, minutes, and seconds, because then the addition of a span
  3719. * would sometimes depend on which order you add the constituent
  3720. * parts, which undermines the concept of a span altogether.
  3721. *
  3722. * If you want alternative functionality, you must use a mixture of
  3723. * the following functions instead:
  3724. *
  3725. * addYears()
  3726. * addMonths()
  3727. * addDays()
  3728. * addHours()
  3729. * addMinutes()
  3730. * addSeconds()
  3731. *
  3732. * @param object $span the time span to add
  3733. *
  3734. * @return void
  3735. * @access public
  3736. */
  3737. function addSpan($span)
  3738. {
  3739. if (!is_a($span, 'Date_Span')) {
  3740. return PEAR::raiseError("Invalid argument - not 'Date_Span' object");
  3741. } else if ($this->ob_invalidtime) {
  3742. return $this->_getErrorInvalidTime();
  3743. }
  3744. $hn_days = $span->day;
  3745. $hn_standardhour = $this->on_standardhour + $span->hour;
  3746. $hn_standardminute = $this->on_standardminute + $span->minute;
  3747. $hn_standardsecond = $this->on_standardsecond + $span->second;
  3748. if ($hn_standardsecond >= 60) {
  3749. ++$hn_standardminute;
  3750. $hn_standardsecond -= 60;
  3751. }
  3752. if ($hn_standardminute >= 60) {
  3753. ++$hn_standardhour;
  3754. $hn_standardminute -= 60;
  3755. }
  3756. if ($hn_standardhour >= 24) {
  3757. ++$hn_days;
  3758. $hn_standardhour -= 24;
  3759. }
  3760. list($hn_standardyear, $hn_standardmonth, $hn_standardday) =
  3761. explode(" ",
  3762. Date_Calc::addDays($hn_days,
  3763. $this->on_standardday,
  3764. $this->on_standardmonth,
  3765. $this->on_standardyear,
  3766. "%Y %m %d"));
  3767. $this->setStandardTime($hn_standardday,
  3768. $hn_standardmonth,
  3769. $hn_standardyear,
  3770. $hn_standardhour,
  3771. $hn_standardminute,
  3772. $hn_standardsecond,
  3773. $this->on_standardpartsecond);
  3774. }
  3775. // }}}
  3776. // {{{ subtractSpan()
  3777. /**
  3778. * Subtracts a time span from the date
  3779. *
  3780. * N.B. it is impossible for this function to count leap seconds,
  3781. * because the result would be dependent on which order the consituent
  3782. * parts of the span are subtracted from the date. Therefore, leap
  3783. * seconds are ignored by this function. If you want to count leap
  3784. * seconds, use 'subtractSeconds()'.
  3785. *
  3786. * @param object $span the time span to subtract
  3787. *
  3788. * @return void
  3789. * @access public
  3790. */
  3791. function subtractSpan($span)
  3792. {
  3793. if (!is_a($span, 'Date_Span')) {
  3794. return PEAR::raiseError("Invalid argument - not 'Date_Span' object");
  3795. } else if ($this->ob_invalidtime) {
  3796. return $this->_getErrorInvalidTime();
  3797. }
  3798. $hn_days = -$span->day;
  3799. $hn_standardhour = $this->on_standardhour - $span->hour;
  3800. $hn_standardminute = $this->on_standardminute - $span->minute;
  3801. $hn_standardsecond = $this->on_standardsecond - $span->second;
  3802. if ($hn_standardsecond < 0) {
  3803. --$hn_standardminute;
  3804. $hn_standardsecond += 60;
  3805. }
  3806. if ($hn_standardminute < 0) {
  3807. --$hn_standardhour;
  3808. $hn_standardminute += 60;
  3809. }
  3810. if ($hn_standardhour < 0) {
  3811. --$hn_days;
  3812. $hn_standardhour += 24;
  3813. }
  3814. list($hn_standardyear, $hn_standardmonth, $hn_standardday) =
  3815. explode(" ",
  3816. Date_Calc::addDays($hn_days,
  3817. $this->on_standardday,
  3818. $this->on_standardmonth,
  3819. $this->on_standardyear,
  3820. "%Y %m %d"));
  3821. $this->setStandardTime($hn_standardday,
  3822. $hn_standardmonth,
  3823. $hn_standardyear,
  3824. $hn_standardhour,
  3825. $hn_standardminute,
  3826. $hn_standardsecond,
  3827. $this->on_standardpartsecond);
  3828. }
  3829. // }}}
  3830. // {{{ dateDiff()
  3831. /**
  3832. * Subtract supplied date and return answer in days
  3833. *
  3834. * If the second parameter '$pb_ignoretime' is specified as false, the time
  3835. * parts of the two dates will be ignored, and the integral no of days
  3836. * between the day-month-year parts of the two dates will be returned. If
  3837. * either of the two dates have an invalid time, the integral no of days
  3838. * will also be returned, else the returned value will be the no of days as
  3839. * a float, with each hour being treated as 1/24th of a day and so on.
  3840. *
  3841. * For example,
  3842. * 21/11/2007 13.00 minus 21/11/2007 01.00
  3843. * returns 0.5
  3844. *
  3845. * Note that if the passed date is in the past, a positive value will be
  3846. * returned, and if it is in the future, a negative value will be returned.
  3847. *
  3848. * @param object $po_date date to subtract
  3849. * @param bool $pb_ignoretime whether to ignore the time values of the two
  3850. * dates in subtraction (defaults to false)
  3851. *
  3852. * @return mixed days between two dates as int or float
  3853. * @access public
  3854. * @since Method available since Release 1.5.0
  3855. */
  3856. function dateDiff($po_date, $pb_ignoretime = false)
  3857. {
  3858. if ($pb_ignoretime || $this->ob_invalidtime) {
  3859. return Date_Calc::dateToDays($this->day,
  3860. $this->month,
  3861. $this->year) -
  3862. Date_Calc::dateToDays($po_date->getDay(),
  3863. $po_date->getMonth(),
  3864. $po_date->getYear());
  3865. }
  3866. $hn_secondscompare = $po_date->getStandardSecondsPastMidnight();
  3867. if (PEAR::isError($hn_secondscompare)) {
  3868. if ($hn_secondscompare->getCode() != DATE_ERROR_INVALIDTIME) {
  3869. return $hn_secondscompare;
  3870. }
  3871. return Date_Calc::dateToDays($this->day,
  3872. $this->month,
  3873. $this->year) -
  3874. Date_Calc::dateToDays($po_date->getDay(),
  3875. $po_date->getMonth(),
  3876. $po_date->getYear());
  3877. }
  3878. $hn_seconds = $this->getStandardSecondsPastMidnight();
  3879. // If time parts are equal, return int, else return float:
  3880. //
  3881. return Date_Calc::dateToDays($this->on_standardday,
  3882. $this->on_standardmonth,
  3883. $this->on_standardyear) -
  3884. Date_Calc::dateToDays($po_date->getStandardDay(),
  3885. $po_date->getStandardMonth(),
  3886. $po_date->getStandardYear()) +
  3887. ($hn_seconds == $hn_secondscompare ? 0 :
  3888. ($hn_seconds - $hn_secondscompare) / 86400);
  3889. }
  3890. // }}}
  3891. // {{{ inEquivalentTimeZones()
  3892. /**
  3893. * Tests whether two dates are in equivalent time zones
  3894. *
  3895. * Equivalence in this context consists in the time zones of the two dates
  3896. * having:
  3897. *
  3898. * an equal offset from UTC in both standard and Summer time (if
  3899. * the time zones observe Summer time)
  3900. * the same Summer time start and end rules, that is, the two time zones
  3901. * must switch from standard time to Summer time, and vice versa, on the
  3902. * same day and at the same time
  3903. *
  3904. * An example of two equivalent time zones is 'Europe/London' and
  3905. * 'Europe/Lisbon', which in London is known as GMT/BST, and in Lisbon as
  3906. * WET/WEST.
  3907. *
  3908. * @param object $po_date1 the first Date object to compare
  3909. * @param object $po_date2 the second Date object to compare
  3910. *
  3911. * @return bool true if the time zones are equivalent
  3912. * @access public
  3913. * @static
  3914. * @since Method available since Release 1.5.0
  3915. */
  3916. function inEquivalentTimeZones($po_date1, $po_date2)
  3917. {
  3918. return $po_date1->tz->isEquivalent($po_date2->getTZID());
  3919. }
  3920. // }}}
  3921. // {{{ compare()
  3922. /**
  3923. * Compares two dates
  3924. *
  3925. * Suitable for use in sorting functions.
  3926. *
  3927. * @param object $od1 the first Date object to compare
  3928. * @param object $od2 the second Date object to compare
  3929. *
  3930. * @return int 0 if the dates are equal, -1 if '$od1' is
  3931. * before '$od2', 1 if '$od1' is after '$od2'
  3932. * @access public
  3933. * @static
  3934. */
  3935. function compare($od1, $od2)
  3936. {
  3937. $d1 = new Date($od1);
  3938. $d2 = new Date($od2);
  3939. // If the time zones are equivalent, do nothing:
  3940. //
  3941. if (!Date::inEquivalentTimeZones($d1, $d2)) {
  3942. // Only a time zone with a valid time can be converted:
  3943. //
  3944. if ($d2->isTimeValid()) {
  3945. $d2->convertTZByID($d1->getTZID());
  3946. } else if ($d1->isTimeValid()) {
  3947. $d1->convertTZByID($d2->getTZID());
  3948. } else {
  3949. // No comparison can be made without guessing the time:
  3950. //
  3951. return PEAR::raiseError("Both dates have invalid time",
  3952. DATE_ERROR_INVALIDTIME);
  3953. }
  3954. }
  3955. $days1 = Date_Calc::dateToDays($d1->getDay(),
  3956. $d1->getMonth(),
  3957. $d1->getYear());
  3958. $days2 = Date_Calc::dateToDays($d2->getDay(),
  3959. $d2->getMonth(),
  3960. $d2->getYear());
  3961. if ($days1 < $days2)
  3962. return -1;
  3963. if ($days1 > $days2)
  3964. return 1;
  3965. $hn_hour1 = $d1->getStandardHour();
  3966. if (PEAR::isError($hn_hour1))
  3967. return $hn_hour1;
  3968. $hn_hour2 = $d2->getStandardHour();
  3969. if (PEAR::isError($hn_hour2))
  3970. return $hn_hour2;
  3971. if ($hn_hour1 < $hn_hour2) return -1;
  3972. if ($hn_hour1 > $hn_hour2) return 1;
  3973. if ($d1->getStandardMinute() < $d2->getStandardMinute()) return -1;
  3974. if ($d1->getStandardMinute() > $d2->getStandardMinute()) return 1;
  3975. if ($d1->getStandardSecond() < $d2->getStandardSecond()) return -1;
  3976. if ($d1->getStandardSecond() > $d2->getStandardSecond()) return 1;
  3977. if ($d1->getStandardPartSecond() < $d2->getStandardPartSecond()) return -1;
  3978. if ($d1->getStandardPartSecond() > $d2->getStandardPartSecond()) return 1;
  3979. return 0;
  3980. }
  3981. // }}}
  3982. // {{{ before()
  3983. /**
  3984. * Test if this date/time is before a certain date/time
  3985. *
  3986. * @param object $when the Date object to test against
  3987. *
  3988. * @return boolean true if this date is before $when
  3989. * @access public
  3990. */
  3991. function before($when)
  3992. {
  3993. $hn_compare = Date::compare($this, $when);
  3994. if (PEAR::isError($hn_compare))
  3995. return $hn_compare;
  3996. if ($hn_compare == -1) {
  3997. return true;
  3998. } else {
  3999. return false;
  4000. }
  4001. }
  4002. // }}}
  4003. // {{{ after()
  4004. /**
  4005. * Test if this date/time is after a certain date/time
  4006. *
  4007. * @param object $when the Date object to test against
  4008. *
  4009. * @return boolean true if this date is after $when
  4010. * @access public
  4011. */
  4012. function after($when)
  4013. {
  4014. $hn_compare = Date::compare($this, $when);
  4015. if (PEAR::isError($hn_compare))
  4016. return $hn_compare;
  4017. if ($hn_compare == 1) {
  4018. return true;
  4019. } else {
  4020. return false;
  4021. }
  4022. }
  4023. // }}}
  4024. // {{{ equals()
  4025. /**
  4026. * Test if this date/time is exactly equal to a certain date/time
  4027. *
  4028. * @param object $when the Date object to test against
  4029. *
  4030. * @return boolean true if this date is exactly equal to $when
  4031. * @access public
  4032. */
  4033. function equals($when)
  4034. {
  4035. $hn_compare = Date::compare($this, $when);
  4036. if (PEAR::isError($hn_compare))
  4037. return $hn_compare;
  4038. if ($hn_compare == 0) {
  4039. return true;
  4040. } else {
  4041. return false;
  4042. }
  4043. }
  4044. // }}}
  4045. // {{{ isFuture()
  4046. /**
  4047. * Determine if this date is in the future
  4048. *
  4049. * @return boolean true if this date is in the future
  4050. * @access public
  4051. */
  4052. function isFuture()
  4053. {
  4054. $now = new Date();
  4055. return $this->after($now);
  4056. }
  4057. // }}}
  4058. // {{{ isPast()
  4059. /**
  4060. * Determine if this date is in the past
  4061. *
  4062. * @return boolean true if this date is in the past
  4063. * @access public
  4064. */
  4065. function isPast()
  4066. {
  4067. $now = new Date();
  4068. return $this->before($now);
  4069. }
  4070. // }}}
  4071. // {{{ isLeapYear()
  4072. /**
  4073. * Determine if the year in this date is a leap year
  4074. *
  4075. * @return boolean true if this year is a leap year
  4076. * @access public
  4077. */
  4078. function isLeapYear()
  4079. {
  4080. return Date_Calc::isLeapYear($this->year);
  4081. }
  4082. // }}}
  4083. // {{{ getJulianDate()
  4084. /**
  4085. * Returns the no of days (1-366) since 31st December of the previous year
  4086. *
  4087. * N.B. this function does not return (and never has returned) the 'Julian
  4088. * Date', as described, for example, at:
  4089. *
  4090. * http://en.wikipedia.org/wiki/Julian_day
  4091. *
  4092. * If you want the day of the year (0-366), use 'getDayOfYear()' instead.
  4093. * If you want the true Julian Day, call one of the following:
  4094. *
  4095. * <code>format("%E")</code>
  4096. * <code>format2("J")</code>
  4097. *
  4098. * There currently is no function that calls the Julian Date (as opposed
  4099. * to the 'Julian Day'), although the Julian Day is an approximation.
  4100. *
  4101. * @return int the Julian date
  4102. * @access public
  4103. * @see Date::getDayOfYear()
  4104. * @deprecated Method deprecated in Release 1.5.0
  4105. */
  4106. function getJulianDate()
  4107. {
  4108. return Date_Calc::julianDate($this->day, $this->month, $this->year);
  4109. }
  4110. // }}}
  4111. // {{{ getDayOfYear()
  4112. /**
  4113. * Returns the no of days (1-366) since 31st December of the previous year
  4114. *
  4115. * @return int an integer between 1 and 366
  4116. * @access public
  4117. * @since Method available since Release 1.5.0
  4118. */
  4119. function getDayOfYear()
  4120. {
  4121. return Date_Calc::dayOfYear($this->day, $this->month, $this->year);
  4122. }
  4123. // }}}
  4124. // {{{ getDayOfWeek()
  4125. /**
  4126. * Gets the day of the week for this date (0 = Sunday)
  4127. *
  4128. * @return int the day of the week (0 = Sunday)
  4129. * @access public
  4130. */
  4131. function getDayOfWeek()
  4132. {
  4133. return Date_Calc::dayOfWeek($this->day, $this->month, $this->year);
  4134. }
  4135. // }}}
  4136. // {{{ getWeekOfYear()
  4137. /**
  4138. * Gets the week of the year for this date
  4139. *
  4140. * @return int the week of the year
  4141. * @access public
  4142. */
  4143. function getWeekOfYear()
  4144. {
  4145. return Date_Calc::weekOfYear($this->day, $this->month, $this->year);
  4146. }
  4147. // }}}
  4148. // {{{ getQuarterOfYear()
  4149. /**
  4150. * Gets the quarter of the year for this date
  4151. *
  4152. * @return int the quarter of the year (1-4)
  4153. * @access public
  4154. */
  4155. function getQuarterOfYear()
  4156. {
  4157. return Date_Calc::quarterOfYear($this->day, $this->month, $this->year);
  4158. }
  4159. // }}}
  4160. // {{{ getDaysInMonth()
  4161. /**
  4162. * Gets number of days in the month for this date
  4163. *
  4164. * @return int number of days in this month
  4165. * @access public
  4166. */
  4167. function getDaysInMonth()
  4168. {
  4169. return Date_Calc::daysInMonth($this->month, $this->year);
  4170. }
  4171. // }}}
  4172. // {{{ getWeeksInMonth()
  4173. /**
  4174. * Gets the number of weeks in the month for this date
  4175. *
  4176. * @return int number of weeks in this month
  4177. * @access public
  4178. */
  4179. function getWeeksInMonth()
  4180. {
  4181. return Date_Calc::weeksInMonth($this->month, $this->year);
  4182. }
  4183. // }}}
  4184. // {{{ getDayName()
  4185. /**
  4186. * Gets the full name or abbreviated name of this weekday
  4187. *
  4188. * @param bool $abbr abbreviate the name
  4189. * @param int $length length of abbreviation
  4190. *
  4191. * @return string name of this day
  4192. * @access public
  4193. */
  4194. function getDayName($abbr = false, $length = 3)
  4195. {
  4196. if ($abbr) {
  4197. return Date_Calc::getWeekdayAbbrname($this->day,
  4198. $this->month,
  4199. $this->year,
  4200. $length);
  4201. } else {
  4202. return Date_Calc::getWeekdayFullname($this->day,
  4203. $this->month,
  4204. $this->year);
  4205. }
  4206. }
  4207. // }}}
  4208. // {{{ getMonthName()
  4209. /**
  4210. * Gets the full name or abbreviated name of this month
  4211. *
  4212. * @param boolean $abbr abbreviate the name
  4213. *
  4214. * @return string name of this month
  4215. * @access public
  4216. */
  4217. function getMonthName($abbr = false)
  4218. {
  4219. if ($abbr) {
  4220. return Date_Calc::getMonthAbbrname($this->month);
  4221. } else {
  4222. return Date_Calc::getMonthFullname($this->month);
  4223. }
  4224. }
  4225. // }}}
  4226. // {{{ getNextDay()
  4227. /**
  4228. * Get a Date object for the day after this one
  4229. *
  4230. * The time of the returned Date object is the same as this time.
  4231. *
  4232. * @return object Date object representing the next day
  4233. * @access public
  4234. */
  4235. function getNextDay()
  4236. {
  4237. $ret = new Date($this);
  4238. $ret->addDays(1);
  4239. return $ret;
  4240. }
  4241. // }}}
  4242. // {{{ getPrevDay()
  4243. /**
  4244. * Get a Date object for the day before this one
  4245. *
  4246. * The time of the returned Date object is the same as this time.
  4247. *
  4248. * @return object Date object representing the previous day
  4249. * @access public
  4250. */
  4251. function getPrevDay()
  4252. {
  4253. $ret = new Date($this);
  4254. $ret->addDays(-1);
  4255. return $ret;
  4256. }
  4257. // }}}
  4258. // {{{ getNextWeekday()
  4259. /**
  4260. * Get a Date object for the weekday after this one
  4261. *
  4262. * The time of the returned Date object is the same as this time.
  4263. *
  4264. * @return object Date object representing the next week-day
  4265. * @access public
  4266. */
  4267. function getNextWeekday()
  4268. {
  4269. $ret = new Date($this);
  4270. list($hs_year, $hs_month, $hs_day) =
  4271. explode(" ", Date_Calc::nextWeekday($this->day,
  4272. $this->month,
  4273. $this->year,
  4274. "%Y %m %d"));
  4275. $ret->setDayMonthYear($hs_day, $hs_month, $hs_year);
  4276. return $ret;
  4277. }
  4278. // }}}
  4279. // {{{ getPrevWeekday()
  4280. /**
  4281. * Get a Date object for the weekday before this one
  4282. *
  4283. * The time of the returned Date object is the same as this time.
  4284. *
  4285. * @return object Date object representing the previous week-day
  4286. * @access public
  4287. */
  4288. function getPrevWeekday()
  4289. {
  4290. $ret = new Date($this);
  4291. list($hs_year, $hs_month, $hs_day) =
  4292. explode(" ", Date_Calc::prevWeekday($this->day,
  4293. $this->month,
  4294. $this->year,
  4295. "%Y %m %d"));
  4296. $ret->setDayMonthYear($hs_day, $hs_month, $hs_year);
  4297. return $ret;
  4298. }
  4299. // }}}
  4300. // {{{ getYear()
  4301. /**
  4302. * Returns the year field of the date object
  4303. *
  4304. * @return int the year
  4305. * @access public
  4306. */
  4307. function getYear()
  4308. {
  4309. return $this->year;
  4310. }
  4311. // }}}
  4312. // {{{ getMonth()
  4313. /**
  4314. * Returns the month field of the date object
  4315. *
  4316. * @return int the minute
  4317. * @access public
  4318. */
  4319. function getMonth()
  4320. {
  4321. return $this->month;
  4322. }
  4323. // }}}
  4324. // {{{ getDay()
  4325. /**
  4326. * Returns the day field of the date object
  4327. *
  4328. * @return int the day
  4329. * @access public
  4330. */
  4331. function getDay()
  4332. {
  4333. return $this->day;
  4334. }
  4335. // }}}
  4336. // {{{ _getErrorInvalidTime()
  4337. /**
  4338. * Returns invalid time PEAR Error
  4339. *
  4340. * @return object
  4341. * @access private
  4342. * @since Method available since Release 1.5.0
  4343. */
  4344. function _getErrorInvalidTime()
  4345. {
  4346. return PEAR::raiseError("Invalid time '" .
  4347. sprintf("%02d.%02d.%02d",
  4348. $this->hour,
  4349. $this->minute,
  4350. $this->second) .
  4351. "' specified for date '" .
  4352. Date_Calc::dateFormat($this->day,
  4353. $this->month,
  4354. $this->year,
  4355. "%Y-%m-%d") .
  4356. "' and in this timezone",
  4357. DATE_ERROR_INVALIDTIME);
  4358. }
  4359. // }}}
  4360. // {{{ _secondsInDayIsValid()
  4361. /**
  4362. * If leap seconds are observed, checks if the seconds in the day is valid
  4363. *
  4364. * Note that only the local standard time is accessed.
  4365. *
  4366. * @return bool
  4367. * @access private
  4368. * @since Method available since Release 1.5.0
  4369. */
  4370. function _secondsInDayIsValid()
  4371. {
  4372. if ($this->ob_countleapseconds) {
  4373. // Convert to UTC:
  4374. //
  4375. list($hn_year,
  4376. $hn_month,
  4377. $hn_day,
  4378. $hn_hour,
  4379. $hn_minute,
  4380. $hn_second,
  4381. $hn_partsecond) =
  4382. $this->_addOffset($this->tz->getRawOffset() * -1,
  4383. $this->on_standardday,
  4384. $this->on_standardmonth,
  4385. $this->on_standardyear,
  4386. $this->on_standardhour,
  4387. $this->on_standardminute,
  4388. $this->on_standardsecond,
  4389. $this->on_standardpartsecond);
  4390. return Date_Calc::secondsPastMidnight($hn_hour,
  4391. $hn_minute,
  4392. $hn_second +
  4393. $hn_partsecond) <
  4394. Date_Calc::getSecondsInDay($hn_day, $hn_month, $hn_year);
  4395. } else {
  4396. return $this->getStandardSecondsPastMidnight() < 86400;
  4397. }
  4398. }
  4399. // }}}
  4400. // {{{ isTimeValid()
  4401. /**
  4402. * Whether the stored time is valid as a local time
  4403. *
  4404. * An invalid time is one that lies in the 'skipped hour' at the point
  4405. * that the clocks go forward. Note that the stored date (i.e.
  4406. * the day/month/year, is always valid).
  4407. *
  4408. * The object is able to store an invalid time because a user might
  4409. * unwittingly and correctly store a valid time, and then add one day so
  4410. * as to put the object in the 'skipped' hour (when the clocks go forward).
  4411. * This could be corrected by a conversion to Summer time (by adding one
  4412. * hour); however, if the user then added another day, and had no need for
  4413. * or interest in the time anyway, the behaviour may be rather unexpected.
  4414. * And anyway in this situation, the time originally specified would now,
  4415. * two days on, be valid again.
  4416. *
  4417. * So this class allows an invalid time like this so long as the user does
  4418. * not in any way make use of or request the time while it is in this
  4419. * semi-invalid state, in order to allow for for the fact that he might be
  4420. * only interested in the date, and not the time, and in order not to behave
  4421. * in an unexpected way, especially without throwing an exception to tell
  4422. * the user about it.
  4423. *
  4424. * @return bool
  4425. * @access public
  4426. * @since Method available since Release 1.5.0
  4427. */
  4428. function isTimeValid()
  4429. {
  4430. return !$this->ob_invalidtime;
  4431. }
  4432. // }}}
  4433. // {{{ getHour()
  4434. /**
  4435. * Returns the hour field of the date object
  4436. *
  4437. * @return int the hour
  4438. * @access public
  4439. */
  4440. function getHour()
  4441. {
  4442. if ($this->ob_invalidtime)
  4443. return $this->_getErrorInvalidTime();
  4444. return $this->hour;
  4445. }
  4446. // }}}
  4447. // {{{ getMinute()
  4448. /**
  4449. * Returns the minute field of the date object
  4450. *
  4451. * @return int the minute
  4452. * @access public
  4453. */
  4454. function getMinute()
  4455. {
  4456. if ($this->ob_invalidtime)
  4457. return $this->_getErrorInvalidTime();
  4458. return $this->minute;
  4459. }
  4460. // }}}
  4461. // {{{ getSecond()
  4462. /**
  4463. * Returns the second field of the date object
  4464. *
  4465. * @return int the second
  4466. * @access public
  4467. */
  4468. function getSecond()
  4469. {
  4470. if ($this->ob_invalidtime)
  4471. return $this->_getErrorInvalidTime();
  4472. return $this->second;
  4473. }
  4474. // }}}
  4475. // {{{ getSecondsPastMidnight()
  4476. /**
  4477. * Returns the no of seconds since midnight (0-86400) as float
  4478. *
  4479. * @return float float which is at least 0 and less than 86400
  4480. * @access public
  4481. * @since Method available since Release 1.5.0
  4482. */
  4483. function getSecondsPastMidnight()
  4484. {
  4485. if ($this->ob_invalidtime)
  4486. return $this->_getErrorInvalidTime();
  4487. return Date_Calc::secondsPastMidnight($this->hour,
  4488. $this->minute,
  4489. $this->second) +
  4490. $this->partsecond;
  4491. }
  4492. // }}}
  4493. // {{{ getPartSecond()
  4494. /**
  4495. * Returns the part-second field of the date object
  4496. *
  4497. * @return float the part-second
  4498. * @access protected
  4499. * @since Method available since Release 1.5.0
  4500. */
  4501. function getPartSecond()
  4502. {
  4503. if ($this->ob_invalidtime)
  4504. return $this->_getErrorInvalidTime();
  4505. return $this->partsecond;
  4506. }
  4507. // }}}
  4508. // {{{ getStandardYear()
  4509. /**
  4510. * Returns the year field of the local standard time
  4511. *
  4512. * @return int the year
  4513. * @access public
  4514. * @since Method available since Release 1.5.0
  4515. */
  4516. function getStandardYear()
  4517. {
  4518. if ($this->ob_invalidtime)
  4519. return $this->_getErrorInvalidTime();
  4520. return $this->on_standardyear;
  4521. }
  4522. // }}}
  4523. // {{{ getStandardMonth()
  4524. /**
  4525. * Returns the month field of the local standard time
  4526. *
  4527. * @return int the minute
  4528. * @access public
  4529. * @since Method available since Release 1.5.0
  4530. */
  4531. function getStandardMonth()
  4532. {
  4533. if ($this->ob_invalidtime)
  4534. return $this->_getErrorInvalidTime();
  4535. return $this->on_standardmonth;
  4536. }
  4537. // }}}
  4538. // {{{ getStandardDay()
  4539. /**
  4540. * Returns the day field of the local standard time
  4541. *
  4542. * @return int the day
  4543. * @access public
  4544. * @since Method available since Release 1.5.0
  4545. */
  4546. function getStandardDay()
  4547. {
  4548. if ($this->ob_invalidtime)
  4549. return $this->_getErrorInvalidTime();
  4550. return $this->on_standardday;
  4551. }
  4552. // }}}
  4553. // {{{ getStandardHour()
  4554. /**
  4555. * Returns the hour field of the local standard time
  4556. *
  4557. * @return int the hour
  4558. * @access public
  4559. * @since Method available since Release 1.5.0
  4560. */
  4561. function getStandardHour()
  4562. {
  4563. if ($this->ob_invalidtime)
  4564. return $this->_getErrorInvalidTime();
  4565. return $this->on_standardhour;
  4566. }
  4567. // }}}
  4568. // {{{ getStandardMinute()
  4569. /**
  4570. * Returns the minute field of the local standard time
  4571. *
  4572. * @return int the minute
  4573. * @access public
  4574. * @since Method available since Release 1.5.0
  4575. */
  4576. function getStandardMinute()
  4577. {
  4578. if ($this->ob_invalidtime)
  4579. return $this->_getErrorInvalidTime();
  4580. return $this->on_standardminute;
  4581. }
  4582. // }}}
  4583. // {{{ getStandardSecond()
  4584. /**
  4585. * Returns the second field of the local standard time
  4586. *
  4587. * @return int the second
  4588. * @access public
  4589. * @since Method available since Release 1.5.0
  4590. */
  4591. function getStandardSecond()
  4592. {
  4593. if ($this->ob_invalidtime)
  4594. return $this->_getErrorInvalidTime();
  4595. return $this->on_standardsecond;
  4596. }
  4597. // }}}
  4598. // {{{ getStandardSecondsPastMidnight()
  4599. /**
  4600. * Returns the no of seconds since midnight (0-86400) of the
  4601. * local standard time as float
  4602. *
  4603. * @return float float which is at least 0 and less than 86400
  4604. * @access public
  4605. * @since Method available since Release 1.5.0
  4606. */
  4607. function getStandardSecondsPastMidnight()
  4608. {
  4609. if ($this->ob_invalidtime)
  4610. return $this->_getErrorInvalidTime();
  4611. return Date_Calc::secondsPastMidnight($this->on_standardhour,
  4612. $this->on_standardminute,
  4613. $this->on_standardsecond) +
  4614. $this->on_standardpartsecond;
  4615. }
  4616. // }}}
  4617. // {{{ getStandardPartSecond()
  4618. /**
  4619. * Returns the part-second field of the local standard time
  4620. *
  4621. * @return float the part-second
  4622. * @access protected
  4623. * @since Method available since Release 1.5.0
  4624. */
  4625. function getStandardPartSecond()
  4626. {
  4627. if ($this->ob_invalidtime)
  4628. return $this->_getErrorInvalidTime();
  4629. return $this->on_standardpartsecond;
  4630. }
  4631. // }}}
  4632. // {{{ _addOffset()
  4633. /**
  4634. * Add a time zone offset to the passed date/time
  4635. *
  4636. * @param int $pn_offset the offset to add in milliseconds
  4637. * @param int $pn_day the day
  4638. * @param int $pn_month the month
  4639. * @param int $pn_year the year
  4640. * @param int $pn_hour the hour
  4641. * @param int $pn_minute the minute
  4642. * @param int $pn_second the second
  4643. * @param float $pn_partsecond the part-second
  4644. *
  4645. * @return array array of year, month, day, hour, minute, second,
  4646. * and part-second
  4647. * @access private
  4648. * @static
  4649. * @since Method available since Release 1.5.0
  4650. */
  4651. function _addOffset($pn_offset,
  4652. $pn_day,
  4653. $pn_month,
  4654. $pn_year,
  4655. $pn_hour,
  4656. $pn_minute,
  4657. $pn_second,
  4658. $pn_partsecond)
  4659. {
  4660. if ($pn_offset == 0) {
  4661. return array((int) $pn_year,
  4662. (int) $pn_month,
  4663. (int) $pn_day,
  4664. (int) $pn_hour,
  4665. (int) $pn_minute,
  4666. (int) $pn_second,
  4667. (float) $pn_partsecond);
  4668. }
  4669. if ($pn_offset % 3600000 == 0) {
  4670. list($hn_year,
  4671. $hn_month,
  4672. $hn_day,
  4673. $hn_hour) =
  4674. Date_Calc::addHours($pn_offset / 3600000,
  4675. $pn_day,
  4676. $pn_month,
  4677. $pn_year,
  4678. $pn_hour);
  4679. $hn_minute = (int) $pn_minute;
  4680. $hn_second = (int) $pn_second;
  4681. $hn_partsecond = (float) $pn_partsecond;
  4682. } else if ($pn_offset % 60000 == 0) {
  4683. list($hn_year,
  4684. $hn_month,
  4685. $hn_day,
  4686. $hn_hour,
  4687. $hn_minute) =
  4688. Date_Calc::addMinutes($pn_offset / 60000,
  4689. $pn_day,
  4690. $pn_month,
  4691. $pn_year,
  4692. $pn_hour,
  4693. $pn_minute);
  4694. $hn_second = (int) $pn_second;
  4695. $hn_partsecond = (float) $pn_partsecond;
  4696. } else {
  4697. list($hn_year,
  4698. $hn_month,
  4699. $hn_day,
  4700. $hn_hour,
  4701. $hn_minute,
  4702. $hn_secondraw) =
  4703. Date_Calc::addSeconds($pn_offset / 1000,
  4704. $pn_day,
  4705. $pn_month,
  4706. $pn_year,
  4707. $pn_hour,
  4708. $pn_partsecond == 0.0 ?
  4709. $pn_second :
  4710. $pn_second + $pn_partsecond,
  4711. false); // N.B. do not count
  4712. // leap seconds
  4713. if (is_float($hn_secondraw)) {
  4714. $hn_second = intval($hn_secondraw);
  4715. $hn_partsecond = $hn_secondraw - $hn_second;
  4716. } else {
  4717. $hn_second = $hn_secondraw;
  4718. $hn_partsecond = 0.0;
  4719. }
  4720. }
  4721. return array($hn_year,
  4722. $hn_month,
  4723. $hn_day,
  4724. $hn_hour,
  4725. $hn_minute,
  4726. $hn_second,
  4727. $hn_partsecond);
  4728. }
  4729. // }}}
  4730. // {{{ setLocalTime()
  4731. /**
  4732. * Sets local time (Summer-time-adjusted) and then calculates local
  4733. * standard time
  4734. *
  4735. * @param int $pn_day the day
  4736. * @param int $pn_month the month
  4737. * @param int $pn_year the year
  4738. * @param int $pn_hour the hour
  4739. * @param int $pn_minute the minute
  4740. * @param int $pn_second the second
  4741. * @param float $pn_partsecond the part-second
  4742. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  4743. * repeated hour is specified (defaults
  4744. * to false)
  4745. * @param bool $pb_correctinvalidtime whether to correct, by adding the
  4746. * local Summer time offset, the
  4747. * specified time if it falls in the
  4748. * skipped hour (defaults to
  4749. * DATE_CORRECTINVALIDTIME_DEFAULT)
  4750. *
  4751. * @return void
  4752. * @access protected
  4753. * @see Date::setStandardTime()
  4754. * @since Method available since Release 1.5.0
  4755. */
  4756. function setLocalTime($pn_day,
  4757. $pn_month,
  4758. $pn_year,
  4759. $pn_hour,
  4760. $pn_minute,
  4761. $pn_second,
  4762. $pn_partsecond,
  4763. $pb_repeatedhourdefault = false,
  4764. $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT)
  4765. {
  4766. settype($pn_day, "int");
  4767. settype($pn_month, "int");
  4768. settype($pn_year, "int");
  4769. settype($pn_hour, "int");
  4770. settype($pn_minute, "int");
  4771. settype($pn_second, "int");
  4772. settype($pn_partsecond, "float");
  4773. $hb_insummertime =
  4774. $this->tz->inDaylightTime(array($pn_day,
  4775. $pn_month, $pn_year, Date_Calc::secondsPastMidnight($pn_hour,
  4776. $pn_minute, $pn_second) + $pn_partsecond),
  4777. $pb_repeatedhourdefault);
  4778. if (PEAR::isError($hb_insummertime)) {
  4779. if ($hb_insummertime->getCode() != DATE_ERROR_INVALIDTIME) {
  4780. return $hb_insummertime;
  4781. } else if ($pb_correctinvalidtime) {
  4782. // Store passed time as local standard time:
  4783. //
  4784. $this->on_standardday = $pn_day;
  4785. $this->on_standardmonth = $pn_month;
  4786. $this->on_standardyear = $pn_year;
  4787. $this->on_standardhour = $pn_hour;
  4788. $this->on_standardminute = $pn_minute;
  4789. $this->on_standardsecond = $pn_second;
  4790. $this->on_standardpartsecond = $pn_partsecond;
  4791. // Add Summer time offset to passed time:
  4792. //
  4793. list($this->year,
  4794. $this->month,
  4795. $this->day,
  4796. $this->hour,
  4797. $this->minute,
  4798. $this->second,
  4799. $this->partsecond) =
  4800. $this->_addOffset($this->tz->getDSTSavings(),
  4801. $pn_day,
  4802. $pn_month,
  4803. $pn_year,
  4804. $pn_hour,
  4805. $pn_minute,
  4806. $pn_second,
  4807. $pn_partsecond);
  4808. $this->ob_invalidtime = !$this->_secondsInDayIsValid();
  4809. } else {
  4810. // Hedge bets - if the user adds/subtracts a day, then the time
  4811. // will be uncorrupted, and if the user does
  4812. // addition/subtraction with the time, or requests the time,
  4813. // then return an error at that point:
  4814. //
  4815. $this->day = $pn_day;
  4816. $this->month = $pn_month;
  4817. $this->year = $pn_year;
  4818. $this->hour = $pn_hour;
  4819. $this->minute = $pn_minute;
  4820. $this->second = $pn_second;
  4821. $this->partsecond = $pn_partsecond;
  4822. $this->ob_invalidtime = true;
  4823. }
  4824. return;
  4825. } else {
  4826. // Passed time is valid as local time:
  4827. //
  4828. $this->day = $pn_day;
  4829. $this->month = $pn_month;
  4830. $this->year = $pn_year;
  4831. $this->hour = $pn_hour;
  4832. $this->minute = $pn_minute;
  4833. $this->second = $pn_second;
  4834. $this->partsecond = $pn_partsecond;
  4835. }
  4836. $this->ob_invalidtime = !$this->_secondsInDayIsValid();
  4837. if ($hb_insummertime) {
  4838. // Calculate local standard time:
  4839. //
  4840. list($this->on_standardyear,
  4841. $this->on_standardmonth,
  4842. $this->on_standardday,
  4843. $this->on_standardhour,
  4844. $this->on_standardminute,
  4845. $this->on_standardsecond,
  4846. $this->on_standardpartsecond) =
  4847. $this->_addOffset($this->tz->getDSTSavings() * -1,
  4848. $pn_day,
  4849. $pn_month,
  4850. $pn_year,
  4851. $pn_hour,
  4852. $pn_minute,
  4853. $pn_second,
  4854. $pn_partsecond);
  4855. } else {
  4856. // Time is already local standard time:
  4857. //
  4858. $this->on_standardday = $pn_day;
  4859. $this->on_standardmonth = $pn_month;
  4860. $this->on_standardyear = $pn_year;
  4861. $this->on_standardhour = $pn_hour;
  4862. $this->on_standardminute = $pn_minute;
  4863. $this->on_standardsecond = $pn_second;
  4864. $this->on_standardpartsecond = $pn_partsecond;
  4865. }
  4866. }
  4867. // }}}
  4868. // {{{ setStandardTime()
  4869. /**
  4870. * Sets local standard time and then calculates local time (i.e.
  4871. * Summer-time-adjusted)
  4872. *
  4873. * @param int $pn_day the day
  4874. * @param int $pn_month the month
  4875. * @param int $pn_year the year
  4876. * @param int $pn_hour the hour
  4877. * @param int $pn_minute the minute
  4878. * @param int $pn_second the second
  4879. * @param float $pn_partsecond the part-second
  4880. *
  4881. * @return void
  4882. * @access protected
  4883. * @see Date::setLocalTime()
  4884. * @since Method available since Release 1.5.0
  4885. */
  4886. function setStandardTime($pn_day,
  4887. $pn_month,
  4888. $pn_year,
  4889. $pn_hour,
  4890. $pn_minute,
  4891. $pn_second,
  4892. $pn_partsecond)
  4893. {
  4894. settype($pn_day, "int");
  4895. settype($pn_month, "int");
  4896. settype($pn_year, "int");
  4897. settype($pn_hour, "int");
  4898. settype($pn_minute, "int");
  4899. settype($pn_second, "int");
  4900. settype($pn_partsecond, "float");
  4901. $this->on_standardday = $pn_day;
  4902. $this->on_standardmonth = $pn_month;
  4903. $this->on_standardyear = $pn_year;
  4904. $this->on_standardhour = $pn_hour;
  4905. $this->on_standardminute = $pn_minute;
  4906. $this->on_standardsecond = $pn_second;
  4907. $this->on_standardpartsecond = $pn_partsecond;
  4908. $this->ob_invalidtime = !$this->_secondsInDayIsValid();
  4909. if ($this->tz->inDaylightTimeStandard(array($pn_day, $pn_month,
  4910. $pn_year, Date_Calc::secondsPastMidnight($pn_hour, $pn_minute,
  4911. $pn_second) + $pn_partsecond))) {
  4912. // Calculate local time:
  4913. //
  4914. list($this->year,
  4915. $this->month,
  4916. $this->day,
  4917. $this->hour,
  4918. $this->minute,
  4919. $this->second,
  4920. $this->partsecond) =
  4921. $this->_addOffset($this->tz->getDSTSavings(),
  4922. $pn_day,
  4923. $pn_month,
  4924. $pn_year,
  4925. $pn_hour,
  4926. $pn_minute,
  4927. $pn_second,
  4928. $pn_partsecond);
  4929. } else {
  4930. // Time is already local time:
  4931. //
  4932. $this->day = $pn_day;
  4933. $this->month = $pn_month;
  4934. $this->year = $pn_year;
  4935. $this->hour = $pn_hour;
  4936. $this->minute = $pn_minute;
  4937. $this->second = $pn_second;
  4938. $this->partsecond = $pn_partsecond;
  4939. }
  4940. }
  4941. // }}}
  4942. // {{{ setYear()
  4943. /**
  4944. * Sets the year field of the date object
  4945. *
  4946. * If specified year forms an invalid date, then PEAR error will be
  4947. * returned, unless the validation is over-ridden using the second
  4948. * parameter.
  4949. *
  4950. * @param int $y the year
  4951. * @param bool $pb_validate whether to check that the new date is valid
  4952. *
  4953. * @return void
  4954. * @access public
  4955. * @see Date::setDayMonthYear(), Date::setDateTime()
  4956. */
  4957. function setYear($y, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
  4958. {
  4959. if ($pb_validate && !Date_Calc::isValidDate($this->day, $this->month, $y)) {
  4960. return PEAR::raiseError("'" .
  4961. Date_Calc::dateFormat($this->day,
  4962. $this->month,
  4963. $y,
  4964. "%Y-%m-%d") .
  4965. "' is invalid calendar date",
  4966. DATE_ERROR_INVALIDDATE);
  4967. } else {
  4968. $this->setLocalTime($this->day,
  4969. $this->month,
  4970. $y,
  4971. $this->hour,
  4972. $this->minute,
  4973. $this->second,
  4974. $this->partsecond);
  4975. }
  4976. }
  4977. // }}}
  4978. // {{{ setMonth()
  4979. /**
  4980. * Sets the month field of the date object
  4981. *
  4982. * If specified year forms an invalid date, then PEAR error will be
  4983. * returned, unless the validation is over-ridden using the second
  4984. * parameter.
  4985. *
  4986. * @param int $m the month
  4987. * @param bool $pb_validate whether to check that the new date is valid
  4988. *
  4989. * @return void
  4990. * @access public
  4991. * @see Date::setDayMonthYear(), Date::setDateTime()
  4992. */
  4993. function setMonth($m, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
  4994. {
  4995. if ($pb_validate && !Date_Calc::isValidDate($this->day, $m, $this->year)) {
  4996. return PEAR::raiseError("'" .
  4997. Date_Calc::dateFormat($this->day,
  4998. $m,
  4999. $this->year,
  5000. "%Y-%m-%d") .
  5001. "' is invalid calendar date",
  5002. DATE_ERROR_INVALIDDATE);
  5003. } else {
  5004. $this->setLocalTime($this->day,
  5005. $m,
  5006. $this->year,
  5007. $this->hour,
  5008. $this->minute,
  5009. $this->second,
  5010. $this->partsecond);
  5011. }
  5012. }
  5013. // }}}
  5014. // {{{ setDay()
  5015. /**
  5016. * Sets the day field of the date object
  5017. *
  5018. * If specified year forms an invalid date, then PEAR error will be
  5019. * returned, unless the validation is over-ridden using the second
  5020. * parameter.
  5021. *
  5022. * @param int $d the day
  5023. * @param bool $pb_validate whether to check that the new date is valid
  5024. *
  5025. * @return void
  5026. * @access public
  5027. * @see Date::setDayMonthYear(), Date::setDateTime()
  5028. */
  5029. function setDay($d, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
  5030. {
  5031. if ($pb_validate && !Date_Calc::isValidDate($d, $this->month, $this->year)) {
  5032. return PEAR::raiseError("'" .
  5033. Date_Calc::dateFormat($d,
  5034. $this->month,
  5035. $this->year,
  5036. "%Y-%m-%d") .
  5037. "' is invalid calendar date",
  5038. DATE_ERROR_INVALIDDATE);
  5039. } else {
  5040. $this->setLocalTime($d,
  5041. $this->month,
  5042. $this->year,
  5043. $this->hour,
  5044. $this->minute,
  5045. $this->second,
  5046. $this->partsecond);
  5047. }
  5048. }
  5049. // }}}
  5050. // {{{ setDayMonthYear()
  5051. /**
  5052. * Sets the day, month and year fields of the date object
  5053. *
  5054. * If specified year forms an invalid date, then PEAR error will be
  5055. * returned. Note that setting each of these fields separately
  5056. * may unintentionally return a PEAR error if a transitory date is
  5057. * invalid between setting these fields.
  5058. *
  5059. * @param int $d the day
  5060. * @param int $m the month
  5061. * @param int $y the year
  5062. *
  5063. * @return void
  5064. * @access public
  5065. * @see Date::setDateTime()
  5066. * @since Method available since Release 1.5.0
  5067. */
  5068. function setDayMonthYear($d, $m, $y)
  5069. {
  5070. if (!Date_Calc::isValidDate($d, $m, $y)) {
  5071. return PEAR::raiseError("'" .
  5072. Date_Calc::dateFormat($d,
  5073. $m,
  5074. $y,
  5075. "%Y-%m-%d") .
  5076. "' is invalid calendar date",
  5077. DATE_ERROR_INVALIDDATE);
  5078. } else {
  5079. $this->setLocalTime($d,
  5080. $m,
  5081. $y,
  5082. $this->hour,
  5083. $this->minute,
  5084. $this->second,
  5085. $this->partsecond);
  5086. }
  5087. }
  5088. // }}}
  5089. // {{{ setHour()
  5090. /**
  5091. * Sets the hour field of the date object
  5092. *
  5093. * Expects an hour in 24-hour format.
  5094. *
  5095. * @param int $h the hour
  5096. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5097. * repeated hour is specified (defaults
  5098. * to false)
  5099. *
  5100. * @return void
  5101. * @access public
  5102. * @see Date::setHourMinuteSecond(), Date::setDateTime()
  5103. */
  5104. function setHour($h, $pb_repeatedhourdefault = false)
  5105. {
  5106. if ($h > 23 || $h < 0) {
  5107. return PEAR::raiseError("Invalid hour value '$h'");
  5108. } else {
  5109. $ret = $this->setHourMinuteSecond($h,
  5110. $this->minute,
  5111. $this->partsecond == 0.0 ?
  5112. $this->second :
  5113. $this->second + $this->partsecond,
  5114. $pb_repeatedhourdefault);
  5115. if (PEAR::isError($ret))
  5116. return $ret;
  5117. }
  5118. }
  5119. // }}}
  5120. // {{{ setMinute()
  5121. /**
  5122. * Sets the minute field of the date object
  5123. *
  5124. * @param int $m the minute
  5125. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5126. * repeated hour is specified (defaults
  5127. * to false)
  5128. *
  5129. * @return void
  5130. * @access public
  5131. * @see Date::setHourMinuteSecond(), Date::setDateTime()
  5132. */
  5133. function setMinute($m, $pb_repeatedhourdefault = false)
  5134. {
  5135. if ($m > 59 || $m < 0) {
  5136. return PEAR::raiseError("Invalid minute value '$m'");
  5137. } else {
  5138. $ret = $this->setHourMinuteSecond($this->hour,
  5139. $m,
  5140. $this->partsecond == 0.0 ?
  5141. $this->second :
  5142. $this->second + $this->partsecond,
  5143. $pb_repeatedhourdefault);
  5144. if (PEAR::isError($ret))
  5145. return $ret;
  5146. }
  5147. }
  5148. // }}}
  5149. // {{{ setSecond()
  5150. /**
  5151. * Sets the second field of the date object
  5152. *
  5153. * @param mixed $s the second as integer or float
  5154. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5155. * repeated hour is specified
  5156. * (defaults to false)
  5157. *
  5158. * @return void
  5159. * @access public
  5160. * @see Date::setHourMinuteSecond(), Date::setDateTime()
  5161. */
  5162. function setSecond($s, $pb_repeatedhourdefault = false)
  5163. {
  5164. if ($s > 60 || // Leap seconds possible
  5165. $s < 0) {
  5166. return PEAR::raiseError("Invalid second value '$s'");
  5167. } else {
  5168. $ret = $this->setHourMinuteSecond($this->hour,
  5169. $this->minute,
  5170. $s,
  5171. $pb_repeatedhourdefault);
  5172. if (PEAR::isError($ret))
  5173. return $ret;
  5174. }
  5175. }
  5176. // }}}
  5177. // {{{ setPartSecond()
  5178. /**
  5179. * Sets the part-second field of the date object
  5180. *
  5181. * @param float $pn_ps the part-second
  5182. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5183. * repeated hour is specified (defaults
  5184. * to false)
  5185. *
  5186. * @return void
  5187. * @access protected
  5188. * @see Date::setHourMinuteSecond(), Date::setDateTime()
  5189. * @since Method available since Release 1.5.0
  5190. */
  5191. function setPartSecond($pn_ps, $pb_repeatedhourdefault = false)
  5192. {
  5193. if ($pn_ps >= 1 || $pn_ps < 0) {
  5194. return PEAR::raiseError("Invalid part-second value '$pn_ps'");
  5195. } else {
  5196. $ret = $this->setHourMinuteSecond($this->hour,
  5197. $this->minute,
  5198. $this->second + $pn_ps,
  5199. $pb_repeatedhourdefault);
  5200. if (PEAR::isError($ret))
  5201. return $ret;
  5202. }
  5203. }
  5204. // }}}
  5205. // {{{ setHourMinuteSecond()
  5206. /**
  5207. * Sets the hour, minute, second and part-second fields of the date object
  5208. *
  5209. * N.B. if the repeated hour, due to the clocks going back, is specified,
  5210. * the default is to assume local standard time.
  5211. *
  5212. * @param int $h the hour
  5213. * @param int $m the minute
  5214. * @param mixed $s the second as integer or float
  5215. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5216. * repeated hour is specified
  5217. * (defaults to false)
  5218. *
  5219. * @return void
  5220. * @access public
  5221. * @see Date::setDateTime()
  5222. * @since Method available since Release 1.5.0
  5223. */
  5224. function setHourMinuteSecond($h, $m, $s, $pb_repeatedhourdefault = false)
  5225. {
  5226. // Split second into integer and part-second:
  5227. //
  5228. if (is_float($s)) {
  5229. $hn_second = intval($s);
  5230. $hn_partsecond = $s - $hn_second;
  5231. } else {
  5232. $hn_second = (int) $s;
  5233. $hn_partsecond = 0.0;
  5234. }
  5235. $this->setLocalTime($this->day,
  5236. $this->month,
  5237. $this->year,
  5238. $h,
  5239. $m,
  5240. $hn_second,
  5241. $hn_partsecond,
  5242. $pb_repeatedhourdefault);
  5243. }
  5244. // }}}
  5245. // {{{ setDateTime()
  5246. /**
  5247. * Sets all the fields of the date object (day, month, year, hour, minute
  5248. * and second)
  5249. *
  5250. * If specified year forms an invalid date, then PEAR error will be
  5251. * returned. Note that setting each of these fields separately
  5252. * may unintentionally return a PEAR error if a transitory date is
  5253. * invalid between setting these fields.
  5254. *
  5255. * N.B. if the repeated hour, due to the clocks going back, is specified,
  5256. * the default is to assume local standard time.
  5257. *
  5258. * @param int $pn_day the day
  5259. * @param int $pn_month the month
  5260. * @param int $pn_year the year
  5261. * @param int $pn_hour the hour
  5262. * @param int $pn_minute the minute
  5263. * @param mixed $pm_second the second as integer or float
  5264. * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
  5265. * repeated hour is specified
  5266. * (defaults to false)
  5267. *
  5268. * @return void
  5269. * @access public
  5270. * @see Date::setDayMonthYear(), Date::setHourMinuteSecond()
  5271. * @since Method available since Release 1.5.0
  5272. */
  5273. function setDateTime($pn_day,
  5274. $pn_month,
  5275. $pn_year,
  5276. $pn_hour,
  5277. $pn_minute,
  5278. $pm_second,
  5279. $pb_repeatedhourdefault = false)
  5280. {
  5281. if (!Date_Calc::isValidDate($d, $m, $y)) {
  5282. return PEAR::raiseError("'" .
  5283. Date_Calc::dateFormat($d,
  5284. $m,
  5285. $y,
  5286. "%Y-%m-%d") .
  5287. "' is invalid calendar date",
  5288. DATE_ERROR_INVALIDDATE);
  5289. } else {
  5290. // Split second into integer and part-second:
  5291. //
  5292. if (is_float($pm_second)) {
  5293. $hn_second = intval($pm_second);
  5294. $hn_partsecond = $pm_second - $hn_second;
  5295. } else {
  5296. $hn_second = (int) $pm_second;
  5297. $hn_partsecond = 0.0;
  5298. }
  5299. $this->setLocalTime($d,
  5300. $m,
  5301. $y,
  5302. $h,
  5303. $m,
  5304. $hn_second,
  5305. $hn_partsecond,
  5306. $pb_repeatedhourdefault);
  5307. }
  5308. }
  5309. // }}}
  5310. }
  5311. // }}}
  5312. /*
  5313. * Local variables:
  5314. * mode: php
  5315. * tab-width: 4
  5316. * c-basic-offset: 4
  5317. * c-hanging-comment-ender-p: nil
  5318. * End:
  5319. */
  5320. ?>