工作中偶然须要夏历盘算,之前从网上找了个JS版本的(摘自wannianli.htm,网上导出都是),直接挪用就能够了,异常轻易。有长处就有瑕玷,该版本文件有点大(20KB以上);有许多代码用不到;代码解释不够直白;明白其道理有点贫苦。
之前用过屡次,对道理也不是很清晰,近来项目须要,从新造了一遍轮子。包括源码解释的文件控制在7KB之内,紧缩后再3KB之内。
在从新造轮子之前,预备对机能优化下。想到的能够优化的部份以下:
1、在年份盘算天数时,每次都要轮回盘算与 1900 年 的 日期天数,将盘算天数改成 2016-2-8(春节)。
2、每次盘算 都要从新盘算天数,斟酌第一次盘算昔时天数后,到场缓存机制。
3、每次猎取 夏历 信息时,假如已依据年份猎取到 夏历信息,能够传给相干函数,不必从新盘算。
4、最主要的,只写夏历盘算相干的盘算,其他无功用,假如须要,经由过程 requrie 本剧本。
造完轮子今后,发明能优化的水平有限,机能与之前提拔不大(盘算40次 约莫都在 0.003 – 0.005 秒左右),终究 取消了 第2项 缓存(节约内存)。
/*
原文地点:http://www.miaoqiyuan.cn/p/lunar
源代码:http://www.miaoqiyuan.cn/products/lunar/lunar.js
紧缩版:http://www.miaoqiyuan.cn/products/lunar/lunar.min.js
*/
var lunar = {
data : [
/*
二进制情势
xxxx xxxx xxxx xxxx xxxx
20-17 16-12 12-9 8-5 4-1
1-4: 示意昔时有没有闰年,有的话,为闰月的月份,没有的话,为0。
5-16:为除了闰月外的一般月份是大月照样小月,1为30天,0为29天。
注重:从1月到12月对应的是第16位到第5位。
17-20: 示意闰月是大月照样小月,仅当存在闰月的情况下有意义。
举个例子:
以我的华诞1987年威力,1987年的数据是: 0x0af46
二进制:0000 1010 1111 0100 0110
从1月到12月的天数依次为:
5-16位 1 0 1 0 1 1 1 1 0 1 0 0
每个月日数 30 29 30 29 30 30 30 30 29 30 29 29
对应月份 1 2 3 4 5 6 7 8 9 10 11 12
0110 (1-4位) 示意1987年有闰月,闰六月
0000 (17-20位) 存在闰月,本字段有用,示意闰小月 29天
补充闰月后的每个月日期
每个月日数 30 29 30 29 30 30 29 30 30 29 30 29 29
对应月份 1 2 3 4 5 6 闰 7 8 9 10 11 12
*/
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, //1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, //1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, //1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, //1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049
0x14b63 //2050
],
//猎取夏历年份信息
getData : function(year){
return this.data[year - 1900];
},
//返回闰月是哪个月,没有闰月返回0
getLeapMonth : function(year, lunarData){
lunarData = lunarData || this.getData(year); //假如传入lunarData,为了进步机能,不再举行盘算
/*
1-4位示意闰月,比方:0010
lunarData : xxxx xxxx xxxx xxxx 0010
0xf : 0000 0000 0000 0000 1111
&运算 : 0000 0000 0000 0000 0010
*/
return lunarData & 0xf;
},
//返回闰月天数
getLeapDays : function(year, lunarData){
lunarData = lunarData || this.getData(year);
if( this.getLeapMonth(year, lunarData) ){
/*
17-20位示意闰月是大小月,比方:0001
lunarData : 0000 xxxx xxxx xxxx xxxx
0x10000 : 0001 0000 0000 0000 0000
&运算 : 0000 0000 0000 0000 0000
*/
return (lunarData & 0x10000) ? 30 : 29;
}else{
return 0;
}
},
//夏历某年某月的天数
getMonthDays : function(year, month, lunarData){
lunarData = lunarData || this.getData(year);
/*
5-16位 示意每个月的天数
lunarData : xxxx 0100 0100 0100 xxxx
0x8000 : 0000 1000 0000 0000 0000
0x8000>>8 : 0000 0000 0001 0000 0000
&运算 : 0000 0000 0000 0000 0000
*/
return (lunarData & 0x8000>>month) ? 30 : 29;
},
//夏历年总天数
getLunarDays : function(year, lunarData){
lunarData = lunarData || this.getData(year);
//假如存在 总天数 缓存,则返回缓存
/*
this.cacheLunarDays = this.cacheLunarDays || {};
if( year in this.cacheLunarDays ){
return this.cacheLunarDays[year];
}
*/
var days = 348; //本年的12个月,都看成小月处置惩罚。 12 x 29 = 348
/*
5-16位 示意每个月的天数
lunarData : xxxx 0100 0100 0100 xxxx
0x8000 : 0000 1000 0000 0000 0000
&运算 : 0000 0000 0000 0000 0000
0x8 : 0000 0000 0000 0000 1000
&运算 : 0000 0000 0000 0000 0000
*/
for(var monthIndex = 0x8000; monthIndex > 0x8; monthIndex >>= 1){
days += (lunarData & monthIndex) ? 1 : 0;
}
return days + this.getLeapDays(year, lunarData);
/*
this.cacheLunarDays[year] = days + this.getLeapDays(year, lunarData);
return this.cacheLunarDays[year];
*/
},
//传入一个日期,盘算夏历信息
toLunar : function(date, _date){
//假如传入 _date,则将夏历信息添加到 _date中
var _date = _date || {};
var currentYear = 2016; //当前年份
var lunarData = this.getData(currentYear); //缓存 lunarData,节约机能
var lunarDays = this.getLunarDays(currentYear, lunarData); //夏历天数
/*
daysOffset == 相差天数
为了削减不必要的机能糟蹋(为何要从1900算到本年),参考日期以2016年春节为准(2016-2-8)
*/
var daysOffset = (new Date(date.getFullYear(), date.getMonth(), date.getDate()) - new Date(2016, 1, 8)) / 86400000;
//猎取年纪
if( daysOffset >= lunarDays ){
//2017年和今后
while( daysOffset >= lunarDays ){
daysOffset -= lunarDays;
lunarData = this.getData(++currentYear);
lunarDays = this.getLunarDays(currentYear, lunarData);
}
}else if( daysOffset < 0 ){
//2016年前
while( daysOffset < 0 ){
lunarData = this.getData(--currentYear);
lunarDays = this.getLunarDays(currentYear, lunarData);
daysOffset += lunarDays;
}
daysOffset++;
}
_date.lunarYear = currentYear;
//本年是不是为闰月
var leapMonth = this.getLeapMonth(currentYear, lunarData);
//猎取月数
var currentMonth, currentMonthDays;
for(currentMonth = 1; currentMonth < 12 ; currentMonth++ ){
_date.isLeap = false;
///假如有闰月
if( leapMonth ){
if( currentMonth > leapMonth ){
currentMonth--;
leapMonth = 0;
_date.isLeap = true;
}
}
currentMonthDays = this.getMonthDays(currentYear, currentMonth);
if( daysOffset > currentMonthDays ){
daysOffset -= currentMonthDays;
}else{
break;
}
}
_date.lunarMonth = currentMonth;
//猎取日
_date.lunarDay = daysOffset;
return _date;
},
//返回本日信息
today : function(){
return this.toLunar(new Date());
}
};
//假如须要模块导出
//module.exports = lunar;
使用方法:
lunar.toLunar(new Date())
/*
Object {
lunarYear: 2016, //夏历年
isLeap: false, //是不是是闰月
lunarMonth: 10, //夏历月
lunarDay: 10 //夏历日
}
*/
还能够传入JS对象,
var date = new Date();
var _date = {
Date : date,
year : date.getFullYear(),
month : date.getMonth() + 1,
date : date.getDate(),
day : date.getDay()
};
_date.value = [_date.year, _date.month, _date.date].map(function(n){
n = n.toString();
return n[1] ? n : '0' + n;
}).join("-");
/*
Object {
Date: Wed Nov 09 2016 18:56:09 GMT+0800 (中国标准时间),
year: 2016,
month: 11,
date: 9,
day: 3,
value: "2016-11-09"
*/
_date = lunar.toLunar(date, _date);
/*
Object {
Date: Wed Nov 09 2016 18:56:09 GMT+0800 (中国标准时间),
year: 2016,
month: 11,
date: 9,
day: 3,
value: "2016-11-09",
isLeap: false,
lunarDay: 10,
lunarMonth: 10,
lunarYear: 2016
*/