I ran into an interesting problem tonight while working on one of my projects that just so happens to use a little bit of the ExtJs SDK. I had dropped an Ext.form.DateField onto a regular old HTML form for a bit of polish. When I started testing I began to notice strange behaviors when leaving the field (onBlur), or even when bringing up the date selector. The date would change at random!!
After a bit of searching on the Ext forums I did find a couple of posts that indicated that this is a known issue, and that it has been fixed in the current Subversion build. In the meantime they did offer up a script you could include AFTER your Ext includes that would correct the issue in the current build (2.1). Below is a copy of that code for anyone who may be looking for it.
/**
* @file Special hotfix patch released by mystix on Ext development team
* to fix known issues with the DateField class. Next release may see the
* need to remove this.
*/
// private
Date.createParser = function(format) {
var funcName = "parse" + Date.parseFunctions.count++;
var regexNum = Date.parseRegexes.length;
var currentGroup = 1;
Date.parseFunctions[format] = funcName;
var code = "Date." + funcName + " = function(input){\n"
+ "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, ms = -1, o, z, u, v;\n"
+ "input = String(input);var d = new Date();\n"
+ "y = d.getFullYear();\n"
+ "m = d.getMonth();\n"
+ "d = d.getDate();\n"
+ "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
+ "if (results && results.length > 0) {";
var regex = "";
var special = false;
var ch = '';
for (var i = 0; i < format.length; ++i) {
ch = format.charAt(i);
if (!special && ch == "\\") {
special = true;
}
else if (special) {
special = false;
regex += String.escape(ch);
}
else {
var obj = Date.formatCodeToRegex(ch, currentGroup);
currentGroup += obj.g;
regex += obj.s;
if (obj.g && obj.c) {
code += obj.c;
}
}
}
code += "if (u){\n"
+ "v = new Date(u * 1000);\n" // give top priority to UNIX time
+ "}else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0 && ms >= 0){\n"
+ "v = new Date(y, m, d, h, i, s, ms);\n"
+ "}else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0){\n"
+ "v = new Date(y, m, d, h, i, s);\n"
+ "}else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0){\n"
+ "v = new Date(y, m, d, h, i);\n"
+ "}else if (y >= 0 && m >= 0 && d > 0 && h >= 0){\n"
+ "v = new Date(y, m, d, h);\n"
+ "}else if (y >= 0 && m >= 0 && d > 0){\n"
+ "v = new Date(y, m, d);\n"
+ "}else if (y >= 0 && m >= 0){\n"
+ "v = new Date(y, m);\n"
+ "}else if (y >= 0){\n"
+ "v = new Date(y);\n"
+ "}\n}\nreturn (v && (z || o))?" // favour UTC offset over GMT offset
+ " (Ext.type(z) == 'number' ? v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - z) :" // reset to UTC, then add offset
+ " v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn))) : v;\n" // reset to GMT, then add offset
+ "}";
Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$", "i");
eval(code);
};
// private
Ext.apply(Date.parseCodes, {
j: {
g:1,
c:"d = parseInt(results[{0}], 10);\n",
s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
},
M: function() {
for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
return Ext.applyIf({
s:"(" + a.join("|") + ")"
}, Date.formatCodeToRegex("F"));
},
n: {
g:1,
c:"m = parseInt(results[{0}], 10) - 1;\n",
s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
},
o: function() {
return Date.formatCodeToRegex("Y");
},
g: function() {
return Date.formatCodeToRegex("G");
},
h: function() {
return Date.formatCodeToRegex("H");
},
P: {
g:1,
c:[
"o = results[{0}];",
"var sn = o.substring(0,1);", // get + / - sign
"var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);", // get hours (performs minutes-to-hour conversion also, just in case)
"var mn = o.substring(4,6) % 60;", // get minutes
"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
].join("\n"),
s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
}
});
// private
Date.formatCodeToRegex = function(character, currentGroup) {
// Note: currentGroup - position in regex result array (see notes for Date.parseCodes above)
var p = Date.parseCodes[character];
if (p) {
p = Ext.type(p) == 'function'? p() : p;
Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
}
return p? Ext.applyIf({
c: p.c? String.format(p.c, currentGroup || "{0}") : p.c
}, p) : {
g:0,
c:null,
s:Ext.escapeRe(character) // treat unrecognised characters as literals
}
};
Date.prototype.getGMTOffset = function(colon) {
return (this.getTimezoneOffset() > 0 ? "-" : "+")
+ String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
+ (colon ? ":" : "")
+ String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
};