综合上篇博文农历的那些事儿(一)获取的农历数据进行公农历转换,同时验证一下数据的正确性。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;
此日历很简单,一个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); } }
数据进行对比
、
两个月数据对比OK,经过本人仔细测试,可以说是最权威的农历数据了。下面是公司项目中运用。
如果有任何问题请告知我修改,免得发生不愉快的事儿。
项目代码 上传github,请自行下载。
关于作者
- QQ交流群:158506055
- Email: YougaKingWu@gmail.com