由于项目中日历需要加一个农历的功能,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包括转换后的文件也再项目中
公农历转换,数据正确性验证见下一篇博客,农历的那些事儿(二)。
关于作者
- QQ交流群:158506055
- Email: YougaKingWu@gmail.com