//---------------------------------------
// DateTimeLib.js
//---------------------------------------


function DecodeDateTime(ADateTime)
{
  //returns a 2 items array
  //item 1 is a copy of ADateTime object, with time truncated to midnight
  //item 2 is the time portion of DateTime, an integer representing the number
    //of milliseconds since midnight
  var Result = new Array;
  var iDay, iMonth, iYear;
  
  if (typeof(ADateTime) == 'string')
    ADateTime = new Date(ADateTime);
    
  iDay = ADateTime.getDate();
  iMonth = ADateTime.getMonth() + 1;
  iYear = ADateTime.getFullYear();
  
  Result[0] = new Date(iMonth + '/' + iDay + '/' + iYear);
  Result[1] = ADateTime - Result[0];
      
  return Result;
  
}       //DecodeDateTime()


function TruncateDateTime(ADateTime)
{

  return DecodeDateTime(ADateTime)[0];

}         //TruncateDateTime()
  

function Now()
{
	return new Date();
}


function Today()
//---------------------------------------------------------
// returns today's date with the time zeroed out (midnight)
//---------------------------------------------------------
{
	var d;
	
	d = new Date();
	return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 00, 00, 00, 00);
	
}


function DaysInMonth(ADateTime)
{
  var dCurrentMonth = ADateTime.getMonth();
  var dTemp = new Date(ADateTime.getFullYear(), ADateTime.getMonth(), 28);
  var iDay;

  while (dCurrentMonth == dTemp.getMonth())
  {
    iDay = dTemp.getDate();
    dTemp = IncDate(dTemp, 1);
  }
  
  return iDay;

}         //DaysInMonth()


function GetCenturyPiece(ADateTime)
{
	return ADateTime.getFullYear().toString().substr(0, 2) 
	
}					//GetCenturyPiece()


function GetCentury(ADateTime)
{
	var yr;
	
	yr = ADateTime.getFullYear();
	return Math.floor(yr / 100) * 100
	
}			//GetCentury()


function GetAbbreviatedYear(ADateTime)			//without the century
{
	return ADateTime.getFullYear() - GetCentury(ADateTime);
			
}					//GetAbbreviatedYear()


function GetMonthString(ADateTime, AResultLength)
{
  var sResult = (ADateTime.getMonth() + 1) + '';    //Add 1 to convert to one-based, concatenate to convert to string

  
  switch (AResultLength)
  {
    case 2:
      if (sResult.length == 2)
        return sResult + ''
      else
        return '0' + sResult;
    default:
      return sResult + '';
  } 

}       //GetMonthString()


function GetDayString(ADateTime, AResultLength)
{
  var sResult = ADateTime.getDate() + '';

  switch (AResultLength)
  {
    case 2:
      if (sResult.length == 2)
        return sResult + ''
      else
        return '0' + sResult;
    default:
      return sResult + '';
  } 

}       //GetDayString()


function GetYearString(ADateTime, AResultLength)
{

  switch (AResultLength)
  {
    case 2:
      return GetAbbreviatedYear(Now()) + '';
    case 4:
      return Now().getFullYear() + '';
    default:
      return Now().getFullYear() + '';
  } 

}       //GetYearString()


function ExpandDateStringx(ADateString, AYearWindow)
{
	var sResult;


	function GetDateWindowStart()
	{
		return Now().getFullYear() - AYearWindow;
	}
	
	
	function GetDateWindowEnd()
	{
		return Now().getFullYear() + (99 - AYearWindow);
	}
	
	
	function GetDerivedYear()
	{
		var yrInput, yrTest, yrWindowStart, yrWindowEnd;
		
		yrInput = GetAbbreviatedYear(new Date(sResult));
		yrTest = GetCentury(Now()) + yrInput;
		yrWindowStart = GetDateWindowStart();
		yrWindowEnd = GetDateWindowEnd();
		
		if (yrTest < yrWindowStart)
			return yrTest + 100
		else
		if (yrTest > yrWindowEnd)
			return yrTest - 100
		else
			return yrTest;
		
	}			//GetDerivedYear()

	if (ADateString.length >= 10)
		return ADateString
	else
		sResult = PadDateString(ADateString);

	if (sResult.length == 10)
		return sResult
	else
		return sResult.substr(0, 6) + GetDerivedYear().toString();
	
}						//ExpandDateStringx()


function ExpandDateString(ADateString)
{
	return ExpandDateStringx(ADateString, 94);
		
}				//ExpandDateString()


function ExpandDOBString(ADateString)
{
	return ExpandDateStringx(ADateString, 99);
	
}


function GetDateDelimiter(ADateString)
{
	var sDelimiters;

	sDelimiters = '/. -';
	for (i=0; i < ADateString.length; i++)
		for (i2=0; i2 < sDelimiters.length; i2++)
			if (sDelimiters.charAt(i2) == ADateString.charAt(i))
			{
				return sDelimiters.charAt(i2);
			}
				
	return '';

}				//GetDateDelimiter()

	
function PadDateString(ADateString)
{
	var sResult, sRemains, sChar;
	
	
	void function DoPadding(ARemains)
	{
		var iIndex, s;
	
		if (ARemains > '')
		{
			iIndex = ARemains.indexOf(sChar);
			if (iIndex == -1)
				s = ARemains;
			else
				s = ARemains.substr(0, iIndex);
				
			if ((s.length == 1) || (s.length == 3))
				sResult = sResult + '0' + s
			else
				sResult = sResult + s;
			
			if (iIndex > -1)		//controls recursive call  			
				DoPadding(ARemains.substr(iIndex + 1, ARemains.length)); //recursion
		}
						
	}				//DoPadding()
	
	sChar = GetDateDelimiter(ADateString);
	sResult = '';
	
	DoPadding(ADateString);
	
	if (sResult.length == 6)
		return sResult.substr(0,2) + sChar + sResult.substr(2,2) + sChar + sResult.substr(4,2)
	else
		return sResult.substr(0,2) + sChar + sResult.substr(2,2) + sChar + sResult.substr(4,4);
		
}				//PadDateString()


function IsValidDate(AMonth, ADay, AYear)
{
	
	return IsValidDateString(AMonth + '/' + ADay + '/' + AYear);

}					//IsValidDate()


function IsValidDateString(ADateString)
{
	var d, iDay, iMonth, iYear, s, sDelimiter;
	
	s = ExpandDateString(ADateString);
	d = new Date(s);
	sDelimiter = GetDateDelimiter(ADateString);
	if (sDelimiter == '')
		return false
	else
	{
		iMonth = s.substr(0, s.indexOf(sDelimiter)) - 0;
		s = s.substr(s.indexOf(sDelimiter) + 1, s.length);
		iDay = s.substr(0, s.indexOf(sDelimiter)) - 0;
		s = s.substr(s.indexOf(sDelimiter) + 1, s.length);
		iYear = s.substr(0, s) - 0;
				
		return ((iDay == d.getDate()) && (iMonth == d.getMonth() + 1) && (iYear == d.getFullYear()));
	}
	
}				//IsValidDateString()


function ValidateDateString(ADateString, ADaysInPastAllowed, ADaysInFutureAllowed)
{
 //External Dependencies: GeneralLib.js
 
	var d, dUpper, dLower;
	
	if (! IsValidDateString(ADateString))
		return errBadValue;
		
	d = Today();
			
	if (ADaysInFutureAllowed == -1)
		dUpper = new Date('12/31/9999')
	else
		dUpper = IncDate(d, ADaysInFutureAllowed);
		
	if (ADaysInPastAllowed == -1)
		dLower = new Date('01/01/1000')
	else
		dLower = DecDate(d, ADaysInPastAllowed);
		
	d = new Date(ADateString);
			
	if ((d < dLower) || (d > dUpper))
	{
		if (d < dLower)
			return errBeforeRange;
		else
			return errAfterRange;
	}
	
	return errNone;
	
}				//ValidateDateString()


function GetInvalidDateDescription(ADateString)
{
  //External Dependency: StringLib.js

	var iDay, iMonth, iYear, iDaysInMonth, sDateString, s, sDescription;

	sDateString = DelimitDateString(ADateString);
	if (GetCharCount(sDateString, '/') != 2)
		return '';
		
	sDateString = ExpandDateString(sDateString);
			
	s = sDateString;
	iMonth = s.substr(0, s.indexOf('/')) - 0;
	s = s.substr(s.indexOf('/') + 1, s.length);
	iDay = s.substr(0, s.indexOf('/')) - 0;
	s = s.substr(s.indexOf('/') + 1, s.length);
	iYear = s;
						
	sDescription = '';
	if (! isNaN(iMonth))
		if ((iMonth > 12) || (iMonth < 1))
			sDescription = 'Month = ' + iMonth + ' (Must be 1-12)' + CrLf();
			
	if (! isNaN(iDay) && (iMonth < 13 && iMonth > 0))
	{
		iDaysInMonth = GetDaysInMonth(iMonth, iDay);
		if (iDay > iDaysInMonth)
			sDescription = sDescription + 'Day = ' + iDay + ' (Must be 1-' + iDaysInMonth + ')' + CrLf();
	}
	else
	{
		if ((iDay < 1) || (iDay > 31))
			sDescription = sDescription + 'Day = ' + iDay + ' (Must be 1-31)' + CrLf()
		else
		if (iDay > 28)
			sDescription = sDescription + 'Day = ' + iDay + ' (Validity cannot be determined)' + CrLf();
	}
				
	if (! isNaN(iYear))
		if (iYear < 1900)
			sDescription = sDescription + 'Year = ' + iYear + ' (Year may be too small)' + CrLf()
		else
		if (iYear > 9999)
			sDescription = sDescription + 'Year = ' + iYear + ' (Year is too large)' + CrLf();
		else
		if (iYear > 2500)
			sDescription = sDescription + 'Year = ' + iYear + ' (Year may be too large)' + CrLf();
				
	return sDescription;

}				//GetInvalidDateDescription()


function GetDaysInMonth(AMonth, AYear)
{

	switch (AMonth)
	{
		case 1: return 31;
		case 2: 
			if (IsValidDate(2, 29, AYear))
				return 29
			else
				return 28;
		case 3: return 31;
		case 4: return 30;
		case 5: return 31;
		case 6: return 30;
		case 7: return 31;
		case 8: return 31;
		case 9: return 30;
		case 10: return 31;
		case 11: return 30;
		case 12: return 31;
	}

}				//GetDaysInMonth()


function DelimitDateString(ADateString)
{
  //External Dependency: StringLib.js
  
	var	sDateString;
	var iReplaceIndex1, iReplaceIndex2;				//one-based indexes
	
	sDateString = '';
	
	if (IsDigitString(ADateString))	
	{
		if (ADateString.length == 8)
			sDateString = ADateString.substr(0, 2) + '/' + 
									ADateString.substr(2, 2) + '/' + 
										ADateString.substr(4, 4)
		else
			if (ADateString.length == 6)
				sDateString = ADateString.substr(0, 2) + '/' + 
										ADateString.substr(2, 2) + '/' + 
											ADateString.substr(4, 2);
	}
	else
		if ((ADateString.length <= 10) && (ADateString.length >= 5) && IsDigit(ADateString.charAt(0)))
		{
			iReplaceIndex1 = -1;
			iReplaceIndex2 = -2;
			if (IsDigit(ADateString.charAt(1)))	//01
			{
				if (! IsDigit(ADateString.charAt(2)))		//01.
				{
					iReplaceIndex1 = 2;
					if (IsDigit(ADateString.charAt(3)))	//01.0
					{
						if (! IsDigit(ADateString.charAt(4))) //01.01
							iReplaceIndex2 = 4
						else
							if (! IsDigit(ADateString.charAt(5)))
								iReplaceIndex2 = 5					//01.1.
							else
								if (ADateString.length == 5)
									iReplaceIndex2 = 4
					}
				}
			}		//if... {2nd character is a digit}
			else
			{
				iReplaceIndex1 = 1;		//1.
				if (IsDigit(ADateString.charAt(2)))		//1.0
				{
					if (! IsDigit(ADateString.charAt(3)))
						iReplaceIndex2 = 3				//1.1.
					else
						if (! IsDigit(ADateString.charAt(4)))		
							iReplaceIndex2 = 4;		//1.01.
				}	//if... {1.0}
			}		//else  {2nd character is not a digit}
			
			if (iReplaceIndex1 > -1 && iReplaceIndex2 > -1)
				sDateString = ADateString.substr(0, iReplaceIndex1) + '/' +
										ADateString.substr(iReplaceIndex1 + 1, iReplaceIndex2 - iReplaceIndex1 - 1) + '/' +
											ADateString.substr(iReplaceIndex2 + 1, ADateString.length);
											
		}			//if... {sDateString has a valid length, and first character is a digit}
		
	if (sDateString == '')
		return ADateString
	else
		return sDateString;
		
}					//DelimitDateString()


function GetDateString(ADateTime)
{
	var s;

	s = (ADateTime.getMonth() + 1) + '/' + ADateTime.getDate() + '/' + ADateTime.getFullYear();
	return PadDateString(s);

}					//GetDateString()


function YearsElapsed(AFromDate, AToDate)
{

  if (typeof(AFromDate) == 'string')
    AFromDate = new Date(AFromDate);
    
  if (typeof(AToDate) == 'string')
    AToDate = new Date(AToDate);
  
  return AToDate.getFullYear() - AFromDate.getFullYear();

}         //YearsElapsed()


function WholeYearsElapsed(AFromDate, AToDate)
{
  var Result;
  var iMonthDiff;
  
  if (typeof(AFromDate) == 'string')
    AFromDate = new Date(AFromDate);
    
  if (typeof(AToDate) == 'string')
    AToDate = new Date(AToDate);
  
  Result = (AToDate.getFullYear() - AFromDate.getFullYear()) - 1;
  
  iMonthDiff = AToDate.getMonth() - AFromDate.getMonth();
  if (iMonthDiff > -1)
    Result++
  else
  if (iMonthDiff == 0)
    if (AToDate.getDate() >= AFromDate.getDate())
      Result++;

  return Result;

}       //WholeYearsElapsed()


function WholeMonthsElapsed(AFromDate, AToDate)
{
  var Result;
  var iMonthDiff;
  var iDayDiff;

  if (typeof(AFromDate) == 'string')
    AFromDate = new Date(AFromDate);
    
  if (typeof(AToDate) == 'string')
    AToDate = new Date(AToDate);
    
  if (AFromDate > AToDate)
    return -1;
    
  Result = WholeYearsElapsed(AFromDate, AToDate) * 12;
  iMonthDiff = AToDate.getMonth() - AFromDate.getMonth();
  iDayDiff = AToDate.getDate() - AFromDate.getDate();
  if (iMonthDiff > -1)
  {
    Result += iMonthDiff;
    if (iDayDiff < 0)
      Result--;
  }
  else
  {
    Result += 11 - (AFromDate.getMonth());    //To Date to Dec 31st
    Result += AToDate.getMonth();             //Jan 1st to From Date

    if (iDayDiff > -1)    
      Result++;
  }
        
  return Result;

}       //WholeMonthsElapsed()


function DaysElapsed(AFromDate, AToDate)
{

  return Trunc(DaysElapsedFloat(AFromDate, AToDate));

}       //DaysElapsed()


function DaysElapsedFloat(AFromDate, AToDate)
{
  
  if (typeof(AFromDate) == 'string')
    AFromDate = new Date(AFromDate);
    
  if (typeof(AToDate) == 'string')
    AToDate = new Date(AToDate);
  
  return (AToDate - AFromDate) / 86400000;

}       //DaysElapsedFloat()


function DaysElapsedWhole(AFromDate, AToDate)
{

  return Math.round(DaysElapsedFloat(AFromDate, AToDate));

}       //DaysElapsedWhole()


function IncDate(ADateTime, ADays)
//---------------------------------------------------------------
//- To understand why this routine is implemented the way it is,
//- see IncDate24Hours()
//---------------------------------------------------------------
{
  var iHours = ADateTime.getHours();
  var iMinutes = ADateTime.getMinutes();
  var iSeconds = ADateTime.getSeconds();
  var iMilliseconds = ADateTime.getMilliseconds();
  var Result;

  //New Date with time set to High Noon  
  Result = new Date(ADateTime.getYear(), ADateTime.getMonth(), ADateTime.getDate(),
                    12, 0, 0, 0);
  Result = new Date(Result.valueOf() + (ADays * 86400000));
  
  Result = new Date(Result.getYear(), Result.getMonth(), Result.getDate(),
                    iHours, iMinutes, iSeconds, iMilliseconds);
                    
  return Result;

}       //IncDate()


function DecDate(ADateTime, ADays)
//---------------------------------------------------------------
//- To understand why this routine is implemented the way it is,
//- see DecDate24Hours()
//---------------------------------------------------------------
{
  var iHours = ADateTime.getHours();
  var iMinutes = ADateTime.getMinutes();
  var iSeconds = ADateTime.getSeconds();
  var iMilliseconds = ADateTime.getMilliseconds();
  var Result;

  //New Date with time set to High Noon  
  Result = new Date(ADateTime.getYear(), ADateTime.getMonth(), ADateTime.getDate(),
                    12, 0, 0, 0);
                    
  Result = new Date(Result.valueOf() - (ADays * 86400000));
  
  Result = new Date(Result.getYear(), Result.getMonth(), Result.getDate(),
                    iHours, iMinutes, iSeconds, iMilliseconds);
                    
  return Result;

}       //DecDate()


function IncDateString(ADateString, ADays)
{
  var d;
  
  d = new Date(ADateString);
  d = IncDate(d, ADays);
  if (ADateString == GetDateString(d))
    d = IncDate(d, .10);          //See IncDate() for why this conditional logic is necessary
  return GetDateString(d);

}     //IncDateString()


function DecDateString(ADateString, ADays)
{
  var d;
  
  d = new Date(ADateString);
  d = DecDate(d, ADays);
  return GetDateString(d);

}     //DecDateString()


function IncDate24Hours(ADateTime, ADays)
//This function increments ADateTime by 24 hours. This can produce an apparently
// anomolous result, particularly in the fall when daylight savings time ends, since the
// last day of daylight savings time is a 23 hour day and the first day of standard 
//  time is a 25 hour day. So routines which pass in ADateTime with time set to midnight
//  could fail to increment ADateTime by an entire "day", when that day is 25 hours long.
{

	return (new Date(ADateTime.valueOf() + (ADays * 86400000)));

}					//IncDate24Hours()


function DecDate24Hours(ADateTime, ADays)
//This function decrements ADateTime by 24 hours. This can produce an apparently
// anomolous result, particularly in the spring, when daylight savings time begins. 
//  See IncDate() for a related explanation. Generally, however, the day will always 
//  appear to change (this decrement routine only, not the increment routine). For 
//    this routine, only the time will be a concern.
{

  return (new Date(ADateTime.valueOf() - (ADays * 86400000)));

}				//DecDate24Hours()


function FormatTimeString(ATimeString, ASuffixFormat)
{
  // - External Dependencies: StringLib.js
  // - See IsValidTimeString for formatting info
  // - ASuffixFormat must be 'a' or 'am'
  
  var s, sSuffix, sTimeString;
  
  function GetNewResult(ATimeString)
  {
    if (ATimeString.length == 4 && IsDigitString(ATimeString))
      return ATimeString.substr(0, 2) + ':' + ATimeString.substr(2, 2) + sSuffix
    else
    if (ATimeString.length == 3 && IsDigitString(ATimeString))
      return ATimeString.substr(0, 1) + ':' + ATimeString.substr(1, 2) + sSuffix
    else
    if (ATimeString.length == 2 && IsDigitString(ATimeString))
      return ATimeString + ':' + '00' + sSuffix
    else
    if (ATimeString.length == 1 && IsDigitString(ATimeString))
      return ATimeString + ':' + '00' + sSuffix
    else
      return ATimeString + sSuffix;
      
  }     //GetNewResult()
  
   
  sSuffix = GetTimeSuffix(ATimeString);
  if (sSuffix == '' || (! IsValidTimeSuffix(sSuffix)))
    sSuffix = FormatTimeSuffix(GetInferredTimeSuffix(ATimeString), ASuffixFormat)
  else
    sSuffix = FormatTimeSuffix(sSuffix, ASuffixFormat);
    
  sTimeString = StripTimeSuffix(ATimeString);
  s = StripChar(sTimeString, ' ');
  s = StripChar(s, '.');
  return GetNewResult(s);

}       //FormatTimeString()


function IsValidTimeString(ATimeString)
{
  // - External Dependencies: StringLib.js
  // - ATimeString must be formatted to be considered valid
  // - Valid formats for this function are:
  //          ([H]H:MM and [H]H.MM) plus (am/pm or a/p).
  
  var bResult, sTimeString, iTimePartHours, iTimePartMinutes;
  
  bResult = (IsValidTimeSuffix(GetTimeSuffix(ATimeString)));
  if (bResult)
  {
    sTimeString = StripTimeSuffix(ATimeString);
    bResult = sTimeString.length == 5 || sTimeString.length == 4;
    if (bResult)
    {
      if (sTimeString.length == 5)
      {
        bResult = IsDigitString(sTimeString.substr(0,2)) && 
                    CharIn(sTimeString.charAt(2), ':.') &&
                      IsDigitString(sTimeString.substr(3,2));
        iTimePartHours = sTimeString.substr(0,2);
        iTimePartMinutes = sTimeString.substr(3,2);
      }
      else
      {
        bResult = IsDigitString(sTimeString.substr(0,1)) && 
                    CharIn(sTimeString.charAt(1), ':.') &&
                      IsDigitString(sTimeString.substr(2,2));
        iTimePartHours = sTimeString.substr(0,1);
        iTimePartMinutes = sTimeString.substr(2,2);
        //alert(bResult);
        //alert(iTimePartHours);
        //alert(iTimePartMinutes);
      }
                    
      if (bResult)    //are hours within range (1-12)
        bResult = iTimePartHours > 0 && iTimePartHours <= 12;
    
      if (bResult)      //are minutes within range (0-59)
        bResult = iTimePartMinutes > -1 && iTimePartMinutes < 60;
        
    }
  }
                  
  return bResult;

}     //IsValidTimeString()


function GetTimeSuffix(ATimeString)
{
  //External Dependencies: StringLib.js
  var sResult, sTmp, i;
    
  sResult = '';
  
  sTmp = TrimSpaces(ATimeString);
  
  if (sTmp == '')
    return sResult;
    
  i = sTmp.length - 1;
  while (IsAlpha(sTmp.charAt(i)))
  {
    sResult = sTmp.charAt(i) + sResult;
    i--;
    if (i < 0)
      break;
  }
  
  return sResult; 
    
}       //GetTimeSuffix()


function IsValidTimeSuffix(ATimeSuffix)
{
  //External Dependencies: StringLib.js
  //Valid suffixes: a, am, p, pm or any variation on case such as AM

  if (ATimeSuffix.length == 1) 
    return CharIn(ATimeSuffix.charAt(0), 'AaPp')
  else
  if (ATimeSuffix.length == 2)
    return CharIn(ATimeSuffix.charAt(0), 'AaPp') &&
            CharIn(ATimeSuffix.charAt(1), 'Mm')
  else
    return false;

}       //IsValidTimeSuffix()


function FormatTimeSuffix(ATimeSuffix, AFormatString)
{
  //External Dependencies: None.
  //valid format strings are based on am, such as a, am, A, AM.
  var sResult;
  
  sResult = '';
  if (ATimeSuffix.length > 0 && AFormatString.length > 0)
  {
    if (AFormatString.charAt(0) == 'a')
      sResult = ATimeSuffix.charAt(0).toLowerCase()
    else
    if (AFormatString.charAt(0) == 'A')
      sResult = ATimeSuffix.charAt(0).toUpperCase()
  }
 
  if (AFormatString.length > 1)
    sResult = sResult + AFormatString.charAt(1);
    
  return sResult;

}       //FormatTimeSuffix()


function StripTimeSuffix(ATimeString)
{
  //External Dependencies: StringLib.js
  var iSuffixStart;
  
  iSuffixStart = ATimeString.length - 1;
  while (iSuffixStart > -1 && IsAlpha(ATimeString.charAt(iSuffixStart)))
    iSuffixStart--;
     
  if (iSuffixStart < 0)
    return ATimeString
  else
    return ATimeString.substr(0, iSuffixStart + 1);
     
}       //StripTimeSuffix()

 
function GetInferredTimeSuffix(ATimeString)
{
  //Infers am/pm based on 12 hour time and assuming daylight hours
  var sTimeString, iHours;
  
  sTimeString = GetDigitString(ATimeString);
  if (sTimeString.length == 4)
    iHours = sTimeString.substr(0,2)
  else
  if (sTimeString.length == 3)
    iHours = sTimeString.substr(0,1)
  else
    iHours = sTimeString;
    
  if (iHours >=7 && iHours <= 11)
    return 'am'
  else
    return 'pm';

}       //GetInferredTimeSuffix()


function GetAge(ADateOfBirth, AReferenceDate)
{
  var dDob;
  var dRefDate;
  var iMonthReference;
  var iMonthDOB;
  var iDayReference;
  var iDayDOB;
  var Result;

  if (typeof(ADateOfBirth) == 'string')
    dDob = new Date(ADateOfBirth)
  else
    dDob = ADateOfBirth;
    
  if (typeof(AReferenceDate) == 'string')
    dRefDate = new Date(AReferenceDate)
  else
    dRefDate = AReferenceDate;
    
  Result = YearsElapsed(dDob, dRefDate);
  
  iMonthReference = dRefDate.getMonth();
  iMonthDOB = dDob.getMonth();

  if (iMonthReference < iMonthDOB)
    Result--
  else
  if (iMonthReference == iMonthDOB)
  {
    iDayReference = dRefDate.getDate();
    iDayDOB = dDob.getDate();
    
    if (iDayReference < iDayDOB)
      Result--;
  }
  
  return Result;

}         //GetAge()


function GetAgeInMonths(ADateOfBirth, AReferenceDate)
{
  var dDob;
  var dRefDate;
  var iMonthReference;
  var iMonthDOB;
  var iDayReference;
  var iDayDOB;

  if (typeof(ADateOfBirth) == 'string')
    dDob = new Date(ADateOfBirth)
  else
    dDob = ADateOfBirth;
    
  if (typeof(AReferenceDate) == 'string')
    dRefDate = new Date(AReferenceDate)
  else
    dRefDate = AReferenceDate;
    
  dDob = TruncateDateTime(dDob);
  dRefDate = TruncateDateTime(dRefDate);
    
  return WholeMonthsElapsed(dDob, dRefDate);
  
}       //GetAgeInMonths()


function GetAgeInDays(ADateOfBirth, AReferenceDate)
{
  //Unlike other Age routines, this one is based on a partial elapsement, not a whole one
  //The routine assumes that time is not avaliable; therefore AgeInDays is actually an estimate
  //  to within one day of the actual age; result may be over-estimated
  
  if (ADateOfBirth > AReferenceDate)
    return ''
  else
    return DaysElapsed(ADateOfBirth, AReferenceDate);

}       //GetAgeInDays()


function GetAgeClinical(ADateOfBirth, AReferenceDate)
{
  var Result = GetAge(ADateOfBirth, AReferenceDate);

  if (typeof(ADateOfBirth) == 'string')
    ADateOfBirth = new Date(ADateOfBirth);

  if (typeof(AReferenceDate) == 'string')
    AReferenceDate = new Date(AReferenceDate);
    
  ADateOfBirth = TruncateDateTime(ADateOfBirth);
  AReferenceDate = TruncateDateTime(AReferenceDate);
  
  if (isNaN(Result))
    Result = -1;
   
  if (Result == -1)
    return Result
  else
    if (ADateOfBirth.valueOf() == AReferenceDate.valueOf())
      return 'NB';
    
  if (Result < 2)
  {
    Result = GetAgeInMonths(ADateOfBirth, AReferenceDate);
    if (Result == 0)
      Result = GetAgeInDays(ADateOfBirth, AReferenceDate) + ' D'
    else
      Result += ' M';
  }
  else
    Result += ' Y';
    
  return Result;

}         //GetAgeClinical()


function GetAgeClinicalLazy(ADateOfBirth, AReferenceDate, AOptionalResultOnError)
{
  var Result;

  try
  {
    Result = GetAgeClinical(ADateOfBirth, AReferenceDate);
    if (Result == - 1 && AOptionalResultOnError != undefined)
      Result = AOptionalResultOnError;
  }
  catch (err)
  {
    alert(err.message);
    if (AOptionalResultOnError != undefined)
      return AOptionalResultOnError
    else
      return -1;
  }
  
  return Result;

}         //GetAgeClinicalLazy()


function GetAgeLazy(ADateOfBirth, AReferenceDate, AOptionalResultOnError)
{
  var Result;

  try
  {
    Result = GetAge(ADateOfBirth, AReferenceDate);
    if (isNaN(Result))
      throw('');
  }
  catch (err)
  {
    if (AOptionalResultOnError)
      return AOptionalResultOnError
    else
      return -1;
  }
  
  return Result;

}         //GetAgeLazy()


function GetMonthAndYearFirstDate(AMonth, AYear)
{

  return new Date(AMonth + '/01/' + AYear);

}


function GetMonthAndYearLastDate(AMonth, AYear)
{

  return new Date(AMonth + '/' + DaysInMonth(GetMonthAndYearFirstDate(AMonth, AYear)) + '/' + AYear);

}


