该版本是一次较大的升级,农历相比公历复杂太多(真佩服古人的智慧),虽然有规律,但涉及到的取舍、近似的感念太多,况且本身的概念就已经很多了,我在网上也是查阅了很多的资料,虽然找到一些计算的方法,但都有些计算缺陷,后来才终于找到“寿天星文历”:一个十分精准的万年历。虽然它的功能十分强大,但相对的涉及到的计算也很多,逻辑和思路都相当的复杂了,维护成本很大,有时候项目中并不一定要用到这么强大的农历,因此该版本目前仅提供了农历的一些基本功能,在下一版本中,我会引入“寿天星文历”,以适合更多的大众需求。
源码:
DateUtil类
新加入代码:(其他代码请参见java日期工具类DateUtil-续一)
[java] view plaincopy
/**
* 获取简单农历对象
* @param date 日期字符串
* @return 简单农历对象
*/
public static SimpleLunarCalendar getSimpleLunarCalendar(String date) {
return new SimpleLunarCalendar(DateUtil.StringToDate(date));
}
/**
* 获取简单农历对象
* @param date 日期
* @return 简单农历对象
*/
public static SimpleLunarCalendar getSimpleLunarCalendar(Date date) {
return new SimpleLunarCalendar(date);
}
SimpleLunarCalendar类
[java] view plaincopy
package com.util;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class SimpleLunarCalendar {
/** 最小时间1900-1-31*/
private final static long minTimeInMillis = -2206425952001L;
/** 最大时间2099-12-31 */
private final static long maxTimeInMillis = 4102416000000L;
/**
* 农历年数据表(1900-2099年)<br>
* <br>
* 每个农历年用16进制来表示,解析时转为2进制<br>
* 前12位分别表示12个农历月份的大小月,1是大月,0是小月<br>
* 最后4位表示闰月,转为十进制后即为闰月值,例如0110,则为闰6月
*/
private final static int[] lunarInfo = { 0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af, 0x9ad0, 0x55d2, 0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd295, 0xb54f, 0xd6a0, 0xada2, 0x95b0,
0x4977, 0x497f, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970, 0x6566, 0xd4a0, 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f, 0xd4a0, 0xd8a6,
0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2, 0xa950, 0xb557, 0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573, 0x52bf, 0xa9a8, 0xe950, 0x6aa0, 0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570,
0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0, 0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6, 0x95bf, 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, 0x5176, 0x52bf, 0xa930, 0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530, 0x5aa0, 0x76a3, 0x96d0, 0x4afb,
0x4ad0, 0xa4d0, 0xd0b6, 0xd25f, 0xd520, 0xdd45, 0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0, 0x4b63, 0x937f, 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f,
0x6b20, 0xa6c4, 0xaaef, 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, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260, 0xe968, 0xd520, 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252, 0xd520 };
/** 十二生肖 */
private final static String[] Animals = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
/** 农历中文字符串一 */
private final static String[] lunarString1 = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
/** 农历中文字符串二 */
private final static String[] lunarString2 = { "初", "十", "廿", "卅", "正", "腊", "冬", "闰" };
/** 农历年 */
private int lunarYear;
/** 农历月 */
private int lunarMonth;
/** 农历日 */
private int lunarDay;
/** 是否是闰月 */
private boolean isLeap;
/** 是否是闰年 */
private boolean isLeapYear;
/** 某农历月的最大天数 */
private int maxDayInMonth = 29;
/**
* 通过 TimeInMillis 构建农历信息
* @param TimeInMillis
*/
public SimpleLunarCalendar(long TimeInMillis) {
this.init(TimeInMillis);
}
/**
* 通过 Date 对象构建农历信息
* @param date 指定日期对象
*/
public SimpleLunarCalendar(Date date) {
if (date == null)
date = new Date();
this.init(date.getTime());
}
/**
* 农历初始化
* @param timeInMillis 时间毫秒数
*/
private void init(long timeInMillis) {
if (timeInMillis > minTimeInMillis && timeInMillis < maxTimeInMillis) {
// 以农历为1900年正月一日的1900-1-31作为起始日期
Calendar baseDate = new GregorianCalendar(1900, 0, 31);
// 距离起始日期间隔的总天数
long offset = (timeInMillis - baseDate.getTimeInMillis()) / 86400000;
// 默认农历年为1900年,且由此开始推算农历年份
this.lunarYear = 1900;
int daysInLunarYear = SimpleLunarCalendar.getLunarYearDays(this.lunarYear);
// 递减每个农历年的总天数,确定农历年份
while (this.lunarYear < 2100 && offset >= daysInLunarYear) {
offset -= daysInLunarYear;
daysInLunarYear = SimpleLunarCalendar.getLunarYearDays(++this.lunarYear);
}
// 获取该农历年的闰月月份
int leapMonth = SimpleLunarCalendar.getLunarLeapMonth(this.lunarYear);
// 没有闰月则不是闰年
this.isLeapYear = leapMonth > 0;
// 默认农历月为正月,且由此开始推荐农历月
int lunarMonth = 1;
// 是否递减农历月
boolean isDecrease = true;
boolean isLeap = false;
int daysInLunarMonth = 0;
// 递减每个农历月的总天数,确定农历月份
while (lunarMonth < 13 && offset > 0) {
if (isLeap && !isDecrease) {
// 该农历年闰月的总天数
daysInLunarMonth = SimpleLunarCalendar.getLunarLeapDays(this.lunarYear);
isDecrease = true;
} else {
// 该农历年正常农历月份的天数
daysInLunarMonth = SimpleLunarCalendar.getLunarMonthDays(this.lunarYear, lunarMonth);
}
if (offset < daysInLunarMonth) {
break;
}
offset -= daysInLunarMonth;
// 如果农历月是闰月,则不递增农历月份
if (leapMonth == lunarMonth && isLeap == false) {
isDecrease = false;
isLeap = true;
} else {
lunarMonth++;
}
}
// 如果daysInLunarMonth为0则说明默认农历月即为返回的农历月
this.maxDayInMonth = daysInLunarMonth != 0 ? daysInLunarMonth : SimpleLunarCalendar.getLunarMonthDays(this.lunarYear, lunarMonth);
this.lunarMonth = lunarMonth;
this.isLeap = (lunarMonth == leapMonth && isLeap);
this.lunarDay = (int) offset + 1;
}
}
/**
* 获取某农历年的总天数
* @param lunarYear 农历年份
* @return 该农历年的总天数
*/
private static int getLunarYearDays(int lunarYear) {
// 按小月计算,农历年最少有12 * 29 = 348天
int daysInLunarYear = 348;
// 遍历前12位
for (int i = 0x8000; i > 0x8; i >>= 1) {
// 每个大月累加一天
daysInLunarYear += ((SimpleLunarCalendar.lunarInfo[lunarYear - 1900] & i) != 0) ? 1 : 0;
}
// 加上闰月天数
daysInLunarYear += SimpleLunarCalendar.getLunarLeapDays(lunarYear);
return daysInLunarYear;
}
/**
* 获取某农历年闰月的总天数
* @param lunarYear 农历年份
* @return 该农历年闰月的总天数,没有闰月返回0
*/
private static int getLunarLeapDays(int lunarYear) {
// 下一年最后4bit为1111,返回30(大月)
// 下一年最后4bit不为1111,返回29(小月)
// 若该年没有闰月,返回0
return SimpleLunarCalendar.getLunarLeapMonth(lunarYear) > 0 ? ((SimpleLunarCalendar.lunarInfo[lunarYear - 1899] & 0xf) == 0xf ? 30 : 29) : 0;
}
/**
* 获取某农历年闰月月份
* @param lunarYear 农历年份
* @return 该农历年闰月的月份,没有闰月返回0
*/
private static int getLunarLeapMonth(int lunarYear) {
// 匹配后4位
int leapMonth = SimpleLunarCalendar.lunarInfo[lunarYear - 1900] & 0xf;
// 若最后4位全为1或全为0,表示没闰
leapMonth = (leapMonth == 0xf ? 0 : leapMonth);
return leapMonth;
}
/**
* 获取某农历年某农历月份的总天数
* @param lunarYear 农历年份
* @param lunarMonth 农历月份
* @return 该农历年该农历月的总天数
*/
private static int getLunarMonthDays(int lunarYear, int lunarMonth) {
// 匹配前12位代表的相应农历月份的大小月,大月30天,小月29天
int daysInLunarMonth = ((SimpleLunarCalendar.lunarInfo[lunarYear - 1900] & (0x10000 >> lunarMonth)) != 0) ? 30 : 29;
return daysInLunarMonth;
}
/**
* 返回指定数字的农历年份表示字符串
* @param lunarYear 农历年份(数字,0为甲子)
* @return 农历年份字符串
*/
private static String getLunarYearString(int lunarYear) {
String lunarYearString = "";
String year = String.valueOf(lunarYear);
for (int i = 0; i < year.length(); i++) {
char yearChar = year.charAt(i);
int index = Integer.parseInt(String.valueOf(yearChar));
lunarYearString += lunarString1[index];
}
return lunarYearString;
}
/**
* 返回指定数字的农历月份表示字符串
* @param lunarMonth 农历月份(数字)
* @return 农历月份字符串 (例:正)
*/
private static String getLunarMonthString(int lunarMonth) {
String lunarMonthString = "";
if (lunarMonth == 1) {
lunarMonthString = SimpleLunarCalendar.lunarString2[4];
} else {
if (lunarMonth > 9)
lunarMonthString += SimpleLunarCalendar.lunarString2[1];
if (lunarMonth % 10 > 0)
lunarMonthString += SimpleLunarCalendar.lunarString1[lunarMonth % 10];
}
return lunarMonthString;
}
/**
* 返回指定数字的农历日表示字符串
* @param lunarDay 农历日(数字)
* @return 农历日字符串 (例: 廿一)
*/
private static String getLunarDayString(int lunarDay) {
if (lunarDay < 1 || lunarDay > 30)
return "";
int i1 = lunarDay / 10;
int i2 = lunarDay % 10;
String c1 = SimpleLunarCalendar.lunarString2[i1];
String c2 = SimpleLunarCalendar.lunarString1[i2];
if (lunarDay < 11)
c1 = SimpleLunarCalendar.lunarString2[0];
if (i2 == 0)
c2 = SimpleLunarCalendar.lunarString2[1];
return c1 + c2;
}
/**
* 取农历年生肖
* @return 农历年生肖(例:龙)
*/
public String getAnimalString() {
if (lunarYear == 0)
return null;
return SimpleLunarCalendar.Animals[(this.lunarYear - 4) % 12];
}
/**
* 返回农历日期字符串
* @return 农历日期字符串
*/
public String getDayString() {
if (lunarDay == 0)
return null;
return SimpleLunarCalendar.getLunarDayString(this.lunarDay);
}
/**
* 返回农历日期字符串
* @return 农历日期字符串
*/
public String getMonthString() {
if (lunarMonth == 0)
return null;
return (this.isLeap() ? "闰" : "") + SimpleLunarCalendar.getLunarMonthString(this.lunarMonth);
}
/**
* 返回农历日期字符串
* @return 农历日期字符串
*/
public String getYearString() {
if (lunarYear == 0)
return null;
return SimpleLunarCalendar.getLunarYearString(this.lunarYear);
}
/**
* 返回农历表示字符串
* @return 农历字符串(例:甲子年正月初三)
*/
public String getDateString() {
if (lunarYear == 0)
return null;
return this.getYearString() + "年" + this.getMonthString() + "月" + this.getDayString() + "日";
}
/**
* 农历年是否是闰月
* @return 农历年是否是闰月
*/
public boolean isLeap() {
return isLeap;
}
/**
* 农历年是否是闰年
* @return 农历年是否是闰年
*/
public boolean isLeapYear() {
return isLeapYear;
}
/**
* 当前农历月是否是大月
* @return 当前农历月是大月
*/
public boolean isBigMonth() {
return this.getMaxDayInMonth() > 29;
}
/**
* 当前农历月有多少天
* @return 天数
*/
public int getMaxDayInMonth() {
if (lunarYear == 0)
return 0;
return this.maxDayInMonth;
}
/**
* 农历日期
* @return 农历日期
*/
public int getDay() {
return lunarDay;
}
/**
* 农历月份
* @return 农历月份
*/
public int getMonth() {
return lunarMonth;
}
/**
* 农历年份
* @return 农历年份
*/
public int getYear() {
return lunarYear;
}
}
说明:该农历核心算法是从网上寻找而来,但我进行了完善,适用的年份为:1900年——2099年,我去掉了不精准的部分(如天干地支),目前该简单农历只能满足显示阴历信息,适合需要较为简单的用户适用。另外若想支持的年限范围更广,则需要修改lunarInfo(农历年数据表),并做适当的调整(默认农历年)即可。
- java日期工具类DateUtil
一名优秀的程序员,不仅需要有着丰富解决问题的方案,还需要的便是代码的沉淀,这不仅有助于自己快速的开发程序,也有利于保证程序的健壮.那如何才能沉淀自己的”代码“呢?从自己编写util开始其实就是一个不错 ...
- java日期工具类DateUtil-续一
上篇文章中,我为大家分享了下DateUtil第一版源码,但就如同文章中所说,我发现了还存在不完善的地方,所以我又做了优化和扩展. 更新日志: 1.修正当字符串日期风格为MM-dd或yyyy-MM时,若 ...
- Java日期工具类,Java时间工具类,Java时间格式化
Java日期工具类,Java时间工具类,Java时间格式化 >>>>>>>>>>>>>>>>>&g ...
- 日期工具类 - DateUtil.java
日期工具类,提供对日期的格式化和转换方法.获取区间日期.指定日期.每月最后一天等. 源码如下:(点击下载 -DateUtil.java.commons-lang-2.6.jar ) import ja ...
- java 日期工具类DateUtils
日期工具类DateUtils CreateTime--2017年5月27日08:48:00Author:Marydon DateUtils.java-对日期类的进一步封装 import java. ...
- JAVA 日期工具类的总结
一般,在项目中,我们会会经常使用到日期的各种方式的处理,在各个业务逻辑操作中,都需要相关的日期操作,因此,实现项目中的日期工具类的提出,还是十分重要的,下面,就项目中常用到的日期的相关操作方式,做了一 ...
- Java 日期工具类(日期,月份加减等)--转
package util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.C ...
- java 日期工具类
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; imp ...
- Java日期工具类DateUtils详解(转)
jar包 appache下的 common-lang3 一. 对指定的日期新增年.月.周.日.小时.分钟.秒.毫秒 public static Date addDays(Date date, int ...
随机推荐
- openGL 坐标系的互相转换
openGL坐标系包括旋转,平移,缩放被塞在一个矩阵里面. 坐标系之间的转换基础是矩阵的运算. 每个矩阵代表的坐标系,就是是原点坐标系通过旋转.平移,缩放得到的坐标系. 当一个矩阵右乘一个向量或是还有 ...
- Android网络开发之WIFI
WIFI全称Wireless Fidelity, 又称802.11b标准.WIFI联盟成立于1999年,当时的名称叫做Wireless Ethernet Compatibility Alliance( ...
- 利用Windows 2003系统中实现两个网段的路由
利用Windows 2003系统中实现两个网段的路由 当一个局域网中存在两个以上网段时,分属于不同网段内的主机彼此互不可见.为了解决这个问 题,就必须在不同的网段之间设置路由器.如果花费上万元资金购买 ...
- poj-----Ultra-QuickSort(离散化+树状数组)
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 38258 Accepted: 13784 ...
- 利用HttpWebRequest模拟表单提交 JQuery 的一个轻量级 Guid 字符串拓展插件. 轻量级Config文件AppSettings节点编辑帮助类
利用HttpWebRequest模拟表单提交 1 using System; 2 using System.Collections.Specialized; 3 using System.IO; ...
- 搭建Go调试环境(LiteIDE)
安装及配置LiteIDE 将 liteidex32.1.windows-qt5.zip解压到D:\即完成安装. 设置编辑环境 查看->编辑当前环境,确认GOROOT变 ...
- 转: Python中的os.path.dirname(__file__)
(1).当"print os.path.dirname(__file__)"所在脚本是以完整路径被运行的, 那么将输出该脚本所在的完整路径,比如: ...
- http请求No peer certificate的解决方法
不少同学在做HTTP请求新浪授权或新浪数据的时候会出现 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate的异常.现给出解决方法 ...
- 什么是BGP线路?什么是BGP机房?
BGP(Border Gateway Protocol,边界网关协议)主要用于互联网AS(自治系统)之间的互联.BGP的最主要功能在于控制路由的传播和选择最好的路由.BGP是Internetproje ...
- MySQL索引经验
在数据库表中,使用索引可以大大提高查询速度. 假如我们创建了一个testIndex表:create TABLE testIndex(i_testID INT NOT NULL,vc_Name VARC ...