/*
|| datef.js
|| Script Version: 0.2.0
|| Copyright (C) 2004 Hawk <w3l_admin@hawk.34sp.com>
|| WebSite URL: http://www.hawk.34sp.com/

DateオブジェクトのtoStringメソッドをオーバーロードし、
任意のフォーマットで出力可能にする
オマケの機能として任意のフォーマットから
値を設定する fromString メソッドも追加される

デフォルトではPHPのdate関数 + αの書式が使用可能

j 日付。先頭にゼロはつかない
d 日付。先頭にゼロがつく
n 月。先頭にゼロはつかない
m 月。先頭にゼロがつく
M 月。英語名で三文字（例：Jan) 
F 月。英語名（例：January) 
Y 年。四桁の整数
y 年。二桁の整数
w 曜日。数値表示（0から6。0が日曜） 
D 曜日。英語名で三文字（例：Mon） 
l 曜日。英語名（例：Monday） 
G 時刻。先頭にゼロはつかない
H 時刻。先頭にゼロがつく
i 分。先頭にゼロがつく
s 秒。先頭にゼロがつく

p 分。先頭にゼロはつかない
q 秒。先頭にゼロはつかない
e 曜日。日本語の漢字一文字（例：月）
f タイムゾーン指定子（Z または +hh:mm または -hh:mm）

フォーマット文字は直前に#を置くことで表示可能
（#自体も直前に#を置くことで表示可能）
例 :
	#Y#ear:Y --置換後--> Year:2004
	##Y     --置換後--> #2004


*/

/**
 * @access public
 * @param int disits
 * @return string
 * 
 * 整数をdigists桁の文字列にして返す
 * 超簡易版
 * 
 */
Number.prototype._formatInt = function(digits, c) {
	if(c==null) c = '0';
	var r = this.toString();
	for(var i=0; r.length<digits; ++i) { r=c+r; }
	return r;
};

/**
 * @access public
 * @return Array
 * 
 * 強制的に配列のキーと値を入れ替える
 * 超簡易版
 * 
 */
Array.prototype._forceFlip = function() {
	var result = [];
	for(var p in this) { if(typeof this[p] != 'function') result[this[p]] = p; }
	return result;
}

/**
 * @access public
 * @param string
 * @return string
 * 
 * 正規表現で特別な意味を持つ文字をクオートする
 * 
 */
RegExp.quote = function (str) {
	var reg = /([\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\\])/g;
	//NN4.x のバグを回避するため一旦別の文字に置き換え
	return str.replace(reg, "##__BACK_SLASH__##$1").replace(/##__BACK_SLASH__##/g, '\\');
}

Date.formatFunctions= {}; //[format_char] => function(Date d)
Date.parseFunctions = {}; //[format_char] => function(Date d, string value)
Date.parseRegExp    = {}; //string

/**
 * @static
 * @access public
 * @param string 
 * @param function
 * @param function
 * @param string
 * 
 */
Date.registerFormat = function(formatc, format_f, parse_f, parse_reg) {
	if(format_f != null) {
		Date.formatFunctions[formatc] = format_f;
	}
	if(parse_f != null && parse_reg !=null) {
		Date.parseFunctions[formatc] = parse_f;
		Date.parseRegExp[formatc] = parse_reg;
	}
};

/**
 * 汎用のフォーマット関数
 * 
 * @access public
 * @param function exists_f
 * @param function replace_f
 * @param format
 * @return string
 * 
 */
function parseDateFormat(exists_f, replace_f, format) {
	var ESCAPE_CHAR = '#';
	var i, len=format.length;
	var c="", result="";
	
	for(c=format.charAt(i=0); i<len; c=format.charAt(++i)) {
		if(c == ESCAPE_CHAR && ++i<len) {
			result += format.charAt(i);
			continue;
		} else if(exists_f(c)) {
			result = replace_f(result, c);
		} else {
			result+= c;
		}
	}
	return result;
}


/* デフォルトのtoStringの退避 */
Date.prototype._oldToString = Date.prototype.toString;

/**
 * @access public
 * @param string format
 * @return string 
 * 
 */
Date.prototype.toString = function(format) {
	if(format == null) { return this._oldToString(); }
	var d = this;
	return parseDateFormat(
		function(c){ return (Date.formatFunctions[c] != null); },
		function(result, c) { return result + Date.formatFunctions[c](d); },
		format
	);
};

/**
 * @static
 * @access public
 * @param Date
 * @param string dateVal 
 * @param string format
 * @return Date
 * 
 */
Date.fromString = function(d, dateVal, format) {
	var reg, reg_str, matches;
	var elements = [];

	reg_str = parseDateFormat(
		function(c) { return (Date.parseRegExp[c] != null); },
		function(reg_str, c) { elements[elements.length]=c; return reg_str +'('+ Date.parseRegExp[c] +')'; },
		RegExp.quote(format)
	);

	reg = new RegExp(reg_str);

	if((matches = reg.exec(dateVal)) == null) {
		return null;
	} else {
		var c = "";
		for(var j=1; j<matches.length; ++j) {
			c = elements[j-1];
			if(Date.parseFunctions[c]!=null) {
				Date.parseFunctions[c](d, matches[j]);
			}
		}
		/*
		   タイムゾーン関連の設定は最後に行う
		*/
		if(d._timezoneDiff!=null && d._timezoneDiff != 0) {
			d.setTime(d.getTime() + d._timezoneDiff);
			d._timezoneDiff = 0;
		}
		return d;
	}
}

/**
 * @access public
 * @param string dateVal
 * @param string format
 * @return Date
 * 
 */
Date.prototype.fromString = function(dateVal, format) {
	return Date.fromString(this, dateVal, format);
}


/**
 * デフォルト登録（PHPのdate関数+α）
 * 
 * 
 */

function _register_default()
{
	var month_s = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
	var month_l = ["January","February","March","April","May","June","July","Augast","September","October","November","December"];
	var day_s   = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
	var day_l   = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
	var day_jp  = ["日", "月", "火", "水", "木", "金", "土"];

	var formats = {
		'Y': {
			'out': function(d)     { return d.getFullYear(); },
			'in' : function(d, str){ d.setFullYear(parseInt(str)); },
			'reg': '\\d{4}'
		},
		'y': {
			'out': function(d)     { var y=d.getYear(); y=(y>=2000?y-2000:(y>=100?y-100:y)); return y._formatInt(2); },
			'in' : function(d, str){ d.setYear(parseInt(str, 10)); },
			'reg': '\\d{2}'
		},
		"n": {
			'out': function(d)     { return d.getMonth()+1; },
			'in' : function(d, str){ d.setMonth(parseInt(str)-1); },
			'reg': '1[0-2]|[1-9]'
		},
		"m": {
			'out': function(d)     { return (d.getMonth()+1)._formatInt(2); },
			'in' : function(d, str){ d.setMonth(parseInt(str, 10)-1); },
			'reg': '0[1-9]|1[0-2]'
		},
		"M": {
			'out': function(d)     { return month_s[d.getMonth()]; },
			'in' : function(d, str){ d.setMonth(month_s._forceFlip()[str]); },
			'reg': month_s.join('|')
		},
		"F": {
			'out': function(d)     { return month_l[d.getMonth()]; },
			'in' : function(d, str){ d.setMonth(month_l._forceFlip()[str]); },
			'reg': month_l.join('|')
		},
		"j": {
			'out': function(d)     { return d.getDate(); },
			'in' : function(d, str){ d.setDate(parseInt(str)); },
			'reg': '3[01]|[1-2][0-9]|[1-9]'
		},
		"d": {
			'out': function(d)     { return d.getDate()._formatInt(2); },
			'in' : function(d, str){ d.setDate(parseInt(str, 10)); },
			'reg': '0[1-9]|[1-2][0-9]|3[01]'
		},
		"w": {
			'out': function(d)     { return d.getDay(); },
			'reg': '[0-6]'
		},
		"D": { 
			'out': function(d)     { return day_s[d.getDay()]; },
			'reg': day_s.join('|')
		},
		"l": { 
			'out': function(d)     { return day_l[d.getDay()]; },
			'reg': day_l.join('|')
		},
		"G": {
			'out': function(d)     { return d.getHours(); },
			'in' : function(d, str){ d.setHours(parseInt(str)); },
			'reg': '1[0-9]|2[0-3]|[0-9]'
		},
		"H": {
			'out': function(d)     { return d.getHours()._formatInt(2); },
			'in' : function(d, str){ d.setHours(parseInt(str,10)); },
			'reg': '[0-1][0-9]|2[0-3]'
		},
		"i": {
			'out': function(d)     { return d.getMinutes()._formatInt(2); },
			'in' : function(d, str){ d.setMinutes(parseInt(str,10)); },
			'reg': '[0-5][0-9]'
		},
		"s": {
			'out': function(d)     { return d.getSeconds()._formatInt(2); },
			'in' : function(d, str){ d.setSeconds(parseInt(str,10)); },
			'reg': '[0-5][0-9]'
		},

		"p": {
			'out': function(d)     { return d.getMinutes(); },
			'in' : function(d, str){ d.setMinutes(parseInt(str,10)); },
			'reg': '[1-5][0-9]|[0-9]'
		},
		"q": {
			'out': function(d)     { return d.getSeconds(); },
			'in' : function(d, str){ d.setSeconds(parseInt(str,10)); },
			'reg': '[1-5][0-9]|[0-9]'
		},

		"e": { 
			'out': function(d)     { return day_jp[d.getDay()]; },
			'reg': day_jp.join('|')
		},
		"f": {
			'out': function(d) { 
				var tz=d.getTimezoneOffset(), sg=(tz>0 ? '-' : '+'); //符号は入れ替わる
				if(tz==0) return 'Z';
				tz=Math.abs(tz);
				return sg + Math.floor(tz/60)._formatInt(2) +':'+ (tz%60)._formatInt(2);
			},
			'in' : function(d, str) {
				var r, local=d.getTimezoneOffset()*-1; //符号反転
				if(str=='Z'){
					r=0;
				} else {
					var matches = /(\+|\-)(\d{2}):(\d{2})/.exec(str);
					r = parseInt(matches[2], 10)*60 + parseInt(matches[3], 10);
					r*= ((matches[1]=='+') ? 1 : -1);
				}
				d._timezoneDiff = (local-r)*60*1000;
				//d.setTime(d.getTime() + (local-r)*60*1000);
			},
			'reg': 'Z|(?:\\+|\\-)(?:[0-1][0-9]|2[0-4]):(?:[0-5][0-9])'
		}
	}
	
	for(var c in formats) {
		if(formats[c]['out'] != null) {
			Date.formatFunctions[c] = formats[c]['out'];
		}
		if(formats[c]['in'] != null) {
			Date.parseFunctions[c] = formats[c]['in'];
		}
		if(formats[c]['reg'] != null) {
			Date.parseRegExp[c] = formats[c]['reg'];
		}
	}
}
_register_default();
