日历组件

177 阅读8分钟

``

```<template>
  <view class="calendar">
    <view class="week">
      <view class="item" wx:for="{{weekList}}" wx:key="item">{{ item }}</view>
    </view>
    <view class="day">
      <view class="item {{currentDay==item.solar?'active':''}}" wx:for="{{dayList}}" wx:key="index">
        <view class="num">{{ item.solar }}</view>
        <view class="tip">{{ item.lunar }}</view>
      </view>
    </view>
  </view>
</template>
<script>
  import { createComponent } from '@mpxjs/core'
  import { getLunarBySolar } from '../../utils/SolarToLunar'
  createComponent({
    properties: {
      day: {
        type: String,
        value: new Date()
      }
    },
    data: {
      weekList: ['日', '一', '二', '三', '四', '五', '六'],
      dayList: [],
      currentDay: 1
    },
    watch: {
      day(newVal, oldVal) {
        let current = new Date(newVal)
        this.currentDay = current.getDate()
        let date = new Date(current.getFullYear(), current.getMonth(), 1)
        let week = date.getDay()
        this.dayList = []
        for (let i = 0; i < week; i++) {
          this.dayList.push({
            solar: '',
            lunar: ''
          })
        }
        let dayCount = new Date(current.getFullYear(), current.getMonth() + 1, 0).getDate()
        for (let i = 1; i <= dayCount; i++) {
          if (i > 1) date.setDate(date.getDate() + 1)
          this.dayList.push({
            solar: i,
            lunar: getLunarBySolar(date)
          })
        }
      }
    }
  })
</script>
<style lang="scss" scoped>
  .calendar {
    padding: 30rpx;
    .week {
      display: flex;
      .item {
        width: 98rpx;
        height: 60rpx;
        font-family: PingFangSC-Regular;
        font-weight: 400;
        font-size: 24rpx;
        color: #5a5f6b;
        letter-spacing: 0;
        text-align: center;
        line-height: 60rpx;
      }
    }
    .day {
      display: flex;
      flex-wrap: wrap;
      .item {
        width: 98rpx;
        text-align: center;
        .num {
          height: 66rpx;
          font-family: PingFangSC-SNaNrpxibold;
          font-weight: 600;
          font-size: 36rpx;
          color: #222222;
          letter-spacing: 0;
          text-align: center;
          line-height: 66rpx;
        }
        .tip {
          height: 46rpx;
          font-family: PingFangSC-Regular;
          font-weight: 400;
          font-size: 20rpx;
          color: #aaaaaa;
          letter-spacing: 0;
          text-align: center;
        }
      }
      .active {
        width: 98rpx;
        background: #661eff;
        border-radius: 30rpx;
        .num {
          color: #ffffff;
        }
        .tip {
          color: #ffffff;
        }
      }
    }
  }
</style>
农历日历互转方法
```js
```const mArr = [
  "春节",
  "二月",
  "三月",
  "四月",
  "五月",
  "六月",
  "七月",
  "八月",
  "九月",
  "十月",
  "十一月",
  "十二月",
];
const dArr = [
  "初二",
  "初三",
  "初四",
  "初五",
  "初六",
  "初七",
  "初八",
  "初九",
  "初十",
  "十一",
  "十二",
  "十三",
  "十四",
  "十五",
  "十六",
  "十七",
  "十八",
  "十九",
  "二十",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "廿一",
  "三十",
];
/**
 * 根据农历获取阳历
 * @see https://blog.csdn.net/zhangjiaqianghh/article/details/115478404
 * @param {农历日期, 2022-01-01} lunar
 * @param {是否闰月, false} leapMonthFlag
 * @return {阳历日期, 2022-02-01} newDate
 */
export const getSolarByLunar = (lunar, leapMonthFlag) => {
  // 使用//g正则替换所有
  var lunarDt = lunar.replace(/-/g, "");
  var lunarYear = parseInt(lunarDt.substring(0, 4));
  var lunarMonth = parseInt(lunarDt.substring(4, 6));
  var lunarDay = parseInt(lunarDt.substring(6, 8));

  checkLunarDate(lunarYear, lunarMonth, lunarDay, leapMonthFlag);

  var offset = 0;
  for (var i = 1900; i < lunarYear; i++) {
    var yearDaysCount = getYearDays(i); // 求阴历某年天数
    offset += yearDaysCount;
  }

  var leapMonth = getLeapMonth(lunarYear);

  //当年没有闰月或月份早于闰月或和闰月同名的月份
  if (
    leapMonth == 0 ||
    lunarMonth < leapMonth ||
    !leapMonthFlag & (lunarMonth == leapMonth)
  ) {
    for (var i = 1; i < lunarMonth; i++) {
      var tempMonthDaysCount = getMonthDays(lunarYear, i);
      offset += tempMonthDaysCount;
    }
    // 检查日期是否大于最大天
    if (lunarDay > getMonthDays(lunarYear, lunarMonth)) {
      console.error("不合法的农历日期!");
    }
    offset += lunarDay; // 加上当月的天数
  } else {
    console.log("当年有闰月,且月份晚于或等于闰月======" + leapMonth);
    //当年有闰月,且月份晚于或等于闰月
    for (var i = 1; i < lunarMonth; i++) {
      var tempMonthDaysCount = getMonthDays(lunarYear, i);
      offset += tempMonthDaysCount;
    }
    if (lunarMonth > leapMonth) {
      var temp = getLeapMonthDays(lunarYear); // 计算闰月天数
      offset += temp; // 加上闰月天数

      if (lunarDay > getMonthDays(lunarYear, lunarMonth)) {
        throw new Exception("不合法的农历日期!");
      }
      offset += lunarDay;
    } else {
      // 如果需要计算的是闰月,则应首先加上与闰月对应的普通月的天数
      // 计算月为闰月
      var temp = getMonthDays(lunarYear, lunarMonth); // 计算非闰月天数
      offset += temp;

      if (lunarDay > getLeapMonthDays(lunarYear)) {
        throw new Exception("不合法的农历日期!");
      }
      offset += lunarDay;
    }
  }

  // 阳历日期计算起点
  var startStr = "1900-01-30";
  var newDate = new Date(startStr);
  newDate.setDate(newDate.getDate() + offset);
  return newDate;
};
/**
 * 根据阳历获取农历
 * @see https://www.iteye.com/blog/lixor-1190599
 * @param {当前日期} curDate
 * @returns {int数组 [1,2]} result:索引1代表天数,索引2代表月份
 */
export const getLunarBySolar = (curDate) => {
  var leapMonth = 0;
  var date = new Date("1900/1/31");
  // 求出当前时间和1900年1月31日相差的天数
  var offset = parseInt((curDate.getTime() - date.getTime()) / 86400000);
  // 用offset减去每农历年的天数,计算当天是农历第几天,i最终结果是农历的年份,offset是当年的第几天
  var iYear,
    daysOfYear = 0;
  for (iYear = 1900; iYear < 2100 && offset > 0; iYear++) {
    daysOfYear = getYearDays(iYear);
    offset -= daysOfYear;
  }
  if (offset < 0) {
    offset += daysOfYear;
    iYear--;
  }
  // 闰哪个月,1-12
  leapMonth = getLeapMonth(iYear);
  var leap = false; // 默认值
  // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
  var iMonth,
    daysOfMonth = 0;
  for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
    // 闰月
    if (leapMonth > 0 && iMonth == leapMonth + 1 && !leap) {
      --iMonth;
      leap = true;
      daysOfMonth = getLeapMonthDays(iYear);
    } else daysOfMonth = getMonthDays(iYear, iMonth);

    offset -= daysOfMonth;
    // 解除闰月
    if (leap && iMonth == leapMonth + 1) leap = false;
  }
  // offset为0时,并且刚才计算的月份是闰月,要校正
  if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
    if (leap) {
      leap = false;
    } else {
      leap = true;
      --iMonth;
    }
  }
  // offset小于0时,也要校正
  if (offset < 0) {
    offset += daysOfMonth;
    --iMonth;
  }
  //   var result = [];
  //   result.push(iMonth);
  //   result.push(offset);
  return numberToChinaDay(iMonth, offset);
};
const numberToChinaDay = (m, d) => {
  return d == 0 ? mArr[m - 1] : dArr[d - 1];
};
const checkLunarDate = (lunarYear, lunarMonth, lunarDay, leapMonthFlag) => {
  if (lunarYear < 1900 || lunarYear > 2100) {
    console.error("非法年份" + lunarYear);
  }
  if (lunarMonth < 1 || lunarMonth > 12) {
    console.error("非法月======" + lunarMonth);
  }
  if (lunarDay < 1 || lunarDay > 31) {
    console.error("非法天======" + lunarDay);
  }
  //计算该年闰几月
  var leapMonth = getLeapMonth(lunarYear);
  if (leapMonthFlag & (leapMonth != lunarMonth)) {
    //  console.error("非润月======");
  }
};
const getMonthDays = (lunarYeay, month) => {
  if (month > 31 || month < 0) {
    throw new Exception("月份有错!");
  }
  // 0X0FFFF[0000 {1111 1111 1111} 1111]中间12位代表12个月,1为大月,0为小月
  var bit = 1 << (16 - month);
  if ((lunarInfo[lunarYeay - 1900] & 0x0ffff & bit) == 0) {
    return 29;
  } else {
    return 30;
  }
};
const getLeapMonthDays = (year) => {
  if (getLeapMonth(year) != 0) {
    if ((lunarInfo[year - 1900] & 0xf0000) == 0) {
      return 29;
    } else {
      return 30;
    }
  } else {
    return 0;
  }
};
const lunarInfo = [
  0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0,
  0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,
  0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,
  0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0,
  0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
  0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2,
  0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573,
  0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4,
  0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5,
  0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
  0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46,
  0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58,
  0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960, 0x0d954, 0x0d4a0, 0x0da50,
  0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0,
  0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
  0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260,
  0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,
  0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0,
  0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
];
const getYearDays = (y) => {
  var i,
    sum = 348;
  for (i = 0x8000; i > 0x8; i >>= 1) {
    if ((lunarInfo[y - 1900] & i) != 0) sum += 1;
  }
  return sum + leapDays(y);
};
const leapDays = (y) => {
  if (getLeapMonth(y) != 0) {
    if ((lunarInfo[y - 1900] & 0x10000) != 0) return 30;
    else return 29;
  } else return 0;
};
const getLeapMonth = (y) => {
  return lunarInfo[y - 1900] & 0xf;
};