3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
17 * A set of useful static methods to deal with date
18 * Note that if Ext.Date is required and loaded, it will copy all methods / properties to
19 * this object for convenience
21 * The date parsing and formatting syntax contains a subset of
22 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
23 * supported will provide results equivalent to their PHP versions.
25 * The following is a list of all currently supported formats:
27 Format Description Example returned values
28 ------ ----------------------------------------------------------------------- -----------------------
29 d Day of the month, 2 digits with leading zeros 01 to 31
30 D A short textual representation of the day of the week Mon to Sun
31 j Day of the month without leading zeros 1 to 31
32 l A full textual representation of the day of the week Sunday to Saturday
33 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
34 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
35 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
36 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
37 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
38 F A full textual representation of a month, such as January or March January to December
39 m Numeric representation of a month, with leading zeros 01 to 12
40 M A short textual representation of a month Jan to Dec
41 n Numeric representation of a month, without leading zeros 1 to 12
42 t Number of days in the given month 28 to 31
43 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
44 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
45 belongs to the previous or next year, that year is used instead)
46 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
47 y A two digit representation of a year Examples: 99 or 03
48 a Lowercase Ante meridiem and Post meridiem am or pm
49 A Uppercase Ante meridiem and Post meridiem AM or PM
50 g 12-hour format of an hour without leading zeros 1 to 12
51 G 24-hour format of an hour without leading zeros 0 to 23
52 h 12-hour format of an hour with leading zeros 01 to 12
53 H 24-hour format of an hour with leading zeros 00 to 23
54 i Minutes, with leading zeros 00 to 59
55 s Seconds, with leading zeros 00 to 59
56 u Decimal fraction of a second Examples:
57 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
60 999876543210 (i.e. 0.999876543210s)
61 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
62 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
63 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
64 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
67 1) If unspecified, the month / day defaults to the current month / day, 1991 or
68 the time defaults to midnight, while the timezone defaults to the 1992-10 or
69 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
70 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
71 are optional. 1995-07-18T17:21:28-02:00 or
72 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
73 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
74 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
75 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
76 date-time granularity which are supported, or see 2000-02-13T21:25:33
77 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
78 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
79 MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
80 \/Date(1238606590509+0800)\/
83 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
86 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
88 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
89 console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
90 console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
91 console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
94 * Here are some standard date/time patterns that you might find helpful. They
95 * are not part of the source of Ext.Date, but to use them you can simply copy this
96 * block of code into any script that is included after Ext.Date and they will also become
97 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
100 ISO8601Long:"Y-m-d H:i:s",
101 ISO8601Short:"Y-m-d",
103 LongDate: "l, F d, Y",
104 FullDateTime: "l, F d, Y g:i:s A",
108 SortableDateTime: "Y-m-d\\TH:i:s",
109 UniversalSortableDateTime: "Y-m-d H:i:sO",
117 console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
119 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
120 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
125 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
126 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
127 * They generate precompiled functions from format patterns instead of parsing and
128 * processing each pattern every time a date is formatted. These functions are available
129 * on every Date object.
134 // create private copy of Ext's Ext.util.Format.format() method
135 // - to remove unnecessary dependency
136 // - to resolve namespace conflict with MS-Ajax's implementation
137 function xf(format) {
138 var args = Array.prototype.slice.call(arguments, 1);
139 return format.replace(/\{(\d+)\}/g, function(m, i) {
146 * Returns the current timestamp
147 * @return {Date} The current timestamp
150 now: Date.now || function() {
158 toString: function(date) {
159 var pad = Ext.String.leftPad;
161 return date.getFullYear() + "-"
162 + pad(date.getMonth() + 1, 2, '0') + "-"
163 + pad(date.getDate(), 2, '0') + "T"
164 + pad(date.getHours(), 2, '0') + ":"
165 + pad(date.getMinutes(), 2, '0') + ":"
166 + pad(date.getSeconds(), 2, '0');
170 * Returns the number of milliseconds between two dates
171 * @param {Date} dateA The first date
172 * @param {Date} dateB (optional) The second date, defaults to now
173 * @return {Number} The difference in milliseconds
175 getElapsed: function(dateA, dateB) {
176 return Math.abs(dateA - (dateB || new Date()));
180 * Global flag which determines if strict date parsing should be used.
181 * Strict date parsing will not roll-over invalid dates, which is the
182 * default behaviour of javascript Date objects.
183 * (see {@link #parse} for more information)
184 * Defaults to <tt>false</tt>.
191 formatCodeToRegex: function(character, currentGroup) {
192 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
193 var p = utilDate.parseCodes[character];
196 p = typeof p == 'function'? p() : p;
197 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
200 return p ? Ext.applyIf({
201 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
205 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
210 * <p>An object hash in which each property is a date parsing function. The property name is the
211 * format string which that function parses.</p>
212 * <p>This object is automatically populated with date parsing functions as
213 * date formats are requested for Ext standard formatting strings.</p>
214 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
215 * may be used as a format string to {@link #parse}.<p>
216 * <p>Example:</p><pre><code>
217 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
219 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
220 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
221 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
222 * (i.e. prevent javascript Date "rollover") (The default must be false).
223 * Invalid date strings should return null when parsed.</div></li>
225 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
226 * formatting function must be placed into the {@link #formatFunctions} property.
227 * @property parseFunctions
232 "MS": function(input, strict) {
233 // note: the timezone offset is ignored since the MS Ajax server sends
234 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
235 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
236 var r = (input || '').match(re);
237 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
243 * <p>An object hash in which each property is a date formatting function. The property name is the
244 * format string which corresponds to the produced formatted date string.</p>
245 * <p>This object is automatically populated with date formatting functions as
246 * date formats are requested for Ext standard formatting strings.</p>
247 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
248 * may be used as a format string to {@link #format}. Example:</p><pre><code>
249 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
251 * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
252 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
254 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
255 * parsing function must be placed into the {@link #parseFunctions} property.
256 * @property formatFunctions
262 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
263 return '\\/Date(' + this.getTime() + ')\\/';
270 * Date interval constant
277 * Date interval constant
284 * Date interval constant
290 /** Date interval constant
297 * Date interval constant
304 * Date interval constant
311 * Date interval constant
318 * <p>An object hash containing default date values used during date parsing.</p>
319 * <p>The following properties are available:<div class="mdetail-params"><ul>
320 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
321 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
322 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
323 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
324 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
325 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
326 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
328 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
329 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
330 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
331 * It is the responsiblity of the developer to account for this.</b></p>
334 // set default day value to the first day of the month
335 Ext.Date.defaults.d = 1;
337 // parse a February date string containing only year and month values.
338 // setting the default day value to 1 prevents weird date rollover issues
339 // when attempting to parse the following date string on, for example, March 31st 2009.
340 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
349 * An array of textual day names.
350 * Override these values for international dates.
353 Ext.Date.dayNames = [
373 * An array of textual month names.
374 * Override these values for international dates.
377 Ext.Date.monthNames = [
402 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
403 * Override these values for international dates.
406 Ext.Date.monthNumbers = {
407 'ShortJanNameInYourLang':0,
408 'ShortFebNameInYourLang':1,
430 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
431 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
432 * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
433 * @property defaultFormat
437 defaultFormat : "m/d/Y",
439 * Get the short month name for the given month number.
440 * Override this function for international dates.
441 * @param {Number} month A zero-based javascript month number.
442 * @return {String} The short month name.
445 getShortMonthName : function(month) {
446 return utilDate.monthNames[month].substring(0, 3);
450 * Get the short day name for the given day number.
451 * Override this function for international dates.
452 * @param {Number} day A zero-based javascript day number.
453 * @return {String} The short day name.
456 getShortDayName : function(day) {
457 return utilDate.dayNames[day].substring(0, 3);
461 * Get the zero-based javascript month number for the given short/full month name.
462 * Override this function for international dates.
463 * @param {String} name The short/full month name.
464 * @return {Number} The zero-based javascript month number.
467 getMonthNumber : function(name) {
468 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
469 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
473 * Checks if the specified format contains hour information
474 * @param {String} format The format to check
475 * @return {Boolean} True if the format contains hour information
479 formatContainsHourInfo : (function(){
480 var stripEscapeRe = /(\\.)/g,
481 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
482 return function(format){
483 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
488 * Checks if the specified format contains information about
489 * anything other than the time.
490 * @param {String} format The format to check
491 * @return {Boolean} True if the format contains information about
492 * date/day information.
496 formatContainsDateInfo : (function(){
497 var stripEscapeRe = /(\\.)/g,
498 dateInfoRe = /([djzmnYycU]|MS)/;
500 return function(format){
501 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
506 * The base format-code to formatting-function hashmap used by the {@link #format} method.
507 * Formatting functions are strings (or functions which return strings) which
508 * will return the appropriate value when evaluated in the context of the Date object
509 * from which the {@link #format} method is called.
510 * Add to / override these mappings for custom date formatting.
511 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
514 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
515 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
521 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
522 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
524 l: "Ext.Date.dayNames[this.getDay()]",
525 N: "(this.getDay() ? this.getDay() : 7)",
526 S: "Ext.Date.getSuffix(this)",
528 z: "Ext.Date.getDayOfYear(this)",
529 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
530 F: "Ext.Date.monthNames[this.getMonth()]",
531 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
532 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
533 n: "(this.getMonth() + 1)",
534 t: "Ext.Date.getDaysInMonth(this)",
535 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
536 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
537 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
538 y: "('' + this.getFullYear()).substring(2, 4)",
539 a: "(this.getHours() < 12 ? 'am' : 'pm')",
540 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
541 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
542 G: "this.getHours()",
543 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
544 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
545 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
546 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
547 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
548 O: "Ext.Date.getGMTOffset(this)",
549 P: "Ext.Date.getGMTOffset(this, true)",
550 T: "Ext.Date.getTimezone(this)",
551 Z: "(this.getTimezoneOffset() * -60)",
553 c: function() { // ISO-8601 -- GMT format
554 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
556 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
558 return code.join(" + ");
561 c: function() { // ISO-8601 -- UTC format
563 "this.getUTCFullYear()", "'-'",
564 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
565 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
567 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
568 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
569 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
575 U: "Math.round(this.getTime() / 1000)"
579 * Checks if the passed Date parameters will cause a javascript Date "rollover".
580 * @param {Number} year 4-digit year
581 * @param {Number} month 1-based month-of-year
582 * @param {Number} day Day of month
583 * @param {Number} hour (optional) Hour
584 * @param {Number} minute (optional) Minute
585 * @param {Number} second (optional) Second
586 * @param {Number} millisecond (optional) Millisecond
587 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
590 isValid : function(y, m, d, h, i, s, ms) {
597 // Special handling for year < 100
598 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
600 return y == dt.getFullYear() &&
601 m == dt.getMonth() + 1 &&
603 h == dt.getHours() &&
604 i == dt.getMinutes() &&
605 s == dt.getSeconds() &&
606 ms == dt.getMilliseconds();
610 * Parses the passed string using the specified date format.
611 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
612 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
613 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
614 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
615 * Keep in mind that the input date string must precisely match the specified format string
616 * in order for the parse operation to be successful (failed parse operations return a null value).
617 * <p>Example:</p><pre><code>
618 //dt = Fri May 25 2007 (current date)
621 //dt = Thu May 25 2006 (today's month/day in 2006)
622 dt = Ext.Date.parse("2006", "Y");
624 //dt = Sun Jan 15 2006 (all date parts specified)
625 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
627 //dt = Sun Jan 15 2006 15:20:01
628 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
630 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
631 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
633 * @param {String} input The raw date string.
634 * @param {String} format The expected date string format.
635 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
636 (defaults to false). Invalid date strings will return null when parsed.
637 * @return {Date} The parsed Date.
640 parse : function(input, format, strict) {
641 var p = utilDate.parseFunctions;
642 if (p[format] == null) {
643 utilDate.createParser(format);
645 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
649 parseDate: function(input, format, strict){
650 return utilDate.parse(input, format, strict);
655 getFormatCode : function(character) {
656 var f = utilDate.formatCodes[character];
659 f = typeof f == 'function'? f() : f;
660 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
663 // note: unknown characters are treated as literals
664 return f || ("'" + Ext.String.escape(character) + "'");
668 createFormat : function(format) {
673 for (var i = 0; i < format.length; ++i) {
674 ch = format.charAt(i);
675 if (!special && ch == "\\") {
677 } else if (special) {
679 code.push("'" + Ext.String.escape(ch) + "'");
681 code.push(utilDate.getFormatCode(ch));
684 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
688 createParser : (function() {
690 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
691 "def = Ext.Date.defaults,",
692 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
697 "if(u != null){", // i.e. unix time is defined
698 "v = new Date(u * 1000);", // give top priority to UNIX time
700 // create Date object representing midnight of the current day;
701 // this will provide us with our date defaults
702 // (note: clearTime() handles Daylight Saving Time automatically)
703 "dt = Ext.Date.clearTime(new Date);",
705 // date calculations (note: these calculations create a dependency on Ext.Number.from())
706 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
707 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
708 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
710 // time calculations (note: these calculations create a dependency on Ext.Number.from())
711 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
712 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
713 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
714 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
716 "if(z >= 0 && y >= 0){",
717 // both the year and zero-based day of year are defined and >= 0.
718 // these 2 values alone provide sufficient info to create a full date object
720 // create Date object representing January 1st for the given year
721 // handle years < 100 appropriately
722 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
724 // then add day of year, checking for Date "rollover" if necessary
725 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
726 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
727 "v = null;", // invalid date, so return null
729 // plain old Date object
730 // handle years < 100 properly
731 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
737 // favour UTC offset over GMT offset
739 // reset to UTC, then add offset
740 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
742 // reset to GMT, then add offset
743 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
750 return function(format) {
751 var regexNum = utilDate.parseRegexes.length,
758 for (var i = 0; i < format.length; ++i) {
759 ch = format.charAt(i);
760 if (!special && ch == "\\") {
762 } else if (special) {
764 regex.push(Ext.String.escape(ch));
766 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
767 currentGroup += obj.g;
769 if (obj.g && obj.c) {
775 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
776 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
784 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
785 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
786 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
790 c:"d = parseInt(results[{0}], 10);\n",
791 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
795 c:"d = parseInt(results[{0}], 10);\n",
796 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
799 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
803 s:"(?:" + a.join("|") +")"
810 s:"(?:" + utilDate.dayNames.join("|") + ")"
816 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
826 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
830 c:"z = parseInt(results[{0}], 10);\n",
831 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
836 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
841 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
842 s:"(" + utilDate.monthNames.join("|") + ")"
846 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
848 s:"(" + a.join("|") + ")"
849 }, utilDate.formatCodeToRegex("F"));
853 c:"m = parseInt(results[{0}], 10) - 1;\n",
854 s:"(\\d{2})" // month number with leading zeros (01 - 12)
858 c:"m = parseInt(results[{0}], 10) - 1;\n",
859 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
864 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
872 return utilDate.formatCodeToRegex("Y");
876 c:"y = parseInt(results[{0}], 10);\n",
877 s:"(\\d{4})" // 4-digit year
881 c:"var ty = parseInt(results[{0}], 10);\n"
882 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
886 * In the am/pm parsing routines, we allow both upper and lower case
887 * even though it doesn't exactly match the spec. It gives much more flexibility
888 * in being able to specify case insensitive regexes.
892 c:"if (/(am)/i.test(results[{0}])) {\n"
893 + "if (!h || h == 12) { h = 0; }\n"
894 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
899 c:"if (/(am)/i.test(results[{0}])) {\n"
900 + "if (!h || h == 12) { h = 0; }\n"
901 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
905 return utilDate.formatCodeToRegex("G");
909 c:"h = parseInt(results[{0}], 10);\n",
910 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
913 return utilDate.formatCodeToRegex("H");
917 c:"h = parseInt(results[{0}], 10);\n",
918 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
922 c:"i = parseInt(results[{0}], 10);\n",
923 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
927 c:"s = parseInt(results[{0}], 10);\n",
928 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
932 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
933 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
939 "var sn = o.substring(0,1),", // get + / - sign
940 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
941 "mn = o.substring(3,5) % 60;", // get minutes
942 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
944 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
950 "var sn = o.substring(0,1),", // get + / - sign
951 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
952 "mn = o.substring(4,6) % 60;", // get minutes
953 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
955 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
960 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
964 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
965 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
966 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
971 utilDate.formatCodeToRegex("Y", 1), // year
972 utilDate.formatCodeToRegex("m", 2), // month
973 utilDate.formatCodeToRegex("d", 3), // day
974 utilDate.formatCodeToRegex("h", 4), // hour
975 utilDate.formatCodeToRegex("i", 5), // minute
976 utilDate.formatCodeToRegex("s", 6), // second
977 {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)
978 {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
979 "if(results[8]) {", // timezone specified
980 "if(results[8] == 'Z'){",
982 "}else if (results[8].indexOf(':') > -1){",
983 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
985 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
991 for (var i = 0, l = arr.length; i < l; ++i) {
999 arr[0].s, // year (required)
1000 "(?:", "-", arr[1].s, // month (optional)
1001 "(?:", "-", arr[2].s, // day (optional)
1003 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
1004 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
1005 "(?::", arr[5].s, ")?", // seconds (optional)
1006 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
1007 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
1016 c:"u = parseInt(results[{0}], 10);\n",
1017 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
1021 //Old Ext.Date prototype methods.
1023 dateFormat: function(date, format) {
1024 return utilDate.format(date, format);
1028 * Formats a date given the supplied format string.
1029 * @param {Date} date The date to format
1030 * @param {String} format The format string
1031 * @return {String} The formatted date
1033 format: function(date, format) {
1034 if (utilDate.formatFunctions[format] == null) {
1035 utilDate.createFormat(format);
1037 var result = utilDate.formatFunctions[format].call(date);
1042 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1044 * Note: The date string returned by the javascript Date object's toString() method varies
1045 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
1046 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
1047 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
1048 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
1049 * from the GMT offset portion of the date string.
1050 * @param {Date} date The date
1051 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
1053 getTimezone : function(date) {
1054 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
1056 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
1057 // 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)
1058 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
1059 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
1060 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
1062 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
1063 // step 1: (?:\((.*)\) -- find timezone in parentheses
1064 // 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
1065 // step 3: remove all non uppercase characters found in step 1 and 2
1066 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
1070 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1071 * @param {Date} date The date
1072 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
1073 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
1075 getGMTOffset : function(date, colon) {
1076 var offset = date.getTimezoneOffset();
1077 return (offset > 0 ? "-" : "+")
1078 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
1079 + (colon ? ":" : "")
1080 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
1084 * Get the numeric day number of the year, adjusted for leap year.
1085 * @param {Date} date The date
1086 * @return {Number} 0 to 364 (365 in leap years).
1088 getDayOfYear: function(date) {
1090 d = Ext.Date.clone(date),
1091 m = date.getMonth(),
1094 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
1095 num += utilDate.getDaysInMonth(d);
1097 return num + date.getDate() - 1;
1101 * Get the numeric ISO-8601 week number of the year.
1102 * (equivalent to the format specifier 'W', but without a leading zero).
1103 * @param {Date} date The date
1104 * @return {Number} 1 to 53
1107 getWeekOfYear : (function() {
1108 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
1109 var ms1d = 864e5, // milliseconds in a day
1110 ms7d = 7 * ms1d; // milliseconds in a week
1112 return function(date) { // return a closure so constants get calculated only once
1113 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
1114 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
1115 Wyr = new Date(AWN * ms7d).getUTCFullYear();
1117 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
1122 * Checks if the current date falls within a leap year.
1123 * @param {Date} date The date
1124 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
1126 isLeapYear : function(date) {
1127 var year = date.getFullYear();
1128 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1132 * Get the first day of the current month, adjusted for leap year. The returned value
1133 * is the numeric day index within the week (0-6) which can be used in conjunction with
1134 * the {@link #monthNames} array to retrieve the textual day name.
1137 var dt = new Date('1/10/2007'),
1138 firstDay = Ext.Date.getFirstDayOfMonth(dt);
1139 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
1141 * @param {Date} date The date
1142 * @return {Number} The day number (0-6).
1144 getFirstDayOfMonth : function(date) {
1145 var day = (date.getDay() - (date.getDate() - 1)) % 7;
1146 return (day < 0) ? (day + 7) : day;
1150 * Get the last day of the current month, adjusted for leap year. The returned value
1151 * is the numeric day index within the week (0-6) which can be used in conjunction with
1152 * the {@link #monthNames} array to retrieve the textual day name.
1155 var dt = new Date('1/10/2007'),
1156 lastDay = Ext.Date.getLastDayOfMonth(dt);
1157 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
1159 * @param {Date} date The date
1160 * @return {Number} The day number (0-6).
1162 getLastDayOfMonth : function(date) {
1163 return utilDate.getLastDateOfMonth(date).getDay();
1168 * Get the date of the first day of the month in which this date resides.
1169 * @param {Date} date The date
1172 getFirstDateOfMonth : function(date) {
1173 return new Date(date.getFullYear(), date.getMonth(), 1);
1177 * Get the date of the last day of the month in which this date resides.
1178 * @param {Date} date The date
1181 getLastDateOfMonth : function(date) {
1182 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
1186 * Get the number of days in the current month, adjusted for leap year.
1187 * @param {Date} date The date
1188 * @return {Number} The number of days in the month.
1191 getDaysInMonth: (function() {
1192 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1194 return function(date) { // return a closure for efficiency
1195 var m = date.getMonth();
1197 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
1202 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1203 * @param {Date} date The date
1204 * @return {String} 'st, 'nd', 'rd' or 'th'.
1206 getSuffix : function(date) {
1207 switch (date.getDate()) {
1224 * Creates and returns a new Date instance with the exact same date value as the called instance.
1225 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1226 * variable will also be changed. When the intention is to create a new variable that will not
1227 * modify the original instance, you should create a clone.
1229 * Example of correctly cloning a date:
1232 var orig = new Date('10/1/2006');
1235 console.log(orig); //returns 'Thu Oct 05 2006'!
1238 var orig = new Date('10/1/2006'),
1239 copy = Ext.Date.clone(orig);
1241 console.log(orig); //returns 'Thu Oct 01 2006'
1243 * @param {Date} date The date
1244 * @return {Date} The new Date instance.
1246 clone : function(date) {
1247 return new Date(date.getTime());
1251 * Checks if the current date is affected by Daylight Saving Time (DST).
1252 * @param {Date} date The date
1253 * @return {Boolean} True if the current date is affected by DST.
1255 isDST : function(date) {
1256 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
1257 // courtesy of @geoffrey.mcgill
1258 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
1262 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
1263 * automatically adjusting for Daylight Saving Time (DST) where applicable.
1264 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
1265 * @param {Date} date The date
1266 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
1267 * @return {Date} this or the clone.
1269 clearTime : function(date, clone) {
1271 return Ext.Date.clearTime(Ext.Date.clone(date));
1274 // get current date before clearing time
1275 var d = date.getDate();
1281 date.setMilliseconds(0);
1283 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
1284 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
1285 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
1287 // increment hour until cloned date == current date
1288 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
1291 date.setHours(c.getHours());
1298 * Provides a convenient method for performing basic date arithmetic. This method
1299 * does not modify the Date instance being called - it creates and returns
1300 * a new Date instance containing the resulting date value.
1305 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
1306 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
1308 // Negative values will be subtracted:
1309 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
1310 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1314 * @param {Date} date The date to modify
1315 * @param {String} interval A valid date interval enum value.
1316 * @param {Number} value The amount to add to the current date.
1317 * @return {Date} The new Date instance.
1319 add : function(date, interval, value) {
1320 var d = Ext.Date.clone(date),
1322 if (!interval || value === 0) return d;
1324 switch(interval.toLowerCase()) {
1325 case Ext.Date.MILLI:
1326 d.setMilliseconds(d.getMilliseconds() + value);
1328 case Ext.Date.SECOND:
1329 d.setSeconds(d.getSeconds() + value);
1331 case Ext.Date.MINUTE:
1332 d.setMinutes(d.getMinutes() + value);
1335 d.setHours(d.getHours() + value);
1338 d.setDate(d.getDate() + value);
1340 case Ext.Date.MONTH:
1341 var day = date.getDate();
1343 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
1346 d.setMonth(date.getMonth() + value);
1349 d.setFullYear(date.getFullYear() + value);
1356 * Checks if a date falls on or between the given start and end dates.
1357 * @param {Date} date The date to check
1358 * @param {Date} start Start date
1359 * @param {Date} end End date
1360 * @return {Boolean} true if this date falls on or between the given start and end dates.
1362 between : function(date, start, end) {
1363 var t = date.getTime();
1364 return start.getTime() <= t && t <= end.getTime();
1367 //Maintains compatibility with old static and prototype window.Date methods.
1368 compat: function() {
1369 var nativeDate = window.Date,
1371 statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
1372 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
1375 Ext.Array.forEach(statics, function(s) {
1376 nativeDate[s] = utilDate[s];
1379 //Append to prototype
1380 Ext.Array.forEach(proto, function(s) {
1381 nativeDate.prototype[s] = function() {
1382 var args = Array.prototype.slice.call(arguments);
1384 return utilDate[s].apply(utilDate, args);
1390 var utilDate = Ext.Date;