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