农历的那些事儿(二)

综合上篇博文农历的那些事儿(一)获取的农历数据进行公农历转换,同时验证一下数据的正确性。copy输出文件中的16进制数组。

private final static int[] LUNAR_INFO = {
        0x10, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x16554, 0x56a0, 0x9ad0, 0x55d2,
        0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0x1d255, 0xb540, 0xd6a0, 0xada2, 0x95b0, 0x14977,
        0x4970, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0x1ab54, 0x2b60, 0x9570, 0x52f2, 0x4970,
        0x6566, 0xd4a0, 0xea50, 0x16a95, 0x5ad0, 0x2b60, 0x186e3, 0x92e0, 0x1c8d7, 0xc950,
        0xd4a0, 0x1d8a6, 0xb550, 0x56a0, 0x1a5b4, 0x25d0, 0x92d0, 0xd2b2, 0xa950, 0xb557,
        0x6ca0, 0xb550, 0x15355, 0x4da0, 0xa5b0, 0x14573, 0x52b0, 0xa9a8, 0xe950, 0x6aa0,
        0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0,
        0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x195a6,
        0x95b0, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46, 0xab60, 0x9570,
        0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58, 0x5ac0, 0xab60, 0x96d5, 0x92e0,
        0xc960, 0xd954, 0xd4a0, 0xda50, 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5,
        0xa950, 0xb4a0, 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x15176, 0x52b0, 0xa930,
        0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530,
        0x5aa0, 0x76a3, 0x96d0, 0x26fb, 0x4ad0, 0xa4d0, 0x1d0b6, 0xd250, 0xd520, 0xdd45,
        0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0x1b255, 0x6d20, 0xada0,
        0x14b63, 0x9370, 0x49f8, 0x4970, 0x64b0, 0x168a6, 0xea50, 0x6aa0, 0x1a6c4, 0xaae0,
        0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0, 0xa6d0, 0x55d4,
        0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50, 0x55a0, 0xaba4, 0xa5b0, 0x52b0,
        0xb273, 0x6930, 0x7337, 0x6aa0, 0xad50, 0x14b55, 0x4b60, 0xa570, 0x54e4, 0xd160,
        0xe968, 0xd520, 0xdaa0, 0x16aa6, 0x56d0, 0x4ae0, 0xa9d4, 0xa2d0, 0xd150, 0xf252,
        0xd520
};

上面博文中转换的16进制数据未详细描述,此片接着分析。1903年为16进制数据为0x54d5,转换成二进制

//0x54d5
System.out.println(Integer.toBinaryString(LUNAR_INFO[1903 - 1900]));
101010011010101

输出为15位的二进制代码,完成的结果为17位二进制代码,由于前两位是0,所以省略掉了。

101010011010101
完整结果:00101010011010101
拆分分析:0 010101001101 0101
由于左边高位有0省略,从低位右边分析.
低位 4位为闰月,4为转换成10进制即为闰月的月份,0101-->十进制为5,表示润5月,0000表示不闰月
接下来12位分别代表12个月,010101001101从高到底分别为1月到12月,1表示月大此月30天,0月表示月小,此月29天.
最高位分别是1/0,1表示所润月月大,0表示所润月月小.

长度位201的16进制数组即代表201年的农历数据,由于该数据是从公农历对应表提取出来的,公历的起始日期为:1901-1-1,截至日期为:2100-12-31,所对应农历的起始日期为:1900-11-11,截至日为:2100-12-1。农历1900的只有一个多月数据,2100年不到12个月数据。

此时进行转换,计算出最小日期,最大日期

static {
    Calendar calendar = Calendar.getInstance();
    calendar.set(1901, 0, 1, 0, 0, 0);//公历1901-1-1 即农历1900-11-11
    MIN_TIME_MILLIS = calendar.getTimeInMillis();
    calendar.set(2100, 11, 31, 23, 59, 59);//公历2100-12-31 即农历2100-12-1
    MAX_TIME_MILLIS = calendar.getTimeInMillis();
}

转换时毫秒值判断

public static Lunar converterDate(long timeInMillis) {
    if (timeInMillis < MIN_TIME_MILLIS || timeInMillis > MAX_TIME_MILLIS) {
        throw new RuntimeException("日期超出农历计算范围,-->minDate:1900-1-1 maxDate 2100-12-31");
    }
    ....
}

算出间隔1900的天数,循环减去1900起始每年的天数,直到小于某年的天数,即日期为某年

Lunar lunar = new Lunar();
// 距离起始日期间隔的总天数 间隔天数和目标日期差一天
long offset = (timeInMillis - MIN_TIME_MILLIS) / (24 * 60 * 60 * 1000);
// 默认农历年为1900年,且由此开始推算农历年份
int lunarYear = 1900;
while (true) {
    int daysInLunarYear = getLunarYearDays(lunarYear);
    if (offset > daysInLunarYear) {
        offset -= daysInLunarYear;
        lunarYear++;
    } else {
        break;
    }
}
lunar.year = lunarYear;

剩下的间隔天数,循环减去该年1月起每月的天数,直到小于某月的天数,即日期为某月

// 递减每个农历月的总天数,确定农历月份,先计算非闰月后计算闰月
while (true) {
    if (lunarMonth == leapMonth) { // 该农历年闰月的天数,先算正常月再算闰月 如果润一月 先减去一月再减去润一月
        daysInLunarMonth = getLunarDays(lunarYear, lunarMonth);
        if (offset > daysInLunarMonth) {//剩余天数>当月天数
            offset -= daysInLunarMonth;//减去差额
            if (offset > getLunarLeapDays(lunarYear)) {//剩余天数>闰月天数
                offset -= getLunarLeapDays(lunarYear);//减去闰月天数
                lunarMonth++;//月份+1
            } else {
                lunarMonth = lunarYear;//标记闰月为当前年
                break;
            }
        } else {
            break;
        }
    } else { // 该农历年正常农历月份的天数
        daysInLunarMonth = getLunarDays(lunarYear, lunarMonth);
        if (offset > daysInLunarMonth) {//剩余天数>当月天数
            offset -= daysInLunarMonth;//减去差额
            lunarMonth++;//月份+1
        } else {
            break;
        }
    }
}

剩下的天数即某天。转换完毕,接下来验证,为了数据对比更直观,直接搞个日历出来。日历大体样式都差不多,显示的都是某月的第一天到最后一天,我们在上面显示出转换出来的农历就OK;

172104_hi9a_2438532.png

此日历很简单,一个RecyclerView 就搞定了,前后月的日期要么文字颜色区分,要么直接空白就可以了。日历可以滑动所以用ViewPager搞定。

每滑动一下加一个月

public static CalendarFragment newInstance(int position) {
    CalendarFragment calendarFragment = new CalendarFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("position", position);
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.DATE, 1);
    calendar.add(Calendar.MONTH, position);
    bundle.putSerializable("Calendar", calendar);
    calendarFragment.setArguments(bundle);
    return calendarFragment;
}

算出item的个数

calendar.set(Calendar.DATE, 1);
int firstWeek = getWeek(calendar);//计算当月第一天星期几
int lastWeek = getLastDayOfMonthToWeek(calendar.getTime());//当月最后一天星期几
int days = getLastDayOfMonth(calendar.getTime());//当月总天数
int diffWeek = (firstWeek - Calendar.SUNDAY);
calendar.add(Calendar.DATE, -diffWeek);
int count = diffWeek + days + (Calendar.SATURDAY - lastWeek);

for (int i = 0; i < count; i++) {
    if (i != 0)
        calendar.add(Calendar.DATE, 1);
    solars.add(new Solar(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1
            , calendar.get(Calendar.DATE), calendar.getTime()));
}
// 获取当月最后一天是周几
public int getLastDayOfMonthToWeek(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.DATE, 1);     // 设置当前月的1号
    calendar.add(Calendar.MONTH, 1);   // 加一个月,变为下月的1号
    calendar.add(Calendar.DATE, -1);    // 减去一天,变为当前月的最后一天
    return getWeek(calendar);
}
// 获取当月最后一天
public int getLastDayOfMonth(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.DATE, 1);     // 设置当前月的1号
    calendar.add(Calendar.MONTH, 1);   // 加一个月,变为下月的1号
    calendar.add(Calendar.DATE, -1);    // 减去一天,变为当前月的最后一天
    return calendar.get(Calendar.DAY_OF_MONTH);
}

item显示

if (position < mStartDiff) {
    mBtnLucky.setVisibility(View.GONE);
    mTvSolar.setTextColor(Color.parseColor("#DFDFDF"));
    mTvLunar.setTextColor(Color.parseColor("#DFDFDF"));
    mLayout.setClickable(false);
    mLayout.setEnabled(false);
} else if (position >= (getItemCount() - mEndDiff)) {
    mTvSolar.setTextColor(Color.parseColor("#DFDFDF"));
    mTvLunar.setTextColor(Color.parseColor("#DFDFDF"));
    mBtnLucky.setVisibility(View.GONE);
    mLayout.setClickable(false);
    mLayout.setEnabled(false);
} else {
    Calendar calendar = Calendar.getInstance();
    if ((solar.date.getTime() - calendar.getTimeInMillis()) > -24 * 60 * 60 * 1000) {
        mLayout.setClickable(true);
        mLayout.setEnabled(true);
        mTvSolar.setTextColor(Color.parseColor("#000000"));
        mTvLunar.setTextColor(Color.parseColor("#989898"));

        if (solar.year == calendar.get(Calendar.YEAR) &&
                solar.month == calendar.get(Calendar.MONTH) + 1 &&
                solar.day == calendar.get(Calendar.DATE)) {
            mTvSolar.setText("今天");
        }

    } else {
        mTvSolar.setTextColor(Color.parseColor("#DFDFDF"));
        mTvLunar.setTextColor(Color.parseColor("#DFDFDF"));
        mBtnLucky.setVisibility(View.GONE);
        mLayout.setClickable(false);
        mLayout.setEnabled(false);
    }

}

数据进行对比

173918_mL2n_2438532.png

174016_0RMO_2438532.png

两个月数据对比OK,经过本人仔细测试,可以说是最权威的农历数据了。下面是公司项目中运用。

174334_Pp3D_2438532.png

如果有任何问题请告知我修改,免得发生不愉快的事儿。

项目代码 上传github,请自行下载。

关于作者

转载于:https://my.oschina.net/u/2438532/blog/759597

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值