package alan.jluzh;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
*
* 农历日历。<br>
*
* 将农历从1901年到2100年之间各年、月的大小以及历年节气保存,然后基于这些数据进行计算。<br>
*
* <br>
*
* 新增了几个用于农历的常量属性字段,可以使用get()方法获取日历对应的值;<br>
*
* 农历年、月、日还可以使用set()/add()/roll()方法设置,其他农历属性自动计算;<br>
*
* 另外,还提供了getChinese(int field)方法用于获得农历的中文文字(仅适用于农历属性和星期)。
*
* <ul>
*
* <li>CHINESE_YEAR - 农历年</li>
*
* <li>CHINESE_MONTH - 农历月</li>
*
* <li>CHINESE_DATE - 农历日</li>
*
* <li>CHINESE_SECTIONAL_TERM - 当月的节气</li>
*
* <li>CHINESE_PRINCIPLE_TERM - 当月的中气</li>
*
* <li>CHINESE_HEAVENLY_STEM - 农历年的天干</li>
*
* <li>CHINESE_EARTHLY_BRANCH - 农历年的地支</li>
*
* <li>CHINESE_ANIMAL - 农历年的属相</li>
*
* </ul>
*
* 注意:<br>
*
* 由于Calendar类的设定,公历月份从0起始。所有方法都遵循了这一约定。<br>
*
* 但所有的农历属性从1起始。即使是在Calendar提供的方法中,农历月也是从1起始的,并以负数表示闰月。<br>
*
* clear()方法在某些情况下会导致农历和公历日期不对应或是不能达到预期的重置效果,应尽量避免使用。<br>
*
* 使用getSimpleDateString()获得公历日期字符串时,公历月已经修正;<br>
*
* 使用getSimpleChineseDateString()获得农历日期字符串时,农历闰月以*表示。<br>
*
* <br>
*
*
*
* @author 猪头
*/
public final class ChineseCalendar extends GregorianCalendar {
private static final long serialVersionUID = 8L;
/** 农历年 */
public static final int CHINESE_YEAR = 801;
/** 农历月 */
public static final int CHINESE_MONTH = 802;
/** 农历日 */
public static final int CHINESE_DATE = 803;
/** 当月的节气对应的公历日(前一个节气) */
public static final int CHINESE_SECTIONAL_TERM = 804;
/** 当月的中气对应的公历日(后一个节气) */
public static final int CHINESE_PRINCIPLE_TERM = 805;
/** 天干 */
public static final int CHINESE_HEAVENLY_STEM = 806;
/** 地支 */
public static final int CHINESE_EARTHLY_BRANCH = 807;
/** 农历年的属相(生肖) */
public static final int CHINESE_ANIMAL = 808;
private int chineseYear;
private int chineseMonth; // 1起始,负数表示闰月
private int chineseDate;
private int sectionalTerm; // 当月节气的公历日
private int principleTerm; // 当月中气的公历日
private boolean areChineseFieldsComputed; // 农历日期是否已经经过计算确认
private boolean areSolarTermsComputed; // 节气是否已经经过计算确认
private boolean lastSetChinese; // 最后设置的是不是农历属性
/** 使用当前时间构造一个实例。 */
public ChineseCalendar() {
super();
}
/** 使用指定时间构造一个实例。 */
public ChineseCalendar(Date d) {
super.setTime(d);
}
/** 使用指定时间构造一个实例。 */
public ChineseCalendar(Calendar c) {
this(c.getTime());
}
/** 使用指定公历日期构造一个实例。 */
public ChineseCalendar(int y, int m, int d) {
super(y, m - 1, d);
}
/**
*
* 使用指定日期构造一个实例。
*
*
*
* @param isChinese
*
* 是否为农历日期
*
* @param y
*
* @param m
*
* @param d
*/
public ChineseCalendar(boolean isChinese, int y, int m, int d) {
if (isChinese) {
set(CHINESE_YEAR, y);
set(CHINESE_MONTH, m);
set(CHINESE_DATE, d);
} else {
set(y, m, d);
}
}
public void set(int field, int value) {
computeIfNeed(field);
if (isChineseField(field)) {
// 农历属性
switch (field) {
case CHINESE_YEAR:
chineseYear = value;
break;
case CHINESE_MONTH:
chineseMonth = value;
break;
case CHINESE_DATE:
chineseDate = value;
break;
default:
throw new IllegalArgumentException("不支持的field设置:" + field);
}
lastSetChinese = true;
} else {
// 非农历属性
super.set(field, value);
lastSetChinese = false;
}
areFieldsSet = false;
areChineseFieldsComputed = false;
areSolarTermsComputed = false;
}
public int get(int field) {
computeIfNeed(field);
if (!isChineseField(field)) {
return super.get(field);
}
if (!areChineseFieldsComputed) {
// 计算农历属性
computeChineseFields();
areChineseFieldsComputed = true;
}
switch (field) {
case CHINESE_YEAR:
return chineseYear;
case CHINESE_MONTH:
return chineseMonth;
case CHINESE_DATE:
return chineseDate;
case CHINESE_SECTIONAL_TERM:
return sectionalTerm;
case CHINESE_PRINCIPLE_TERM:
return principleTerm;
case CHINESE_HEAVENLY_STEM:
return (chineseYear - 4) % 10 + 1;
case CHINESE_EARTHLY_BRANCH:
case CHINESE_ANIMAL:
return (chineseYear - 4) % 12 + 1;
default:
throw new IllegalArgumentException("不支持的field获取:" + field);
}
}
public void add(int field, int amount) {
computeIfNeed(field);
if (!isChineseField(field)) {
super.add(field, amount);
lastSetChinese = false;
areChineseFieldsComputed = false;
areSolarTermsComputed = false;
return;
}
switch (field) {
case CHINESE_YEAR:
chineseYear += amount;
break;
case CHINESE_MONTH:
for (int i = 0; i < amount; i++) {
chineseMonth = nextChineseMonth(chineseYear, chineseMonth);
if (chineseMonth == 1) {
chineseYear++;
}
}
break;
case CHINESE_DATE:
int maxDate = daysInChineseMonth(chineseYear, chineseMonth);
for (int i = 0; i < amount; i++) {
chineseDate++;
if (chineseDate > maxDate) {
chineseDate = 1;
chineseMonth = nextChineseMonth(chineseYear, chineseMonth);
if (chineseMonth == 1) {
chineseYear++;
}
maxDate = daysInChineseMonth(chineseYear, chineseMonth);
}
}
default:
throw new IllegalArgumentException("不支持的field:" + field);
}
lastSetChinese = true;
areFieldsSet = false;
areSolarTermsComputed = false;
}
public void roll(int field, int amount) {
computeIfNeed(field);
if (!isChineseField(field)) {
super.roll(field, amount);
lastSetChinese = false;
areChineseFieldsComputed = false;
areSolarTermsComputed = false;
return;
}
switch (field) {
case CHINESE_YEAR:
chineseYear += amount;
break;
case CHINESE_MONTH:
for (int i = 0; i < amount; i++) {
chineseMonth = nextChineseMonth(chineseYear, chineseMonth);
}
break;
case CHINESE_DATE:
int maxDate = daysInChineseMonth(chineseYear, chineseMonth);
for (int i = 0; i < amount; i++) {
chineseDate++;
if (chineseDate > maxDate) {
chineseDate = 1;
}
}
default:
throw new IllegalArgumentException("不支持的field:" + field);
}
lastSetChinese = true;
areFieldsSet = false;
areSolarTermsComputed = false;
}
/**
*
* 获得属性的中文,可以使用的属性字段为DAY_OF_WEEK以及所有农历属性字段。
*
*
*
* @param field
*
* @return
*/
public String getChinese(int field) {
switch (field) {
case CHINESE_YEAR:
return getChinese(CHINESE_HEAVENLY_STEM)
+ getChinese(CHINESE_EARTHLY_BRANCH);
case CHINESE_MONTH:
if (chineseMonth > 0)
return chineseMonthNames[chineseMonth];
else
return "闰" + chineseMonthNames[-chineseMonth];
case CHINESE_DATE:
return chineseDateNames[chineseDate];
case CHINESE_SECTIONAL_TERM:
return sectionalTermNames[get(Calendar.MONTH)];
case CHINESE_PRINCIPLE_TERM:
return principleTermNames[get(Calendar.MONTH)];
case CHINESE_HEAVENLY_STEM:
if (!areSolarTermsComputed) {
computeSolarTerms();
areSolarTermsComputed