5 * Github : https://github.com/zzyss86/LunarCalendar
6 * HomePage : http://www.tuijs.com/
8 * Email : zzyss86@qq.com
12 var extend
= function(o
, c
){
13 if(o
&& c
&& typeof c
== "object"){
21 var creatLenArr
= function(year
,month
,len
,start
){
26 for(var i
=0;i
<len
;i
++){
27 arr
.push({year
:year
,month
:month
,day
:k
});
33 var errorCode
= { //错误码列表
34 100 : '输入的年份超过了可查询范围,仅支持1891至2100年',
38 var cache
= null; //某年相同计算进行cache,以加速计算速度
39 var cacheUtil
= { //cache管理工具
41 setCurrent
: function(year
){
42 if(this.current
!= year
){
47 set
: function(key
,value
){
48 if(!cache
) cache
= {};
53 if(!cache
) cache
= {};
61 var formateDayD4
= function(month
,day
){
63 month
= month
<10 ? '0'+month
: month
;
64 day
= day
<10 ? '0'+day
: day
;
68 var minYear
= 1890;//最小年限
69 var maxYear
= 2100;//最大年限
71 heavenlyStems
: ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'], //天干
72 earthlyBranches
: ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'], //地支
73 zodiac
: ['鼠','牛','虎','兔','龙','蛇','马','羊','猴','鸡','狗','猪'], //对应地支十二生肖
74 solarTerm
: ['小寒', '大寒', '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪','冬至'], //二十四节气
75 monthCn
: ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'],
76 dateCn
: ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十', '卅一']
79 //中国节日放假安排,外部设置,0无特殊安排,1工作,2放假
82 worktime
.y2013
= {"d0101":2,"d0102":2,"d0103":2,"d0105":1,"d0106":1,"d0209":2,"d0210":2,"d0211":2,"d0212":2,"d0213":2,"d0214":2,"d0215":2,"d0216":1,"d0217":1,"d0404":2,"d0405":2,"d0406":2,"d0407":1,"d0427":1,"d0428":1,"d0429":2,"d0430":2,"d0501":2,"d0608":1,"d0609":1,"d0610":2,"d0611":2,"d0612":2,"d0919":2,"d0920":2,"d0921":2,"d0922":1,"d0929":1,"d1001":2,"d1002":2,"d1003":2,"d1004":2,"d1005":2,"d1006":2,"d1007":2,"d1012":1};
83 worktime
.y2014
= {"d0101":2,"d0126":1,"d0131":2,"d0201":2,"d0202":2,"d0203":2,"d0204":2,"d0205":2,"d0206":2,"d0208":1,"d0405":2,"d0407":2,"d0501":2,"d0502":2,"d0503":2,"d0504":1,"d0602":2,"d0908":2,"d0928":1,"d1001":2,"d1002":2,"d1003":2,"d1004":2,"d1005":2,"d1006":2,"d1007":2,"d1011":1};
95 'd0312':'植树节 孙中山逝世纪念日',
98 'd0317':'中国国医节 国际航海日',
99 'd0321':'世界森林日 消除种族歧视国际日 世界儿歌日',
103 'd0325':'全国中小学生安全教育日',
105 'd0401':'愚人节 全国爱国卫生运动月(四月) 税收宣传月(四月)',
129 'd0701':'香港回归纪念日 中共诞辰 世界建筑日',
135 'd0808':'中国男子节(爸爸节)',
137 'd0908':'国际扫盲日 国际新闻工作者日',
146 'd1001':'国庆节 世界音乐日 国际老人节',
147 'd1002':'国际和平与民主自由斗争日',
150 'd1008':'全国高血压日 世界视觉日',
151 'd1009':'世界邮政日 万国邮联日',
152 'd1010':'辛亥革命纪念日 世界精神卫生日',
153 'd1013':'世界保健日 国际教师节',
155 'd1015':'国际盲人节(白手杖节)',
159 'd1024':'联合国日 世界发展信息日',
161 'd1107':'十月社会主义革命纪念日',
163 'd1109':'全国消防安全宣传教育日',
165 'd1111':'国际科学与和平周(本日所属的一周)',
168 'd1117':'国际大学生节 世界学生节',
169 'd1121':'世界问候日 世界电视日',
170 'd1129':'国际声援巴勒斯坦人民国际日',
173 'd1205':'国际经济和社会发展志愿人员日',
178 'd1213':'南京大屠杀(1937年)纪念日!紧记血泪史!',
187 var lunarFestival
= {
206 * [闰月所在月,0为没有闰月; *正月初一对应公历月; *正月初一对应公历日; *农历每月的天数的数组(需转换为二进制,得到每月大小,0=小月(29日),1=大月(30日));]
208 var lunarInfo
= [[2,1,21,22184],[0,2,9,21936],[6,1,30,9656],[0,2,17,9584],[0,2,6,21168],[5,1,26,43344],[0,2,13,59728],[0,2,2,27296],[3,1,22,44368],[0,2,10,43856],[8,1,30,19304],[0,2,19,19168],[0,2,8,42352],[5,1,29,21096],[0,2,16,53856],[0,2,4,55632],[4,1,25,27304],[0,2,13,22176],[0,2,2,39632],[2,1,22,19176],[0,2,10,19168],[6,1,30,42200],[0,2,18,42192],[0,2,6,53840],[5,1,26,54568],[0,2,14,46400],[0,2,3,54944],[2,1,23,38608],[0,2,11,38320],[7,2,1,18872],[0,2,20,18800],[0,2,8,42160],[5,1,28,45656],[0,2,16,27216],[0,2,5,27968],[4,1,24,44456],[0,2,13,11104],[0,2,2,38256],[2,1,23,18808],[0,2,10,18800],[6,1,30,25776],[0,2,17,54432],[0,2,6,59984],[5,1,26,27976],[0,2,14,23248],[0,2,4,11104],[3,1,24,37744],[0,2,11,37600],[7,1,31,51560],[0,2,19,51536],[0,2,8,54432],[6,1,27,55888],[0,2,15,46416],[0,2,5,22176],[4,1,25,43736],[0,2,13,9680],[0,2,2,37584],[2,1,22,51544],[0,2,10,43344],[7,1,29,46248],[0,2,17,27808],[0,2,6,46416],[5,1,27,21928],[0,2,14,19872],[0,2,3,42416],[3,1,24,21176],[0,2,12,21168],[8,1,31,43344],[0,2,18,59728],[0,2,8,27296],[6,1,28,44368],[0,2,15,43856],[0,2,5,19296],[4,1,25,42352],[0,2,13,42352],[0,2,2,21088],[3,1,21,59696],[0,2,9,55632],[7,1,30,23208],[0,2,17,22176],[0,2,6,38608],[5,1,27,19176],[0,2,15,19152],[0,2,3,42192],[4,1,23,53864],[0,2,11,53840],[8,1,31,54568],[0,2,18,46400],[0,2,7,46752],[6,1,28,38608],[0,2,16,38320],[0,2,5,18864],[4,1,25,42168],[0,2,13,42160],[10,2,2,45656],[0,2,20,27216],[0,2,9,27968],[6,1,29,44448],[0,2,17,43872],[0,2,6,38256],[5,1,27,18808],[0,2,15,18800],[0,2,4,25776],[3,1,23,27216],[0,2,10,59984],[8,1,31,27432],[0,2,19,23232],[0,2,7,43872],[5,1,28,37736],[0,2,16,37600],[0,2,5,51552],[4,1,24,54440],[0,2,12,54432],[0,2,1,55888],[2,1,22,23208],[0,2,9,22176],[7,1,29,43736],[0,2,18,9680],[0,2,7,37584],[5,1,26,51544],[0,2,14,43344],[0,2,3,46240],[4,1,23,46416],[0,2,10,44368],[9,1,31,21928],[0,2,19,19360],[0,2,8,42416],[6,1,28,21176],[0,2,16,21168],[0,2,5,43312],[4,1,25,29864],[0,2,12,27296],[0,2,1,44368],[2,1,22,19880],[0,2,10,19296],[6,1,29,42352],[0,2,17,42208],[0,2,6,53856],[5,1,26,59696],[0,2,13,54576],[0,2,3,23200],[3,1,23,27472],[0,2,11,38608],[11,1,31,19176],[0,2,19,19152],[0,2,8,42192],[6,1,28,53848],[0,2,15,53840],[0,2,4,54560],[5,1,24,55968],[0,2,12,46496],[0,2,1,22224],[2,1,22,19160],[0,2,10,18864],[7,1,30,42168],[0,2,17,42160],[0,2,6,43600],[5,1,26,46376],[0,2,14,27936],[0,2,2,44448],[3,1,23,21936],[0,2,11,37744],[8,2,1,18808],[0,2,19,18800],[0,2,8,25776],[6,1,28,27216],[0,2,15,59984],[0,2,4,27424],[4,1,24,43872],[0,2,12,43744],[0,2,2,37600],[3,1,21,51568],[0,2,9,51552],[7,1,29,54440],[0,2,17,54432],[0,2,5,55888],[5,1,26,23208],[0,2,14,22176],[0,2,3,42704],[4,1,23,21224],[0,2,11,21200],[8,1,31,43352],[0,2,19,43344],[0,2,7,46240],[6,1,27,46416],[0,2,15,44368],[0,2,5,21920],[4,1,24,42448],[0,2,12,42416],[0,2,2,21168],[3,1,22,43320],[0,2,9,26928],[7,1,29,29336],[0,2,17,27296],[0,2,6,44368],[5,1,26,19880],[0,2,14,19296],[0,2,3,42352],[4,1,24,21104],[0,2,10,53856],[8,1,30,59696],[0,2,18,54560],[0,2,7,55968],[6,1,27,27472],[0,2,15,22224],[0,2,5,19168],[4,1,25,42216],[0,2,12,42192],[0,2,1,53584],[2,1,21,55592],[0,2,9,54560]];
211 * 二十四节气数据,节气点时间(单位是分钟)
214 var termInfo
= [0,21208,42467,63836,85337,107014,128867,150921,173149,195551,218072,240693,263343,285989,308563,331033,353350,375494,397447,419210,440795,462224,483532,504758];
218 * @param {Number} year 农历年
219 * return 闰月数 (月份从1开始)
221 function getLunarLeapYear(year
){
222 var yearData
= lunarInfo
[year
-minYear
];
227 * 获取农历年份一年的每月的天数及一年的总天数
228 * @param {Number} year 农历年
230 function getLunarYearDays(year
){
231 var yearData
= lunarInfo
[year
-minYear
];
232 var leapMonth
= yearData
[0]; //闰月
233 var monthData
= yearData
[3].toString(2);
234 var monthDataArr
= monthData
.split('');
236 //还原数据至16位,少于16位的在前面插入0(二进制存储时前面的0被忽略)
237 for(var i
=0;i
<16-monthDataArr
.length
;i
++){
238 monthDataArr
.unshift(0);
241 var len
= leapMonth
? 13 : 12; //该年有几个月
244 for(var i
=0;i
<len
;i
++){
245 if(monthDataArr
[i
]==0){
256 monthDays
: monthDays
262 * @param {Number} year,between 农历年,间隔天数
264 function getLunarDateByBetween(year
,between
){
265 var lunarYearDays
= getLunarYearDays(year
);
266 var end
= between
>0 ? between
: lunarYearDays
.yearDays
- Math
.abs(between
);
267 var monthDays
= lunarYearDays
.monthDays
;
270 for(var i
=0;i
<monthDays
.length
;i
++){
271 tempDays
+= monthDays
[i
];
274 tempDays
= tempDays
-monthDays
[i
];
279 return [year
,month
,end
- tempDays
+ 1];
284 * @param {Number} year 公历年,月,日
286 function getLunarByBetween(year
,month
,day
){
287 var yearData
= lunarInfo
[year
-minYear
];
288 var zenMonth
= yearData
[1];
289 var zenDay
= yearData
[2];
290 var between
= getDaysBetweenSolar(year
,zenMonth
-1,zenDay
,year
,month
,day
);
291 if(between
==0){ //正月初一
294 var lunarYear
= between
>0 ? year
: year
-1;
295 return getLunarDateByBetween(lunarYear
,between
);
302 function getDaysBetweenSolar(year
,month
,day
,year1
,month1
,day1
){
303 var date
= new Date(year
,month
,day
).getTime();
304 var date1
= new Date(year1
,month1
,day1
).getTime();
305 return (date1
-date
) / 86400000;
310 * @param {Number} year,month,day 农年,月(0-12,有闰月),日
312 function getDaysBetweenZheng(year
,month
,day
){
313 var lunarYearDays
= getLunarYearDays(year
);
314 var monthDays
= lunarYearDays
.monthDays
;
316 for(var i
=0;i
<monthDays
.length
;i
++){
318 days
+= monthDays
[i
];
328 * 31556925974.7为地球公转周期,是毫秒
329 * 1890年的正小寒点:01-05 16:02:31,1890年为基准点
330 * @param {Number} y 公历年
331 * @param {Number} n 第几个节气,从0小寒起算
333 function getTerm(y
,n
) {
334 var offDate
= new Date( ( 31556925974.7*(y
-1890) + termInfo
[n
]*60000 ) + Date
.UTC(1890,0,5,16,2,31) );
335 return(offDate
.getUTCDate());
340 * 返回key:日期,value:节气中文名
342 function getYearTerm(year
){
345 for(var i
=0;i
<24;i
++){
346 var day
= getTerm(year
,i
);
348 res
[formateDayD4(month
-1,day
)] = DATA
.solarTerm
[i
];
355 * @param {Number} year 干支所在年(默认以立春前的公历年作为基数)
357 function getYearZodiac(year
){
358 var num
= year
-1890+25; //参考干支纪年的计算,生肖对应地支
359 return DATA
.zodiac
[num
%12];
364 * @param {Number} num 60进制中的位置(把60个天干地支,当成一个60进制的数)
366 function cyclical(num
) {
367 return(DATA
.heavenlyStems
[num
%10]+DATA
.earthlyBranches
[num
%12]);
372 * @param {Number} year 干支所在年
373 * @param {Number} offset 偏移量,默认为0,便于查询一个年跨两个干支纪年(以立春为分界线)
375 function getLunarYearName(year
,offset
){
376 offset
= offset
|| 0;
377 //1890年1月小寒(小寒一般是1月5或6日)以前为己丑年,在60进制中排25
378 return cyclical(year
-1890+25+offset
);
383 * @param {Number} year,month 公历年,干支所在月
384 * @param {Number} offset 偏移量,默认为0,便于查询一个月跨两个干支纪月(有立春的2月)
386 function getLunarMonthName(year
,month
,offset
){
387 offset
= offset
|| 0;
388 //1890年1月小寒以前为丙子月,在60进制中排12
389 return cyclical((year
-1890)*12+month
+12+offset
);
394 * @param {Number} year,month,day 公历年,月,日
396 function getLunarDayName(year
,month
,day
){
398 //1890/1/1与 1970/1/1 相差29219日, 1890/1/1 日柱为壬午日(60进制18)
399 var dayCyclical
= Date
.UTC(year
,month
,day
)/86400000+29219+18;
400 return cyclical(dayCyclical
);
405 * @param {Number} year 公历年
406 * @param {Number} month 公历月
408 function getSolarMonthDays(year
,month
){
409 var monthDays
= [31,isLeapYear(year
)?29:28,31,30,31,30,31,31,30,31,30,31];
410 return monthDays
[month
];
415 * @param {Number} year 公历年
417 function isLeapYear(year
){
418 return ((year
%4==0 && year
%100 !=0) || (year
%400==0));
422 * 统一日期输入参数(输入月份从1开始,内部月份统一从0开始)
424 function formateDate(year
,month
,day
,_minYear
){
425 var argsLen
= arguments
.length
;
426 var now
= new Date();
427 year
= argsLen
? parseInt(year
,10) : now
.getFullYear();
428 month
= argsLen
? parseInt(month
-1,10) : now
.getMonth();
429 day
= argsLen
? parseInt(day
,10) || now
.getDate() : now
.getDate();
430 if(year
< (_minYear
? _minYear
: minYear
+1) || year
> maxYear
)return {error
:100, msg
:errorCode
[100]};
440 * @param {Number} year,month,day 农历年,月(1-13,有闰月),日
442 function lunarToSolar(_year
,_month
,_day
){
443 var inputDate
= formateDate(_year
,_month
,_day
);
444 if(inputDate
.error
)return inputDate
;
445 var year
= inputDate
.year
;
446 var month
= inputDate
.month
;
447 var day
= inputDate
.day
;
449 var between
= getDaysBetweenZheng(year
,month
,day
); //离正月初一的天数
450 var yearData
= lunarInfo
[year
-minYear
];
451 var zenMonth
= yearData
[1];
452 var zenDay
= yearData
[2];
454 var offDate
= new Date(year
,zenMonth
-1,zenDay
).getTime() + between
* 86400000;
455 offDate
= new Date(offDate
);
457 year
: offDate
.getFullYear(),
458 month
: offDate
.getMonth()+1,
459 day
: offDate
.getDate()
465 * @param {Number} year,month,day 公历年,月,日
467 function solarToLunar(_year
,_month
,_day
){
468 var inputDate
= formateDate(_year
,_month
,_day
,minYear
);
469 if(inputDate
.error
)return inputDate
;
470 var year
= inputDate
.year
;
471 var month
= inputDate
.month
;
472 var day
= inputDate
.day
;
474 cacheUtil
.setCurrent(year
);
476 var term2
= cacheUtil
.get('term2') ? cacheUtil
.get('term2') : cacheUtil
.set('term2',getTerm(year
,2));
478 var termList
= cacheUtil
.get('termList') ? cacheUtil
.get('termList') : cacheUtil
.set('termList',getYearTerm(year
));
480 var firstTerm
= getTerm(year
,month
*2); //某月第一个节气开始日期
481 var GanZhiYear
= (month
>1 || month
==1 && day
>=term2
) ? year
+1 : year
;//干支所在年份
482 var GanZhiMonth
= day
>=firstTerm
? month
+1 : month
; //干支所在月份(以节气为界)
484 var lunarDate
= getLunarByBetween(year
,month
,day
);
485 var lunarLeapMonth
= getLunarLeapYear(lunarDate
[0]);
486 var lunarMonthName
= '';
487 if(lunarLeapMonth
>0 && lunarLeapMonth
==lunarDate
[1]){
488 lunarMonthName
= '闰'+DATA
.monthCn
[lunarDate
[1]-1]+'月';
489 }else if(lunarLeapMonth
>0 && lunarDate
[1]>lunarLeapMonth
){
490 lunarMonthName
= DATA
.monthCn
[lunarDate
[1]-1]+'月';
492 lunarMonthName
= DATA
.monthCn
[lunarDate
[1]]+'月';
497 var lunarMonthDays
= getLunarYearDays(lunarDate
[0]).monthDays
;
499 if(lunarDate
[1] == lunarMonthDays
.length
-1 && lunarDate
[2]==lunarMonthDays
[lunarMonthDays
.length
-1]){
500 lunarFtv
= lunarFestival
['d0100'];
501 }else if(lunarLeapMonth
>0 && lunarDate
[1]>lunarLeapMonth
){
502 lunarFtv
= lunarFestival
[formateDayD4(lunarDate
[1]-1,lunarDate
[2])];
504 lunarFtv
= lunarFestival
[formateDayD4(lunarDate
[1],lunarDate
[2])];
508 zodiac
: getYearZodiac(GanZhiYear
),
509 GanZhiYear
: getLunarYearName(GanZhiYear
),
510 GanZhiMonth
: getLunarMonthName(year
,GanZhiMonth
),
511 GanZhiDay
: getLunarDayName(year
,month
,day
),
512 //放假安排:0无特殊安排,1工作,2放假
513 worktime
: worktime
['y'+year
] && worktime
['y'+year
][formateDayD4(month
,day
)] ? worktime
['y'+year
][formateDayD4(month
,day
)] : 0,
514 term
: termList
[formateDayD4(month
,day
)],
516 lunarYear
: lunarDate
[0],
517 lunarMonth
: lunarDate
[1]+1,
518 lunarDay
: lunarDate
[2],
519 lunarMonthName
: lunarMonthName
,
520 lunarDayName
: DATA
.dateCn
[lunarDate
[2]-1],
521 lunarLeapMonth
: lunarLeapMonth
,
523 solarFestival
: solarFestival
[formateDayD4(month
,day
)],
524 lunarFestival
: lunarFtv
533 * @param {Number} year,month 公历年,月
534 * @param {Boolean} fill 是否用上下月数据补齐首尾空缺,首例数据从周日开始
536 function calendar(_year
,_month
,fill
){
537 var inputDate
= formateDate(_year
,_month
);
538 if(inputDate
.error
)return inputDate
;
539 var year
= inputDate
.year
;
540 var month
= inputDate
.month
;
542 var calendarData
= solarCalendar(year
,month
+1,fill
);
543 for(var i
=0;i
<calendarData
.monthData
.length
;i
++){
544 var cData
= calendarData
.monthData
[i
];
545 var lunarData
= solarToLunar(cData
.year
,cData
.month
,cData
.day
);
546 extend(calendarData
.monthData
[i
],lunarData
);
554 * @param {Number} year,month 公历年,月
555 * @param {Boolean} fill 是否用上下月数据补齐首尾空缺,首例数据从周日开始 (7*6阵列)
557 function solarCalendar(_year
,_month
,fill
){
558 var inputDate
= formateDate(_year
,_month
);
559 if(inputDate
.error
)return inputDate
;
560 var year
= inputDate
.year
;
561 var month
= inputDate
.month
;
563 var firstDate
= new Date(year
,month
,1);
564 var preMonthDays
,preMonthData
,nextMonthData
;
567 firstDay
: firstDate
.getDay(), //该月1号星期几
568 monthDays
: getSolarMonthDays(year
,month
), //该月天数
572 res
.monthData
= creatLenArr(year
,month
+1,res
.monthDays
,1);
575 if(res
.firstDay
> 0){ //前补
576 var preYear
= month
-1<0 ? year
-1 : year
;
577 var preMonth
= month
-1<0 ? 11 : month
-1;
578 preMonthDays
= getSolarMonthDays(preYear
,preMonth
);
579 preMonthData
= creatLenArr(preYear
,preMonth
+1,res
.firstDay
,preMonthDays
-res
.firstDay
+1);
580 res
.monthData
= preMonthData
.concat(res
.monthData
);
583 if(7*6 - res
.monthData
.length
!=0){ //后补
584 var nextYear
= month
+1>11 ? year
+1 : year
;
585 var nextMonth
= month
+1>11 ? 0 : month
+1;
586 var fillLen
= 7*6 - res
.monthData
.length
;
587 nextMonthData
= creatLenArr(nextYear
,nextMonth
+1,fillLen
,1);
588 res
.monthData
= res
.monthData
.concat(nextMonthData
);
597 * @param {Object} workData
599 function setWorktime(workData
){
600 extend(worktime
,workData
);
603 var LunarCalendar
= {
604 solarToLunar
: solarToLunar
,
605 lunarToSolar
: lunarToSolar
,
607 solarCalendar
: solarCalendar
,
608 setWorktime
: setWorktime
611 if (typeof define
=== 'function'){
613 return LunarCalendar
;
615 }else if(typeof exports
=== 'object'){
616 module
.exports
= LunarCalendar
;
618 window
.LunarCalendar
= LunarCalendar
;