(function() {
Date.ATOM = 'Y-m-d\\TH:i:sP';
Date.COOKIE = 'l, d-M-y H:i:s T';
Date.ISO8601 = 'Y-m-d\\TH:i:sO';
Date.RFC822 = 'D, d M y H:i:s O';
Date.RFC850 = 'l, d-M-y H:i:s T';
Date.RFC1036 = 'D, d M y H:i:s O';
Date.RFC1123 = 'D, d M Y H:i:s O';
Date.RFC2822 = 'D, d M Y H:i:s O';
Date.RFC3339 = 'Y-m-d\\TH:i:sP';
Date.RSS = 'D, d M Y H:i:s O';
Date.W3C = 'Y-m-d\\TH:i:sP';
var monthFullNames = [
	'January', 'February', 'March', 'April', 'May', 'June',
	'July', 'August', 'September', 'October', 'November', 'December'
];
var monthShortNames = [
	'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
	'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
var dayFullNames = [
	'Sunday', 'Monday', 'Tuesday', 'Wednesday',
	'Thursday', 'Friday', 'Saturday'
];
var dayShortNames = [
	'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'
];
var timezone = {
	id  : 'Asia/Tokyo',
	abbr: 'JST'
};
var DateParse = Date.parse;
var parsePattern = [
	/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([\+|\-]{1}\d{2}:\d{2})?/,
	/^[a-z]+, (\d{2,})[\s\-]([a-z]{3})[\s\-](\d{2}) (\d{2}):(\d{2}):(\d{2})\s*(.+)?/i
];
var isOpera = typeof window.opera !== 'undefined';
function inArray(array, value) {
	if (array.indexOf) {
		return array.indexOf(value);
	}
	for (var i = 0, length = array.length; i < length; ++i) {
		if (array[i] === value) {
			return i;
		}
	}
	return -1;
}
function zp(value, digit) {
	digit = digit || 2;
	for (var i = 0; i < digit; ++i) {
		value = '0' + value;
	}
	return value.slice(-digit);
}
Date.parse = function(dateString, baseYear) {
	var m, time;
	time = isOpera ? NaN : DateParse(dateString);
	if (!isNaN(time)) {
		return time;
	}
	if (m = dateString.match(parsePattern[0])) {
		time = Date.UTC(m[1], m[2] - 1, m[3], m[4], m[5], m[6]);
		return m[7] ? Date.applyDiffTime(time, m[7]) : time;
	}
	if (m = dateString.match(parsePattern[1])) {
		time = Date.UTC(+m[3] + (baseYear || 2000), inArray(monthShortNames, m[2]), m[1], m[4], m[5], m[6]);
		return m[7] ? Date.applyDiffTime(time, m[7]) : time;
	}
	return isOpera && isNaN(time) ? DateParse(dateString) : time;
};
Date.applyDiffTime = function(time, diff) {
	diff = diff.replace('JST', '+0900').replace('UTC', '+0000');
	var diffTime = diff.match(/\d{2}/g);
	diffTime = ((Math.abs(diffTime[0]) * 60) + Math.abs(diffTime[1])) * 60 * 1000;
	return diff.match(/^./)[0] === '-' ? time + diffTime : time - diffTime;
};
Date.prototype.getISOWeekNumber = function(year, month, date) {
	var _doy, doy;
	year  = year  || this.getFullYear();
	month = month || this.getMonth();
	date  = date  || this.getDate();
	_doy = (new Date(year, 0, 4).getDay() || 7) - 3;
	doy = _doy + this.getElapseDays(year);
	if (doy <= 0) {
		year = year - 1;
		doy  = _doy + (new Date(year, 11, 31).getElapseDays(year));
	}
	if (month < 11 || date < 29) {
		return Math.ceil(doy / 7);
	}
	if ((this.getDay() || 7) <= (3 - (31 - date))) {
		return 1;
	}
	return Math.ceil(doy / 7);
};
Date.prototype.getISOYear = function(year, month, date) {
	year  = year  || this.getFullYear();
	month = month || this.getMonth();
	date  = date  || this.getDate();
	weekNumber = this.getISOWeekNumber(year, month, date);
	return date <= 3 && weekNumber >= 52 ? year - 1
	     : date >= 29 && weekNumber == 1 ? year + 1
	     : year;
};
Date.prototype.getUTCTime = function() {
	return this.getTime() + (this.getTimezoneOffset() * 60 * 1000);
};
Date.prototype.isLeapYear = function(year) {
	year = year || this.getFullYear();
	return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};
Date.prototype.getInternetTime = function(hour, min, sec) {
	var beat, gmt = this.toGMTString().split(' ')[4].split(':');
	hour = hour || +gmt[0];
	min  = min  || +gmt[1];
	sec  = sec  || +gmt[2];
	beat = (hour * 3600 + min * 60 + sec + 3600) / 86.4;
	return zp(Math.floor(beat >= 1000 ? beat - 1000 : beat), 3);
};
Date.prototype.getSuffix = function(date) {
	date = '' + (date || this.getDate());
	var last = date.slice(-1);
	return last === '1' && date !== '11' ? 'st'
	     : last === '2' && date !== '12' ? 'nd'
	     : last === '3' && date !== '13' ? 'rd'
	     : 'th';
};
Date.prototype.getElapseDays = function(year, month, date) {
	var start = new Date(year || this.getFullYear(), month || 0, date  || 1),
	    now   = new Date(this.getFullYear(), this.getMonth(), this.getDate());
	return Math.floor((now - start) / 60 / 60 / 24 / 1000);
};
Date.prototype.getMonthTotalDays = function(year, month) {
	year  = year  || this.getFullYear();
	month = month || this.getMonth();
	return new Date(year, month + 1, 0).getDate();
};
Date.prototype.getHalfHours = function(hour) {
	hour = hour || this.getHours();
	return hour > 12 ? hour - 12 : hour === 0 ? 12 : hour;
};
Date.prototype.getGMTDiff = function(colon) {
	var offset = this.getTimezoneOffset() / 60;
	return (offset > 0 ? '-' : '+') + zp(Math.abs(offset)) + (colon ? ':' : '') + '00';
};
Date.prototype.format = function(format, timestamp) {
	if (!timestamp) {
		return _formatter.call(this, format);
	}
	if (typeof timestamp !== 'number') {
		timestamp = Date.parse(timestamp);
	}
	var _timestamp = this.getTime();
	this.setTime(timestamp);
	var ret = _formatter.call(this, format);
	this.setTime(_timestamp);
	return ret;
};
function _formatter(format) {
	format = format + '';
	var result = [];
	for (var i = 0, str; str = format.charAt(i); ++i) {
		if (str === '\\') {
			result[++i] = format.charAt(i);
			continue;
		}
		result[i]
			= str === 'd' ? zp(this.getDate())
			: str === 'D' ? dayShortNames[this.getDay()]
			: str === 'j' ? this.getDate()
			: str === 'l' ? dayFullNames[this.getDay()]
			: str === 'N' ? this.getDay() === 0 ? 7 : this.getDay()
			: str === 'S' ? this.getSuffix(this.getDate())
			: str === 'w' ? this.getDay()
			: str === 'z' ? this.getElapseDays()
			: str === 'W' ? zp(this.getISOWeekNumber())
			: str === 'F' ? monthFullNames[this.getMonth()]
			: str === 'm' ? zp(this.getMonth() + 1)
			: str === 'M' ? monthShortNames[this.getMonth()]
			: str === 'n' ? this.getMonth() + 1
			: str === 't' ? this.getMonthTotalDays()
			: str === 'L' ? this.isLeapYear() ? 1 : 0
			: str === 'o' ? this.getISOYear()
			: str === 'Y' ? this.getFullYear()
			: str === 'y' ? (this.getFullYear() + '').slice(-2)
			: str === 'a' ? this.getHours() < 12 ? 'am' : 'pm'
			: str === 'A' ? this.getHours() < 12 ? 'AM' : 'PM'
			: str === 'B' ? this.getInternetTime()
			: str === 'g' ? this.getHalfHours()
			: str === 'G' ? this.getHours()
			: str === 'h' ? zp(this.getHalfHours())
			: str === 'H' ? zp(this.getHours())
			: str === 'i' ? zp(this.getMinutes())
			: str === 's' ? zp(this.getSeconds())
			: str === 'u' ? zp(this.getMilliseconds(), 3) + '000'
			: str === 'e' ? timezone['id']
			: str === 'I' ? 0
			: str === 'O' ? this.getGMTDiff()
			: str === 'P' ? this.getGMTDiff(true)
			: str === 'T' ? timezone['abbr']
			: str === 'Z' ? (this.getTimezoneOffset() > 0 ? '-' : '')
			              + Math.abs(this.getTimezoneOffset() * 60)
			: str === 'c' ? arguments.callee.call(this, Date.ATOM)
			: str === 'r' ? arguments.callee.call(this, Date.RFC2822)
			: str === 'U' ? (this.getTime() + '').slice(0, -3)
			: str;
	}
	return result.join('');
}

})();
