;(function() {
|
var root = this;
|
|
///////////////////////////////////////////////////
|
//
|
// lunarInfo
|
//
|
///////////////////////////////////////////////////
|
|
// base data about chinese year informace
|
// 保存公历农历之间的转换信息:以任意一年作为起点,
|
// 把从这一年起若干年(依需要而定)的农历信息保存起来。 要保存一年的信息,只要两个信息就够了: 1)农历每个月的大小;2)今年是否有闰月,闰几月以及闰月的大小。 用一个整数来保存这些信息就足够了。 具体的方法是:用一位来表示一个月的大小,大月记为1,小月记为0,
|
// 这样就用掉12位(无闰月)或13位(有闰月),再用高四位来表示闰月的月份,没有闰月记为0。 ※-----例----: 2000年的信息数据是0xc96,化成二进制就是110010010110B,
|
// 表示的含义是:1、2、5、8、10、11月大,其余月份小。 2001年的农历信息数据是0x1a95(因为闰月,所以有13位),
|
// 具体的就是1、2、4、5、8、10、12月大, 其余月份小(0x1a95=1101010010101B),
|
// 4月的后面那一个0表示的是闰4月小,接着的那个1表示5月大。 这样就可以用一个数组来保存这些信息。在这里用数组lunarInfo[]来保存这些信息
|
var lunarInfo=new Array(
|
0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,
|
0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,
|
0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,
|
0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,
|
0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,
|
0x1ab54,0x02b60,0x09570,0x052f2,0x04970,
|
0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,
|
0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,
|
0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,
|
0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,
|
0x06ca0,0x0b550,0x15355,0x04da0,0x0a5d0,
|
0x14573,0x052d0,0x0a9a8,0x0e950,0x06aa0,
|
0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,
|
0x05260,0x0f263,0x0d950,0x05b57,0x056a0,
|
0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,
|
0x0d250,0x0d558,0x0b540,0x0b5a0,0x195a6,
|
0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,
|
0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,
|
0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,
|
0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,
|
0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,
|
0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,
|
0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,
|
0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,
|
0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,
|
0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,
|
0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,
|
0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,
|
0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,
|
0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0);
|
|
var Gan=new Array("甲","乙","丙","丁","戊","己","庚","辛","壬","癸");
|
var Zhi=new Array("子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥");
|
var Animals=new Array("鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪");
|
// TODO is it need to do
|
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('初','十','廿','卅','□');
|
// var monthName = new Array("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC");
|
var cmonthName = new Array('正','二','三','四','五','六','七','八','九','十','十一','腊');
|
|
//公历节日 *表示放假日
|
var sFtv = new Array(
|
"0101*元旦",
|
"0214 情人节",
|
"0308 妇女节",
|
"0312 植树节",
|
"0401 愚人节",
|
"0422 地球日",
|
"0501 劳动节",
|
"0504 青年节",
|
"0531 无烟日",
|
"0601 儿童节",
|
"0606 爱眼日",
|
"0701 建党日",
|
"0707 抗战纪念日",
|
"0801 建军节",
|
"0910 教师节",
|
"0918 九·一八事变纪念日",
|
"1001*国庆节",
|
"1031 万圣节",
|
"1111 光棍节",
|
"1201 艾滋病日",
|
"1213 南京大屠杀纪念日",
|
"1224 平安夜",
|
"1225 圣诞节");
|
|
//某月的第几个星期几。 5,6,7,8 表示到数第 1,2,3,4 个星期几
|
var wFtv = new Array(
|
//一月的最后一个星期日(月倒数第一个星期日)
|
"0520 母亲节",
|
"0630 父亲节",
|
"1144 感恩节");
|
|
//农历节日
|
var lFtv = new Array(
|
"0101*春节",
|
"0115 元宵节",
|
"0202 龙抬头",
|
"0505 端午节",
|
"0707 七夕",
|
"0715 中元节",
|
"0815 中秋节",
|
"0909 重阳节",
|
"1208 腊八节",
|
"1223 小年",
|
"0100*除夕");
|
|
//====================================== 返回农历 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-1900] & 0x10000)? 30: 29)
|
else return(0)
|
}
|
|
//====================================== 返回农历 y年闰哪个月 1-12,没闰返回 0
|
function leapMonth(y) {
|
return(lunarInfo[y-1900] & 0xf)
|
}
|
|
//====================================== 返回农历 y年m月的总天数
|
function monthDays(y,m) {
|
return( (lunarInfo[y-1900] & (0x10000>>m))? 30: 29 )
|
}
|
|
//====================================== 算出农历,传入日期对象,返回农历日期日期对象
|
// 该对象属性有 .year .month .day .isLeap .yearCyl .dayCyl .monCyl
|
function Lunar(date) {
|
var objDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
var i, leap=0, temp=0
|
var baseDate = new Date(1900,0,31)
|
// Mac和linux平台的firefox在此处会产生浮点数错误
|
var offset = Math.round((objDate - baseDate)/86400000)
|
|
this.dayCyl = offset + 40
|
this.monCyl = 14
|
|
for(i=1900; i<2050 && offset>0; i++) {
|
temp = lYearDays(i)
|
offset -= temp
|
this.monCyl += 12
|
}
|
|
if(offset<0) {
|
offset += temp;
|
i--;
|
this.monCyl -= 12
|
}
|
|
this.year = i
|
this.yearCyl = i-1864
|
|
leap = leapMonth(i) //闰哪个月
|
this.isLeap = false
|
|
for(i=1; i<13 && offset>0; i++) {
|
//闰月
|
if(leap>0 && i==(leap+1) && this.isLeap==false)
|
{ --i; this.isLeap = true; temp = leapDays(this.year); }
|
else
|
{ temp = monthDays(this.year, i); }
|
|
//解除闰月
|
if(this.isLeap==true && i==(leap+1)) this.isLeap = false
|
|
offset -= temp
|
if(this.isLeap == false) this.monCyl ++
|
}
|
|
if(offset==0 && leap>0 && i==leap+1)
|
if(this.isLeap)
|
{ this.isLeap = false; }
|
else
|
{ this.isLeap = true; --i; --this.monCyl;}
|
|
if(offset<0){ offset += temp; --i; --this.monCyl; }
|
|
this.month = i
|
this.day = offset + 1
|
}
|
|
///////////////////////////////////////////////////////////
|
//
|
// lunar 2 solar
|
//
|
///////////////////////////////////////////////////////////
|
// year .month .day .isLeap .yearCyl .dayCyl .monCyl
|
function Solar(date, isLeapMonth) {
|
var lyear = date.getFullYear(),
|
lmonth = date.getMonth() + 1,
|
lday = date.getDate(),
|
offset = 0,
|
leap = isLeap(lyear);
|
|
// increment year
|
for(var i = 1900; i < lyear; i++) {
|
offset += lYearDays(i);
|
}
|
|
// increment month
|
// add days in all months up to the current month
|
for (var i = 1; i < lmonth; i++) {
|
// add extra days for leap month
|
if (i == leapMonth(lyear)) {
|
offset += leapDays(lyear);
|
}
|
offset += monthDays(lyear, i);
|
}
|
// if current month is leap month, add days in normal month
|
if (leap && isLeapMonth) {
|
offset += monthDays(lyear, i);
|
}
|
|
// increment
|
offset += parseInt(lday) - 1;
|
|
var baseDate = new Date(1900,0,31);
|
var solarDate = new Date(baseDate.valueOf() + offset * 86400000);
|
|
this.year = solarDate.getFullYear();
|
this.month = solarDate.getMonth();
|
this.day = solarDate.getDate();
|
this.isLeap = leap;
|
}
|
|
function isLeap(year) {
|
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
}
|
|
function getAnimalYear(year) {
|
return Animals[((year - 1900)%12)];
|
}
|
|
|
//============================== 传入 offset 返回干支, 0=甲子
|
function cyclical(num) {
|
return(Gan[num%10]+Zhi[num%12]);
|
}
|
//======================================= 返回该年的复活节(春分后第一次满月周后的第一主日)
|
function easter(y) {
|
|
var term2=sTerm(y,5); //取得春分日期
|
var dayTerm2 = new Date(Date.UTC(y,2,term2,0,0,0,0)); //取得春分的公历日期控件(春分一定出现在3月)
|
var lDayTerm2 = new Lunar(dayTerm2); //取得取得春分农历
|
|
if(lDayTerm2.day<15) //取得下个月圆的相差天数
|
var lMlen= 15-lDayTerm2.day;
|
else
|
var lMlen= (lDayTerm2.isLeap? leapDays(y): monthDays(y,lDayTerm2.month)) - lDayTerm2.day + 15;
|
|
//一天等于 1000*60*60*24 = 86400000 毫秒
|
var l15 = new Date(dayTerm2.getTime() + 86400000*lMlen ); //求出第一次月圆为公历几日
|
var dayEaster = new Date(l15.getTime() + 86400000*( 7-l15.getUTCDay() ) ); //求出下个周日
|
|
this.m = dayEaster.getUTCMonth();
|
this.d = dayEaster.getUTCDate();
|
|
}
|
|
//====================== 中文日期
|
function getCDay(d){
|
var s;
|
|
switch (d) {
|
case 10:
|
s = '初十'; break;
|
case 20:
|
s = '二十'; break;
|
break;
|
case 30:
|
s = '三十'; break;
|
break;
|
default :
|
s = nStr2[Math.floor(d/10)];
|
s += nStr1[d%10];
|
}
|
return(s);
|
}
|
|
////////////////////////////////////////////////////////////////
|
//
|
// 24 节气
|
//
|
///////////////////////////////////////////////////////////////
|
var solarTerm = new Array("小寒","大寒","立春","雨水","惊蛰","春分","清明",
|
"谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分",
|
"寒露","霜降","立冬","小雪","大雪","冬至");
|
|
var solarTermBase = new Array(4,19,3,18,4,19,4,19,4,20,4,20,6,22,6,22,6,22,7,22,6,21,6,21);
|
var solarTermIdx = '0123415341536789:;<9:=<>:=1>?012@015@015@015AB78CDE8CD=1FD01GH01GH01IH01IJ0KLMN;LMBEOPDQRST0RUH0RVH0RWH0RWM0XYMNZ[MB\\]PT^_ST`_WH`_WH`_WM`_WM`aYMbc[Mde]Sfe]gfh_gih_Wih_WjhaWjka[jkl[jmn]ope]qph_qrh_sth_W';
|
var solarTermOS = '211122112122112121222211221122122222212222222221222122222232222222222222222233223232223232222222322222112122112121222211222122222222222222222222322222112122112121222111211122122222212221222221221122122222222222222222222223222232222232222222222222112122112121122111211122122122212221222221221122122222222222222221211122112122212221222211222122222232222232222222222222112122112121111111222222112121112121111111222222111121112121111111211122112122112121122111222212111121111121111111111122112122112121122111211122112122212221222221222211111121111121111111222111111121111111111111111122112121112121111111222111111111111111111111111122111121112121111111221122122222212221222221222111011111111111111111111122111121111121111111211122112122112121122211221111011111101111111111111112111121111121111111211122112122112221222211221111011111101111111110111111111121111111111111111122112121112121122111111011111121111111111111111011111111112111111111111011111111111111111111221111011111101110111110111011011111111111111111221111011011101110111110111011011111101111111111211111001011101110111110110011011111101111111111211111001011001010111110110011011111101111111110211111001011001010111100110011011011101110111110211111001011001010011100110011001011101110111110211111001010001010011000100011001011001010111110111111001010001010011000111111111111111111111111100011001011001010111100111111001010001010000000111111000010000010000000100011001011001010011100110011001011001110111110100011001010001010011000110011001011001010111110111100000010000000000000000011001010001010011000111100000000000000000000000011001010001010000000111000000000000000000000000011001010000010000000';
|
|
// 形式如function sTerm(year, n),用来计算某年的第n个节气(从0小寒算起)为几号,这也基本被认可为节气计算的基本形式。由于没个月份有两个节气,计算时需要调用两次(n和n+1)
|
//===== 某年的第n个节气为几日(从0小寒起算)
|
function sTerm(y,n) {
|
return(solarTermBase[n] + Math.floor( solarTermOS.charAt( ( Math.floor(solarTermIdx.charCodeAt(y-1900)) - 48) * 24 + n ) ) );
|
}
|
/////////////////////////////////////////////////////////////////
|
//
|
// calElement model
|
//
|
/////////////////////////////////////////////////////////////////
|
|
//============================== 阴历属性
|
function calElement(sYear,sMonth,sDay,week,lYear,lMonth,lDay,isLeap,cYear,cMonth,cDay) {
|
//瓣句
|
this.sYear = sYear; //公元年4位数字
|
this.sMonth = sMonth; //公元月数字
|
this.sDay = sDay; //公元日数字
|
this.week = week; //星期, 1个中文
|
//农历
|
this.lYear = lYear; //公元年4位数字
|
this.lMonth = lMonth; //农历月数字
|
this.lDay = lDay; //农历日数字
|
this.isLeap = isLeap; //是否为农历闰月?
|
//八字
|
this.cYear = cYear; //年柱, 2个中文
|
this.cMonth = cMonth; //月柱, 2个中文
|
this.cDay = cDay; //日柱, 2个中文
|
|
this.lunarDay = getCDay(lDay);
|
this.lunarMonth = cmonthName[lMonth - 1];
|
this.lunarYear = getAnimalYear(lYear);
|
|
// this.color = '';
|
|
this.lunarFestival = ''; //农历节日
|
this.solarFestival = ''; //公历节日
|
this.solarTerms = ''; //节气
|
}
|
///////////////////////////////////////////////////////////////
|
//
|
// main
|
//
|
///////////////////////////////////////////////////////////////
|
// date's month should be --, example: 2012-5-21 -> new Date(2012, 4, 21)
|
// no matter solar or lunar
|
function CalendarConverter() {
|
this.solar2lunar = function(date) {
|
var sYear = date.getFullYear(),
|
sMonth = date.getMonth(),
|
sDay = date.getDate(),
|
weekDay = nStr1[date.getDay()],
|
lunar = new Lunar(date),
|
lunarYear = lunar.year,
|
lunarMonth = lunar.month,
|
lunarDay = lunar.day,
|
isLeap = lunar.isLeap;
|
|
return addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap);
|
}
|
|
this.lunar2solar = function(date, isLeapMonth) {
|
var lunarYear = date.getFullYear(),
|
lunarMonth = date.getMonth() + 1,
|
lunarDay = date.getDate(),
|
solar = new Solar(date, isLeapMonth),
|
sYear = solar.year,
|
sMonth = solar.month,
|
sDay = solar.day,
|
weekDay = nStr1[new Date(sYear, sMonth, sDay).getDay()],
|
isLeap = solar.isLeap,
|
cYear, cMonth, cDay, that = {};
|
|
return addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap);
|
}
|
|
}
|
function addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap) {
|
var cYear, cMonth, cDay, that = {};
|
////////年柱 1900年立春后为庚子年(60进制36)
|
if(sMonth < 2 ) {
|
cYear=cyclical(sYear-1900+36-1);
|
} else {
|
cYear=cyclical(sYear-1900+36);
|
}
|
var term2=sTerm(sYear,2); //立春日期
|
|
////////月柱 1900年1月小寒以前为 丙子月(60进制12)
|
var firstNode = sTerm(sYear, sMonth*2) //返回当月「节」为几日开始
|
cMonth = cyclical((sYear - 1900) * 12 + sMonth + 12);
|
|
//依节气调整二月分的年柱, 以立春为界
|
if(sMonth == 1 && sDay >= term2) cYear = cyclical(sYear - 1900+36);
|
//依节气月柱, 以「节」为界
|
if(sDay >= firstNode) cMonth = cyclical((sYear - 1900) * 12 + sMonth + 13);
|
//当月一日与 1900/1/1 相差天数
|
//1900/1/1与 1970/1/1 相差25567日, 1900/1/1 日柱为甲戌日(60进制10)
|
var dayCyclical = Date.UTC(sYear, sMonth, 1, 0, 0, 0, 0)/86400000 + 25567 + 10;
|
//日柱
|
cDay = cyclical(dayCyclical + sDay - 1);
|
|
//sYear,sMonth,sDay,weekDay,
|
//lYear,lMonth,lDay,isLeap,
|
//cYear,cMonth,cDay
|
that = new calElement(sYear, sMonth + 1, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap, cYear, cMonth, cDay);
|
|
// 节气
|
tmp1=sTerm(sYear, sMonth * 2) - 1;
|
tmp2=sTerm(sYear, sMonth * 2 + 1) - 1;
|
if (tmp1 == (sDay - 1)) {
|
that.solarTerms = solarTerm[sMonth * 2];
|
}
|
if (tmp2 == (sDay - 1)) {
|
that.solarTerms = solarTerm[sMonth * 2 + 1];
|
}
|
|
//公历节日
|
for (var i = 0, item; item = sFtv[i]; i++) {
|
if(item.match(/^(\d{2})(\d{2})([\s\*])(.+)$/)) {
|
if(Number(RegExp.$1)==(sMonth+1)) {
|
if (Number(RegExp.$2) == sDay) {
|
that.solarFestival += RegExp.$4 + ' ';
|
}
|
}
|
}
|
}
|
|
//月周节日
|
for (i = 0, item; item = wFtv[i]; i++) {
|
if (item.match(/^(\d{2})(\d)(\d)([\s\*])(.+)$/)) {
|
if (Number(RegExp.$1) == (sMonth + 1)) {
|
tmp1 = Number(RegExp.$2);
|
tmp2 = Number(RegExp.$3);
|
if (tmp1 < 5) {
|
var wFtvDate = (tmp2 == 0 ? 7 : 0) + (tmp1 - 1)*7 + tmp2;
|
if (wFtvDate == sDay) {
|
that.solarFestival += RegExp.$5 + ' ';
|
break;
|
}
|
}
|
}
|
}
|
}
|
|
// 农历节日
|
for (i = 0, item; item = lFtv[i]; i++) {
|
if (item.match(/^(\d{2})(.{2})([\s\*])(.+)$/)) {
|
tmp1 = Number(RegExp.$1);
|
tmp2 = Number(RegExp.$2);
|
lMonLen = monthDays(lunarYear, lunarMonth);
|
// 月份是12月,且为最后一天,则设置为春节
|
if ((tmp1 == lunarMonth && tmp2 == lunarDay) || (tmp2 == '00' && lunarMonth == 12 && lMonLen == lunarDay)) {
|
that.lunarFestival += RegExp.$4 + ' ';
|
break;
|
}
|
}
|
}
|
|
return that;
|
}
|
|
/*
|
* example:
|
* var cc =new CalendarConverter;
|
*
|
* cc.lunar2solar(new Date(2011, 0, 3)); ---> 2010,11,29
|
* cc.solar2lunar(new Date(2010, 10, 29)); ----> 2011, 1, 3
|
*
|
* 农历转公历时,如果那一月是那一年的闰月,则需额外传一个参数,才能得到正确的公历日期
|
* cc.solar2lunar(new Date(2012, 4, 27)); ---> 2012年4月初7, 其中 isLeap为true,表示为闰四月
|
* cc.lunar2solar(new Date(2012, 3, 7)) ---> 得到错误时间:2012, 4, 27
|
* cc.lunar2solar(new Date(2012, 3, 7), true) --> 正确: 2012, 5, 27
|
*
|
*result:
|
* {
|
* cDay: "戊戌"
|
, cMonth: "丁未"
|
, cYear: "壬辰"
|
, isLeap: false // 该月是否为闰月
|
, lDay: 18
|
, lMonth: 6
|
, lYear: 2012
|
, lunarDay: "十八"
|
, lunarFestival: ""
|
, lunarMonth: "六"
|
, lunarYear: "龙"
|
, sDay: 5
|
, sMonth: 8
|
, sYear: 2012
|
, solarFestival: "" // 节日
|
, solarTerms: "" // 节气
|
, week: "日" // 周几
|
}
|
*
|
*/
|
window.CalendarConverter = CalendarConverter;
|
if ('undefined' !== typeof module && module.exports) {
|
module.exports = CalendarConverter;
|
} else {
|
root.CalendarConverter = CalendarConverter;
|
}
|
})()
|