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 };