1 //http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/ 2 /* 3 * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU Lesser General Public License as published by the 7 * Free Software Foundation, version 2.1. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 12 * details. 13 */ 14 Date.parseFunctions = { 15 count: 0 16 }; 17 Date.parseRegexes = []; 18 Date.formatFunctions = { 19 count: 0 20 }; 21 Date.prototype.dateFormat = function(format) { 22 if (Date.formatFunctions[format] == null) { 23 Date.createNewFormat(format) 24 } 25 var func = Date.formatFunctions[format]; 26 return this[func]() 27 }; 28 Date.createNewFormat = function(format) { 29 var funcName = "format" + Date.formatFunctions.count++; 30 Date.formatFunctions[format] = funcName; 31 var code = "Date.prototype." + funcName + " = function() {return "; 32 var special = false; 33 var ch = ''; 34 for (var i = 0; i < format.length; ++i) { 35 ch = format.charAt(i); 36 if (!special && ch == "\\") { 37 special = true 38 } else if (special) { 39 special = false; 40 code += "'" + String.escape(ch) + "' + " 41 } else { 42 code += Date.getFormatCode(ch) 43 } 44 } 45 eval(code.substring(0, code.length - 3) + ";}") 46 }; 47 Date.getFormatCode = function(character) { 48 switch (character) { 49 case "d": 50 return "String.leftPad(this.getDate(), 2, '0') + "; 51 case "D": 52 return "Date.dayNames[this.getDay()].substring(0, 3) + "; 53 case "j": 54 return "this.getDate() + "; 55 case "l": 56 return "Date.dayNames[this.getDay()] + "; 57 case "S": 58 return "this.getSuffix() + "; 59 case "w": 60 return "this.getDay() + "; 61 case "z": 62 return "this.getDayOfYear() + "; 63 case "W": 64 return "this.getWeekOfYear() + "; 65 case "F": 66 return "Date.monthNames[this.getMonth()] + "; 67 case "m": 68 return "String.leftPad(this.getMonth() + 1, 2, '0') + "; 69 case "M": 70 return "Date.monthNames[this.getMonth()].substring(0, 3) + "; 71 case "n": 72 return "(this.getMonth() + 1) + "; 73 case "t": 74 return "this.getDaysInMonth() + "; 75 case "L": 76 return "(this.isLeapYear() ? 1 : 0) + "; 77 case "Y": 78 return "this.getFullYear() + "; 79 case "y": 80 return "('' + this.getFullYear()).substring(2, 4) + "; 81 case "a": 82 return "(this.getHours() < 12 ? 'am' : 'pm') + "; 83 case "A": 84 return "(this.getHours() < 12 ? 'AM' : 'PM') + "; 85 case "g": 86 return "((this.getHours() %12) ? this.getHours() % 12 : 12) + "; 87 case "G": 88 return "this.getHours() + "; 89 case "h": 90 return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + "; 91 case "H": 92 return "String.leftPad(this.getHours(), 2, '0') + "; 93 case "i": 94 return "String.leftPad(this.getMinutes(), 2, '0') + "; 95 case "s": 96 return "String.leftPad(this.getSeconds(), 2, '0') + "; 97 case "O": 98 return "this.getGMTOffset() + "; 99 case "T": 100 return "this.getTimezone() + "; 101 case "Z": 102 return "(this.getTimezoneOffset() * -60) + "; 103 default: 104 return "'" + String.escape(character) + "' + " 105 } 106 }; 107 Date.parseDate = function(input, format) { 108 if (Date.parseFunctions[format] == null) { 109 Date.createParser(format) 110 } 111 var func = Date.parseFunctions[format]; 112 return Date[func](input) 113 }; 114 Date.createParser = function(format) { 115 var funcName = "parse" + Date.parseFunctions.count++; 116 var regexNum = Date.parseRegexes.length; 117 var currentGroup = 1; 118 Date.parseFunctions[format] = funcName; 119 var code = "Date." + funcName + " = function(input) {\n" + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n" + "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) {"; 120 var regex = ""; 121 var special = false; 122 var ch = ''; 123 for (var i = 0; i < format.length; ++i) { 124 ch = format.charAt(i); 125 if (!special && ch == "\\") { 126 special = true 127 } else if (special) { 128 special = false; 129 regex += String.escape(ch) 130 } else { 131 obj = Date.formatCodeToRegex(ch, currentGroup); 132 currentGroup += obj.g; 133 regex += obj.s; 134 if (obj.g && obj.c) { 135 code += obj.c 136 } 137 } 138 } 139 code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n" + "{return new Date(y, m, d, h, i, s);}\n" + "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n" + "{return new Date(y, m, d, h, i);}\n" + "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n" + "{return new Date(y, m, d, h);}\n" + "else if (y > 0 && m >= 0 && d > 0)\n" + "{return new Date(y, m, d);}\n" + "else if (y > 0 && m >= 0)\n" + "{return new Date(y, m);}\n" + "else if (y > 0)\n" + "{return new Date(y);}\n" + "}return null;}"; 140 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$"); 141 eval(code) 142 }; 143 Date.formatCodeToRegex = function(character, currentGroup) { 144 switch (character) { 145 case "D": 146 return { 147 g: 148 0, 149 c: null, 150 s: "(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)" 151 }; 152 case "j": 153 case "d": 154 return { 155 g: 156 1, 157 c: "d = parseInt(results[" + currentGroup + "], 10);\n", 158 s: "(\\d{1,2})" 159 }; 160 case "l": 161 return { 162 g: 163 0, 164 c: null, 165 s: "(?:" + Date.dayNames.join("|") + ")" 166 }; 167 case "S": 168 return { 169 g: 170 0, 171 c: null, 172 s: "(?:st|nd|rd|th)" 173 }; 174 case "w": 175 return { 176 g: 177 0, 178 c: null, 179 s: "\\d" 180 }; 181 case "z": 182 return { 183 g: 184 0, 185 c: null, 186 s: "(?:\\d{1,3})" 187 }; 188 case "W": 189 return { 190 g: 191 0, 192 c: null, 193 s: "(?:\\d{2})" 194 }; 195 case "F": 196 return { 197 g: 198 1, 199 c: "m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n", 200 s: "(" + Date.monthNames.join("|") + ")" 201 }; 202 case "M": 203 return { 204 g: 205 1, 206 c: "m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n", 207 s: "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)" 208 }; 209 case "n": 210 case "m": 211 return { 212 g: 213 1, 214 c: "m = parseInt(results[" + currentGroup + "], 10) - 1;\n", 215 s: "(\\d{1,2})" 216 }; 217 case "t": 218 return { 219 g: 220 0, 221 c: null, 222 s: "\\d{1,2}" 223 }; 224 case "L": 225 return { 226 g: 227 0, 228 c: null, 229 s: "(?:1|0)" 230 }; 231 case "Y": 232 return { 233 g: 234 1, 235 c: "y = parseInt(results[" + currentGroup + "], 10);\n", 236 s: "(\\d{4})" 237 }; 238 case "y": 239 return { 240 g: 241 1, 242 c: "var ty = parseInt(results[" + currentGroup + "], 10);\n" + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", 243 s: "(\\d{1,2})" 244 }; 245 case "a": 246 return { 247 g: 248 1, 249 c: "if (results[" + currentGroup + "] == 'am') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", 250 s: "(am|pm)" 251 }; 252 case "A": 253 return { 254 g: 255 1, 256 c: "if (results[" + currentGroup + "] == 'AM') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", 257 s: "(AM|PM)" 258 }; 259 case "g": 260 case "G": 261 case "h": 262 case "H": 263 return { 264 g: 265 1, 266 c: "h = parseInt(results[" + currentGroup + "], 10);\n", 267 s: "(\\d{1,2})" 268 }; 269 case "i": 270 return { 271 g: 272 1, 273 c: "i = parseInt(results[" + currentGroup + "], 10);\n", 274 s: "(\\d{2})" 275 }; 276 case "s": 277 return { 278 g: 279 1, 280 c: "s = parseInt(results[" + currentGroup + "], 10);\n", 281 s: "(\\d{2})" 282 }; 283 case "O": 284 return { 285 g: 286 0, 287 c: null, 288 s: "[+-]\\d{4}" 289 }; 290 case "T": 291 return { 292 g: 293 0, 294 c: null, 295 s: "[A-Z]{3}" 296 }; 297 case "Z": 298 return { 299 g: 300 0, 301 c: null, 302 s: "[+-]\\d{1,5}" 303 }; 304 default: 305 return { 306 g: 307 0, 308 c: null, 309 s: String.escape(character) 310 } 311 } 312 }; 313 Date.prototype.getTimezone = function() { 314 return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3") 315 }; 316 Date.prototype.getGMTOffset = function() { 317 return (this.getTimezoneOffset() > 0 ? "-": "+") + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0") + String.leftPad(Math.abs(this.getTimezoneOffset()) % 60, 2, "0") 318 }; 319 Date.prototype.getDayOfYear = function() { 320 var num = 0; 321 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; 322 for (var i = 0; i < this.getMonth(); ++i) { 323 num += Date.daysInMonth[i] 324 } 325 return num + this.getDate() - 1 326 }; 327 Date.prototype.getWeekOfYear = function() { 328 var now = this.getDayOfYear() + (4 - this.getDay()); 329 var jan1 = new Date(this.getFullYear(), 0, 1); 330 var then = (7 - jan1.getDay() + 4); 331 document.write(then); 332 return String.leftPad(((now - then) / 7) + 1, 2, "0") 333 }; 334 Date.prototype.isLeapYear = function() { 335 var year = this.getFullYear(); 336 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))) 337 }; 338 Date.prototype.getFirstDayOfMonth = function() { 339 var day = (this.getDay() - (this.getDate() - 1)) % 7; 340 return (day < 0) ? (day + 7) : day 341 }; 342 Date.prototype.getLastDayOfMonth = function() { 343 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7; 344 return (day < 0) ? (day + 7) : day 345 }; 346 Date.prototype.getDaysInMonth = function() { 347 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; 348 return Date.daysInMonth[this.getMonth()] 349 }; 350 Date.prototype.getSuffix = function() { 351 switch (this.getDate()) { 352 case 1: 353 case 21: 354 case 31: 355 return "st"; 356 case 2: 357 case 22: 358 return "nd"; 359 case 3: 360 case 23: 361 return "rd"; 362 default: 363 return "th" 364 } 365 }; 366 String.escape = function(string) { 367 return string.replace(/('|\\)/g, "\\$1") 368 }; 369 String.leftPad = function(val, size, ch) { 370 var result = new String(val); 371 if (ch == null) { 372 ch = " " 373 } 374 while (result.length < size) { 375 result = ch + result 376 } 377 return result 378 }; 379 Date.daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 380 Date.monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 381 Date.dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 382 Date.y2kYear = 50; 383 Date.monthNumbers = { 384 Jan: 0, 385 Feb: 1, 386 Mar: 2, 387 Apr: 3, 388 May: 4, 389 Jun: 5, 390 Jul: 6, 391 Aug: 7, 392 Sep: 8, 393 Oct: 9, 394 Nov: 10, 395 Dec: 11 396 }; 397 Date.patterns = { 398 ISO8601LongPattern: "Y-m-d H:i:s", 399 ISO8601ShortPattern: "Y-m-d", 400 ShortDatePattern: "n/j/Y", 401 LongDatePattern: "l, F d, Y", 402 FullDateTimePattern: "l, F d, Y g:i:s A", 403 MonthDayPattern: "F d", 404 ShortTimePattern: "g:i A", 405 LongTimePattern: "g:i:s A", 406 SortableDateTimePattern: "Y-m-d\\TH:i:s", 407 UniversalSortableDateTimePattern: "Y-m-d H:i:sO", 408 YearMonthPattern: "F, Y" 409 };