如何实现日历(公历+农历)

您所在的位置:网站首页 如何在表里将农历转化成公历 如何实现日历(公历+农历)

如何实现日历(公历+农历)

2023-10-20 11:39| 来源: 网络整理| 查看: 265

如何实现日历(公历+农历)

技术文档,转载请注明出处!

关于日历

​ 在我们现在的日常工作、生活中,日历通常指代公历,又称为“阳历”、“太阳历”。公历是以地球绕太阳公转的运动周期为基础而制定的历法,一个公历年近似等于一个回归年,公历的历年平均长度与回归年只有26秒之差,要累积3300年才差一日。目前为世界上大多数国家通用,并以公元作为纪年。

​ 公历的规律很明显,我们可以利用内置的Date对象很容易实现一个万年历。这里,我们主要探讨农历实现方法,农历是我国的一种历法,又称“夏历”、“中历”、“旧历”,俗称“阴历”。定月的方法是用朔望月周期给出,朔所在日为初一,朔望月长约29天半,所以农历大月30天,小月29天。农历平年有十二个月,全年354天或355天,闰年为十三个月,其中某一月为闰月,月名依前一月名而定(例如:前月是八月,闰月则为闰八月),闰年全年383天或384天。一个月的大小,取决于天文观测的结果,与规则无关。

农历的效果图

我们先要定一个小目标,目标明确了,我们只要朝着目标靠近就行了。

日历.png

实现方法 公历的实现

​ 每一台日历,都有一个默认状态,我们也不例外,默认当前年月日。公历月历表可分为三个模块,上月日历日,本月日历日,下月日历日(如图所示:)。

公历.png

​ 我们这里从默认值new Date()着手,获取当前月份的起始日、终止日,以及起始日的星期,接下来我们就可以分别获取当前月历表的上月日,当月日,下月日的基本信息情况。

/** * 创建当前月表包含的每一天(为了方便,这里我们引入了moment.js) * * @param { string } date 传入的日期 (如:'2021-3-18') * @returns { array } days 返回传入日所在月份表的所有日的数组 */ function createMonthDays(date) { let day = moment(date) || moment(); let days = []; let start = day.clone().startOf('month'); let end = day.clone().endOf('month'); let weekday = start.weekday(); // 获取上月日数据 for (let i = 1; i 二月,农历闰四月初一 ->闰四月) const toChinaMonth = function(month, isleap) { isleap = isleap || false; return isleap ? (ChinaElement[4] + ChinaMonths[month] + ChinaMonths[0]) : (ChinaMonths[month] + ChinaMonths[0]); } const nowInfo = function() { let now = new Date(); return { y: now.getFullYear(), m: now.getMonth()+1, d: now.getDate() }; } // 某年农历闰月月份 const leapMonth = function(year) { year = year || nowInfo().y; return lunarYears[year - 1900] & 0xF; } // 某年农历闰月天数 const leapDays = function(year) { year = year || nowInfo().y; if(leapMonth(year)) { return (lunarYears[year-1900] & 0x10000) ? 30 : 29; } return 0; } // 某年份农历各月天数 const lunarMonthDays = function(year) { year = year || nowInfo().y; let lunarYear = lunarYears[year - 1900]; let monthDays = []; for(let i = 4; i< 16; i++) { let monthDay = (lunarYear >> i & 0x1) ? 30 :29; monthDays.push(monthDay); } monthDays.reverse(); // 添加闰月 let leapM = leapMonth(year); if(leapM) monthDays.splice(leapM, 0 , leapDays(year)); return monthDays; } // 某年农历天数 const lunarYearDays = function(year) { year = year || nowInfo().y; let num = 0; lunarMonthDays(yar).forEach(item => { num += item; }); return num; }

推算公历某日对应的农历日,逻辑清楚了,代码也就不难了。

const solarToLunar = function(y,m,d) { if(y < 1901 || y > 2100) return -1; let date; if(!y) { date = new Date(); } else { date = new Date(y,m-1,d); } // 参照日期 1901-02-19 正月初一 let offset = (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(1901,1,19))/86400000; let temp = 0, i; for(i = 1901; i < 2101 && offset > 0; i++ ){ temp = lunarYearDays(i); offset -= temp; } if(offset < 0) { offset += temp; i--; } // 农历年、月、日 let isLeap = false, j; let monthDays = lunarMonthDays(i); let leapM = leapMonth(i); if(offset > 0) { for(j = 0; j < monthDays.length && offset > 0; j++) { temp = monthDays[j]; offset -=temp; } if(offset == 0) { j++; } if(offset < 0) { offset += temp; } } else { // 补偿公历1901年2月的农历信息 if(offset == -23) { return { lunarY: i, lunarM: 12, lunarD: 8, isLeap: false } } } // 矫正闰年月 if(leapM) { if(j == leapM + 1) { isLeap = true } if(j >= leapM + 1) { j-- } } return { lunarY: i, lunarM: j, lunarD: ++offset, isLeap: isLeap } }

​ 至此,按理我们本应该能够实现1900-2100年的所有日的转化,但是仔细想一下,如果每一日都要不断循环200年的农历源数据去计算的话,未免不太像话。因此,对于月历表,我们应该只计算第一条数据,后面的每日应该去逐个推算才比较合理。关于如何推算,这其中其实还是有一些逻辑,这里我细细的整理了一下。

公历月对等转化农历.png

代码整理如下:

// 公历月表推算表中各农历日 const solarToLunarMonthTable = function(firstDay, days) { let firstMoonDay = solarToLunar(firstDay.years, firstDay.months + 1, firstDay.date); let curY = firstMoonDay.lunarY, curM = firstMoonDay.lunarM, curD = firstMoonDay.lunarD, leap = firstMoonDay.isLeap; // 判断当前是否为闰年闰月 let leap_m = leapMonth(curY); let isleap = false; if(leap_m === curM) { isleap = true; } // 获取当前年份各农历月天数, 首先获取当前月在当前农历年份中的天数 let moonMonthDays = lunarMonthDays(curY); let moonMonthTotal; if(moonMonthDays.length === 12 || (moonMonthDays.length > 12 && curM < leap_m) || (moonMonthDays.length > 12 && curM === leap_m && !leap)) { moonMonthTotal = moonMonthDays[curM - 1]; } else { moonMonthTotal = moonMonthDays[curM]; } for(let i = 0, len = days.length; i < len; i++) { if(moonMonthTotal < curD) { if(!isleap || leap) { curM++; } curD = 1; if(curM > 12) { curY++; curM = 1; curD = 1; moonMonthTotal = lunarMonthDays(curY)[0]; } if(isleap) leap = !leap; } days[i].lunarY = curY; days[i].lunarM = curM; days[i].lunarD = curD === 1 ? toChinaMonth(curM, leap) : toChinaDay(curD); days[i].isLeap = leap; curD++; } return days; }

有了上述月历表农历信息转化方法,就可以插入上文中创建月历表公历信息的方法 createMonthDays中 ,这个农历才算完成。细心的朋友可能会注意到公历日转农历日中的 solarToLunar 方法中有段1901年2月的补偿代码,这是由于我们定了基准点日期,且对 offset 初始值为负数时并为计算(即对1901.2.19的农历没有进行推算,因此调用方法时,1901.2.19日前农历日都是 undefined, 因此我们需要补充1901.2月月历表中第一日的农历数据。对于1901.1月份的农历信息我们就不打算补充了,我们可以在日历的选择设置的时候人为控制不给选1901年1月,如果先选定了1月再选1901年,可以强制切换到2月,当然如果有必要,还是可以的 。 特别要注意的是,由于我们最初定的星期展示方式为每周开始日期为周日,如果时周一,这里需要对数据进行调整)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3