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>.
190 formatCodeToRegex: function(character, currentGroup) {
191 // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
192 var p = utilDate.parseCodes[character];
195 p = typeof p == 'function'? p() : p;
196 utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
199 return p ? Ext.applyIf({
200 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
204 s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
209 * <p>An object hash in which each property is a date parsing function. The property name is the
210 * format string which that function parses.</p>
211 * <p>This object is automatically populated with date parsing functions as
212 * date formats are requested for Ext standard formatting strings.</p>
213 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
214 * may be used as a format string to {@link #parse}.<p>
215 * <p>Example:</p><pre><code>
216 Ext.Date.parseFunctions['x-date-format'] = myDateParser;
218 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
219 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
220 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
221 * (i.e. prevent javascript Date "rollover") (The default must be false).
222 * Invalid date strings should return null when parsed.</div></li>
224 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
225 * formatting function must be placed into the {@link #formatFunctions} property.
226 * @property parseFunctions
230 "MS": function(input, strict) {
231 // note: the timezone offset is ignored since the MS Ajax server sends
232 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
233 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
234 var r = (input || '').match(re);
235 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
241 * <p>An object hash in which each property is a date formatting function. The property name is the
242 * format string which corresponds to the produced formatted date string.</p>
243 * <p>This object is automatically populated with date formatting functions as
244 * date formats are requested for Ext standard formatting strings.</p>
245 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
246 * may be used as a format string to {@link #format}. Example:</p><pre><code>
247 Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
249 * <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>
250 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
252 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
253 * parsing function must be placed into the {@link #parseFunctions} property.
254 * @property formatFunctions
259 // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
260 return '\\/Date(' + this.getTime() + ')\\/';
267 * Date interval constant
273 * Date interval constant
279 * Date interval constant
284 /** Date interval constant
290 * Date interval constant
296 * Date interval constant
302 * Date interval constant
308 * <p>An object hash containing default date values used during date parsing.</p>
309 * <p>The following properties are available:<div class="mdetail-params"><ul>
310 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
311 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
312 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
313 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
314 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
315 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
316 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
318 * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
319 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
320 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
321 * It is the responsiblity of the developer to account for this.</b></p>
324 // set default day value to the first day of the month
325 Ext.Date.defaults.d = 1;
327 // parse a February date string containing only year and month values.
328 // setting the default day value to 1 prevents weird date rollover issues
329 // when attempting to parse the following date string on, for example, March 31st 2009.
330 Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
338 * @property {String[]} dayNames
339 * An array of textual day names.
340 * Override these values for international dates.
343 Ext.Date.dayNames = [
361 * @property {String[]} monthNames
362 * An array of textual month names.
363 * Override these values for international dates.
366 Ext.Date.monthNames = [
389 * @property {Object} monthNumbers
390 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
391 * Override these values for international dates.
394 Ext.Date.monthNumbers = {
395 'ShortJanNameInYourLang':0,
396 'ShortFebNameInYourLang':1,
416 * @property {String} defaultFormat
417 * <p>The date format string that the {@link Ext.util.Format#dateRenderer}
418 * and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.</p>
419 * <p>This may be overridden in a locale file.</p>
421 defaultFormat : "m/d/Y",
423 * Get the short month name for the given month number.
424 * Override this function for international dates.
425 * @param {Number} month A zero-based javascript month number.
426 * @return {String} The short month name.
428 getShortMonthName : function(month) {
429 return utilDate.monthNames[month].substring(0, 3);
433 * Get the short day name for the given day number.
434 * Override this function for international dates.
435 * @param {Number} day A zero-based javascript day number.
436 * @return {String} The short day name.
438 getShortDayName : function(day) {
439 return utilDate.dayNames[day].substring(0, 3);
443 * Get the zero-based javascript month number for the given short/full month name.
444 * Override this function for international dates.
445 * @param {String} name The short/full month name.
446 * @return {Number} The zero-based javascript month number.
448 getMonthNumber : function(name) {
449 // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
450 return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
454 * Checks if the specified format contains hour information
455 * @param {String} format The format to check
456 * @return {Boolean} True if the format contains hour information
459 formatContainsHourInfo : (function(){
460 var stripEscapeRe = /(\\.)/g,
461 hourInfoRe = /([gGhHisucUOPZ]|MS)/;
462 return function(format){
463 return hourInfoRe.test(format.replace(stripEscapeRe, ''));
468 * Checks if the specified format contains information about
469 * anything other than the time.
470 * @param {String} format The format to check
471 * @return {Boolean} True if the format contains information about
472 * date/day information.
475 formatContainsDateInfo : (function(){
476 var stripEscapeRe = /(\\.)/g,
477 dateInfoRe = /([djzmnYycU]|MS)/;
479 return function(format){
480 return dateInfoRe.test(format.replace(stripEscapeRe, ''));
485 * The base format-code to formatting-function hashmap used by the {@link #format} method.
486 * Formatting functions are strings (or functions which return strings) which
487 * will return the appropriate value when evaluated in the context of the Date object
488 * from which the {@link #format} method is called.
489 * Add to / override these mappings for custom date formatting.
490 * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
493 Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
494 console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
499 d: "Ext.String.leftPad(this.getDate(), 2, '0')",
500 D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
502 l: "Ext.Date.dayNames[this.getDay()]",
503 N: "(this.getDay() ? this.getDay() : 7)",
504 S: "Ext.Date.getSuffix(this)",
506 z: "Ext.Date.getDayOfYear(this)",
507 W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
508 F: "Ext.Date.monthNames[this.getMonth()]",
509 m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
510 M: "Ext.Date.getShortMonthName(this.getMonth())", // get localised short month name
511 n: "(this.getMonth() + 1)",
512 t: "Ext.Date.getDaysInMonth(this)",
513 L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
514 o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
515 Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
516 y: "('' + this.getFullYear()).substring(2, 4)",
517 a: "(this.getHours() < 12 ? 'am' : 'pm')",
518 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
519 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
520 G: "this.getHours()",
521 h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
522 H: "Ext.String.leftPad(this.getHours(), 2, '0')",
523 i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
524 s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
525 u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
526 O: "Ext.Date.getGMTOffset(this)",
527 P: "Ext.Date.getGMTOffset(this, true)",
528 T: "Ext.Date.getTimezone(this)",
529 Z: "(this.getTimezoneOffset() * -60)",
531 c: function() { // ISO-8601 -- GMT format
532 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
534 code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
536 return code.join(" + ");
539 c: function() { // ISO-8601 -- UTC format
541 "this.getUTCFullYear()", "'-'",
542 "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
543 "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
545 "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
546 "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
547 "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
553 U: "Math.round(this.getTime() / 1000)"
557 * Checks if the passed Date parameters will cause a javascript Date "rollover".
558 * @param {Number} year 4-digit year
559 * @param {Number} month 1-based month-of-year
560 * @param {Number} day Day of month
561 * @param {Number} hour (optional) Hour
562 * @param {Number} minute (optional) Minute
563 * @param {Number} second (optional) Second
564 * @param {Number} millisecond (optional) Millisecond
565 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
567 isValid : function(y, m, d, h, i, s, ms) {
574 // Special handling for year < 100
575 var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);
577 return y == dt.getFullYear() &&
578 m == dt.getMonth() + 1 &&
580 h == dt.getHours() &&
581 i == dt.getMinutes() &&
582 s == dt.getSeconds() &&
583 ms == dt.getMilliseconds();
587 * Parses the passed string using the specified date format.
588 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
589 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
590 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
591 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
592 * Keep in mind that the input date string must precisely match the specified format string
593 * in order for the parse operation to be successful (failed parse operations return a null value).
594 * <p>Example:</p><pre><code>
595 //dt = Fri May 25 2007 (current date)
598 //dt = Thu May 25 2006 (today's month/day in 2006)
599 dt = Ext.Date.parse("2006", "Y");
601 //dt = Sun Jan 15 2006 (all date parts specified)
602 dt = Ext.Date.parse("2006-01-15", "Y-m-d");
604 //dt = Sun Jan 15 2006 15:20:01
605 dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
607 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
608 dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
610 * @param {String} input The raw date string.
611 * @param {String} format The expected date string format.
612 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
613 (defaults to false). Invalid date strings will return null when parsed.
614 * @return {Date} The parsed Date.
616 parse : function(input, format, strict) {
617 var p = utilDate.parseFunctions;
618 if (p[format] == null) {
619 utilDate.createParser(format);
621 return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
625 parseDate: function(input, format, strict){
626 return utilDate.parse(input, format, strict);
631 getFormatCode : function(character) {
632 var f = utilDate.formatCodes[character];
635 f = typeof f == 'function'? f() : f;
636 utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
639 // note: unknown characters are treated as literals
640 return f || ("'" + Ext.String.escape(character) + "'");
644 createFormat : function(format) {
649 for (var i = 0; i < format.length; ++i) {
650 ch = format.charAt(i);
651 if (!special && ch == "\\") {
653 } else if (special) {
655 code.push("'" + Ext.String.escape(ch) + "'");
657 code.push(utilDate.getFormatCode(ch));
660 utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
664 createParser : (function() {
666 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
667 "def = Ext.Date.defaults,",
668 "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings
673 "if(u != null){", // i.e. unix time is defined
674 "v = new Date(u * 1000);", // give top priority to UNIX time
676 // create Date object representing midnight of the current day;
677 // this will provide us with our date defaults
678 // (note: clearTime() handles Daylight Saving Time automatically)
679 "dt = Ext.Date.clearTime(new Date);",
681 // date calculations (note: these calculations create a dependency on Ext.Number.from())
682 "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
683 "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
684 "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",
686 // time calculations (note: these calculations create a dependency on Ext.Number.from())
687 "h = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
688 "i = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
689 "s = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
690 "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",
692 "if(z >= 0 && y >= 0){",
693 // both the year and zero-based day of year are defined and >= 0.
694 // these 2 values alone provide sufficient info to create a full date object
696 // create Date object representing January 1st for the given year
697 // handle years < 100 appropriately
698 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
700 // then add day of year, checking for Date "rollover" if necessary
701 "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
702 "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
703 "v = null;", // invalid date, so return null
705 // plain old Date object
706 // handle years < 100 properly
707 "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
713 // favour UTC offset over GMT offset
715 // reset to UTC, then add offset
716 "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
718 // reset to GMT, then add offset
719 "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
726 return function(format) {
727 var regexNum = utilDate.parseRegexes.length,
734 for (var i = 0; i < format.length; ++i) {
735 ch = format.charAt(i);
736 if (!special && ch == "\\") {
738 } else if (special) {
740 regex.push(Ext.String.escape(ch));
742 var obj = utilDate.formatCodeToRegex(ch, currentGroup);
743 currentGroup += obj.g;
745 if (obj.g && obj.c) {
751 utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
752 utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
760 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
761 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
762 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
766 c:"d = parseInt(results[{0}], 10);\n",
767 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
771 c:"d = parseInt(results[{0}], 10);\n",
772 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
775 for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
779 s:"(?:" + a.join("|") +")"
786 s:"(?:" + utilDate.dayNames.join("|") + ")"
792 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
802 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
806 c:"z = parseInt(results[{0}], 10);\n",
807 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
812 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
817 c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
818 s:"(" + utilDate.monthNames.join("|") + ")"
822 for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
824 s:"(" + a.join("|") + ")"
825 }, utilDate.formatCodeToRegex("F"));
829 c:"m = parseInt(results[{0}], 10) - 1;\n",
830 s:"(\\d{2})" // month number with leading zeros (01 - 12)
834 c:"m = parseInt(results[{0}], 10) - 1;\n",
835 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
840 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
848 return utilDate.formatCodeToRegex("Y");
852 c:"y = parseInt(results[{0}], 10);\n",
853 s:"(\\d{4})" // 4-digit year
857 c:"var ty = parseInt(results[{0}], 10);\n"
858 + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
862 * In the am/pm parsing routines, we allow both upper and lower case
863 * even though it doesn't exactly match the spec. It gives much more flexibility
864 * in being able to specify case insensitive regexes.
868 c:"if (/(am)/i.test(results[{0}])) {\n"
869 + "if (!h || h == 12) { h = 0; }\n"
870 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
875 c:"if (/(am)/i.test(results[{0}])) {\n"
876 + "if (!h || h == 12) { h = 0; }\n"
877 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
881 return utilDate.formatCodeToRegex("G");
885 c:"h = parseInt(results[{0}], 10);\n",
886 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
889 return utilDate.formatCodeToRegex("H");
893 c:"h = parseInt(results[{0}], 10);\n",
894 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
898 c:"i = parseInt(results[{0}], 10);\n",
899 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
903 c:"s = parseInt(results[{0}], 10);\n",
904 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
908 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
909 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
915 "var sn = o.substring(0,1),", // get + / - sign
916 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
917 "mn = o.substring(3,5) % 60;", // get minutes
918 "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
920 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
926 "var sn = o.substring(0,1),", // get + / - sign
927 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
928 "mn = o.substring(4,6) % 60;", // get minutes
929 "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
931 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
936 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
940 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
941 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
942 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
947 utilDate.formatCodeToRegex("Y", 1), // year
948 utilDate.formatCodeToRegex("m", 2), // month
949 utilDate.formatCodeToRegex("d", 3), // day
950 utilDate.formatCodeToRegex("h", 4), // hour
951 utilDate.formatCodeToRegex("i", 5), // minute
952 utilDate.formatCodeToRegex("s", 6), // second
953 {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)
954 {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
955 "if(results[8]) {", // timezone specified
956 "if(results[8] == 'Z'){",
958 "}else if (results[8].indexOf(':') > -1){",
959 utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
961 utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
967 for (var i = 0, l = arr.length; i < l; ++i) {
975 arr[0].s, // year (required)
976 "(?:", "-", arr[1].s, // month (optional)
977 "(?:", "-", arr[2].s, // day (optional)
979 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
980 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
981 "(?::", arr[5].s, ")?", // seconds (optional)
982 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
983 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
992 c:"u = parseInt(results[{0}], 10);\n",
993 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
997 //Old Ext.Date prototype methods.
999 dateFormat: function(date, format) {
1000 return utilDate.format(date, format);
1004 * Formats a date given the supplied format string.
1005 * @param {Date} date The date to format
1006 * @param {String} format The format string
1007 * @return {String} The formatted date
1009 format: function(date, format) {
1010 if (utilDate.formatFunctions[format] == null) {
1011 utilDate.createFormat(format);
1013 var result = utilDate.formatFunctions[format].call(date);
1018 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1020 * Note: The date string returned by the javascript Date object's toString() method varies
1021 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
1022 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
1023 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
1024 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
1025 * from the GMT offset portion of the date string.
1026 * @param {Date} date The date
1027 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
1029 getTimezone : function(date) {
1030 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
1032 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
1033 // 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)
1034 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
1035 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
1036 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
1038 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
1039 // step 1: (?:\((.*)\) -- find timezone in parentheses
1040 // 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
1041 // step 3: remove all non uppercase characters found in step 1 and 2
1042 return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
1046 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1047 * @param {Date} date The date
1048 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
1049 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
1051 getGMTOffset : function(date, colon) {
1052 var offset = date.getTimezoneOffset();
1053 return (offset > 0 ? "-" : "+")
1054 + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
1055 + (colon ? ":" : "")
1056 + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
1060 * Get the numeric day number of the year, adjusted for leap year.
1061 * @param {Date} date The date
1062 * @return {Number} 0 to 364 (365 in leap years).
1064 getDayOfYear: function(date) {
1066 d = Ext.Date.clone(date),
1067 m = date.getMonth(),
1070 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
1071 num += utilDate.getDaysInMonth(d);
1073 return num + date.getDate() - 1;
1077 * Get the numeric ISO-8601 week number of the year.
1078 * (equivalent to the format specifier 'W', but without a leading zero).
1079 * @param {Date} date The date
1080 * @return {Number} 1 to 53
1083 getWeekOfYear : (function() {
1084 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
1085 var ms1d = 864e5, // milliseconds in a day
1086 ms7d = 7 * ms1d; // milliseconds in a week
1088 return function(date) { // return a closure so constants get calculated only once
1089 var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
1090 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
1091 Wyr = new Date(AWN * ms7d).getUTCFullYear();
1093 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
1098 * Checks if the current date falls within a leap year.
1099 * @param {Date} date The date
1100 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
1102 isLeapYear : function(date) {
1103 var year = date.getFullYear();
1104 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1108 * Get the first day of the current month, adjusted for leap year. The returned value
1109 * is the numeric day index within the week (0-6) which can be used in conjunction with
1110 * the {@link #monthNames} array to retrieve the textual day name.
1113 var dt = new Date('1/10/2007'),
1114 firstDay = Ext.Date.getFirstDayOfMonth(dt);
1115 console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
1117 * @param {Date} date The date
1118 * @return {Number} The day number (0-6).
1120 getFirstDayOfMonth : function(date) {
1121 var day = (date.getDay() - (date.getDate() - 1)) % 7;
1122 return (day < 0) ? (day + 7) : day;
1126 * Get the last day of the current month, adjusted for leap year. The returned value
1127 * is the numeric day index within the week (0-6) which can be used in conjunction with
1128 * the {@link #monthNames} array to retrieve the textual day name.
1131 var dt = new Date('1/10/2007'),
1132 lastDay = Ext.Date.getLastDayOfMonth(dt);
1133 console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
1135 * @param {Date} date The date
1136 * @return {Number} The day number (0-6).
1138 getLastDayOfMonth : function(date) {
1139 return utilDate.getLastDateOfMonth(date).getDay();
1144 * Get the date of the first day of the month in which this date resides.
1145 * @param {Date} date The date
1148 getFirstDateOfMonth : function(date) {
1149 return new Date(date.getFullYear(), date.getMonth(), 1);
1153 * Get the date of the last day of the month in which this date resides.
1154 * @param {Date} date The date
1157 getLastDateOfMonth : function(date) {
1158 return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
1162 * Get the number of days in the current month, adjusted for leap year.
1163 * @param {Date} date The date
1164 * @return {Number} The number of days in the month.
1167 getDaysInMonth: (function() {
1168 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1170 return function(date) { // return a closure for efficiency
1171 var m = date.getMonth();
1173 return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
1178 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1179 * @param {Date} date The date
1180 * @return {String} 'st, 'nd', 'rd' or 'th'.
1182 getSuffix : function(date) {
1183 switch (date.getDate()) {
1200 * Creates and returns a new Date instance with the exact same date value as the called instance.
1201 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1202 * variable will also be changed. When the intention is to create a new variable that will not
1203 * modify the original instance, you should create a clone.
1205 * Example of correctly cloning a date:
1208 var orig = new Date('10/1/2006');
1211 console.log(orig); //returns 'Thu Oct 05 2006'!
1214 var orig = new Date('10/1/2006'),
1215 copy = Ext.Date.clone(orig);
1217 console.log(orig); //returns 'Thu Oct 01 2006'
1219 * @param {Date} date The date
1220 * @return {Date} The new Date instance.
1222 clone : function(date) {
1223 return new Date(date.getTime());
1227 * Checks if the current date is affected by Daylight Saving Time (DST).
1228 * @param {Date} date The date
1229 * @return {Boolean} True if the current date is affected by DST.
1231 isDST : function(date) {
1232 // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
1233 // courtesy of @geoffrey.mcgill
1234 return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
1238 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
1239 * automatically adjusting for Daylight Saving Time (DST) where applicable.
1240 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
1241 * @param {Date} date The date
1242 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
1243 * @return {Date} this or the clone.
1245 clearTime : function(date, clone) {
1247 return Ext.Date.clearTime(Ext.Date.clone(date));
1250 // get current date before clearing time
1251 var d = date.getDate();
1257 date.setMilliseconds(0);
1259 if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
1260 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
1261 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
1263 // increment hour until cloned date == current date
1264 for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));
1267 date.setHours(c.getHours());
1274 * Provides a convenient method for performing basic date arithmetic. This method
1275 * does not modify the Date instance being called - it creates and returns
1276 * a new Date instance containing the resulting date value.
1281 var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
1282 console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'
1284 // Negative values will be subtracted:
1285 var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
1286 console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1290 * @param {Date} date The date to modify
1291 * @param {String} interval A valid date interval enum value.
1292 * @param {Number} value The amount to add to the current date.
1293 * @return {Date} The new Date instance.
1295 add : function(date, interval, value) {
1296 var d = Ext.Date.clone(date),
1298 if (!interval || value === 0) return d;
1300 switch(interval.toLowerCase()) {
1301 case Ext.Date.MILLI:
1302 d.setMilliseconds(d.getMilliseconds() + value);
1304 case Ext.Date.SECOND:
1305 d.setSeconds(d.getSeconds() + value);
1307 case Ext.Date.MINUTE:
1308 d.setMinutes(d.getMinutes() + value);
1311 d.setHours(d.getHours() + value);
1314 d.setDate(d.getDate() + value);
1316 case Ext.Date.MONTH:
1317 var day = date.getDate();
1319 day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
1322 d.setMonth(date.getMonth() + value);
1325 d.setFullYear(date.getFullYear() + value);
1332 * Checks if a date falls on or between the given start and end dates.
1333 * @param {Date} date The date to check
1334 * @param {Date} start Start date
1335 * @param {Date} end End date
1336 * @return {Boolean} true if this date falls on or between the given start and end dates.
1338 between : function(date, start, end) {
1339 var t = date.getTime();
1340 return start.getTime() <= t && t <= end.getTime();
1343 //Maintains compatibility with old static and prototype window.Date methods.
1344 compat: function() {
1345 var nativeDate = window.Date,
1347 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'],
1348 proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];
1351 Ext.Array.forEach(statics, function(s) {
1352 nativeDate[s] = utilDate[s];
1355 //Append to prototype
1356 Ext.Array.forEach(proto, function(s) {
1357 nativeDate.prototype[s] = function() {
1358 var args = Array.prototype.slice.call(arguments);
1360 return utilDate[s].apply(utilDate, args);
1366 var utilDate = Ext.Date;