PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/hippo/src/main/webapp/ext/src/util/Date.js

http://hdbc.googlecode.com/
JavaScript | 1317 lines | 654 code | 85 blank | 578 comment | 75 complexity | a823729d5863650cdbc6dcbb3aa4c100 MD5 | raw file
  1. /*!
  2. * Ext JS Library 3.0.0
  3. * Copyright(c) 2006-2009 Ext JS, LLC
  4. * licensing@extjs.com
  5. * http://www.extjs.com/license
  6. */
  7. /**
  8. * @class Date
  9. *
  10. * The date parsing and formatting syntax contains a subset of
  11. * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
  12. * supported will provide results equivalent to their PHP versions.
  13. *
  14. * The following is a list of all currently supported formats:
  15. * <pre>
  16. Format Description Example returned values
  17. ------ ----------------------------------------------------------------------- -----------------------
  18. d Day of the month, 2 digits with leading zeros 01 to 31
  19. D A short textual representation of the day of the week Mon to Sun
  20. j Day of the month without leading zeros 1 to 31
  21. l A full textual representation of the day of the week Sunday to Saturday
  22. N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
  23. S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
  24. w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
  25. z The day of the year (starting from 0) 0 to 364 (365 in leap years)
  26. W ISO-8601 week number of year, weeks starting on Monday 01 to 53
  27. F A full textual representation of a month, such as January or March January to December
  28. m Numeric representation of a month, with leading zeros 01 to 12
  29. M A short textual representation of a month Jan to Dec
  30. n Numeric representation of a month, without leading zeros 1 to 12
  31. t Number of days in the given month 28 to 31
  32. L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
  33. o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
  34. belongs to the previous or next year, that year is used instead)
  35. Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  36. y A two digit representation of a year Examples: 99 or 03
  37. a Lowercase Ante meridiem and Post meridiem am or pm
  38. A Uppercase Ante meridiem and Post meridiem AM or PM
  39. g 12-hour format of an hour without leading zeros 1 to 12
  40. G 24-hour format of an hour without leading zeros 0 to 23
  41. h 12-hour format of an hour with leading zeros 01 to 12
  42. H 24-hour format of an hour with leading zeros 00 to 23
  43. i Minutes, with leading zeros 00 to 59
  44. s Seconds, with leading zeros 00 to 59
  45. u Decimal fraction of a second Examples:
  46. (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
  47. 100 (i.e. 0.100s) or
  48. 999 (i.e. 0.999s) or
  49. 999876543210 (i.e. 0.999876543210s)
  50. O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
  51. P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
  52. T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
  53. Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
  54. c ISO 8601 date
  55. Notes: Examples:
  56. 1) If unspecified, the month / day defaults to the current month / day, 1991 or
  57. the time defaults to midnight, while the timezone defaults to the 1992-10 or
  58. browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
  59. and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
  60. are optional. 1995-07-18T17:21:28-02:00 or
  61. 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
  62. least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
  63. of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
  64. Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
  65. date-time granularity which are supported, or see 2000-02-13T21:25:33
  66. http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
  67. U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
  68. M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
  69. \/Date(1238606590509+0800)\/
  70. </pre>
  71. *
  72. * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
  73. * <pre><code>
  74. // Sample date:
  75. // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
  76. var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
  77. document.write(dt.format('Y-m-d')); // 2007-01-10
  78. document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
  79. document.write(dt.format('l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
  80. </code></pre>
  81. *
  82. * Here are some standard date/time patterns that you might find helpful. They
  83. * are not part of the source of Date.js, but to use them you can simply copy this
  84. * block of code into any script that is included after Date.js and they will also become
  85. * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
  86. * <pre><code>
  87. Date.patterns = {
  88. ISO8601Long:"Y-m-d H:i:s",
  89. ISO8601Short:"Y-m-d",
  90. ShortDate: "n/j/Y",
  91. LongDate: "l, F d, Y",
  92. FullDateTime: "l, F d, Y g:i:s A",
  93. MonthDay: "F d",
  94. ShortTime: "g:i A",
  95. LongTime: "g:i:s A",
  96. SortableDateTime: "Y-m-d\\TH:i:s",
  97. UniversalSortableDateTime: "Y-m-d H:i:sO",
  98. YearMonth: "F, Y"
  99. };
  100. </code></pre>
  101. *
  102. * Example usage:
  103. * <pre><code>
  104. var dt = new Date();
  105. document.write(dt.format(Date.patterns.ShortDate));
  106. </code></pre>
  107. * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
  108. * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
  109. */
  110. /*
  111. * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
  112. * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
  113. * They generate precompiled functions from format patterns instead of parsing and
  114. * processing each pattern every time a date is formatted. These functions are available
  115. * on every Date object.
  116. */
  117. (function() {
  118. /**
  119. * Global flag which determines if strict date parsing should be used.
  120. * Strict date parsing will not roll-over invalid dates, which is the
  121. * default behaviour of javascript Date objects.
  122. * (see {@link #parseDate} for more information)
  123. * Defaults to <tt>false</tt>.
  124. * @static
  125. * @type Boolean
  126. */
  127. Date.useStrict = false;
  128. // create private copy of Ext's String.format() method
  129. // - to remove unnecessary dependency
  130. // - to resolve namespace conflict with M$-Ajax's implementation
  131. function xf(format) {
  132. var args = Array.prototype.slice.call(arguments, 1);
  133. return format.replace(/\{(\d+)\}/g, function(m, i) {
  134. return args[i];
  135. });
  136. }
  137. // private
  138. Date.formatCodeToRegex = function(character, currentGroup) {
  139. // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
  140. var p = Date.parseCodes[character];
  141. if (p) {
  142. p = typeof p == 'function'? p() : p;
  143. Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
  144. }
  145. return p? Ext.applyIf({
  146. c: p.c? xf(p.c, currentGroup || "{0}") : p.c
  147. }, p) : {
  148. g:0,
  149. c:null,
  150. s:Ext.escapeRe(character) // treat unrecognised characters as literals
  151. }
  152. }
  153. // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
  154. var $f = Date.formatCodeToRegex;
  155. Ext.apply(Date, {
  156. /**
  157. * <p>An object hash in which each property is a date parsing function. The property name is the
  158. * format string which that function parses.</p>
  159. * <p>This object is automatically populated with date parsing functions as
  160. * date formats are requested for Ext standard formatting strings.</p>
  161. * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
  162. * may be used as a format string to {@link #parseDate}.<p>
  163. * <p>Example:</p><pre><code>
  164. Date.parseFunctions['x-date-format'] = myDateParser;
  165. </code></pre>
  166. * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
  167. * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
  168. * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
  169. * (i.e. prevent javascript Date "rollover") (The default must be false).
  170. * Invalid date strings should return null when parsed.</div></li>
  171. * </ul></div></p>
  172. * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
  173. * formatting function must be placed into the {@link #formatFunctions} property.
  174. * @property parseFunctions
  175. * @static
  176. * @type Object
  177. */
  178. parseFunctions: {
  179. "M$": function(input, strict) {
  180. // note: the timezone offset is ignored since the M$ Ajax server sends
  181. // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
  182. var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
  183. var r = (input || '').match(re);
  184. return r? new Date(((r[1] || '') + r[2]) * 1) : null;
  185. }
  186. },
  187. parseRegexes: [],
  188. /**
  189. * <p>An object hash in which each property is a date formatting function. The property name is the
  190. * format string which corresponds to the produced formatted date string.</p>
  191. * <p>This object is automatically populated with date formatting functions as
  192. * date formats are requested for Ext standard formatting strings.</p>
  193. * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
  194. * may be used as a format string to {@link #format}. Example:</p><pre><code>
  195. Date.formatFunctions['x-date-format'] = myDateFormatter;
  196. </code></pre>
  197. * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
  198. * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
  199. * </ul></div></p>
  200. * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
  201. * parsing function must be placed into the {@link #parseFunctions} property.
  202. * @property formatFunctions
  203. * @static
  204. * @type Object
  205. */
  206. formatFunctions: {
  207. "M$": function() {
  208. // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
  209. return '\\/Date(' + this.getTime() + ')\\/';
  210. }
  211. },
  212. y2kYear : 50,
  213. /**
  214. * Date interval constant
  215. * @static
  216. * @type String
  217. */
  218. MILLI : "ms",
  219. /**
  220. * Date interval constant
  221. * @static
  222. * @type String
  223. */
  224. SECOND : "s",
  225. /**
  226. * Date interval constant
  227. * @static
  228. * @type String
  229. */
  230. MINUTE : "mi",
  231. /** Date interval constant
  232. * @static
  233. * @type String
  234. */
  235. HOUR : "h",
  236. /**
  237. * Date interval constant
  238. * @static
  239. * @type String
  240. */
  241. DAY : "d",
  242. /**
  243. * Date interval constant
  244. * @static
  245. * @type String
  246. */
  247. MONTH : "mo",
  248. /**
  249. * Date interval constant
  250. * @static
  251. * @type String
  252. */
  253. YEAR : "y",
  254. /**
  255. * <p>An object hash containing default date values used during date parsing.</p>
  256. * <p>The following properties are available:<div class="mdetail-params"><ul>
  257. * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
  258. * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
  259. * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
  260. * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
  261. * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
  262. * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
  263. * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
  264. * </ul></div></p>
  265. * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
  266. * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
  267. * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
  268. * It is the responsiblity of the developer to account for this.</b></p>
  269. * Example Usage:
  270. * <pre><code>
  271. // set default day value to the first day of the month
  272. Date.defaults.d = 1;
  273. // parse a February date string containing only year and month values.
  274. // setting the default day value to 1 prevents weird date rollover issues
  275. // when attempting to parse the following date string on, for example, March 31st 2009.
  276. Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
  277. </code></pre>
  278. * @property defaults
  279. * @static
  280. * @type Object
  281. */
  282. defaults: {},
  283. /**
  284. * An array of textual day names.
  285. * Override these values for international dates.
  286. * Example:
  287. * <pre><code>
  288. Date.dayNames = [
  289. 'SundayInYourLang',
  290. 'MondayInYourLang',
  291. ...
  292. ];
  293. </code></pre>
  294. * @type Array
  295. * @static
  296. */
  297. dayNames : [
  298. "Sunday",
  299. "Monday",
  300. "Tuesday",
  301. "Wednesday",
  302. "Thursday",
  303. "Friday",
  304. "Saturday"
  305. ],
  306. /**
  307. * An array of textual month names.
  308. * Override these values for international dates.
  309. * Example:
  310. * <pre><code>
  311. Date.monthNames = [
  312. 'JanInYourLang',
  313. 'FebInYourLang',
  314. ...
  315. ];
  316. </code></pre>
  317. * @type Array
  318. * @static
  319. */
  320. monthNames : [
  321. "January",
  322. "February",
  323. "March",
  324. "April",
  325. "May",
  326. "June",
  327. "July",
  328. "August",
  329. "September",
  330. "October",
  331. "November",
  332. "December"
  333. ],
  334. /**
  335. * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
  336. * Override these values for international dates.
  337. * Example:
  338. * <pre><code>
  339. Date.monthNumbers = {
  340. 'ShortJanNameInYourLang':0,
  341. 'ShortFebNameInYourLang':1,
  342. ...
  343. };
  344. </code></pre>
  345. * @type Object
  346. * @static
  347. */
  348. monthNumbers : {
  349. Jan:0,
  350. Feb:1,
  351. Mar:2,
  352. Apr:3,
  353. May:4,
  354. Jun:5,
  355. Jul:6,
  356. Aug:7,
  357. Sep:8,
  358. Oct:9,
  359. Nov:10,
  360. Dec:11
  361. },
  362. /**
  363. * Get the short month name for the given month number.
  364. * Override this function for international dates.
  365. * @param {Number} month A zero-based javascript month number.
  366. * @return {String} The short month name.
  367. * @static
  368. */
  369. getShortMonthName : function(month) {
  370. return Date.monthNames[month].substring(0, 3);
  371. },
  372. /**
  373. * Get the short day name for the given day number.
  374. * Override this function for international dates.
  375. * @param {Number} day A zero-based javascript day number.
  376. * @return {String} The short day name.
  377. * @static
  378. */
  379. getShortDayName : function(day) {
  380. return Date.dayNames[day].substring(0, 3);
  381. },
  382. /**
  383. * Get the zero-based javascript month number for the given short/full month name.
  384. * Override this function for international dates.
  385. * @param {String} name The short/full month name.
  386. * @return {Number} The zero-based javascript month number.
  387. * @static
  388. */
  389. getMonthNumber : function(name) {
  390. // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
  391. return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
  392. },
  393. /**
  394. * The base format-code to formatting-function hashmap used by the {@link #format} method.
  395. * Formatting functions are strings (or functions which return strings) which
  396. * will return the appropriate value when evaluated in the context of the Date object
  397. * from which the {@link #format} method is called.
  398. * Add to / override these mappings for custom date formatting.
  399. * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
  400. * Example:
  401. * <pre><code>
  402. Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
  403. (new Date()).format("X"); // returns the current day of the month
  404. </code></pre>
  405. * @type Object
  406. * @static
  407. */
  408. formatCodes : {
  409. d: "String.leftPad(this.getDate(), 2, '0')",
  410. D: "Date.getShortDayName(this.getDay())", // get localised short day name
  411. j: "this.getDate()",
  412. l: "Date.dayNames[this.getDay()]",
  413. N: "(this.getDay() ? this.getDay() : 7)",
  414. S: "this.getSuffix()",
  415. w: "this.getDay()",
  416. z: "this.getDayOfYear()",
  417. W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
  418. F: "Date.monthNames[this.getMonth()]",
  419. m: "String.leftPad(this.getMonth() + 1, 2, '0')",
  420. M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
  421. n: "(this.getMonth() + 1)",
  422. t: "this.getDaysInMonth()",
  423. L: "(this.isLeapYear() ? 1 : 0)",
  424. o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
  425. Y: "this.getFullYear()",
  426. y: "('' + this.getFullYear()).substring(2, 4)",
  427. a: "(this.getHours() < 12 ? 'am' : 'pm')",
  428. A: "(this.getHours() < 12 ? 'AM' : 'PM')",
  429. g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
  430. G: "this.getHours()",
  431. h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
  432. H: "String.leftPad(this.getHours(), 2, '0')",
  433. i: "String.leftPad(this.getMinutes(), 2, '0')",
  434. s: "String.leftPad(this.getSeconds(), 2, '0')",
  435. u: "String.leftPad(this.getMilliseconds(), 3, '0')",
  436. O: "this.getGMTOffset()",
  437. P: "this.getGMTOffset(true)",
  438. T: "this.getTimezone()",
  439. Z: "(this.getTimezoneOffset() * -60)",
  440. c: function() { // ISO-8601 -- GMT format
  441. for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
  442. var e = c.charAt(i);
  443. code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
  444. }
  445. return code.join(" + ");
  446. },
  447. /*
  448. c: function() { // ISO-8601 -- UTC format
  449. return [
  450. "this.getUTCFullYear()", "'-'",
  451. "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
  452. "String.leftPad(this.getUTCDate(), 2, '0')",
  453. "'T'",
  454. "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
  455. "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
  456. "String.leftPad(this.getUTCSeconds(), 2, '0')",
  457. "'Z'"
  458. ].join(" + ");
  459. },
  460. */
  461. U: "Math.round(this.getTime() / 1000)"
  462. },
  463. /**
  464. * Checks if the passed Date parameters will cause a javascript Date "rollover".
  465. * @param {Number} year 4-digit year
  466. * @param {Number} month 1-based month-of-year
  467. * @param {Number} day Day of month
  468. * @param {Number} hour (optional) Hour
  469. * @param {Number} minute (optional) Minute
  470. * @param {Number} second (optional) Second
  471. * @param {Number} millisecond (optional) Millisecond
  472. * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
  473. * @static
  474. */
  475. isValid : function(y, m, d, h, i, s, ms) {
  476. // setup defaults
  477. h = h || 0;
  478. i = i || 0;
  479. s = s || 0;
  480. ms = ms || 0;
  481. var dt = new Date(y, m - 1, d, h, i, s, ms);
  482. return y == dt.getFullYear() &&
  483. m == dt.getMonth() + 1 &&
  484. d == dt.getDate() &&
  485. h == dt.getHours() &&
  486. i == dt.getMinutes() &&
  487. s == dt.getSeconds() &&
  488. ms == dt.getMilliseconds();
  489. },
  490. /**
  491. * Parses the passed string using the specified date format.
  492. * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
  493. * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
  494. * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
  495. * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
  496. * Keep in mind that the input date string must precisely match the specified format string
  497. * in order for the parse operation to be successful (failed parse operations return a null value).
  498. * <p>Example:</p><pre><code>
  499. //dt = Fri May 25 2007 (current date)
  500. var dt = new Date();
  501. //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
  502. dt = Date.parseDate("2006", "Y");
  503. //dt = Sun Jan 15 2006 (all date parts specified)
  504. dt = Date.parseDate("2006-01-15", "Y-m-d");
  505. //dt = Sun Jan 15 2006 15:20:01
  506. dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
  507. // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
  508. dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
  509. </code></pre>
  510. * @param {String} input The raw date string.
  511. * @param {String} format The expected date string format.
  512. * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
  513. (defaults to false). Invalid date strings will return null when parsed.
  514. * @return {Date} The parsed Date.
  515. * @static
  516. */
  517. parseDate : function(input, format, strict) {
  518. var p = Date.parseFunctions;
  519. if (p[format] == null) {
  520. Date.createParser(format);
  521. }
  522. return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
  523. },
  524. // private
  525. getFormatCode : function(character) {
  526. var f = Date.formatCodes[character];
  527. if (f) {
  528. f = typeof f == 'function'? f() : f;
  529. Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
  530. }
  531. // note: unknown characters are treated as literals
  532. return f || ("'" + String.escape(character) + "'");
  533. },
  534. // private
  535. createFormat : function(format) {
  536. var code = [],
  537. special = false,
  538. ch = '';
  539. for (var i = 0; i < format.length; ++i) {
  540. ch = format.charAt(i);
  541. if (!special && ch == "\\") {
  542. special = true;
  543. } else if (special) {
  544. special = false;
  545. code.push("'" + String.escape(ch) + "'");
  546. } else {
  547. code.push(Date.getFormatCode(ch))
  548. }
  549. }
  550. Date.formatFunctions[format] = new Function("return " + code.join('+'));
  551. },
  552. // private
  553. createParser : function() {
  554. var code = [
  555. "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
  556. "def = Date.defaults,",
  557. "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
  558. "if(results){",
  559. "{1}",
  560. "if(u != null){", // i.e. unix time is defined
  561. "v = new Date(u * 1000);", // give top priority to UNIX time
  562. "}else{",
  563. // create Date object representing midnight of the current day;
  564. // this will provide us with our date defaults
  565. // (note: clearTime() handles Daylight Saving Time automatically)
  566. "dt = (new Date()).clearTime();",
  567. // date calculations (note: these calculations create a dependency on Ext.num())
  568. "y = y >= 0? y : Ext.num(def.y, dt.getFullYear());",
  569. "m = m >= 0? m : Ext.num(def.m - 1, dt.getMonth());",
  570. "d = d >= 0? d : Ext.num(def.d, dt.getDate());",
  571. // time calculations (note: these calculations create a dependency on Ext.num())
  572. "h = h || Ext.num(def.h, dt.getHours());",
  573. "i = i || Ext.num(def.i, dt.getMinutes());",
  574. "s = s || Ext.num(def.s, dt.getSeconds());",
  575. "ms = ms || Ext.num(def.ms, dt.getMilliseconds());",
  576. "if(z >= 0 && y >= 0){",
  577. // both the year and zero-based day of year are defined and >= 0.
  578. // these 2 values alone provide sufficient info to create a full date object
  579. // create Date object representing January 1st for the given year
  580. "v = new Date(y, 0, 1, h, i, s, ms);",
  581. // then add day of year, checking for Date "rollover" if necessary
  582. "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
  583. "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
  584. "v = null;", // invalid date, so return null
  585. "}else{",
  586. // plain old Date object
  587. "v = new Date(y, m, d, h, i, s, ms);",
  588. "}",
  589. "}",
  590. "}",
  591. "if(v){",
  592. // favour UTC offset over GMT offset
  593. "if(zz != null){",
  594. // reset to UTC, then add offset
  595. "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
  596. "}else if(o){",
  597. // reset to GMT, then add offset
  598. "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
  599. "}",
  600. "}",
  601. "return v;"
  602. ].join('\n');
  603. return function(format) {
  604. var regexNum = Date.parseRegexes.length,
  605. currentGroup = 1,
  606. calc = [],
  607. regex = [],
  608. special = false,
  609. ch = "";
  610. for (var i = 0; i < format.length; ++i) {
  611. ch = format.charAt(i);
  612. if (!special && ch == "\\") {
  613. special = true;
  614. } else if (special) {
  615. special = false;
  616. regex.push(String.escape(ch));
  617. } else {
  618. var obj = $f(ch, currentGroup);
  619. currentGroup += obj.g;
  620. regex.push(obj.s);
  621. if (obj.g && obj.c) {
  622. calc.push(obj.c);
  623. }
  624. }
  625. }
  626. Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
  627. Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
  628. }
  629. }(),
  630. // private
  631. parseCodes : {
  632. /*
  633. * Notes:
  634. * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
  635. * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
  636. * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
  637. */
  638. d: {
  639. g:1,
  640. c:"d = parseInt(results[{0}], 10);\n",
  641. s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
  642. },
  643. j: {
  644. g:1,
  645. c:"d = parseInt(results[{0}], 10);\n",
  646. s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
  647. },
  648. D: function() {
  649. for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
  650. return {
  651. g:0,
  652. c:null,
  653. s:"(?:" + a.join("|") +")"
  654. }
  655. },
  656. l: function() {
  657. return {
  658. g:0,
  659. c:null,
  660. s:"(?:" + Date.dayNames.join("|") + ")"
  661. }
  662. },
  663. N: {
  664. g:0,
  665. c:null,
  666. s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
  667. },
  668. S: {
  669. g:0,
  670. c:null,
  671. s:"(?:st|nd|rd|th)"
  672. },
  673. w: {
  674. g:0,
  675. c:null,
  676. s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
  677. },
  678. z: {
  679. g:1,
  680. c:"z = parseInt(results[{0}], 10);\n",
  681. s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
  682. },
  683. W: {
  684. g:0,
  685. c:null,
  686. s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
  687. },
  688. F: function() {
  689. return {
  690. g:1,
  691. c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
  692. s:"(" + Date.monthNames.join("|") + ")"
  693. }
  694. },
  695. M: function() {
  696. for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
  697. return Ext.applyIf({
  698. s:"(" + a.join("|") + ")"
  699. }, $f("F"));
  700. },
  701. m: {
  702. g:1,
  703. c:"m = parseInt(results[{0}], 10) - 1;\n",
  704. s:"(\\d{2})" // month number with leading zeros (01 - 12)
  705. },
  706. n: {
  707. g:1,
  708. c:"m = parseInt(results[{0}], 10) - 1;\n",
  709. s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
  710. },
  711. t: {
  712. g:0,
  713. c:null,
  714. s:"(?:\\d{2})" // no. of days in the month (28 - 31)
  715. },
  716. L: {
  717. g:0,
  718. c:null,
  719. s:"(?:1|0)"
  720. },
  721. o: function() {
  722. return $f("Y");
  723. },
  724. Y: {
  725. g:1,
  726. c:"y = parseInt(results[{0}], 10);\n",
  727. s:"(\\d{4})" // 4-digit year
  728. },
  729. y: {
  730. g:1,
  731. c:"var ty = parseInt(results[{0}], 10);\n"
  732. + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
  733. s:"(\\d{1,2})"
  734. },
  735. a: {
  736. g:1,
  737. c:"if (results[{0}] == 'am') {\n"
  738. + "if (h == 12) { h = 0; }\n"
  739. + "} else { if (h < 12) { h += 12; }}",
  740. s:"(am|pm)"
  741. },
  742. A: {
  743. g:1,
  744. c:"if (results[{0}] == 'AM') {\n"
  745. + "if (h == 12) { h = 0; }\n"
  746. + "} else { if (h < 12) { h += 12; }}",
  747. s:"(AM|PM)"
  748. },
  749. g: function() {
  750. return $f("G");
  751. },
  752. G: {
  753. g:1,
  754. c:"h = parseInt(results[{0}], 10);\n",
  755. s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
  756. },
  757. h: function() {
  758. return $f("H");
  759. },
  760. H: {
  761. g:1,
  762. c:"h = parseInt(results[{0}], 10);\n",
  763. s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
  764. },
  765. i: {
  766. g:1,
  767. c:"i = parseInt(results[{0}], 10);\n",
  768. s:"(\\d{2})" // minutes with leading zeros (00 - 59)
  769. },
  770. s: {
  771. g:1,
  772. c:"s = parseInt(results[{0}], 10);\n",
  773. s:"(\\d{2})" // seconds with leading zeros (00 - 59)
  774. },
  775. u: {
  776. g:1,
  777. c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
  778. s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
  779. },
  780. O: {
  781. g:1,
  782. c:[
  783. "o = results[{0}];",
  784. "var sn = o.substring(0,1),", // get + / - sign
  785. "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
  786. "mn = o.substring(3,5) % 60;", // get minutes
  787. "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
  788. ].join("\n"),
  789. s: "([+\-]\\d{4})" // GMT offset in hrs and mins
  790. },
  791. P: {
  792. g:1,
  793. c:[
  794. "o = results[{0}];",
  795. "var sn = o.substring(0,1),", // get + / - sign
  796. "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
  797. "mn = o.substring(4,6) % 60;", // get minutes
  798. "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
  799. ].join("\n"),
  800. s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
  801. },
  802. T: {
  803. g:0,
  804. c:null,
  805. s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
  806. },
  807. Z: {
  808. g:1,
  809. c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
  810. + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
  811. s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
  812. },
  813. c: function() {
  814. var calc = [],
  815. arr = [
  816. $f("Y", 1), // year
  817. $f("m", 2), // month
  818. $f("d", 3), // day
  819. $f("h", 4), // hour
  820. $f("i", 5), // minute
  821. $f("s", 6), // second
  822. {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
  823. {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
  824. "if(results[8]) {", // timezone specified
  825. "if(results[8] == 'Z'){",
  826. "zz = 0;", // UTC
  827. "}else if (results[8].indexOf(':') > -1){",
  828. $f("P", 8).c, // timezone offset with colon separator
  829. "}else{",
  830. $f("O", 8).c, // timezone offset without colon separator
  831. "}",
  832. "}"
  833. ].join('\n')}
  834. ];
  835. for (var i = 0, l = arr.length; i < l; ++i) {
  836. calc.push(arr[i].c);
  837. }
  838. return {
  839. g:1,
  840. c:calc.join(""),
  841. s:[
  842. arr[0].s, // year (required)
  843. "(?:", "-", arr[1].s, // month (optional)
  844. "(?:", "-", arr[2].s, // day (optional)
  845. "(?:",
  846. "(?:T| )?", // time delimiter -- either a "T" or a single blank space
  847. arr[3].s, ":", arr[4].s, // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
  848. "(?::", arr[5].s, ")?", // seconds (optional)
  849. "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
  850. "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
  851. ")?",
  852. ")?",
  853. ")?"
  854. ].join("")
  855. }
  856. },
  857. U: {
  858. g:1,
  859. c:"u = parseInt(results[{0}], 10);\n",
  860. s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
  861. }
  862. }
  863. });
  864. }());
  865. Ext.apply(Date.prototype, {
  866. // private
  867. dateFormat : function(format) {
  868. if (Date.formatFunctions[format] == null) {
  869. Date.createFormat(format);
  870. }
  871. return Date.formatFunctions[format].call(this);
  872. },
  873. /**
  874. * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
  875. *
  876. * Note: The date string returned by the javascript Date object's toString() method varies
  877. * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
  878. * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
  879. * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
  880. * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
  881. * from the GMT offset portion of the date string.
  882. * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
  883. */
  884. getTimezone : function() {
  885. // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
  886. //
  887. // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
  888. // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
  889. // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
  890. // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
  891. // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
  892. //
  893. // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
  894. // step 1: (?:\((.*)\) -- find timezone in parentheses
  895. // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
  896. // step 3: remove all non uppercase characters found in step 1 and 2
  897. return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
  898. },
  899. /**
  900. * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
  901. * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
  902. * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
  903. */
  904. getGMTOffset : function(colon) {
  905. return (this.getTimezoneOffset() > 0 ? "-" : "+")
  906. + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
  907. + (colon ? ":" : "")
  908. + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
  909. },
  910. /**
  911. * Get the numeric day number of the year, adjusted for leap year.
  912. * @return {Number} 0 to 364 (365 in leap years).
  913. */
  914. getDayOfYear: function() {
  915. var i = 0,
  916. num = 0,
  917. d = this.clone(),
  918. m = this.getMonth();
  919. for (i = 0, d.setMonth(0); i < m; d.setMonth(++i)) {
  920. num += d.getDaysInMonth();
  921. }
  922. return num + this.getDate() - 1;
  923. },
  924. /**
  925. * Get the numeric ISO-8601 week number of the year.
  926. * (equivalent to the format specifier 'W', but without a leading zero).
  927. * @return {Number} 1 to 53
  928. */
  929. getWeekOfYear : function() {
  930. // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
  931. var ms1d = 864e5, // milliseconds in a day
  932. ms7d = 7 * ms1d; // milliseconds in a week
  933. return function() { // return a closure so constants get calculated only once
  934. var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
  935. AWN = Math.floor(DC3 / 7), // an Absolute Week Number
  936. Wyr = new Date(AWN * ms7d).getUTCFullYear();
  937. return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
  938. }
  939. }(),
  940. /**
  941. * Checks if the current date falls within a leap year.
  942. * @return {Boolean} True if the current date falls within a leap year, false otherwise.
  943. */
  944. isLeapYear : function() {
  945. var year = this.getFullYear();
  946. return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
  947. },
  948. /**
  949. * Get the first day of the current month, adjusted for leap year. The returned value
  950. * is the numeric day index within the week (0-6) which can be used in conjunction with
  951. * the {@link #monthNames} array to retrieve the textual day name.
  952. * Example:
  953. * <pre><code>
  954. var dt = new Date('1/10/2007');
  955. document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
  956. </code></pre>
  957. * @return {Number} The day number (0-6).
  958. */
  959. getFirstDayOfMonth : function() {
  960. var day = (this.getDay() - (this.getDate() - 1)) % 7;
  961. return (day < 0) ? (day + 7) : day;
  962. },
  963. /**
  964. * Get the last day of the current month, adjusted for leap year. The returned value
  965. * is the numeric day index within the week (0-6) which can be used in conjunction with
  966. * the {@link #monthNames} array to retrieve the textual day name.
  967. * Example:
  968. * <pre><code>
  969. var dt = new Date('1/10/2007');
  970. document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
  971. </code></pre>
  972. * @return {Number} The day number (0-6).
  973. */
  974. getLastDayOfMonth : function() {
  975. return this.getLastDateOfMonth().getDay();
  976. },
  977. /**
  978. * Get the date of the first day of the month in which this date resides.
  979. * @return {Date}
  980. */
  981. getFirstDateOfMonth : function() {
  982. return new Date(this.getFullYear(), this.getMonth(), 1);
  983. },
  984. /**
  985. * Get the date of the last day of the month in which this date resides.
  986. * @return {Date}
  987. */
  988. getLastDateOfMonth : function() {
  989. return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
  990. },
  991. /**
  992. * Get the number of days in the current month, adjusted for leap year.
  993. * @return {Number} The number of days in the month.
  994. */
  995. getDaysInMonth: function() {
  996. var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  997. return function() { // return a closure for efficiency
  998. var m = this.getMonth();
  999. return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
  1000. }
  1001. }(),
  1002. /**
  1003. * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
  1004. * @return {String} 'st, 'nd', 'rd' or 'th'.
  1005. */
  1006. getSuffix : function() {
  1007. switch (this.getDate()) {
  1008. case 1:
  1009. case 21:
  1010. case 31:
  1011. return "st";
  1012. case 2:
  1013. case 22:
  1014. return "nd";
  1015. case 3:
  1016. case 23:
  1017. return "rd";
  1018. default:
  1019. return "th";
  1020. }
  1021. },
  1022. /**
  1023. * Creates and returns a new Date instance with the exact same date value as the called instance.
  1024. * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
  1025. * variable will also be changed. When the intention is to create a new variable that will not
  1026. * modify the original instance, you should create a clone.
  1027. *
  1028. * Example of correctly cloning a date:
  1029. * <pre><code>
  1030. //wrong way:
  1031. var orig = new Date('10/1/2006');
  1032. var copy = orig;
  1033. copy.setDate(5);
  1034. document.write(orig); //returns 'Thu Oct 05 2006'!
  1035. //correct way:
  1036. var orig = new Date('10/1/2006');
  1037. var copy = orig.clone();
  1038. copy.setDate(5);
  1039. document.write(orig); //returns 'Thu Oct 01 2006'
  1040. </code></pre>
  1041. * @return {Date} The new Date instance.
  1042. */
  1043. clone : function() {
  1044. return new Date(this.getTime());
  1045. },
  1046. /**
  1047. * Checks if the current date is affected by Daylight Saving Time (DST).
  1048. * @return {Boolean} True if the current date is affected by DST.
  1049. */
  1050. isDST : function() {
  1051. // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
  1052. // courtesy of @geoffrey.mcgill
  1053. return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
  1054. },
  1055. /**
  1056. * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
  1057. * automatically adjusting for Daylight Saving Time (DST) where applicable.
  1058. * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
  1059. * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
  1060. * @return {Date} this or the clone.
  1061. */
  1062. clearTime : function(clone) {
  1063. if (clone) {
  1064. return this.clone().clearTime();
  1065. }
  1066. // get current date before clearing time
  1067. var d = this.getDate();
  1068. // clear time
  1069. this.setHours(0);
  1070. this.setMinutes(0);
  1071. this.setSeconds(0);
  1072. this.setMilliseconds(0);
  1073. if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
  1074. // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
  1075. // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
  1076. // increment hour until cloned date == current date
  1077. for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
  1078. this.setDate(d);
  1079. this.setHours(c.getHours());
  1080. }
  1081. return this;
  1082. },
  1083. /**
  1084. * Provides a convenient method for performing basic date arithmetic. This method
  1085. * does not modify the Date instance being called - it creates and returns
  1086. * a new Date instance containing the resulting date value.
  1087. *
  1088. * Examples:
  1089. * <pre><code>
  1090. // Basic usage:
  1091. var dt = new Date('10/29/2006').add(Date.DAY, 5);
  1092. document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
  1093. // Negative values will be subtracted:
  1094. var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
  1095. document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
  1096. // You can even chain several calls together in one line:
  1097. var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
  1098. document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
  1099. </code></pre>
  1100. *
  1101. * @param {String} interval A valid date interval enum value.
  1102. * @param {Number} value The amount to add to the current date.
  1103. * @return {Date} The new Date instance.
  1104. */
  1105. add : function(interval, value) {
  1106. var d = this.clone();
  1107. if (!interval || value === 0) return d;
  1108. switch(interval.toLowerCase()) {
  1109. case Date.MILLI:
  1110. d.setMilliseconds(this.getMilliseconds() + value);
  1111. break;
  1112. case Date.SECOND:
  1113. d.setSeconds(this.getSeconds() + value);
  1114. break;
  1115. case Date.MINUTE:
  1116. d.setMinutes(this.getMinutes() + value);
  1117. break;
  1118. case Date.HOUR:
  1119. d.setHours(this.getHours() + value);
  1120. break;
  1121. case Date.DAY:
  1122. d.setDate(this.getDate() + value);
  1123. break;
  1124. case Date.MONTH:
  1125. var day = this.getDate();
  1126. if (day > 28) {
  1127. day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
  1128. }
  1129. d.setDate(day);
  1130. d.setMonth(this.getMonth() + value);
  1131. break;
  1132. case Date.YEAR:
  1133. d.setFullYear(this.getFullYear() + value);
  1134. break;
  1135. }
  1136. return d;
  1137. },
  1138. /**
  1139. * Checks if this date falls on or between the given start and end dates.
  1140. * @param {Date} start Start date
  1141. * @param {Date} end End date
  1142. * @return {Boolean} true if this date falls on or between the given start and end dates.
  1143. */
  1144. between : function(start, end) {
  1145. var t = this.getTime();
  1146. return start.getTime() <= t && t <= end.getTime();
  1147. }
  1148. });
  1149. /**
  1150. * Formats a date given the supplied format string.
  1151. * @param {String} format The format string.
  1152. * @return {String} The formatted date.
  1153. * @method format
  1154. */
  1155. Date.prototype.format = Date.prototype.dateFormat;
  1156. // private
  1157. if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
  1158. Ext.apply(Date.prototype, {
  1159. _xMonth : Date.prototype.setMonth,
  1160. _xDate : Date.prototype.setDate,
  1161. // Bug in Safari 1.3, 2.0 (WebKit build < 420)
  1162. // Date.setMonth does not work consistently if iMonth is not 0-11
  1163. setMonth : function(num) {
  1164. if (num <= -1) {
  1165. var n = Math.ceil(-num),
  1166. back_year = Math.ceil(n / 12),
  1167. month = (n % 12) ? 12 - n % 12 : 0;
  1168. this.setFullYear(this.getFullYear() - back_year);
  1169. return this._xMonth(month);
  1170. } else {
  1171. return this._xMonth(num);
  1172. }
  1173. },
  1174. // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
  1175. // The parameter for Date.setDate() is converted to a signed byte integer in Safari
  1176. // http://brianary.blogspot.com/2006/03/safari-date-bug.html
  1177. setDate : function(d) {
  1178. // use setTime() to workaround setDate() bug
  1179. // subtract current day of month in milliseconds, then add desired day of month in milliseconds
  1180. return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
  1181. }
  1182. });
  1183. }
  1184. /* Some basic Date tests... (requires Firebug)
  1185. Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
  1186. console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
  1187. // standard tests
  1188. console.group('Standard Date.parseDate() Tests');
  1189. console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
  1190. console.log('Date.parseDate("2009-02-04T12:37:55.001000", "c") = %o', Date.parseDate("2009-02-04T12:37:55.001000", "c")); // assumes browser's timezone setting
  1191. console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
  1192. console.log('Date.parseDate("2009-04-02T14:35:53.901000-0530", "c") = %o', Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")); // GMT-0530
  1193. console.log('Date.parseDate("2009-05-01T15:34:52,9876000+08:00", "c") = %o', Date.parseDate("2009-05-01T15:34:52,987600+08:00", "c")); // GMT+08:00
  1194. console.groupEnd();
  1195. // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
  1196. // -- accepts ALL 6 levels of date-time granularity
  1197. console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
  1198. console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
  1199. console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
  1200. console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
  1201. console.log('Date.parseDate("1997-07-16T19:20+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20+01:00", "c")); // YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00)
  1202. console.log('Date.parseDate("1997-07-16T19:20:30+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30+01:00", "c")); // YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:30+01:00)
  1203. console.log('Date.parseDate("1997-07-16T19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")); // YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
  1204. console.log('Date.parseDate("1997-07-16 19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")); // YYYY-MM-DD hh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
  1205. console.log('Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)= %o', Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)); // strict date parsing with invalid month value
  1206. console.groupEnd();
  1207. //*/