工做中有時須要農曆計算,以前從網上找了個JS版本的(摘自wannianli.htm,網上導出都是),直接調用就能夠了,很是方便。有優勢就有缺點,該版本文件有點大(20KB以上);有不少代碼用不到;代碼註釋不夠直白;理解其原理有點麻煩。緩存
以前用過屢次,對原理也不是很清楚,最近項目須要,從新造了一遍輪子。包含源碼註釋的文件控制在7KB之內,壓縮後再3KB之內。性能優化
在從新造輪子以前,準備對性能優化下。想到的能夠優化的部分以下:函數
一、在年份計算天數時,每次都要循環計算與 1900 年 的 日期天數,將計算天數改成 2016-2-8(春節)。性能
二、每次計算 都要從新計算天數,考慮第一次計算當年天數後,加入緩存機制。優化
三、每次獲取 農曆 信息時,若是已經根據年份獲取到 農曆信息,能夠傳給相關函數,不用從新計算。this
四、最重要的,只寫農曆計算相關的計算,其餘無功能,若是須要,經過 requrie 本腳本。code
造完輪子以後,發現能優化的程度有限,性能與以前提高不大(計算40次 大約都在 0.003 – 0.005 秒左右),最終 取消了 第2項 緩存(節省內存)。htm
/* 原文地址: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 */