﻿//=============================================
//   以下程序的数据及原始算法来自:
//      http://sean.o4u.com/ap/calendar/
//      原作者:Sean Lin (林洵贤)
//
//   修改者: 漠石 mostone@hotmail.com
//   修改目的: 去除月历显示,只实现日期查询
//   主要方法:
//              Lunar()     传入公历,返回农历对象
//              Solar()     传入农历,返回公历对象
//
//

var lunarInfo=new Array(
0x4bd8,0x4ae0,0xa570,0x54d5,0xd260,0xd950,0x5554,0x56af,0x9ad0,0x55d2,
0x4ae0,0xa5b6,0xa4d0,0xd250,0xd295,0xb54f,0xd6a0,0xada2,0x95b0,0x4977,
0x497f,0xa4b0,0xb4b5,0x6a50,0x6d40,0xab54,0x2b6f,0x9570,0x52f2,0x4970,
0x6566,0xd4a0,0xea50,0x6a95,0x5adf,0x2b60,0x86e3,0x92ef,0xc8d7,0xc95f,
0xd4a0,0xd8a6,0xb55f,0x56a0,0xa5b4,0x25df,0x92d0,0xd2b2,0xa950,0xb557,
0x6ca0,0xb550,0x5355,0x4daf,0xa5b0,0x4573,0x52bf,0xa9a8,0xe950,0x6aa0,
0xaea6,0xab50,0x4b60,0xaae4,0xa570,0x5260,0xf263,0xd950,0x5b57,0x56a0,
0x96d0,0x4dd5,0x4ad0,0xa4d0,0xd4d4,0xd250,0xd558,0xb540,0xb6a0,0x95a6,
0x95bf,0x49b0,0xa974,0xa4b0,0xb27a,0x6a50,0x6d40,0xaf46,0xab60,0x9570,
0x4af5,0x4970,0x64b0,0x74a3,0xea50,0x6b58,0x5ac0,0xab60,0x96d5,0x92e0,
0xc960,0xd954,0xd4a0,0xda50,0x7552,0x56a0,0xabb7,0x25d0,0x92d0,0xcab5,
0xa950,0xb4a0,0xbaa4,0xad50,0x55d9,0x4ba0,0xa5b0,0x5176,0x52bf,0xa930,
0x7954,0x6aa0,0xad50,0x5b52,0x4b60,0xa6e6,0xa4e0,0xd260,0xea65,0xd530,
0x5aa0,0x76a3,0x96d0,0x4afb,0x4ad0,0xa4d0,0xd0b6,0xd25f,0xd520,0xdd45,
0xb5a0,0x56d0,0x55b2,0x49b0,0xa577,0xa4b0,0xaa50,0xb255,0x6d2f,0xada0,
0x4b63,0x937f,0x49f8,0x4970,0x64b0,0x68a6,0xea5f,0x6b20,0xa6c4,0xaaef,
0x92e0,0xd2e3,0xc960,0xd557,0xd4a0,0xda50,0x5d55,0x56a0,0xa6d0,0x55d4,
0x52d0,0xa9b8,0xa950,0xb4a0,0xb6a6,0xad50,0x55a0,0xaba4,0xa5b0,0x52b0,
0xb273,0x6930,0x7337,0x6aa0,0xad50,0x4b55,0x4b6f,0xa570,0x54e4,0xd260,
0xe968,0xd520,0xdaa0,0x6aa6,0x56df,0x4ae0,0xa9d4,0xa4d0,0xd150,0xf252,
0xd520);

var Gan=new Array("甲","乙","丙","丁","戊","己","庚","辛","壬","癸");
var Zhi=new Array("子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥");
var Animals=new Array("鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪");
var solarTerm = new Array("小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至");
var sTermInfo = new Array(0,21208,42467,63836,85337,107014,128867,150921,173149,195551,218072,240693,263343,285989,308563,331033,353350,375494,397447,419210,440795,462224,483532,504758);
var nStr1 = new Array('零','一','二','三','四','五','六','七','八','九');
var nStr2 = new Array('初','十','廿','卅','卌');

/*****************************************************************************
					日期计算
*****************************************************************************/
//========================================================= 
//
//       算出农历, 传入日期对象, 传回农历日期对象
//
//  属性:
//      year : 农历年
//      month : 农历月
//      day : 农历日
//      isLeap : 农历是否闰月
//      cYear:  八字中的年
//      cMonth : 八字中的月
//      cDay :  八字中的日
//      term:  二十四节气
//      animal:  生肖
//      cDate:  农历日期汉字表示
//      
//========================================================= 
function Lunar(objDate) {
	
	// 公历年,月,日
	var gY = objDate.getFullYear();
	var gM = objDate.getMonth();
	var gD = objDate.getDate();
	
	// 农历年,月,日, 是否闰月
	var nY, nM, nD, isLeap;
	
	// 年柱,月柱,日柱
	var cY, cM, cD;
	
	var bz = cyclicalDate(objDate);
	cY = bz[0];
	cM = bz[1];
	cD = bz[2];
	
	//=====================================================================
	// 指定公历日期与公历1900/1/31日的天数
	var offset = (Date.UTC(gY, gM, gD) - Date.UTC(1900, 0, 31)) / 86400000;
	var i, leap=0, temp=0;
	
	for(i=1900; i<2100 && offset>0; i++)
	{
		temp=lYearDays(i);
		offset-=temp;
	}
	
	if(offset<0) 
	{ 
		offset+=temp; 

		i--; 
	}
	
	nY = i;
	
	leap = leapMonth(i); //闰哪个月
	isLeap = false;
	
	for(i=1; i<13 && offset>0; i++)
	{
		//闰月
		if(leap>0 && i==(leap+1) && isLeap==false)
		{
			--i;
			isLeap = true;
			temp = leapDays(nY);
		}
		else
		{
			temp = monthDays(nY, i);
		}
		
		//解除闰月
		if(isLeap==true && i==(leap+1)) isLeap = false;
		
		offset -= temp;
	}
		
	if(offset==0 && leap>0 && i==leap+1)
	if(isLeap)
	{
		isLeap = false;
	}
	else
	{
		isLeap = true; 
		--i;
	}
		
	if(offset<0)
	{
		offset += temp;
		--i;
	}
	
	nM = i;
	nD = offset + 1;
	
	
	//==================================================
	this.year = nY;
	this.month = nM;
	this.day = nD;
	this.isLeap = isLeap;
	this.cYear = cY;
	this.cMonth = cM;
	this.cDay = cD;
	this.term = getTerm(objDate);
	this.animal = getAnimal(nY);
	this.cDate = getCNYear(nY) + "年" + (isLeap ? "闰" : "") + getCNMonth(nM) + "月" + getCNDay(nD);
	this.yearStr = getCNYear(nY);
	this.monthStr = (isLeap ? "闰" : "") + getCNMonth(nM);
	this.dayStr = getCNDay(nD);
}

//========================================================= 
//
//       算出农历, 传入日期对象, 传回农历日期对象
//
//  属性:
//      date : 公历日期对象
//      cYear:  八字中的年
//      cMonth : 八字中的月
//      cDay :  八字中的日
//      term : 二十四节气
//      animal:  生肖
//      
//========================================================= 
function Solar(nY, nM, nD, isLeap)
{
	nY = parseInt(nY);
	nM = parseInt(nM);
	nD = parseInt(nD);
	if (typeof(isLeap) == "undefined")
	{
		var isLeap = false;
	}
	else
	{
		switch(isLeap)
		{
			case "0": isLeap = false; break;
			case "1": isLeap = true; break;					
		}
	}
	
	var offset = 0;
	// 指定农历与农历1901/1/1日的天数
	for(var i = nY - 1; i > 1900; i--)
	{
		offset += lYearDays(i);
	}
	
	// 闰哪个月
	leap = leapMonth(nY);
	if (leap > 0 && leap < nM)
	{
		offset += leapDays(nY);
	}
	
	for(var i = nM - 1; i > 0; i--)
	{
		offset += monthDays(nY, i);
	}
	
	if (isLeap && leap == nM)
	{
		offset += monthDays(nY, nM);
	}
	
	offset += nD - 1;
	
	
	// 由农历1901/01/01的公历日期1901/02/19算出指定农历的公历日期
	
	var resultDate = new Date( Date.UTC(1901, 1, 19) + (offset * 86400000));
	var bz = cyclicalDate(resultDate);
	
	//============================================
	this.date = resultDate;
	this.cYear = bz[0];
	this.cMonth = bz[1];
	this.cDay = bz[2];
	this.term = getTerm(resultDate);
	this.animal = getAnimal(nY);
}

//====================================== 传回农历 y年的总天数
function lYearDays(y) {
	var i, sum = 348;
	for(i=0x8000; i>0x8; i>>=1) sum += (lunarInfo[y - 1900] & i)? 1: 0;
	return(sum+leapDays(y));
}

//====================================== 传回农历 y年闰月的天数
function leapDays(y) {
	if(leapMonth(y))
	{
		return( (lunarInfo[y - 1899]&0xf)==0xf ? 30: 29);
	}
	else
	{
		return(0);
	}
}

//====================================== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
function leapMonth(y) {
	var lm = lunarInfo[y - 1900] & 0xf;
	return(lm==0xf ? 0: lm);
}

//====================================== 传回农历 y年m月的总天数
function monthDays(y,m) {
	return( (lunarInfo[y - 1900] & (0x10000>>m))? 30: 29 );
}

//=================================================
//   传回公历日期的八字(数组)
//      [0] :  八字中的年
//      [1] :  八字中的月
//      [2] :  八字中的日
//=================================================
function cyclicalDate(objDate)
{
	// 公历年,月,日
	var gY = objDate.getFullYear();
	var gM = objDate.getMonth();
	var gD = objDate.getDate();

	////////年柱 1900年立春後为庚子年(60进制36)
	var term2=sTerm(gY, 2); //立春日期
	//依节气调整二月分的年柱, 以立春为界
	if(gM < 1 || (gM == 1 && gD < term2))
	{
		cY = cyclical(gY - 1900 + 36 - 1);
	}
	else
	{
		cY = cyclical(gY - 1900 + 36);
	}
	
	////////月柱 1900年1月小寒以前为 丙子月(60进制12)
	var firstNode = sTerm(gY, gM * 2) //传回当月「节」为几日开始
	//依节气月柱, 以「节」为界
	if (gD < firstNode)
	{
		cM = cyclical((gY - 1900) * 12 + gM + 12);
	}
	else
	{
		cM = cyclical((gY - 1900) * 12 + gM + 13);
	}
	
	//当月一日与 1900/1/1 相差天数
	//1900/1/1与 1970/1/1 相差25567日, 1900/1/1 日柱为甲戌日(60进制10)
	cD = cyclical(Date.UTC(gY, gM, gD,0,0,0,0) / 86400000 + 25567 + 10);
	
	return [cY, cM, cD];
}

function getCNYear(nY)
{
	var result = "";
	result += nStr1[Math.floor(nY / 1000)];
	var y = nY % 1000;
	result += nStr1[Math.floor(y / 100)];
	y = nY % 100;
	result += nStr1[Math.floor(y / 10)];
	result += nStr1[y % 10];
	
	return result;
}

function getCNMonth(nM)
{
	var result = "";
	if (nM == 1)
	{
		result = "正";
	}
	else
	{
		if (nM > 9) result += nStr2[1];
		if (nM % 10 > 0) result += nStr1[nM % 10];
	}
	
	return result;
}

function getCNDay(nD)
{
	var result = "";
	if (nD == 10)
	{
		result += nStr2[0] + nStr2[1];
	}
	else if (nD % 10 > 0)
	{
		result += nStr2[Math.floor(nD / 10)];
		result += nStr1[nD % 10];
	}
	else
	{
		result += nStr1[Math.floor(nD / 10)];
		result += nStr2[1];
	}
	
	return result;
}


//======================传回指定公历是否是节气,返回节气名
function getTerm(objDate)
{
	// 公历年,月,日
	var gY = objDate.getFullYear();
	var gM = objDate.getMonth();
	var gD = objDate.getDate();
	
	// 二十四节气
	var term = "";
	if (sTerm(gY, gM * 2) == gD)
	{
		term = solarTerm[gM * 2];
	}
	else if (sTerm(gY, gM * 2 + 1) == gD)
	{
		term = solarTerm[gM * 2 + 1];
	}
	
	return term;
}

//      animal:  生肖
//      
function getAnimal(nY)
{
	return Animals[(nY - 4)%12];
}


//============================== 传入 offset 传回干支, 0=甲子
function cyclical(num) {
	return(Gan[num%10]+Zhi[num%12]);
}

//===== 某年的第n个节气为几日(从0小寒起算)
function sTerm(y,n) {
	var offDate = new Date( ( 31556925974.7*(y-1900) + sTermInfo[n]*60000  ) + Date.UTC(1900,0,6,2,5) );
	return(offDate.getUTCDate());
}
