hjg
2024-01-17 efbf825aa2f845bc6f9b26b0fa10139c8071deef
提交 | 用户 | 时间
58d006 1 ;(function() {
A 2   var root = this;
3
4   ///////////////////////////////////////////////////
5   //
6   // lunarInfo
7   //
8   ///////////////////////////////////////////////////
9
10   // base data about chinese year informace
11   // 保存公历农历之间的转换信息:以任意一年作为起点,
12   // 把从这一年起若干年(依需要而定)的农历信息保存起来。 要保存一年的信息,只要两个信息就够了: 1)农历每个月的大小;2)今年是否有闰月,闰几月以及闰月的大小。 用一个整数来保存这些信息就足够了。 具体的方法是:用一位来表示一个月的大小,大月记为1,小月记为0,
13   // 这样就用掉12位(无闰月)或13位(有闰月),再用高四位来表示闰月的月份,没有闰月记为0。 ※-----例----: 2000年的信息数据是0xc96,化成二进制就是110010010110B,
14   // 表示的含义是:1、2、5、8、10、11月大,其余月份小。 2001年的农历信息数据是0x1a95(因为闰月,所以有13位),
15   // 具体的就是1、2、4、5、8、10、12月大, 其余月份小(0x1a95=1101010010101B),
16   // 4月的后面那一个0表示的是闰4月小,接着的那个1表示5月大。 这样就可以用一个数组来保存这些信息。在这里用数组lunarInfo[]来保存这些信息
17   var lunarInfo=new Array(
18   0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,
19   0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,
20   0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,
21   0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,
22   0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,
23   0x1ab54,0x02b60,0x09570,0x052f2,0x04970,
24   0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,
25   0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,
26   0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,
27   0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,
28   0x06ca0,0x0b550,0x15355,0x04da0,0x0a5d0,
29   0x14573,0x052d0,0x0a9a8,0x0e950,0x06aa0,
30   0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,
31   0x05260,0x0f263,0x0d950,0x05b57,0x056a0,
32   0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,
33   0x0d250,0x0d558,0x0b540,0x0b5a0,0x195a6,
34   0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,
35   0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,
36   0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,
37   0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,
38   0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,
39   0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,
40   0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,
41   0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,
42   0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,
43   0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,
44   0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,
45   0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,
46   0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,
47   0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0);
48
49   var Gan=new Array("甲","乙","丙","丁","戊","己","庚","辛","壬","癸");
50   var Zhi=new Array("子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥");
51   var Animals=new Array("鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪");
52   // TODO is it need to do
53   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);
54   var nStr1 = new Array('日','一','二','三','四','五','六','七','八','九','十');
55   var nStr2 = new Array('初','十','廿','卅','□');
56   // var monthName = new Array("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC");
57   var cmonthName = new Array('正','二','三','四','五','六','七','八','九','十','十一','腊');
58
59   //公历节日 *表示放假日
60   var sFtv = new Array(
61   "0101*元旦",
62   "0214 情人节",
63   "0308 妇女节",
64   "0312 植树节",
65   "0401 愚人节",
66   "0422 地球日",
67   "0501 劳动节",
68   "0504 青年节",
69   "0531 无烟日",
70   "0601 儿童节",
71   "0606 爱眼日",
72   "0701 建党日",
73   "0707 抗战纪念日",
74   "0801 建军节",
75   "0910 教师节",
76   "0918 九·一八事变纪念日",
77   "1001*国庆节",
78   "1031 万圣节",
79   "1111 光棍节",
80   "1201 艾滋病日",
81   "1213 南京大屠杀纪念日",
82   "1224 平安夜",
83   "1225 圣诞节");
84
85   //某月的第几个星期几。 5,6,7,8 表示到数第 1,2,3,4 个星期几
86   var wFtv = new Array(
87   //一月的最后一个星期日(月倒数第一个星期日)
88   "0520 母亲节",
89   "0630 父亲节",
90   "1144 感恩节");
91
92   //农历节日
93   var lFtv = new Array(
94   "0101*春节",
95   "0115 元宵节",
96   "0202 龙抬头",
97   "0505 端午节",
98   "0707 七夕",
99   "0715 中元节",
100   "0815 中秋节",
101   "0909 重阳节",
102   "1208 腊八节",
103   "1223 小年",
104   "0100*除夕");
105
106   //====================================== 返回农历 y年的总天数
107   function lYearDays(y) {
108     var i, sum = 348
109     for(i=0x8000; i>0x8; i>>=1) sum += (lunarInfo[y-1900] & i)? 1: 0
110     return(sum+leapDays(y))
111   }
112
113   //====================================== 返回农历 y年的闰月的天数
114   function leapDays(y) {
115     if(leapMonth(y)) return((lunarInfo[y-1900] & 0x10000)? 30: 29)
116     else return(0)
117   }
118
119   //====================================== 返回农历 y年闰哪个月 1-12,没闰返回 0
120   function leapMonth(y) {
121     return(lunarInfo[y-1900] & 0xf)
122   }
123
124   //====================================== 返回农历 y年m月的总天数
125   function monthDays(y,m) {
126     return( (lunarInfo[y-1900] & (0x10000>>m))? 30: 29 )
127   }
128
129   //====================================== 算出农历,传入日期对象,返回农历日期日期对象
130   // 该对象属性有 .year .month .day .isLeap .yearCyl .dayCyl .monCyl
131   function Lunar(date) {
132     var objDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
133     var i, leap=0, temp=0
134     var baseDate = new Date(1900,0,31)
135     // Mac和linux平台的firefox在此处会产生浮点数错误
136     var offset = Math.round((objDate - baseDate)/86400000)
137
138     this.dayCyl = offset + 40
139     this.monCyl = 14
140
141     for(i=1900; i<2050 && offset>0; i++) {
142       temp = lYearDays(i)
143       offset -= temp
144       this.monCyl += 12
145     }
146
147     if(offset<0) {
148       offset += temp;
149       i--;
150       this.monCyl -= 12
151     }
152
153     this.year = i
154     this.yearCyl = i-1864
155
156     leap = leapMonth(i) //闰哪个月
157     this.isLeap = false
158
159     for(i=1; i<13 && offset>0; i++) {
160     //闰月
161       if(leap>0 && i==(leap+1) && this.isLeap==false)
162       { --i; this.isLeap = true; temp = leapDays(this.year); }
163       else
164       { temp = monthDays(this.year, i); }
165
166       //解除闰月
167       if(this.isLeap==true && i==(leap+1)) this.isLeap = false
168
169       offset -= temp
170       if(this.isLeap == false) this.monCyl ++
171     }
172
173     if(offset==0 && leap>0 && i==leap+1)
174     if(this.isLeap)
175     { this.isLeap = false; }
176     else
177     { this.isLeap = true; --i; --this.monCyl;}
178
179     if(offset<0){ offset += temp; --i; --this.monCyl; }
180
181     this.month = i
182     this.day = offset + 1
183   }
184
185   ///////////////////////////////////////////////////////////
186   //
187   // lunar 2 solar
188   //
189   ///////////////////////////////////////////////////////////
190   // year .month .day .isLeap .yearCyl .dayCyl .monCyl
191   function Solar(date, isLeapMonth) {
192     var lyear = date.getFullYear(),
193       lmonth = date.getMonth() + 1,
194       lday = date.getDate(),
195       offset = 0,
196       leap = isLeap(lyear);
197
198     // increment year
199     for(var i = 1900; i < lyear; i++) {
200       offset += lYearDays(i);
201     }
202
203     // increment month
204     // add days in all months up to the current month
205     for (var i = 1; i < lmonth; i++) {
206       // add extra days for leap month
207       if (i == leapMonth(lyear)) {
208         offset += leapDays(lyear);
209       }
210       offset += monthDays(lyear, i);
211     }
212     // if current month is leap month, add days in normal month
213     if (leap && isLeapMonth) {
214       offset += monthDays(lyear, i);
215     }
216
217     // increment
218     offset += parseInt(lday) - 1;
219
220     var baseDate = new Date(1900,0,31);
221     var solarDate = new Date(baseDate.valueOf() + offset * 86400000);
222
223     this.year = solarDate.getFullYear();
224     this.month = solarDate.getMonth();
225     this.day = solarDate.getDate();
226     this.isLeap = leap;
227   }
228
229   function isLeap(year) {
230       return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
231   }
232
233   function getAnimalYear(year) {
234     return Animals[((year - 1900)%12)];
235   }
236
237
238   //============================== 传入 offset 返回干支, 0=甲子
239   function cyclical(num) {
240     return(Gan[num%10]+Zhi[num%12]);
241   }
242   //======================================= 返回该年的复活节(春分后第一次满月周后的第一主日)
243   function easter(y) {
244
245     var term2=sTerm(y,5); //取得春分日期
246     var dayTerm2 = new Date(Date.UTC(y,2,term2,0,0,0,0)); //取得春分的公历日期控件(春分一定出现在3月)
247     var lDayTerm2 = new Lunar(dayTerm2); //取得取得春分农历
248
249     if(lDayTerm2.day<15) //取得下个月圆的相差天数
250        var lMlen= 15-lDayTerm2.day;
251     else
252        var lMlen= (lDayTerm2.isLeap? leapDays(y): monthDays(y,lDayTerm2.month)) - lDayTerm2.day + 15;
253
254     //一天等于 1000*60*60*24 = 86400000 毫秒
255     var l15 = new Date(dayTerm2.getTime() + 86400000*lMlen ); //求出第一次月圆为公历几日
256     var dayEaster = new Date(l15.getTime() + 86400000*( 7-l15.getUTCDay() ) ); //求出下个周日
257
258     this.m = dayEaster.getUTCMonth();
259     this.d = dayEaster.getUTCDate();
260
261   }
262
263   //====================== 中文日期
264   function getCDay(d){
265     var s;
266
267     switch (d) {
268        case 10:
269           s = '初十'; break;
270        case 20:
271           s = '二十'; break;
272           break;
273        case 30:
274           s = '三十'; break;
275           break;
276        default :
277           s = nStr2[Math.floor(d/10)];
278           s += nStr1[d%10];
279     }
280     return(s);
281   }
282
283   ////////////////////////////////////////////////////////////////
284   //
285   // 24 节气
286   //
287   ///////////////////////////////////////////////////////////////
288   var solarTerm = new Array("小寒","大寒","立春","雨水","惊蛰","春分","清明",
289   "谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分",
290   "寒露","霜降","立冬","小雪","大雪","冬至");
291
292   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);
293   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';
294   var solarTermOS = '211122112122112121222211221122122222212222222221222122222232222222222222222233223232223232222222322222112122112121222211222122222222222222222222322222112122112121222111211122122222212221222221221122122222222222222222222223222232222232222222222222112122112121122111211122122122212221222221221122122222222222222221211122112122212221222211222122222232222232222222222222112122112121111111222222112121112121111111222222111121112121111111211122112122112121122111222212111121111121111111111122112122112121122111211122112122212221222221222211111121111121111111222111111121111111111111111122112121112121111111222111111111111111111111111122111121112121111111221122122222212221222221222111011111111111111111111122111121111121111111211122112122112121122211221111011111101111111111111112111121111121111111211122112122112221222211221111011111101111111110111111111121111111111111111122112121112121122111111011111121111111111111111011111111112111111111111011111111111111111111221111011111101110111110111011011111111111111111221111011011101110111110111011011111101111111111211111001011101110111110110011011111101111111111211111001011001010111110110011011111101111111110211111001011001010111100110011011011101110111110211111001011001010011100110011001011101110111110211111001010001010011000100011001011001010111110111111001010001010011000111111111111111111111111100011001011001010111100111111001010001010000000111111000010000010000000100011001011001010011100110011001011001110111110100011001010001010011000110011001011001010111110111100000010000000000000000011001010001010011000111100000000000000000000000011001010001010000000111000000000000000000000000011001010000010000000';
295
296   // 形式如function sTerm(year, n),用来计算某年的第n个节气(从0小寒算起)为几号,这也基本被认可为节气计算的基本形式。由于没个月份有两个节气,计算时需要调用两次(n和n+1)
297   //===== 某年的第n个节气为几日(从0小寒起算)
298   function sTerm(y,n) {
299     return(solarTermBase[n] +  Math.floor( solarTermOS.charAt( ( Math.floor(solarTermIdx.charCodeAt(y-1900)) - 48) * 24 + n  ) ) );
300   }
301   /////////////////////////////////////////////////////////////////
302   //
303   //  calElement model
304   //
305   /////////////////////////////////////////////////////////////////
306
307   //============================== 阴历属性
308   function calElement(sYear,sMonth,sDay,week,lYear,lMonth,lDay,isLeap,cYear,cMonth,cDay) {
309     //瓣句
310     this.sYear      = sYear;   //公元年4位数字
311     this.sMonth     = sMonth;  //公元月数字
312     this.sDay       = sDay;    //公元日数字
313     this.week       = week;    //星期, 1个中文
314     //农历
315     this.lYear      = lYear;   //公元年4位数字
316     this.lMonth     = lMonth;  //农历月数字
317     this.lDay       = lDay;    //农历日数字
318     this.isLeap     = isLeap;  //是否为农历闰月?
319     //八字
320     this.cYear      = cYear;   //年柱, 2个中文
321     this.cMonth     = cMonth;  //月柱, 2个中文
322     this.cDay       = cDay;    //日柱, 2个中文
323
324     this.lunarDay      = getCDay(lDay);
325     this.lunarMonth    = cmonthName[lMonth - 1];
326     this.lunarYear     = getAnimalYear(lYear);
327
328     // this.color      = '';
329
330     this.lunarFestival = ''; //农历节日
331     this.solarFestival = ''; //公历节日
332     this.solarTerms    = ''; //节气
333   }
334   ///////////////////////////////////////////////////////////////
335   //
336   //  main
337   //
338   ///////////////////////////////////////////////////////////////
339   // date's month should be --, example: 2012-5-21 -> new Date(2012, 4, 21)
340   // no matter solar or lunar
341   function CalendarConverter() {
342     this.solar2lunar = function(date) {
343       var sYear = date.getFullYear(),
344         sMonth = date.getMonth(),
345         sDay = date.getDate(),
346         weekDay = nStr1[date.getDay()],
347         lunar = new Lunar(date),
348         lunarYear = lunar.year,
349         lunarMonth = lunar.month,
350         lunarDay = lunar.day,
351         isLeap = lunar.isLeap;
352
353       return addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap);
354     }
355
356     this.lunar2solar = function(date, isLeapMonth) {
357       var lunarYear = date.getFullYear(),
358         lunarMonth = date.getMonth() + 1,
359         lunarDay = date.getDate(),
360         solar = new Solar(date, isLeapMonth),
361         sYear = solar.year,
362         sMonth = solar.month,
363         sDay = solar.day,
364         weekDay = nStr1[new Date(sYear, sMonth, sDay).getDay()],
365         isLeap = solar.isLeap,
366         cYear, cMonth, cDay, that = {};
367
368       return addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap);
369     }
370
371   }
372   function addFstv(sYear, sMonth, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap) {
373     var cYear, cMonth, cDay, that = {};
374     ////////年柱 1900年立春后为庚子年(60进制36)
375     if(sMonth < 2 ) {
376       cYear=cyclical(sYear-1900+36-1);
377     } else {
378       cYear=cyclical(sYear-1900+36);
379     }
380     var term2=sTerm(sYear,2); //立春日期
381
382     ////////月柱 1900年1月小寒以前为 丙子月(60进制12)
383     var firstNode = sTerm(sYear, sMonth*2) //返回当月「节」为几日开始
384     cMonth = cyclical((sYear - 1900) * 12 + sMonth + 12);
385
386     //依节气调整二月分的年柱, 以立春为界
387     if(sMonth == 1 && sDay >= term2) cYear = cyclical(sYear - 1900+36);
388     //依节气月柱, 以「节」为界
389     if(sDay >= firstNode) cMonth = cyclical((sYear - 1900) * 12 + sMonth + 13);
390     //当月一日与 1900/1/1 相差天数
391     //1900/1/1与 1970/1/1 相差25567日, 1900/1/1 日柱为甲戌日(60进制10)
392     var dayCyclical = Date.UTC(sYear, sMonth, 1, 0, 0, 0, 0)/86400000 + 25567 + 10;
393     //日柱
394     cDay = cyclical(dayCyclical + sDay - 1);
395
396     //sYear,sMonth,sDay,weekDay,
397     //lYear,lMonth,lDay,isLeap,
398     //cYear,cMonth,cDay
399     that = new calElement(sYear, sMonth + 1, sDay, weekDay, lunarYear, lunarMonth, lunarDay, isLeap, cYear, cMonth, cDay);
400
401     // 节气
402     tmp1=sTerm(sYear, sMonth * 2) - 1;
403     tmp2=sTerm(sYear, sMonth * 2 + 1) - 1;
404     if (tmp1 == (sDay - 1)) {
405       that.solarTerms = solarTerm[sMonth * 2];
406     }
407     if (tmp2 == (sDay - 1)) {
408       that.solarTerms = solarTerm[sMonth * 2 + 1];
409     }
410
411     //公历节日
412     for (var i = 0, item; item = sFtv[i]; i++) {
413       if(item.match(/^(\d{2})(\d{2})([\s\*])(.+)$/)) {
414         if(Number(RegExp.$1)==(sMonth+1)) {
415             if (Number(RegExp.$2) == sDay) {
416                 that.solarFestival += RegExp.$4 + ' ';
417             }
418         }
419       }
420     }
421
422     //月周节日
423     for (i = 0, item; item = wFtv[i]; i++) {
424       if (item.match(/^(\d{2})(\d)(\d)([\s\*])(.+)$/)) {
425         if (Number(RegExp.$1) == (sMonth + 1)) {
426           tmp1 = Number(RegExp.$2);
427           tmp2 = Number(RegExp.$3);
428           if (tmp1 < 5) {
429             var wFtvDate = (tmp2 == 0 ? 7 : 0) + (tmp1 - 1)*7 + tmp2;
430             if (wFtvDate == sDay) {
431               that.solarFestival += RegExp.$5 + ' ';
432               break;
433             }
434           }
435         }
436       }
437     }
438
439     // 农历节日
440     for (i = 0, item; item = lFtv[i]; i++) {
441       if (item.match(/^(\d{2})(.{2})([\s\*])(.+)$/)) {
442         tmp1 = Number(RegExp.$1);
443         tmp2 = Number(RegExp.$2);
444         lMonLen = monthDays(lunarYear, lunarMonth);
445         // 月份是12月,且为最后一天,则设置为春节
446         if ((tmp1 == lunarMonth && tmp2 == lunarDay) || (tmp2 == '00' && lunarMonth == 12 && lMonLen == lunarDay)) {
447           that.lunarFestival += RegExp.$4 + ' ';
448           break;
449         }
450       }
451     }
452
453     return that;
454   }
455
456   /*
457    * example:
458    * var cc  =new CalendarConverter;
459    *
460    * cc.lunar2solar(new Date(2011, 0, 3)); ---> 2010,11,29
461    * cc.solar2lunar(new Date(2010, 10, 29)); ----> 2011, 1, 3
462    *
463    * 农历转公历时,如果那一月是那一年的闰月,则需额外传一个参数,才能得到正确的公历日期
464    * cc.solar2lunar(new Date(2012, 4, 27)); ---> 2012年4月初7, 其中 isLeap为true,表示为闰四月
465    * cc.lunar2solar(new Date(2012, 3, 7)) ---> 得到错误时间:2012, 4, 27
466    * cc.lunar2solar(new Date(2012, 3, 7), true) --> 正确: 2012, 5, 27
467    *
468    *result:
469    *  {
470    *    cDay: "戊戌"
471         , cMonth: "丁未"
472         , cYear: "壬辰"
473         , isLeap: false             // 该月是否为闰月
474         , lDay: 18
475         , lMonth: 6
476         , lYear: 2012
477         , lunarDay: "十八"
478         , lunarFestival: ""
479         , lunarMonth: "六"
480         , lunarYear: "龙"
481         , sDay: 5
482         , sMonth: 8
483         , sYear: 2012
484         , solarFestival: ""         // 节日
485         , solarTerms: ""            // 节气
486         , week: "日"                // 周几
487       }
488    *
489    */
490   window.CalendarConverter = CalendarConverter;
491   if ('undefined' !== typeof module && module.exports) {
492     module.exports = CalendarConverter;
493   } else {
494     root.CalendarConverter = CalendarConverter;
495   }
496 })()