在項目中會員進行註冊的時候需要進行生日的選擇,由於通用的UI庫都是公曆的並且萬年曆的形式不符合。也在網上查了好多資料,最後找到一套算法感覺還是不錯的。經過加工分析封裝成了一個vue組件。
實現公農曆轉換的算法
/**
* 數組LunarDaysOfMonth存入農曆1901年到2050年每年中的月天數信息
* 農曆每月只能是29或30天,一年用12(或13)個二進制位表示,從高到低,對應位爲1表示30天,否則29天
*/
var LunarDaysOfMonth = new Array
(
0x4ae0, 0xa570, 0x5268, 0xd260, 0xd950, 0x6aa8, 0x56a0, 0x9ad0, 0x4ae8, 0x4ae0, // 1901-1910
0xa4d8, 0xa4d0, 0xd250, 0xd548, 0xb550, 0x56a0, 0x96d0, 0x95b0, 0x49b8, 0x49b0, // 1920
0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada8, 0x2b60, 0x9570, 0x4978, 0x4970, 0x64b0, // 1930
0xd4a0, 0xea50, 0x6d48, 0x5ad0, 0x2b60, 0x9370, 0x92e0, 0xc968, 0xc950, 0xd4a0, // 1940
0xda50, 0xb550, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950, 0xb4a8, 0x6ca0, // 1950
0xb550, 0x55a8, 0x4da0, 0xa5b0, 0x52b8, 0x52b0, 0xa950, 0xe950, 0x6aa0, 0xad50, // 1960
0xab50, 0x4b60, 0xa570, 0xa570, 0x5260, 0xe930, 0xd950, 0x5aa8, 0x56a0, 0x96d0, // 1970
0x4ae8, 0x4ad0, 0xa4d0, 0xd268, 0xd250, 0xd528, 0xb540, 0xb6a0, 0x96d0, 0x95b0, // 1980
0x49b0, 0xa4b8, 0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada0, 0xab60, 0x9370, 0x4978, // 1990
0x4970, 0x64b0, 0x6a50, 0xea50, 0x6b28, 0x5ac0, 0xab60, 0x9368, 0x92e0, 0xc960, // 2000
0xd4a8, 0xd4a0, 0xda50, 0x5aa8, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950, // 2001-2010
0xb4a0, 0xb550, 0xb550, 0x55a8, 0x4ba0, 0xa5b0, 0x52b8, 0x52b0, 0xa930, 0x74a8, // 2011-2020
0x6aa0, 0xad50, 0x4da8, 0x4b60, 0x9570, 0xa4e0, 0xd260, 0xe930, 0xd530, 0x5aa0, // 2021-2030
0x6b50, 0x96d0, 0x4ae8, 0x4ad0, 0xa4d0, 0xd258, 0xd250, 0xd520, 0xdaa0, 0xb5a0, // 2031-2040
0x56d0, 0x4ad8, 0x49b0, 0xa4b8, 0xa4b0, 0xaa50, 0xb528, 0x6d20, 0xada0, 0x55b0 // 2041-2050
);
/**
* 數組LunarLeapYear存放農曆1901年到2050年閏月的月份,如沒有則爲0,從高到低,每字節存兩年
*/
var LunarLeapYear = new Array
(
0x00, 0x50, 0x04, 0x00, 0x20, // 1901-1910
0x60, 0x05, 0x00, 0x20, 0x70, // 1920
0x05, 0x00, 0x40, 0x02, 0x06, // 1930
0x00, 0x50, 0x03, 0x07, 0x00, // 1940
0x60, 0x04, 0x00, 0x20, 0x70, // 1950
0x05, 0x00, 0x30, 0x80, 0x06, // 1960
0x00, 0x40, 0x03, 0x07, 0x00, // 1970
0x50, 0x04, 0x08, 0x00, 0x60, // 1980
0x04, 0x0a, 0x00, 0x60, 0x05, // 1990
0x00, 0x30, 0x80, 0x05, 0x00, // 2000
0x40, 0x02, 0x07, 0x00, 0x50, // 2001-2010
0x04, 0x09, 0x00, 0x60, 0x04, // 2011-2020
0x00, 0x20, 0x60, 0x05, 0x00, // 2021-2030
0x30, 0xb0, 0x06, 0x00, 0x50, // 2031-2040
0x02, 0x07, 0x00, 0x50, 0x03 // 2041-2050
);
/**
* 日期轉換
* 使用方法
* var dc = new dateChange();
* var a=dc.getSolarDate([2005,1,10]); //農曆轉公曆
* var b=dc.getLunarDate([2005,1,10]); //公曆轉農曆
*/
// function dateChange() {
/**
* 返回農曆iLunarYear年的閏月月份,如沒有則返回0
*/
function GetLeapMonth(iLunarYear) {
var Leap = LunarLeapYear[(iLunarYear - 1901) >> 1];
return (((iLunarYear - 1901) & 1) == 0) ? (Leap >> 4) : (Leap & 0x0f);
}
/**
* 返回農曆iLunarYer年iLunarMonth月的天數,結果是一個長整數
* 如果iLunarMonth不是閏月, 高字爲0,低字爲該月的天數
* 如果iLunarMonth是閏月, 高字爲後一個月的天數,低字爲前一個月的天數
*/
function LunarMonthDays(iLunarYear, iLunarMonth) {
var High;
var Low;
var Bit;
High = 0;
Low = 29;
Bit = 16 - iLunarMonth;
if ((iLunarMonth > GetLeapMonth(iLunarYear)) && (GetLeapMonth(iLunarYear) > 0)) Bit--;
if ((LunarDaysOfMonth[iLunarYear - 1901] & (1 << Bit)) > 0) Low++;
if (iLunarMonth == GetLeapMonth(iLunarYear)) {
High = ((LunarDaysOfMonth[iLunarYear - 1901] & (1 << (Bit - 1))) > 0) ? 30 : 29;
}
return Low + (High << 16);
}
/**
* 返回公曆iSolarYear年iSolarMonth月的天數,結果是一個長整數
*/
function SolarMonthDays(iSolarYear, iSolarMonth) {
var MonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var isl = isLeapYear(iSolarYear);
if (isl) MonthDays[1] = 29;
return MonthDays[iSolarMonth - 1];
}
/**
* 返回農曆iLunarYear年的總天數
*/
function LunarYearDays(iLunarYear) {
var Days;
var tmp;
Days = 0;
for (var i = 1; i <= 12; i++) {
tmp = LunarMonthDays(iLunarYear, i);
Days = Days + ((tmp >> 16) & 0xffff); //取高位
Days = Days + (tmp & 0xffff); //取低位
}
return Days;
}
/**
* 返回公曆iSolarYear年的總天數
*/
function SolarYearDays(iSolarYear) {
var isl = isLeapYear(iSolarYear);
return isl ? 366 : 365;
}
/**
* 判斷公曆年是否是潤年
*/
function isLeapYear(iSolarYear) {
var y = parseInt(iSolarYear);
return ((y % 100 == 0 && y % 400 == 0) || (y % 4 == 0 & y % 100 != 0));
}
/**
* 將農曆iLunarYear年格式化成天干地支記年法表示的字符串
*/
function FormatLunarYear(iLunarYear) {
var szText1 = new String("甲乙丙丁戊己庚辛壬癸");
var szText2 = new String("子醜寅卯辰巳午未申酉戌亥");
var strYear;
strYear = szText1.substr((iLunarYear - 4) % 10, 1);
strYear = strYear + szText2.substr((iLunarYear - 4) % 12, 1);
return strYear + "年";
}
/**
* 將農曆iLunarMonth月格式化成農曆表示的字符串
*/
function FormatLunarMonth(iLunarMonth) {
var szText = new String("正二三四五六七八九十");
var strMonth;
if (iLunarMonth <= 10) {
strMonth = szText.substr(iLunarMonth - 1, 1);
}
else if (iLunarMonth == 11) strMonth = "冬";
else strMonth = "臘";
return strMonth + "月";
}
/**
* 將農曆iLunarDay日格式化成農曆表示的字符串
*/
function FormatLunarDay(iLunarDay) {
var szText1 = new String("初十廿三");
var szText2 = new String("一二三四五六七八九十");
var strDay;
if ((iLunarDay != 20) && (iLunarDay != 30)) {
strDay = szText1.substr((iLunarDay - 1) / 10, 1) + szText2.substr((iLunarDay - 1) % 10, 1);
}
else if (iLunarDay != 20) {
strDay = szText1.substr(iLunarDay / 10, 1) + "十";
}
else {
strDay = "二十";
}
return strDay;
}
/**
* 計算兩個公曆日期相差的天數
*/
function CaculateSolarSpanDays(start, end) {
var starty = start[0];
var startm = start[1];
var startd = start[2];
var endy = end[0];
var endm = end[1];
var endd = end[2];
var daynum = 0;
//年份的天數
for (var i = starty; i < endy; i++) {
daynum += SolarYearDays(i);
}
//月份的天數
for (var i = 1; i < endm; i++) {
daynum += SolarMonthDays(endy, i);
}
daynum += endd - startd;
return daynum;
}
/**
* 計算兩個農曆日期相差的天數
* isleap--> end的月份是閏月還是非閏月
*/
function CaculateLunarSpanDays(start, end, isleap) {
var starty = start[0];
var startm = start[1];
var startd = start[2];
var endy = end[0];
var endm = end[1];
var endd = end[2];
var daynum = 0;
//年份的天數
for (var i = starty; i < endy; i++) {
daynum += LunarYearDays(i);
}
//月份的天數
for (var i = 1; i < endm; i++) {
var mdays = 0;
var tmp = LunarMonthDays(endy, i);
mdays += ((tmp >> 16) & 0xffff); //取高位
mdays += (tmp & 0xffff); //取低位
daynum += mdays;
//w('lunar'+endy+'年'+i+'月的天數爲'+ LunarMonthDays(endy,i));
}
//如果當前月是閏月,加上當前月的非閏月天數
if (isleap) {
var Low;
var Bit;
Low = 29;
Bit = 16 - endm;
if ((endm > GetLeapMonth(endy)) && (GetLeapMonth(endy) > 0)) Bit--;
if ((LunarDaysOfMonth[endy - 1901] & (1 << Bit)) > 0) Low++;
daynum += Low;
}
daynum += endd - startd;
return daynum;
}
/**
* 將公曆日期轉換爲農曆日期,返回農曆表示的字符串
*/
function GetLunarDateString(SolarDate) {
var tmp;
var iLunarYear;
var iLunarMonth;
var iLunarDay;
var Leap = false;
var MinMilli = 1000 * 60;
var HrMilli = MinMilli * 60;
var DyMilli = HrMilli * 24;
/* 從1901年1月1日算起,給定的公曆日期已經過去的天數
Date是從1970年1月1日作爲起點的 */
var StartDate = [1901, 1, 1];
var iSpanDays = CaculateSolarSpanDays(StartDate, SolarDate);
//w('公曆'+StartDate+'至'+SolarDate+'的總天數:'+iSpanDays);
// 公曆1901年2月19日爲農曆1901年正月初一,差49天
if (iSpanDays <= 18) {
iLunarYear = 1900;
iLunarMonth = 11;
iLunarDay = 11 + iSpanDays;
}
else if (iSpanDays > 18 && iSpanDays < 49) {
iLunarYear = 1900;
iLunarMonth = 12;
iLunarDay = iSpanDays - 18;
}
else {
// 從農曆1901年正月初一算起
iSpanDays = iSpanDays - 49;
iLunarYear = 1901;
iLunarMonth = 1;
iLunarDay = 1;
// 計算農曆年
tmp = LunarYearDays(iLunarYear);
while (iSpanDays >= tmp) {
iSpanDays -= tmp;
iLunarYear++;
tmp = LunarYearDays(iLunarYear);
//w(iLunarYear + '總天數' + tmp + ' 減後剩餘天數:' +iSpanDays);
}
//w('年--'+iLunarYear);
// 計算農曆月
tmp = LunarMonthDays(iLunarYear, iLunarMonth) & 0xffff; //取低字
while (iSpanDays >= tmp) {
iSpanDays -= tmp;
if (iLunarMonth == GetLeapMonth(iLunarYear)) // 該年該月閏月
{
tmp = LunarMonthDays(iLunarYear, iLunarMonth) >> 16; //取高字
if (iSpanDays < tmp) {
Leap = (tmp > 0) ? true : false; // 閏月的後個月?
break;
}
iSpanDays = iSpanDays - tmp;
}
iLunarMonth++;
tmp = LunarMonthDays(iLunarYear, iLunarMonth) & 0xffff; //取低字
}
// 計算農曆日
iLunarDay += iSpanDays;
}
return [iLunarYear, (Leap ? iLunarMonth * 10 : iLunarMonth), iLunarDay, FormatLunarYear(iLunarYear) + '(' + iLunarYear + ')', (Leap ? "閏" : "") + FormatLunarMonth(iLunarMonth), FormatLunarDay(iLunarDay), 'fromSolarDay' + SolarDate];
//return FormatLunarYear(iLunarYear) + (Leap ? "閏" : "") + FormatLunarMonth(iLunarMonth) + FormatLunarDay(iLunarDay);
}
/**
* 將農曆日期轉換爲公曆日期
*/
function GetSolarDateString(LunarDate) {
var tmp;
var iSolarYear;
var iSolarMonth;
var iSolarDay;
var Leap = false;
var MinMilli = 1000 * 60;
var HrMilli = MinMilli * 60;
var DyMilli = HrMilli * 24;
//判斷LunarDate的月份是否屬於閏月, 例 4爲非閏月 40爲閏月.
var monthIsLeap = false;
if (LunarDate[1] > 12) {
monthIsLeap = true;
LunarDate[1] = LunarDate[1] / 10;
}
if (LunarDate[0] < 1900) {
alert('超出計算範圍0');
return false;
}
// 從1900年冬月十一(公曆1901年1月1日)算起,給定的農曆日期已經過去的天數
// 公曆1901年2月19日爲農曆1901年正月初一,差49天
// 公曆1901年1月1日爲農曆1900年11月11,差49天
var StartDate = [1901, 1, 1];
if (LunarDate[0] == 1900) {
if (LunarDate[1] == 11) {
var iSpanDays = LunarDate[2] - 11;
} else if (LunarDate[1] == 12) {
var iSpanDays = LunarDate[2] + 18;
} else {
alert('超出計算範圍');
return false;
}
} else {
var iSpanDays = CaculateLunarSpanDays(StartDate, LunarDate, monthIsLeap) + 49;
}
//w('1900年冬月十一 至 '+LunarDate+'的總天數:'+iSpanDays);
// 從公曆1901年1月1日算起
iSolarYear = 1901;
iSolarMonth = 1;
iSolarDay = 1;
// 計算公曆年
tmp = SolarYearDays(iSolarYear);
//w(iSolarYear+'年'+'的總天數:'+tmp);
while (iSpanDays >= tmp) {
iSpanDays -= tmp;
iSolarYear++;
tmp = SolarYearDays(iSolarYear);
//w(iSolarYear+'年'+'的總天數:'+tmp);
}
//w('計算公曆年後剩餘天數:'+iSpanDays);
// 計算公曆月
tmp = SolarMonthDays(iSolarYear, iSolarMonth);
//w(iSolarYear+'年'+iSolarMonth+'月'+'的天數:'+tmp);
while (iSpanDays >= tmp) {
iSpanDays -= tmp;
iSolarMonth++;
tmp = SolarMonthDays(iSolarYear, iSolarMonth);
//w(iSolarYear+'年'+iSolarMonth+'月'+'的天數:'+tmp + ' 減後:'+iSpanDays);
}
//w('計算月後剩餘天數:'+iSpanDays);
// 計算公曆日
iSolarDay += iSpanDays;
return [iSolarYear, iSolarMonth, iSolarDay, iSolarYear + '年', iSolarMonth + '月', iSolarDay + '日', 'fromLunarDays' + LunarDate];
}
export {GetLunarDateString,GetSolarDateString,SolarYearDays,SolarMonthDays,LunarYearDays,LunarMonthDays,GetLeapMonth,FormatLunarMonth,FormatLunarDay,FormatLunarYear}
組件封裝
<template>
<div>
<div class="btn">
<div class="btn_left" @click="err">取消</div>
<div class="btn_right" @click="send">確定</div>
</div>
<div class="select">
<div>
<select name id @change="lunary" ref="lunary" class="select_y" size="5">
<option v-for="(item,index) in ylist" :key="index" selected>{{item}}</option>
</select>
</div>
<div>
<select name id @change="lunarm" ref="lunarm" class="select_m" size="5">
<option v-for="(item,index) in mlist" :key="index">{{item}}</option>
</select>
</div>
<div>
<select name id @change="lunard" ref="lunard" class="select_d" size="5">
<option v-for="(item,index) in dlist" :key="index">{{item}}</option>
</select>
</div>
</div>
</div>
</template>
<script>
import {
GetLunarDateString,
GetSolarDateString,
SolarYearDays,
SolarMonthDays,
LunarYearDays,
LunarMonthDays,
GetLeapMonth,
FormatLunarMonth,
FormatLunarDay,
FormatLunarYear
} from "./lucnDate.js";
import { stringify } from "querystring";
import { nextTick } from "q";
export default {
data() {
return {
ylist: null,
mlist: null,
dlist: null,
data: {}
};
},
methods: {
// 取消
err() {
// 向父組件傳值
this.$emit("func", this.data);
},
// 確認
send() {
if (
this.$refs.lunary.value &&
this.$refs.lunarm.value &&
this.$refs.lunard.value
) {
console.log(this.$refs.lunard.selectedIndex);
this.data = {
lunary: this.$refs.lunary.value,
lunarm: this.$refs.lunarm.value,
lunard: this.$refs.lunard.value,
dindex: this.$refs.lunard.selectedIndex + 1
};
this.$emit("func", this.data);
} else {
this.$toast({
message: "請選擇生日",
duration: 1000
});
}
},
// 年
lunary() {
this.mlist = this.lunarMonthOptions(this.$refs.lunary.value);
console.log(this.lunarMonthOptions(this.$refs.lunary.value));
// 從新選擇年再次計算
this.lunarm();
},
// 月
lunarm() {
console.log(this.$refs.lunarm.value);
// 將農曆月轉化爲公曆月
var disLength = this.$refs.lunarm.value.length;
var shortName = this.$refs.lunarm.value.substring(disLength - 2, disLength);
console.log(shortName);
switch (shortName) {
case "正月":
this.m = 1;
break;
case "二月":
this.m = 2;
break;
case "三月":
this.m = 3;
break;
case "四月":
this.m = 4;
break;
case "五月":
this.m = 5;
break;
case "六月":
this.m = 6;
break;
case "七月":
this.m = 7;
break;
case "八月":
this.m = 8;
break;
case "九月":
this.m = 9;
break;
case "十月":
this.m = 10;
break;
case "冬月":
this.m = 11;
break;
case "臘月":
this.m = 12;
break;
default:
}
// 輸入當年當月的天數
this.dlist = this.lunarDayOptions(this.$refs.lunary.value, this.m);
},
// 天
lunard() {
//$("select").on("blur", function() {
// window.scroll(0, 0); //失焦後強制讓頁面歸位
//});
},
// 循環年
lunarYearOptions(from, to, curyear) {
var str = [];
for (var i = from; i <= to; i++) {
str.push(i);
// str.push(FormatLunarYear(i) + "(" + i + ")");
}
return str;
},
// 循環月
lunarMonthOptions(year, month) {
var leapMonth = GetLeapMonth(year);
var str = [];
var from = 1;
var to = 12;
var leap = false;
if (month > 12) {
leap = true;
month = month / 10;
}
for (var i = from; i <= to; i++) {
str.push(FormatLunarMonth(i));
if (leapMonth > 0 && i == leapMonth) {
str.push("閏" + FormatLunarMonth(i));
}
}
return str;
},
// 循環天
lunarDayOptions(year, month, day) {
console.log(year, month, day)
var from = 1;
var to = 0;
var leap = false;
if (month > 12) {
month = month / 10;
leap = true;
var monthdaynum = (LunarMonthDays(year, month) >> 16) & 0xffff;
} else {
var monthdaynum = LunarMonthDays(year, month) & 0xffff;
}
to = monthdaynum;
console.log(to)
var str = [];
for (var i = from; i <= to; i++) {
str.push(FormatLunarDay(i));
}
return str;
}
},
mounted() {
// //設定當前農曆公曆日期
var curSolarDate = [2019, 11, 4];
// 年,月,日
var curLunarDate = GetSolarDateString(curSolarDate);
this.ylist = this.lunarYearOptions(1902, 2019, curLunarDate[0]);
this.mlist = this.lunarMonthOptions(
curLunarDate[0],
curLunarDate[1],
curLunarDate[2]
);
}
};
</script>
<style scoped>
.btn {
height: 0.3rem;
line-height: 1rem;
font-size: 0.29rem;
}
.btn_left {
float: left;
padding-left: 0.1rem;
color: #1989fa;
}
.btn_right {
float: right;
padding-right: 0.1rem;
color: #1989fa;
}
.select {
margin-top: 1rem;
width: 100%;
/* text-align: center; */
display: flex;
justify-content: space-around;
overflow-y: hidden;
}
.select_y option {
/* width: 33%; */
/*去掉默認的下拉三角*/
/* appearance: none; */
height: 0.7rem;
padding: 0.2rem 0;
}
.select_m option {
/* width: 33%; */
height: 0.7rem;
padding: 0.2rem 0;
}
.select_d option {
/* width: 33%; */
height: 0.7rem;
padding: 0.2rem 0;
}
</style>>
在父組件中的使用
<van-popup v-else v-model="show" position="bottom" :style="{ height: '40%' }">
<nongli @func="getMsgFormSon"></nongli>
</van-popup>
//這裏在對農曆進行了轉換方便後臺的存儲
getMsgFormSon(data) {
if (Object.keys(data).length !== 0) {
var disLength = data.lunarm.length;
var shortName = data.lunarm.substring(disLength - 2, disLength);
console.log(shortName);
switch (shortName) {
case "正月":
this.m = 1;
break;
case "二月":
this.m = 2;
break;
case "三月":
this.m = 3;
break;
case "四月":
this.m = 4;
break;
case "五月":
this.m = 5;
break;
case "六月":
this.m = 6;
break;
case "七月":
this.m = 7;
break;
case "八月":
this.m = 8;
break;
case "九月":
this.m = 9;
break;
case "十月":
this.m = 10;
break;
case "冬月":
this.m = 11;
break;
case "臘月":
this.m = 12;
break;
default:
}
this.y = data.lunary;
this.dindex = data.dindex + 1;
var time = this.y + "-" + this.m + "-" + this.dindex;
this.BirthdaySR = dayjs(time).format("YYYY-MM-DD");
this.userInfo.BirthdaySR = this.BirthdaySR + " " + "23:59:00";
this.show = false;
console.log(this.y + "-" + this.m + "-" + this.d);
} else {
this.show = false;
}
},
到此整個農曆日期選擇器基本實現,剩下的就是一些對樣式的修改和調整。