//
// Calendar Functions
//
// Based on algorithms found in Chapter 2 of "Calendrical Calculations" 
// by Nachum Dershowitz and Edward M. Reingold. These algorithms work
// Rata dia (R.D.), or fixed date, which numbers the days using the
// Gregorian Epoch as its origin.
//
var GregorianEpoch = 1;
//
// Days of the Week.
//
var Sunday = 0, 
    Monday = 1, 
    Tuesday = 2, 
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6;
//
// Note that Javascript uses a different notation for month. Its notation 
// is 0 - 11 instead of 1 - 12.
//
var January = 1,
    February = 2,
    March = 3,
    April = 4,
    May = 5,
    June = 6,
    July = 7,
    August = 8,
    September = 9,
    October = 10,
    November = 11,
    December = 12;
//
// The modulo function for calendars.
//
function CalMod(x, y)
{
   return (x - y * Math.floor(x/y));
}
//
// Returns the RD for today.
//
function RdToday()
{
	var now = new Date();
	var d = now.getDate();
	var m = now.getMonth() + 1;
	var y = y2k(now.getYear());
	
	return(FixedFromGregorian(m,d,y));
}
//
// Returns the name of the month, where month is an integer given
// according to the definitions above.
//
function GregorianMonthName(month)
{
   var months = new Array("January", "February", "March", 
                          "April", "May", "June", 
                          "July", "August", "September",
                          "October", "November", "December");

   return(months[month - 1]);
}
//
// Returns the name of a weekday according to the definitions above.
//
function GregorianWeekdayName(weekday)
{
   var weekdays = new Array("Sunday", "Monday", "Tuesday", "Wednesday", 
                            "Thursday", "Friday", "Saturday");
   
   return(weekdays[weekday]);
}
//
// Returns the number of days in a month, where month is an integer
// given according to the denfinitions above. Year is required to
// adjust for the (Gregorian) leap year.
//
function DaysInMonth(month, year)
{
   var days = new Array(31, 28, 31, 30, 31, 30, 
                        31, 31, 30, 31, 30, 31);

   result = days[month - 1];

   if (GregorianLeapYear(year) && month == February) {
      result = result + 1;
   }

   return(result);
}
//
// Returns the day of the week (according to the definition above) 
// for a given R.D.
//
function DayOfWeekFromFixed(rd)
{
   return CalMod(rd, 7);
}
//
// The following functions find the day of the week (0 = Sunday, etc.) 
// that occurs on or before, on or after, before, and after the given
// R.D., respectively.
//
function KdayOnOrBefore(rd, k)
{
   return (rd - DayOfWeekFromFixed(rd - k));
}

function KdayOnOrAfter(rd, k)
{
   return (KdayOnOrBefore(rd+6, k));
}

function KdayBefore(rd, k)
{
   return (KdayOnOrBefore(rd - 1, k));
}

function KdayAfter(rd, k)
{
   return (KdayOnOrBefore(rd + 7, k));
}
//
// This function adjusts for the different ways Javascript can represent
// a year: 4 digits or 2 digits.
//
function y2k(number)
{
   return (number < 1000) ? number + 1900 : number;
}
//
// Returns whether or not a given year is a leap year.
//
function GregorianLeapYear(year)
{
   if ((year % 400) == 0) return true;
   if ((year % 100) == 0) return false;
   if ((year % 4) == 0) return true;
   return false;
}
//
// Returns the R.D. given a Gregorian month, day, and year.
//
function FixedFromGregorian(month, day, year)
{

   if (month <= February) {
      adjust = 0;
   }
   else if (month > February && GregorianLeapYear(year)) {
      adjust = -1;
   }
   else {
      adjust = -2;
   }

   ymo = year - 1

   retval = GregorianEpoch - 1 +
            365 * (ymo) + 
            Math.floor(ymo/4) - 
            Math.floor(ymo/100) +
            Math.floor(ymo/400) +
            Math.floor((367 * month - 362)/12) +
            adjust +
            day;

   return retval;
}

var First = 1,
    Second = 2,
    Third = 3,
    Fourth = 4,
    Next = 0,
    Last = -1,
    NextToLast = -2;
//
// Returns the R.D. of the n-th day of the week after or before a
// given Gregorian date. Use the above vars for 'n'.
//
function NthKDay(n, k, month, day, year)
{
   if (n > 0) {
      retval = (7 * n) + KdayBefore(FixedFromGregorian(month, day, year),k);
   }
   else {
      retval = (7 * n) + KdayAfter(FixedFromGregorian(month, day, year),k);
   }

   return retval;
}
//
// Returns the Gregorian year of the given R.D.
//
function GregorianYearFromFixed(rd)
{
   d0 = rd - GregorianEpoch;

   n400 = Math.floor(d0/146097);
   d1 = CalMod(d0, 146097);

   n100 = Math.floor(d1/36524);
   d2 = CalMod(d1, 36524);

   n4 = Math.floor(d2/1461);
   d3 = CalMod(d2, 1461);

   n1 = Math.floor(d3/365);
   d4 = CalMod(d3, 365) + 1;

   year = 400*n400 + 100*n100 + 4*n4 + n1;

   if (n100 == 4 || n1 == 4) {
      retval = year;
   }
   else {
      retval = year + 1;
   }

   return retval;
}
//
// Returns the Gregoian month from the given R.D.
// 
function GregorianMonthFromFixed(rd, year)
{
   prior_days = rd - FixedFromGregorian(January, 1, year);

   if (rd < FixedFromGregorian(March, 1, year)) {
      correction = 0;
   }
   else if (rd >= FixedFromGregorian(March, 1, year) &&
            GregorianLeapYear(year)) {
      correction = 1;
   }
   else {
      correction = 2;
   }

   month = Math.floor((12 * (prior_days + correction) + 373)/367);

   return month;
}
//
// Returns the day of the month from a given R.D.
//
function GregorianDayFromFixed(rd, month, year)
{
   day = rd - FixedFromGregorian(month, 1, year) + 1;
   return day;
}
