文档章节

农历的那些事儿(一)

YougaKing
 YougaKing
发布于 2016/10/17 16:28
字数 1267
阅读 209
收藏 0

由于项目中日历需要加一个农历的功能,github关于农历也蛮多,但大都是集成在View中,换个日历View成本太高,所以copy其中一个农历转换代码集成在项目中。但是发现数据不准确,竟然两个初一连在一起。。。所以准备仔细研究一下农历和公历时如何转换的。

农历不像公历那么有规律,农历的数据貌似都是天文台的观测结果然后整理发布出来的。google的结果只有南京紫金山天文台香港天文台捣鼓这事儿。

网上流传的农历数据都是

像这样的16进制数组,不知道数据的来源是什么,我根据此博客copy出来的数据换算过来总是相差那么一两天,所以我要自己挖掘农历数据源。google、百度都没什么结果,唯一有用的结果就是香港天文台贴出的1901-2100年公农历PDF。

首先先把这200年的pdf下载下来,无赖没有提供打包下载,只能一张张下载。手动点击太慢了吧,此时需要一个类似迅雷的下载软件。

循环地址下载地址

        String url = "http://www.hko.gov.hk/gts/time/calendar/pdf/";
        for (int i = 1901; i < 2101; i++) {
            System.out.println(url + i + ".pdf");
        }

复制到迅雷

下载很快结束了。

此时需要Adobe Acrobat DC软件,这玩意儿是付费软件,不过可以免费使用30天,此软件功能强大,值得拥有。Acrobat 把200张pdf合并成一个pdf,为毛要合并?因为随后需要把pdf转成excel,我可不想一张张转换。合并:工具--合并文件

选择所有pdf文件,合并。然后导出excel就可以,当然也可以导出xml什么。看你怎么方便怎么处理

此时Excel文件怎么才能变成我们想要的数据比如json,或者类似上面的16进制数据呢?很简单读取excel文件进行转换就行了嘛。对于咋android程序猿来说不是难事儿,纯java代码就跑出来嘛。当然你也可以来点有些挑战性的,直接搞个PC应用程序来读取excel。以前闲着没事儿捣鼓一点JavaFx,于是就开搞。

先拖个界面出来

输出路径为读取excel后输出我们想要的数据文件。直接开搞代码

文件选择框

        JFileChooser fileChooser = new JFileChooser();
        //设置选择路径模式
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        //设置对话框标题
        fileChooser.setDialogTitle("请选择Excel文件");
        //设置过滤器
        fileChooser.setFileFilter(new ExcelFilter());

        if (JFileChooser.APPROVE_OPTION == fileChooser.showOpenDialog(null)) {//用户点击了确定
            String path = fileChooser.getSelectedFile().getAbsolutePath();//取得路径选择
            System.out.println("path:" + path);
            try {
                readCalendarExcel(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

获得excel文件对象

FileInputStream fis = new FileInputStream(new File(path));

应该是获取第一张表的意思。。。没深究api的用法,因为这不是重点,能用就行。。。

XSSFWorkbook book = new XSSFWorkbook(fis);
XSSFSheet sheet = book.getSheetAt(0);

然后就是根据表中数据循环读取,此是分析表中数据,由于是pdf转换而来,存在多个表头,每一年有一个人表头。

excel提供数数据是从公历1901-01-01到2100-12-31,相对应的农历是1900-11-11到2100-12-01,表头是公历1-31日:左边第一列是公历的月份。单元格对应的就是农历的日期。

读取单元格内容

List<String> rowArray = new ArrayList<>();
while (cellIterator.hasNext()) {
    Cell cell = cellIterator.next();
    switch (cell.getCellType()) {
        case Cell.CELL_TYPE_STRING:
            String cellString = cell.getStringCellValue();
            if (!cellString.isEmpty()) {
                cellString = cellString.replaceAll("\\s*", "");
                rowArray.add(cellString);
            }
            break;
        case Cell.CELL_TYPE_NUMERIC:
            int cellInt = (int) cell.getNumericCellValue();
            rowArray.add(String.valueOf(cellInt));
            break;
        case Cell.CELL_TYPE_BOOLEAN:
            rowArray.add(String.valueOf(cell.getBooleanCellValue()));
            break;
        default:
    }
}

首先把表头读取出来存入List<Strnig>,不要重复读取做个判断,然后循环读取数据

if (!rowArray.isEmpty() && stringSet.isEmpty() && "公曆日期".equals(rowArray.get(0))) {
    stringSet.addAll(rowArray);
}
if (!rowArray.isEmpty() && rowArray.get(0).endsWith("月")) {
    String solarMonth = rowArray.get(0).substring(0, rowArray.get(0).length() - 1);
    boolean first = true;
    for (int i = 2; i < rowArray.size() - 1; i++) {
        String lunarDay = rowArray.get(i);
        int lunarMonth = 0;
        int leapMonth = 0;
        if (lunarDay.endsWith("月")) {
            if (lunarDay.startsWith("閏")) {
                lunarDay = lunarDay.replace("閏", "");
                lunarMonth = 13;
                leapMonth = mMonths.indexOf(lunarDay.substring(0, lunarDay.indexOf("月")));
            } else {
                lunarMonth = mMonths.indexOf(lunarDay.substring(0, lunarDay.indexOf("月")));
            }
            lunarDay = "初一";
        } else {
            if (!calendarSet.isEmpty())
                lunarMonth = calendarSet.get(calendarSet.size() - 1).lunarMonth;
        }
        String s = lunarDay.substring(0, 1);
        String e = lunarDay.substring(1, 2);
        if (calendarSet.isEmpty()) {
            calendarSet.add(new Calendar(1901, Integer.valueOf(solarMonth), Integer.valueOf(stringSet.get(i - 1)),
                    1900, 11, mExtra.indexOf(s) * 10 + mNumbers.indexOf(e), leapMonth));
        } else {
            Calendar calendar = calendarSet.get(calendarSet.size() - 1);
            int lunarYear, solarYear;
            if ("正月".equals(rowArray.get(i))) {
                lunarYear = calendar.lunarYear + 1;
            } else {
                lunarYear = calendar.lunarYear;
            }
            if ("1月".equals(rowArray.get(0)) && calendar.solarMonth == 12 && first) {
                solarYear = calendar.solarYear + 1;
                first = false;
            } else {
                solarYear = calendar.solarYear;
            }
            calendarSet.add(new Calendar(solarYear, Integer.valueOf(solarMonth), Integer.valueOf(stringSet.get(i - 1)),
                    lunarYear, lunarMonth, (mExtra.indexOf(s) == -1) ? 20 :
                    mExtra.indexOf(s) * 10 + (mExtra.indexOf(s) == 3 ? 0 : mNumbers.indexOf(e)), leapMonth));
        }
    }
}

然后再拆分成单独年月,转换成16进制数组。然后写入文件输出,OK,运行看看。

OK,转换完毕,去看下结果吧。

此时只需要验证一下OK了。JavaFxReadExcel 读取excel转换都上传github上,有兴趣请自行下载。农历pdf和excel包括转换后的文件也再项目中

公农历转换,数据正确性验证见下一篇博客,农历的那些事儿(二)

关于作者

© 著作权归作者所有

共有 人打赏支持
YougaKing
粉丝 5
博文 7
码字总数 6795
作品 0
西安
程序员
看到百度的那个万年历了吗,咱们也来做一个吧。

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

dorseyCh
07/11
0
0
谈谈历法知识

新春期间,我忍不住又想到了一个老问题: 为什么每年春节的日期都不一样? 从2007年到2010年,春节的日期分别为2月18日、2月7日、1月26日、2月14日和2月3日。为什么会这样?到底是怎么计算的...

阮一峰
2009/01/26
0
0
MVP那些事儿(1) 用场景说话

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

叫我丹尼尔
2017/12/02
0
0
UbuntuKylin 上的那些定制软件

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

罗树鹏
2013/03/19
3.5K
17
书荐——《项目管理那些事儿》

跟《项目经理成长手记》不同,这是一本类似于“最佳实践/解决方案”的书。这么说并不是说这真的是最佳的那个实践,而是说这本书记录的是:那个人(驱动者),在那个时候(环境/上下文),为了...

悟空太多啦
01/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

nginx模块学习六 add_header 跨域访问

语法 Syntax: add_header name value [always];Default: --Context:http,server,location,if in location 例:/etc/nginx/conf.d/default.conf server {    listen       80; ......

Romanceling
今天
0
0
SpringBoot初探

#SpringBoot初探 三种创建SpringBoot项目的方式: 第一种:使用IDEA创建maven项目,选择maven-archetype-quickstart; 第二种:使用IDEA创建Spring Initializer,选择web组件; 第三种:使用...

向码而生
今天
2
0
IO

JAVA中IO技术:BIO、NIO、AIO 1、同步异步、阻塞非阻塞概念 同步和异步是针对应用程序和内核的交互而言的。 阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方...

DemonsI
今天
0
0
org.apache.commons 常用工具类

一. org.apache.commons.io.IOUtils closeQuietly 关闭一个IO流、socket、或者selector且不抛出异常。通常放在finally块。 toString 转换IO流、 Uri、 byte[]为String。 copy IO流数据复制,...

sprouting
今天
0
0
linux使用Inotify监控目录或者文件状态变更

基本概念: Inotify 是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。 需求: 1.有一个文件采集进程,...

mickelfeng
今天
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部