
//////////// BEGIN CLASS DEFINITION/CONSTRUCTOR ////////////


function Calendar(timeInMillis)
{

	this.date
	if (arguments.length==0)
	{
		this.date=new Date()
	}
	else
	{
		this.date=new Date(timeInMillis)
	}
	
	
	var c
	
	// field constants:
	this.MILLISECOND="millisecond"
	this.SECOND="second"
	this.MINUTE="minute"
	this.HOUR="hour"
	this.DATE="date"
	this.MONTH="month"
	this.YEAR="year"
	this.DAY="day"
	
	// day-of-week constants:
	c=0
	this.SUNDAY=c; c++
	this.MONDAY=c; c++
	this.TUESDAY=c; c++
	this.WEDNESDAY=c; c++
	this.THURSDAY=c; c++
	this.FRIDAY=c; c++
	this.SATURDAY=c; c++
	
	// month constants:
	c=0
	this.JANUARY=c; c++
	this.FEBRUARY=c; c++
	this.MARCH=c; c++
	this.APRIL=c; c++
	this.MAY=c; c++
	this.JUNE=c; c++
	this.JULY=c; c++
	this.AUGUST=c; c++
	this.SEPTEMBER=c; c++
	this.OCTOBER=c; c++
	this.NOVEMBER=c; c++
	this.DECEMBER=c; c++

	// array constants:
	c=0
	this.WEEK_VERBOSE="weekdayNamesVerbose"
	this.WEEK_ABBREV="weekdayNamesAbbrev"
	this.MONTH_VERBOSE="monthNamesVerbose"
	this.MONTH_ABBREV="monthNamesAbbrev"
	
	// format constants:
	c=0
	this.SHORT=c; c++
	this.MEDIUM=c; c++
	this.LONG=c; c++
	this.FULL=c; c++
	
	// format type constants:
	c=0
	this.DATE_ONLY=c; c++
	this.TIME_ONLY=c; c++
	this.BOTH=c; c++
	
	
	// other constants:
	// note: use getDaysInMonth to determine month length, since February can have 28 or 29 days:
	this._DAYS_IN_MONTHS=[31,28,31,30,31,30,31,31,30,31,30,31]
	
	this._MAX_VALUES=new Object()
	this._MAX_VALUES[this.MILLISECOND]=999
	this._MAX_VALUES[this.SECOND]=59
	this._MAX_VALUES[this.MINUTE]=59
	this._MAX_VALUES[this.HOUR]=23
	// DATE varies per month so no constant for this
	this._MAX_VALUES[this.MONTH]=11
	this._MAX_VALUES[this.YEAR]=999999
	this._MAX_VALUES[this.DAY]=6

	this._MIN_VALUES=new Object()
	this._MIN_VALUES[this.MILLISECOND]=0
	this._MIN_VALUES[this.SECOND]=0
	this._MIN_VALUES[this.MINUTE]=0
	this._MIN_VALUES[this.HOUR]=0
	this._MIN_VALUES[this.DATE]=1
	this._MIN_VALUES[this.MONTH]=0
	this._MIN_VALUES[this.YEAR]=-999999
	this._MIN_VALUES[this.DAY]=0
	
	this._SETTER_GETTER_SUFFICES=new Object()
	this._SETTER_GETTER_SUFFICES[this.MILLISECOND]="Milliseconds"
	this._SETTER_GETTER_SUFFICES[this.SECOND]="Seconds"
	this._SETTER_GETTER_SUFFICES[this.MINUTE]="Minutes"
	this._SETTER_GETTER_SUFFICES[this.HOUR]="Hours"
	this._SETTER_GETTER_SUFFICES[this.DATE]="Date"
	this._SETTER_GETTER_SUFFICES[this.MONTH]="Month"
	this._SETTER_GETTER_SUFFICES[this.YEAR]="FullYear"
	this._SETTER_GETTER_SUFFICES[this.DAY]="Day"
		
	this._FIELD_NAMES=["millisecond","second","minute","hour","date","month","year","day"]
	
	
	
	// overrideable via set methods (for non-English languages)
	this.weekdayNamesVerbose=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
	this.weekdayNamesAbbrev=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
	this.monthNamesVerbose=["January","February","March","April","May","June","July","August","September","October","November","December"]
	this.monthNamesAbbrev=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]

	// instance methods:
	this.add=_Calendar_add
	this.after=_Calendar_after
	this.before=_Calendar_before
	this.clone=_Calendar_clone
	this.equals=_Calendar_equals
	this.equalsIgnoreTime=_Calendar_equalsIgnoreTime
	this.get=_Calendar_get
	this.getTime=_Calendar_getTime
	this.getTimeInMillis=_Calendar_getTimeInMillis
	this.roll=_Calendar_roll
	this.set=_Calendar_set
	this.setTime=_Calendar_setTime
	this.setTimeInMillis=_Calendar_setTimeInMillis
	this.compareTo=_Calendar_compareTo
	this.getArray=_Calendar_getArray
	this.setArray=_Calendar_setArray
	this.getArrayEl=_Calendar_getArrayEl
	this.setArrayEl=_Calendar_setArrayEl
	this.getName=_Calendar_getName
	this.getFormatted=_Calendar_getFormatted
	this.toString=_Calendar_toString
	this.getDaysInMonth=_Calendar_getDaysInMonth
	this.getFirstDayInMonth=_Calendar_getFirstDayInMonth
	this.getHoursInDay=_Calendar_getHoursInDay
	this.isInLeapYear=_Calendar_isInLeapYear
	this.isLeapYear=_Calendar_isLeapYear
	this.getOffset=_Calendar_getOffset
}
//////////// END CLASS DEFINITION/CONSTRUCTOR ////////////



//////////// BEGIN METHODS ////////////

function _Calendar_roll(field,amount)
{
	
	var fieldValues=new Object()
	for (var i=0; i<this._FIELD_NAMES.length; i++)
	{
		fieldValues[this._FIELD_NAMES[i]]=this.date["get"+this._SETTER_GETTER_SUFFICES[this._FIELD_NAMES[i]]]()
	}
	
	var minValue=this._MIN_VALUES[field]
	
	var maxValue
	if (field!=this.DATE)
	{
		maxValue=this._MAX_VALUES[field]
	}
	else
	{
		maxValue=this.getDaysInMonth()
	}
	fieldValues[field]+=amount
	while (fieldValues[field]>maxValue)
	{
		fieldValues[field]-=(maxValue+1)
	}
	while (fieldValues[field]<minValue)
	{
		fieldValues[field]+=(maxValue+1)
	}

		
	this.date=new Date
	(
		fieldValues.year,
		fieldValues.month,
		fieldValues.date,
		fieldValues.hour,
		fieldValues.minute,
		fieldValues.second,
		fieldValues.millisecond
	)
}


function _Calendar_add(field,amount)
{
	var currValue=this.date["get"+this._SETTER_GETTER_SUFFICES[field]]()
	this.date["set"+this._SETTER_GETTER_SUFFICES[field]](currValue+amount)
}


function _Calendar_set(field,value)
{
	this.date["set"+this._SETTER_GETTER_SUFFICES[field]](value)
}


function _Calendar_after(cal)
{
	return (this.date.getTime()>cal.date.getTime())
}

function _Calendar_before(cal)
{
	return (this.date.getTime()<cal.date.getTime())
}

function _Calendar_clone()
{
	var ret=new Calendar(this.date.getTime())
	ret.weekdayNamesVerbose=this.weekdayNamesVerbose
	ret.weekdayNamesAbbrev=this.weekdayNamesAbbrev
	ret.monthNamesVerbose=this.monthNamesVerbose
	ret.monthNamesAbbrev=this.monthNamesAbbrev
	return ret
}

function _Calendar_equals(cal)
{
	return (this.date.getTime()==cal.date.getTime())
}

function _Calendar_equalsIgnoreTime(cal)
{
	if
	(
		this.get(this.YEAR)==cal.get(this.YEAR) &&
		this.get(this.MONTH)==cal.get(this.MONTH) &&
		this.get(this.DATE)==cal.get(this.DATE)
	)
	{
		return true
	}
	return false
}


function _Calendar_get(field)
{	
	return this.date["get"+this._SETTER_GETTER_SUFFICES[field]]()
}

function _Calendar_getTime()
{
	return this.date
}

function _Calendar_getTimeInMillis()
{
	return this.date.getTime()
}

function _Calendar_setTime(date)
{
	this.date=new Date(date.getTime())
}

function _Calendar_setTimeInMillis(time)
{
	this.date=new Date(time)
}

function _Calendar_compareTo(cal)
{
	if (this.equals(cal))
	{
		return 0
	}
	return (this.after(cal)?1:-1)
}

function _Calendar_getArray(arrayConst)
{
	return this[arrayConst]
}

function _Calendar_setArray(arrayConst,array)
{
	this[arrayConst]=array
}

function _Calendar_getArrayEl(arrayConst,idx)
{
	return this[arrayConst][idx]
}

function _Calendar_setArrayEl(arrayConst,idx,value)
{
	this[arrayConst][idx]=value
}

function _Calendar_getName(type,idx,verbose)
{
	return this[(type==this.MONTH?"month":"weekday")+"Names"+(verbose?"Verbose":"Abbrev")][idx]
}

function _Calendar_getFormatted(format,formatType,military)
{
	var dateStr
	var timeStr
	
	if (formatType==this.DATE_ONLY || formatType==BOTH)
	{
		dateStr=""
	}
	if (formatType==this.TIME_ONLY || formatType==BOTH)
	{
		timeStr=""
	}
	
	if (formatType==this.DATE_ONLY)
	{
		return dateStr
	}
	else if (formatType==this.TIME_ONLY)
	{
		return timeStr
	}
	return dateStr+", "+timeStr
}

function _Calendar_toString()
{
	return ""+this.date
}

function _Calendar_getDaysInMonth()
{
	var month=this.date.getMonth()
	var year=this.date.getFullYear()
	if (month!=this.FEBRUARY)
	{
		return this._DAYS_IN_MONTHS[month]
	}
	else
	{
		return (this.isInLeapYear()?29:28)
	}

}

function _Calendar_getFirstDayInMonth()
{
	var cal=this.clone()
	cal.set(this.DATE,1)
	return cal.getTime().getDay()
}


function _Calendar_getHoursInDay()
{
	// Days have 24 hours unless there's a switch from DST to ST (25 hours) or vice-versa (23 hours):
	var preRollDate=new Date(this.date.getFullYear(),this.date.getMonth(), this.date.getDate(),0,0,0,0)
	var postRollDate=new Date(preRollDate.getTime()+1000*60*60*24)
	if (preRollDate.getDate()==postRollDate.getDate())
	{
		return 25
	}
	else if 
	(
		preRollDate.getHours()==postRollDate.getHours() && 
		preRollDate.getMinutes()==postRollDate.getMinutes() && 
		preRollDate.getSeconds()==postRollDate.getSeconds() && 
		preRollDate.getMilliseconds()==postRollDate.getMilliseconds()
	)
	{
		return 24
	}
	else
	{
		return 23
	}


}

function _Calendar_isLeapYear(year)
{
	if (year%4!=0)
	{
		return false
	}
	// the year is divisible by 4.
	if ((""+year).substring(2,4)=="00")
	{
		// the year ends in "00"...
		if (year%400==0)
		{
			// ...but the year is divisible by 400.
			return true
		}
		else
		{
			// ...and the year is not divisible by 400.
			return false
		}
	}
	else
	{
		// The year does not end in "00".
		return true
	}
	
}

function _Calendar_isInLeapYear()
{
	return (new Calendar().isLeapYear(this.date.getFullYear()))
}

function _Calendar_getOffset(field,cal1,cal2)
{
	alert("_Calendar_getOffset not implemented.")
	// need to think about what this would actually return for month and year. Also, for all fields, does it round?
}
//////////// END METHODS ////////////




