// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// file: date.js
// desc: localized date prototypes, by tor2k@hotmail.com
// date object references act upon the original; i.e., d = date; modifying d modifies date.
// date: Mon Apr 18 18:55:14 UTC-0400 2005
// updt: Tue Oct 17 16:20:29 CDT 2006 Code Project version
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

// globals
var gsMonthNames = new Array(
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
);

var gsDayNames = new Array(
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
);

var gEaster = 0;

// date diff in days
Date.prototype.dateDiff = function(d) { return Math.round((d.valueOf() - this.valueOf()) / 86400000); }

// date adds
Date.prototype.add 		= function(n) { var d = new Date(this); d.setDate(d.getDate() + n); return d; }
Date.prototype.addMonth	= function(n) { var d = new Date(this); d.setMonth(d.getMonth() + n); return d; }
Date.prototype.addYear 	= function(n) { var d = new Date(this); d.setFullYear(d.getFullYear() + n); return d; }

// first and last date of month
Date.prototype.getFirstDate	= function() { var d = new Date(this); d.setDate(1); return d; }
Date.prototype.getLastDate  = function() { var d = this.addMonth(1); d.setDate(1); return d.add(-1); }

// date between [d1, d2]
Date.prototype.between = function(d1, d2) { return 0 <= d1.dateDiff(this) && d2.dateDiff(this) <= 0; } 

// easter calculator
Date.prototype.getEaster = function(y)
{
	if (!y)
		y = this.getFullYear();
	
	var c, n, k, i, j, l, m, d;

    c = parseInt(y / 100);
    n = y - 19 * parseInt(y / 19);
    k = parseInt((c - 17) / 25);
    i = c - parseInt(c / 4) - parseInt((c - k) / 3) + 19 * n + 15;
    i = i - 30 * parseInt(i / 30);
    i = i - parseInt(i / 28) * (1 - parseInt(i / 28) * parseInt(29 / (i + 1)) * parseInt((21 - n) / 11));
    j = y + parseInt(y / 4) + i + 2 - c + parseInt(c / 4);
    j = j - 7 * parseInt(j / 7);
    l = i - j;
    m = 3 + parseInt((l + 40) / 44);
    d = l + 28 - 31 * parseInt(m / 4);

    return new Date(y, m - 1, d);
}

// local holiday test
Date.prototype.isHoliday = function()
{
    // sundays
    if (this.getDay() == 0)
    	return true;
        
    var y = this.getFullYear();
    var m = this.getMonth();
    var d = this.getDate();
    var r;
    
    // fixed holidays
    switch(m)
    {
    case 0: case 4:	r = d ==  1;			break;
    case 3:			r = d == 19;			break;
    case 5:			r = d == 24;			break;
    case 6:			r = d == 24 || d == 5;	break;
    case 9:			r = d == 12;			break;
    case 11:		r = d == 25 || d == 31;	break;
    default:
    }
    
    // moveable easter-based holidays
	if (!r)
	{
		// skip the easter recalc
		if (!gEaster)
			gEaster = this.getEaster(y);
		else if (gEaster.getFullYear() != y)
			gEaster = this.getEaster(y);
		
		// integer date diff decision 
    	switch (this.dateDiff(gEaster))
    	{
    	case 2: case 3: case 48: case 47: r = true;
    	default:
    	}
    }
    
    return r;
}

// string date part names
Date.prototype.monthName	= function() { return gsMonthNames[this.getMonth()]; }
Date.prototype.dayName		= function() { return gsDayNames[this.getDay()]; }

// format with replace function (JScript 5.5 or higher)
Date.prototype.format = function(f)
{
	if (!this.valueOf())
		return '&nbsp;';
	
	var d = this;
	
	return f.replace(/(yyyy|mmmm|mmm|mm|dddd|ddd|dd|hh|nn|ss|s\/p|a\/p)/gi,
		function($1)
		{
			switch ($1.toLowerCase())
			{
			case 'yyyy':	return d.getFullYear();
			case 'mmmm':	return gsMonthNames[d.getMonth()];
			case 'mmm':		return gsMonthNames[d.getMonth()].substr(0, 3);
			case 'mm':		return (d.getMonth() + 1).zp(2);
			case 'dddd':	return gsDayNames[d.getDay()];
			case 'ddd':		return gsDayNames[d.getDay()].substr(0, 3);
			case 'dd':		return d.getDate().zp(2);
			case 'hh':		return ((h = d.getHours() % 12) ? h : 12).zp(2);
			case 'nn':		return d.getMinutes().zp(2);
			case 'ss':		return d.getSeconds().zp(2);
			case 'a/p':		return d.getHours() < 12 ? 'a' : 'p';
			case 's/p':		var i = d.getDate() % 10; return 'thstndrd'.substr(2 * (i < 4) * i, 2);
			}
		}		
	);
}

