文档章节

农历的那些事儿(二)

YougaKing
 YougaKing
发布于 2016/10/17 16:31
字数 1565
阅读 187
收藏 0

综合上篇博文农历的那些事儿(一)获取的农历数据进行公农历转换,同时验证一下数据的正确性。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,请自行下载。

关于作者

© 著作权归作者所有

YougaKing
粉丝 6
博文 7
码字总数 6795
作品 0
西安
程序员
私信 提问
看到百度的那个万年历了吗,咱们也来做一个吧。

原创声明,转载请注明出处。 咱先来看看具体效果吧, https://dorsey.oss-cn-hangzhou.aliyuncs.com/PC/module/module/calendar.html 细心的你会发现好像只有这一天是初几,没有月份,嗯是的...

dorseyCh
2018/07/11
0
0
初级第五旬07— 初级课程第五旬试题

一、修准提法一定要用镜坛吗?答:可以使用准提镜也可以干脆不要镜坛的修法也都可以,可以修法到一定程度后有一定体悟之后,在设置镜坛修法。 二、准提镜坛的功德有哪些?答:1、可以当下净化...

知止内明
2017/12/09
0
0
UbuntuKylin 上的那些定制软件

UbuntuKylin 是Ubuntu 官方与中国的麒麟操作系统合作定制的中文Ubuntu衍生版。既然是中文定制版必然会提供一系列的适合中国人使用的软件应用。 Dash中的在线音乐搜索: 更加简单、方便的音乐搜...

罗树鹏
2013/03/18
3.7K
17
不想知道吗?你的个人数据是如何被企业商业化的

原作者:不愿透露姓名的数据圈资深从业者天降财神。 排版:同样不愿透露姓名的吴妹妹。 前言 关于数据爬虫、数据存储、数据记录等获得各类数据源的方法相信你都知道了。 数据圈不能说的秘密之...

qq58ec71ea677ac
2017/05/25
0
0
MVP那些事儿(5) 中介者模式与MVP的关系

目录 MVP那些事儿(1)……用场景说话 MVP那些事儿(2)……MVC架构初探 MVP那些事儿(3)……在Android中使用MVC(上) MVP那些事儿(4)……在Android中使用MVC(下) MVP那些事儿(5)……中介者模式...

不能用真名
2017/12/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud 笔记之Spring cloud config client

观察者模式它的数据的变化是被动的。 观察者模式在java中的实现: package com.hxq.springcloud.springcloudconfigclient;import org.springframework.context.ApplicationListener;i...

xiaoxiao_go
今天
4
0
CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
今天
4
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
今天
7
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
今天
7
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部