/* * Copyright (C) 2004 Baron Schwartz * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, version 2.1. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ Date.parseFunctions = {count:0}; Date.parseRegexes = []; Date.formatFunctions = {count:0}; Date.prototype.dateFormat = function(format) { if (Date.formatFunctions[format] == null) { Date.createNewFormat(format); } var func = Date.formatFunctions[format]; return this[func](); } Date.createNewFormat = function(format) { var funcName = "format" + Date.formatFunctions.count++; Date.formatFunctions[format] = funcName; var code = "Date.prototype." + funcName + " = function(){return "; 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; code += "'" + String.escape(ch) + "' + "; } else { code += Date.getFormatCode(ch); } } eval(code.substring(0, code.length - 3) + ";}"); } Date.getFormatCode = function(character) { switch (character) { case "d": return "String.leftPad(this.getDate(), 2, '0') + "; case "D": return "Date.dayNames[this.getDay()].substring(0, 3) + "; case "j": return "this.getDate() + "; case "l": return "Date.dayNames[this.getDay()] + "; case "S": return "this.getSuffix() + "; case "w": return "this.getDay() + "; case "z": return "this.getDayOfYear() + "; case "W": return "this.getWeekOfYear() + "; case "F": return "Date.monthNames[this.getMonth()] + "; case "m": return "String.leftPad(this.getMonth() + 1, 2, '0') + "; case "M": return "Date.monthNames[this.getMonth()].substring(0, 3) + "; case "n": return "(this.getMonth() + 1) + "; case "t": return "this.getDaysInMonth() + "; case "L": return "(this.isLeapYear() ? 1 : 0) + "; case "Y": return "this.getFullYear() + "; case "y": return "('' + this.getFullYear()).substring(2, 4) + "; case "a": return "(this.getHours() < 12 ? 'am' : 'pm') + "; case "A": return "(this.getHours() < 12 ? 'AM' : 'PM') + "; case "g": return "((this.getHours() %12) ? this.getHours() % 12 : 12) + "; case "G": return "this.getHours() + "; case "h": return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + "; case "H": return "String.leftPad(this.getHours(), 2, '0') + "; case "i": return "String.leftPad(this.getMinutes(), 2, '0') + "; case "s": return "String.leftPad(this.getSeconds(), 2, '0') + "; case "O": return "this.getGMTOffset() + "; case "T": return "this.getTimezone() + "; case "Z": return "(this.getTimezoneOffset() * -60) + "; default: return "'" + String.escape(character) + "' + "; } } Date.parseDate = function(input, format) { if (Date.parseFunctions[format] == null) { Date.createParser(format); } var func = Date.parseFunctions[format]; return Date[func](input); } 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;\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) {" 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 { obj = Date.formatCodeToRegex(ch, currentGroup); currentGroup += obj.g; regex += obj.s; if (obj.g && obj.c) { code += obj.c; } } } 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;}"; Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$"); eval(code); } Date.formatCodeToRegex = function(character, currentGroup) { switch (character) { case "D": return {g:0, c:null, s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"}; case "j": case "d": return {g:1, c:"d = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{1,2})"}; case "l": return {g:0, c:null, s:"(?:" + Date.dayNames.join("|") + ")"}; case "S": return {g:0, c:null, s:"(?:st|nd|rd|th)"}; case "w": return {g:0, c:null, s:"\\d"}; case "z": return {g:0, c:null, s:"(?:\\d{1,3})"}; case "W": return {g:0, c:null, s:"(?:\\d{2})"}; case "F": return {g:1, c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n", s:"(" + Date.monthNames.join("|") + ")"}; case "M": return {g:1, c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n", s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"}; case "n": case "m": return {g:1, c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n", s:"(\\d{1,2})"}; case "t": return {g:0, c:null, s:"\\d{1,2}"}; case "L": return {g:0, c:null, s:"(?:1|0)"}; case "Y": return {g:1, c:"y = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{4})"}; case "y": return {g:1, c:"var ty = parseInt(results[" + currentGroup + "], 10);\n" + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", s:"(\\d{1,2})"}; case "a": return {g:1, c:"if (results[" + currentGroup + "] == 'am') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s:"(am|pm)"}; case "A": return {g:1, c:"if (results[" + currentGroup + "] == 'AM') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s:"(AM|PM)"}; case "g": case "G": case "h": case "H": return {g:1, c:"h = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{1,2})"}; case "i": return {g:1, c:"i = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{2})"}; case "s": return {g:1, c:"s = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{2})"}; case "O": return {g:0, c:null, s:"[+-]\\d{4}"}; case "T": return {g:0, c:null, s:"[A-Z]{3}"}; case "Z": return {g:0, c:null, s:"[+-]\\d{1,5}"}; default: return {g:0, c:null, s:String.escape(character)}; } } Date.prototype.getTimezone = function() { 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"); } Date.prototype.getGMTOffset = function() { return (this.getTimezoneOffset() > 0 ? "-" : "+") + String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0") + String.leftPad(this.getTimezoneOffset() % 60, 2, "0"); } Date.prototype.getDayOfYear = function() { var num = 0; Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; for (var i = 0; i < this.getMonth(); ++i) { num += Date.daysInMonth[i]; } return num + this.getDate() - 1; } Date.prototype.getWeekOfYear = function() { // Skip to Thursday of this week var now = this.getDayOfYear() + (4 - this.getDay()); // Find the first Thursday of the year var jan1 = new Date(this.getFullYear(), 0, 1); var then = (7 - jan1.getDay() + 4); document.write(then); return String.leftPad(((now - then) / 7) + 1, 2, "0"); } Date.prototype.isLeapYear = function() { var year = this.getFullYear(); return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))); } Date.prototype.getFirstDayOfMonth = function() { var day = (this.getDay() - (this.getDate() - 1)) % 7; return (day < 0) ? (day + 7) : day; } Date.prototype.getLastDayOfMonth = function() { var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7; return (day < 0) ? (day + 7) : day; } Date.prototype.getDaysInMonth = function() { Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; return Date.daysInMonth[this.getMonth()]; } Date.prototype.getSuffix = function() { switch (this.getDate()) { case 1: case 21: case 31: return "st"; case 2: case 22: return "nd"; case 3: case 23: return "rd"; default: return "th"; } } String.escape = function(string) { return string.replace(/('|\\)/g, "\\$1"); } String.leftPad = function (val, size, ch) { var result = new String(val); if (ch == null) { ch = " "; } while (result.length < size) { result = ch + result; } return result; } Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]; Date.monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; Date.dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; Date.y2kYear = 50; Date.monthNumbers = { Jan:0, Feb:1, Mar:2, Apr:3, May:4, Jun:5, Jul:6, Aug:7, Sep:8, Oct:9, Nov:10, Dec:11}; Date.patterns = { ISO8601LongPattern:"Y-m-d H:i:s", ISO8601ShortPattern:"Y-m-d", ShortDatePattern: "n/j/Y", LongDatePattern: "l, F d, Y", FullDateTimePattern: "l, F d, Y g:i:s A", MonthDayPattern: "F d", ShortTimePattern: "g:i A", LongTimePattern: "g:i:s A", SortableDateTimePattern: "Y-m-d\\TH:i:s", UniversalSortableDateTimePattern: "Y-m-d H:i:sO", YearMonthPattern: "F, Y"}; var date = new Date("1/1/2007 1:11:11"); for (i = 0; i < 900000; ++i) { var shortFormat = date.dateFormat("Y-m-d"); var longFormat = date.dateFormat("l, F d, Y g:i:s A"); date.setTime(date.getTime() + 84266956); } // FIXME: Find a way to validate this test. // https://bugs.webkit.org/show_bug.cgi?id=114849