/* 
 * Date extension.
 *
 * $Id: apiDate.js,v 1.1 2006/06/19 15:33:48 dread Exp dread $
 */

/*
 *$Log: apiDate.js,v $
 *Revision 1.1  2006/06/19 15:33:48  dread
 *Initial revision
 *
 */

/*extern padString, trimString, ordinal */

var DayNames = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday','Friday', 'Saturday');
var MonthNames = new Array(
		'January', 'February', 'March', 'April', 'May', 'June', 
		'July', 'August', 'September', 'October', 'November', 'December');

if (typeof today == 'undefined') {
	var today = new Date();
}

function isLeapyear(y) {
	return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}

function y2k(y) {
	y %= 100;
	y += (y < 38) ? 2000: 1900;
	return y;
}

function getDayName(d) {
	if (d < 0) 
		d += 35;
	return DayNames[d % 7];
}

function getMonthName(m) {
	if (m < 0) 
		m += 12;
	return MonthNames[m % 12];
}

function getDaysInMonth(m, y) {
// return 32 - new Date(year, month, 32).getDate();
	switch (m) {
		case 1: return (isLeapyear(y)) ? 29 : 28;
		case 3:
		case 5:
		case 8:
		case 10: return 30;
	}
	return 31;
}

function getDaysInYear(d, m, y) {
	while (m-- > 0) {
		d += getDaysInMonth(m, y);
	}
	return d;
}

if (typeof Date.prototype.getDayName == 'undefined') 
	Date.prototype.getDayName = function(d) { return getDayName( (d) ? d : this.getDay() ); }

if (typeof Date.prototype.getMonthName == 'undefined') 
	Date.prototype.getMonthName = function(m) { return getMonthName( (m) ? m : this.getMonth() ); }

if (typeof Date.prototype.getDaysInMonth == 'undefined') {
	Date.prototype.getDaysInMonth = function(m, y) { 
		return getDaysInMonth( (m) ? m : this.getMonth(), (y) ? y : this.getFullYear() ); }
}
if (typeof Date.prototype.getDaysInYear == 'undefined') {
	Date.prototype.getDaysInYear = function (d, m, y) {
		return getDaysInYear( (d) ? d : this.getDate(), (m) ? m : this.getMonth(), (y) ? y : this.getFullYear() ); }
}

function getWeek(date, weekbeg) {					// weekbeg: Sun=0, Mon=1, ISO=2
	var day1 = weekbeg;
	day1++;
	var year = y2k(date.getFullYear());
	var newYear = new Date(year,0,1);
	var offset = 7 + day1 - newYear.getDay();
	if (offset > 7 ) offset -= 7;
	var daynum = ((Date.UTC(year, date.getMonth(), date.getDate(), 0, 0, 0) - Date.UTC(year, 0, 1, 0, 0, 0)) / 86400000) + 1;
	var weeknum = Math.floor((daynum-offset+7) / 7);
	if ((weeknum == 0) && (weekbeg == 2)) {					/// ISO says this is the last week of prev year
		year--;
		var prevNewYear = new Date(year, 0, 1);
		var prevOffset = 7 + 1 - prevNewYear.getDay();
		// if (prevOffset == 2 || prevOffset == 8) weeknum = 53; else weeknum = 52;
		weeknum = (prevOffset == 4 || prevOffset == 6) ? 53 : 52;
	}
	return weeknum;
}

if (typeof Date.prototype.getWeek == 'undefined') 
	Date.prototype.getWeek = function(d) { return getWeek(this, (d) ? d : 0); }


function getBeat(date) {						// Swatch Internet Beat http://www.quirksmode.org/js/beat.html
	var off = (date.getTimezoneOffset() + 60) * 60;
	var theSeconds = (date.getHours() * 3600) + (date.getMinutes() * 60) + date.getSeconds() + off;
	var beat = Math.floor(theSeconds / 86.4);
	if (beat > 1000) beat -= 1000;
	if (beat < 0) beat += 1000;
	return beat;
}

function formatDate(fmt, date) {
	var a, astr, j, m;

	if (! fmt)
		fmt = '%F %T';				// default to ISO formatting
	if (! date)
		date = new Date;

	if (fmt.indexOf('%') == -1)		// nothing to do
		return fmt;

	// do the 'macro' tokens, 
	// from the FreeBSD 4.11 man page '$ man strftime'
	var str = fmt;

	str = str.replace('%c', '%a %b %e %T %Y');
	str = str.replace('%+', '%a %b %e %T %Z %Y');
	str = str.replace('%D', '%m/%d/%y');
	str = str.replace('%F', '%Y-%m-%d');
	str = str.replace('%T', '%H:%M:%S');
	str = str.replace('%R', '%H:%M');
	str = str.replace('%r', '%I:%M:%S %p');
	str = str.replace('%v', '%e-%b-%Y');
	str = str.replace('%h', '%b');
	
	// POSIX extentions

	str = str.replace('%O', '%E');
	if (str.search(/%E[YCjmeuklIUWVw]/) != -1) {
		a = date.getFullYear();
		a = y2k(a); 
		str = str.replace(/%EY/g, ordinal(a));
		a = 1 + Math.floor(a / 100);							 // 2000  - 21st Century
		str = str.replace(/%EC/g, padString(ordinal(a), 4));
		a = date.getDaysInYear();
		str = str.replace(/%Ej/g, ordinal(a));
		a = 1 + date.getMonth();
		str = str.replace(/%Em/g, padString(ordinal(a), 4));	// 3rd Month
		a = date.getDate();
		str = str.replace(/%Ee/g, padString(ordinal(a), 4));	// 22nd Day of Month
		a = date.getDay();
		str = str.replace(/%Ew/g, ordinal(a));					// 0th Day of Week Sunday = 0
		str = str.replace(/%Eu/g, ordinal(a+1));				// 2nd Day of Week 
		a = date.getHours();
		str = str.replace(/%Ek/g, padString(ordinal(a), 4));	// 0th - 23rd hour
		a = (a > 12) ? a-12 : a;
		str = str.replace(/%EI/g, padString(ordinal(1+a), 4));	// 1st - 12th 12am = 1st
		a = (a < 1) ? 12 : a;
		str = str.replace(/%El/g, padString(ordinal(a), 4));	// 1st - 12th 12am = 12th
		a = date.getWeek();
		str = str.replace(/%EU/g, padString(ordinal(a), 4));	// 0th Week Sunday
		a = date.getWeek(1);
		str = str.replace(/%EW/g, padString(ordinal(a), 4));	// 0th Week Monday
	}
	str = str.replace(/%E/g, '%');			// remove any remaining POSIX tokens

// year stuff
	if (str.search(/%[YyCj]/) != -1) {
		a = date.getFullYear();
		a = y2k(a); 

		astr = a + ""; 
		str = str.replace(/%Y/g, astr);
		str = str.replace(/%y/g, padString((a % 100), 2, '0'));
		str = str.replace(/%C/g, astr.substring(0, 2));

		j = date.getDaysInYear();
		str = str.replace(/%j/g, padString(j, 3, '0'));
	}

// month
	if (str.search(/%[mBb]/) != -1) {
		a = 1 + date.getMonth();
		str = str.replace(/%m/g, padString(a, 2, '0'));

		astr = date.getMonthName();
		str = str.replace(/%B/g, astr);
		str = str.replace(/%b/g, astr.substring(0, 3));
	}

// dates and days
	if (str.search(/%[eduAawj]/) != -1) {
		a = date.getDate();
		str = str.replace(/%e/g, padString(a, 2));
		str = str.replace(/%d/g, padString(a, 2, '0'));

		a = date.getDay();
		str = str.replace(/%w/g, a);
		str = str.replace(/%u/g, a + 1);

		astr = date.getDayName();
		str = str.replace(/%A/g, astr);
		str = str.replace(/%a/g, astr.substring(0, 3));
	}

// hours
	if (str.search(/%[HIklp]/) != -1) {
		a = date.getHours();
		astr = (a < 12) ? 'am' : 'pm';

		str = str.replace(/%p/g, astr);
		str = str.replace(/%H/g, padString(a, 2, '0'));
		str = str.replace(/%k/g, padString(a, 2));

		a = (a > 12) ? a-12 : a;
		a = ( a < 1) ? 12 : a;
		str = str.replace(/%I/g, padString(a, 2, '0'));
		str = str.replace(/%l/g, padString(a, 2));
	}


// seconds
	if (str.search(/%[Ss@]/) != -1) {
		a = date.getSeconds();
		str = str.replace(/%S/g, padString(a, 2, '0'));

		a = date.getTime();									// Seconds since the Epoch
		a = Math.floor(a / 1000);
		str = str.replace(/%s/g, a);

		a = getBeat(date);
		str = str.replace(/%@/g, '@' + a);
	}

// TimeZone
	if (str.search(/%[Zz]/) != -1) {
		astr = date.toString();
		m = astr.match(/([-+']\d\d\d\d)\W+(\w\w\w)/);
		if (m) {
			str = str.replace(/%z/g, m[1]);
			str = str.replace(/%Z/g, m[2]);
		}
	}

// Locale dependant
	if (str.search(/%[Xx]/) != -1) {
		astr = date.toLocaleString();
		m = astr.match(/\s(\d+:\d+.+)/);						// Isolate time
		if (m) {
			str = str.replace(/%X/g, m[1]);							// time
			str = str.replace(/%x/g, astr.replace(m[1], ''));		// date
		}
	}

// Week
	if (str.search(/%[UVW]/) != -1) {
		a = date.getWeekSun(0);
		str = str.replace(/%U/g, padString(a, 2, '0'));
		a = date.getWeekMon(1);
		str = str.replace(/%W/g, padString(a, 2, '0'));
		a = date.getWeek(2);
		str = str.replace(/%V/g, padString(a, 2, '0'));
	}

	str = str.replace('%t', "\t");
	str = str.replace('%n', "\n");

	str = str.replace('%%', '_kEeP_Me_');
	str = str.replace(/%/g, '');		// remove the rest of the tokens
	str = str.replace('_kEeP_Me_', '%');

	return str;
}

if (typeof Date.prototype.strftime == 'undefined') 
	Date.prototype.strftime= function(f) { return formatDate(f, this); }


function guessFormat(str) {
    var fmt = null;

    if (str.indexOf('/') > 0 ) {        // American style mm/dd/yyyy
        fmt = 'A';
    } else if (str.indexOf('.') > 0 ) { // European style dd.mm.yyyy
        fmt = 'E';
    } else if (str.indexOf('-') > 0) {  // ISO style date yyyy-mm-dd
        fmt = 'I';
    } else if (str.match(/[19|20]\d\d[01]\d[0-3]\d/)) {		// yyyymmdd
		fmt = 'Z'
	}
    if ((fmt) && (str.length < 9))
        fmt = fmt.toLowerCase();
    return fmt;
}

function str2Date(str) {
	var y, m, d, H=0, I=0, S=0;
	var redPat = /(\d+)[-\/\.]+(\d+)[-\/\.]+(\d+)\s?/;
	var reRep = null;

	var fmt = guessFormat(trimString(str));
	if (! fmt)
		return null;
	switch(fmt) {
		case 'a': 
		case 'A': reRep = '$3-$1-$2'; break;
		case 'e':
		case 'E': reRep = '$3-$2-$1'; break;
		case 'z':
		case 'Z': redPat = /(\d{4})(\d\d)(\d\d)/;
		case 'I': reRep = '$1-$2-$3'; break;
	}

	if (reRep) {
		var dstr = str.replace(redPat, reRep);
		var a = dstr.split('-');
		if ((a) && (a.length == 3)) {
			y = parseInt(a[0], 10); m = parseInt(a[1], 10); d = parseInt(a[2], 10);

			/* TODO need to validate that the format guess was correct */

			y = y2k(y);
			var date = new Date(y, m-1, d, H, I, S);
			return date;
		}
	}
	return null;
}


