历元(第0日):公历1899年12月22日 星期五 vs 农历己亥年(光绪二十五年)十一月二十日 甲子 冬至
农历范围:农历庚子年腊月初一(1900年1月1日)至 (第六轮)壬寅年腊月三十(2222年1月31日)
package cn.dairy;
import java.util.Calendar;
import java.util.Objects;
import java.util.TimeZone;
import static cn.dairy.Nongli.Branch.BRANCH;
import static cn.dairy.Nongli.Info.info;
import static cn.dairy.Nongli.Info.values;
import static cn.dairy.Nongli.Stem.STEM;
import static cn.dairy.SolarTerm.solarTerm;
class Nongli{
private static final String DIGIT = "初一二三四五六七八九十正腊闰年月时刻更点廿星期日
int day;
private int weekday;
int year;
int month;
int date;
int length;
Nongli(){
Calendar da = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
year = da.get(Calendar.YEAR);
month = da.get(Calendar.MONTH) + 1;
date = da.get(Calendar.DATE);
day = Gregorian.day(year, month, date);
while(day(year, month) > day) {
month--;
if(month < 1) month = months(--year);
}
date = day - day(year, month);
length = length(year, month);
weekday = weekday(day);
}
Nongli(int day){
this.day = day;
year = 1899 + day/384;
while(day > day(year + 1)) year++;
month = (day - day(year))/30;
while(day > day(year, month + 1)) month++;
length = length(year, month);
date = day - day(year, month);
weekday = weekday(day);
}
static String CALENDAR_TEXT(){
return CALENDAR_TEXT(day());
}
static String FESTIVAL_WEEKDAY(){
return FESTIVAL_WEEKDAY(day());
}
private static String FESTIVAL_WEEKDAY(int day){
if(FESTIVAL(day) != null) return FESTIVAL(day);
else if(Gregorian.FESTIVAL(day) != null) return Gregorian.FESTIVAL(day);
else return WEEKDAY(day);
}
static String CALENDAR_TEXT(int day){
Nongli nd = new Nongli(day);
if(FESTIVAL(day) != null) return FESTIVAL(day);
else if(Gregorian.FESTIVAL(day) != null) return Gregorian.FESTIVAL(day);
else if(nd.date == 1) return MONTH(nd.year, nd.month);
else return DATE(nd.date);
}
static String DATE_TEXT(){
return DATE_TEXT(day());
}
private static String DATE_TEXT(int day){ //全字段日期
Nongli nd = new Nongli(day);
return YEAR(nd.year) + MONTH(nd.year, nd.month) + DATE(nd.date) + " " + DAY(nd.date);
}
private static int day(){
return new Nongli().day;
}
private static int day(int year){
return day(year, 0);
}
private static int day(int year, int month){
return day(year, month, 0);
}
private static int day(int year, int month, int date){
int metonic = year/19 - 100;
while(month > 1) date += length(year, --month);
while(year > year - year%19) date += length(--year);
while(metonic > 0) date += values(--metonic).length;
return year > 1899 ? date + 39 : date - 316;
}
static int weekday(){
return weekday(day());
}
static int weekday(int day){
return (day + 5)%7;
}
private static int leap(int year){ //默冬章十九年,逢0、3、6、9、11、14、17年闰,个别例外
int metonic = year/19 - 100;
switch(year%19) {
case 0:
if(metonic == 15) return 0;
else return values(metonic).y0;
case 3:
return values(metonic).y3;
case 6:
return values(metonic).y6;
case 9:
if(year == 4) return 0;
else return values(metonic).y9;
case 11:
return values(metonic).y11;
case 14:
return values(metonic).y14;
case 17:
return values(metonic).y17;
default:
if(year == 1984) return 10;
else if(year == 2186) return 2;
else return 0;
}
}
private static int months(int year){
return leap(year) > 0 ? 13 : 12;
}
private static int length(int year){
int info = info(year) >>> 13;
if(leap(year) > 0) return 381 + info;
else return 351 + info;
}
private static int length(int year, int month){
while(month > months(year)) month -= months(year++);
while(month < 1) month += months(--year);
int info = 1 & (info(year) >>> (month - 1));
return 30 - info;
}
static String TEXT(int i){
return String.valueOf(DIGIT.charAt(i));
}
static String TEXT(int i, int j){
return TEXT(i) + TEXT(j);
}
static String TEXT(int i, int j, int k){
return TEXT(i, j) + TEXT(k);
}
static String TEXT(int i, int j, int k, int l){
return TEXT(i, j, k) + TEXT(l);
}
static String FESTIVAL(){
return FESTIVAL(day());
}
private static String FESTIVAL(int day){
Nongli nd = new Nongli(day);
String ND = MONTH(nd.year, nd.month) + DATE(nd.date);
String FESTIVAL = solarTerm(day);
for(Festival f : Festival.values())
if(Objects.equals(ND, f.date)) FESTIVAL = f.name();
if(day == Nongli.day(nd.year + 1)) FESTIVAL = "除夕";
return FESTIVAL;
}
static boolean IS_REST_DAY(){
return IS_REST_DAY(day());
}
private static boolean IS_REST_DAY(int day){
Nongli nd = new Nongli(day);
String ND = MONTH(nd.year, nd.month) + DATE(nd.date);
boolean IS_REST_DAY = (nd.weekday%6 == 0) || SolarTerm.IS_REST_DAY(day);
for(Festival f : Festival.values())
if(Objects.equals(ND, f.date) && f.isRestDay) IS_REST_DAY = true;
if(day == Nongli.day(nd.year + 1)) IS_REST_DAY = true;
return IS_REST_DAY;
}
static String WEEKDAY(){
return WEEKDAY(day());
}
static String WEEKDAY(int day){
int weekday=weekday(day);
if(weekday(day) == 0) return DIGIT.substring(21, 24);
else return DIGIT.substring(21, 23) + TEXT(weekday);
}
static String DATE(int date){
if(date%10 == 0) return date > 10 ? TEXT(date/10, 10) : TEXT(0, date);
else return date < 30 ? TEXT(date - date%10, date%10) : TEXT(date/10, 10, date%10);
}
private static String MONTH(int year, int month){
int leap = Nongli.leap(year);
StringBuilder s = new StringBuilder();
if(month - 1 == leap && leap > 0) s = s.append(TEXT(13));
if(month > leap && leap > 0) month--;
if(month == 1) s = s.append(TEXT(11));
else if(month == 11) s = s.append(TEXT(10, 1));
else s = s.append(TEXT(month));
return s.append(TEXT(15)).toString();
}
private static String YEAR(int year){
return STEM(year - 4) + BRANCH(year - 4) + TEXT(14);
}
private static String DAY(int day){
return STEM(day) + BRANCH(day);
}
enum Info{ //农历数据
椭梭腚䦵榴蕤檩檕蒦梭梭據據斴歔浒誔璖艖(8, 5, 4, 2, 6, 5, 2, 6940),
扭慭拚斲斩浉詊椫腖慭慭務櫔薨浉蒥椫然梶(7, 5, 4, 2, 6, 5, 3, 6939),
楬敬櫔疤蕒檕蒪摛撶敬敪櫒櫉蕒橕橍艚抵抵(7, 6, 4, 2, 7, 5, 3, 6940),
啪蕨檩畊蔪椭煚腚䦵獨蕤檥檕蒖梭播據榴斴(8, 6, 4, 3, 7, 5, 4, 6939),
歔浒誒璖艖扭拚拚斲斩浉穊褪腖慭慭拙喩薨(8, 6, 4, 10, 6, 5, 3, 6940),
欩沥褪椶梶楬櫔櫔薤檥檕蒪摛撶敬敪櫒畒蕊(8, 5, 4, 2, 7, 5, 4, 6940),
橕樭艚抵抵捪櫑檩蕊橍椭煚棚榴獨荔檥甩蒖(9, 6, 4, 2, 6, 5, 3, 6940),
梭播據斴斴歔窤詒蒕播扭拚拚斪歒歉詊牕脶(11, 6, 5, 2, 7, 5, 3, 6940),
慭慭拙喩薨檩礪袪梶煬楬櫔櫔薤檥檕蒚梵撵(8, 6, 4, 3, 7, 5, 4, 6939),
敪敪櫒畒蕊橕瑚艚抵捪捩暱檩蕊橍椭腚况楴(8, 6, 4, 3, 7, 5, 4, 6939),
獨歔誤甩蒕梭棚撺整此歔皤詒葕播扭拚斴斪(8, 6, 4, 2, 7, 5, 4, 6940),
歒歉襊牕脶慭拙拕斩歉檩甪蒪梵煬楪櫔疨蕤(9, 6, 4, 3, 7, 5, 4, 6940),
檥檕蒚梵撵擪櫔檲蕒檕橕瑚艚抵拪择暱畒蔩(11, 6, 5, 2, 7, 5, 4, 6940),
橋椭腚兵楴苨嚩蚤蔥椫梫梺撺整櫔歒蚔璥艕(11, 6, 5, 3, 7, 6, 4, 6940),
撫扝抺敪敪櫒畒蕊艕扛慝抹抵敩櫉檥蔪椵梭(10, 6, 5, 3, 7, 6, 4, 6939),
腚劵檴畨蕔檥甪蒚梭敚據斴暲蕒檕橋葖播扭(2, 6, 5, 3, 7, 6, 4, 6940),
拚拙暩浒蔩椫牖脶慭務櫘疨蚤蔥椫梫梶楬敬(9, 6, 4, 3, 7, 5, 4, 6939);
int length;
private int y0, y3, y6, y9, y11, y14, y17;
Info(int y0, int y3, int y6, int y9, int y11, int y14, int y17, int length){
this.y0 = y0;
this.y3 = y3;
this.y6 = y6;
this.y9 = y9;
this.y11 = y11;
this.y14 = y14;
this.y17 = y17;
this.length = length;
}
static int info(int year){
int metonic = year/19 - 100;
if(year == 1899) return 0x852A;
else return values(metonic).name().charAt(year%19);
}
static Info values(int i){
return values()[i];
}
}
enum Stem{
甲, 乙, 丙, 丁, 戊, 己, 庚, 辛, 壬, 癸;
static String STEM(int i){
return values()[i%10].name();
}
static Stem values(int i){
return values()[i%10];
}
Stem add(int i){
return values()[(ordinal() + i)%10];
}
}
enum Branch{
子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥;
static String BRANCH(int i){
return values()[i%12].name();
}
static Branch values(int i){
return values()[i%12];
}
Branch add(int i){
return values()[(ordinal() + i)%12];
}
}
private enum Festival{
春节("正月初一", true),
初二("正月初二", true),
元宵("正月十五", false),
花朝节("二月初二", false),
上巳("三月初三", false),
清明("清明当日", true),
佛诞("四月初八", false),
端午("五月初五", true),
七夕("七月初七", false),
中元("七月十五", false),
中秋("八月十五", true),
重阳("九月初九", false),
十月朝("十月初一", false),
下元节("十月十五", false),
腊八节("腊月初八", false),
小年("腊月廿三", false);
String date;
boolean isRestDay;
Festival(String date, boolean isRestDay){
this.date = date;
this.isRestDay = isRestDay;
}
}
}
package cn.dairy;
import static cn.dairy.Nongli.*;
import static cn.dairy.Nongli.Branch.戌;
import static cn.dairy.Nongli.Branch.未;
import static cn.dairy.Nongli.TEXT;
import static cn.dairy.Nongli.Stem.丙;
import static cn.dairy.Nongli.Stem.壬;
import static cn.dairy.Nongli.Stem.庚;
import static cn.dairy.Nongli.Stem.戊;
enum SolarTerm{
冬至(0, 8, 56),
小寒(15, 2, 6),
大寒(29, 19, 34),
立春(44, 13, 53),
雨水(59, 10, 2),
惊蛰(74, 8, 23),
春分(89, 9, 40),
清明(104, 13, 53),
谷雨(119, 21, 27),
立夏(135, 7, 55),
小满(150, 21, 17),
芒种(166, 12, 38),
夏至(182, 5, 39),
小暑(197, 23, 9),
大暑(213, 16, 35),
立秋(229, 8, 49),
处暑(244, 23, 19),
白露(260, 11, 16),
秋分(275, 20, 20),
寒露(291, 2, 13),
霜降(306, 4, 56),
立冬(321, 4, 41),
小雪(336, 1, 50),
大雪(350, 20, 58);
int day;
int hour;
int minute;
boolean isRestDay;
private Stem stem;
private Branch branch;
SolarTerm(int day, int hour, int minute){
this.day = day;
this.hour = hour;
this.minute = minute;
this.isRestDay = ordinal() == 7;
this.stem = Stem.values(day);
this.branch = Branch.values(day);
}
static int SolarYear(int day){
int year = day/366;
while(day >= 冬至.day(year + 1)) year++;
return year;
}
static boolean IS_REST_DAY(int day){
int year=SolarYear(day);
return day==清明.day(year);
}
static String solarTerm(int day){
int year = SolarYear(day);
String solar = null;
if(day == 冬至.day(105, year)) solar = "寒食";
if(day == 冬至.day(戌, 3, year)) solar = "腊日";
if(day == 立春.day(戊, 5, year)) solar = "春社";
if(day == 立秋.day(戊, 5, year)) solar = "秋社";
if(day == 夏至.day(庚, 3, year)) solar = "头伏";
if(day == 夏至.day(庚, 4, year)) solar = "中伏";
if(day == 立秋.day(庚, year)) solar = "末伏";
if(day == 芒种.day(丙, year)) solar = "入梅";
if(day == 小暑.day(未, year)) solar = "出梅";
for(int i = 0; i < 9; i++) if(day - 冬至.day(壬, year) == 9*i) solar = TEXT(i + 1, 9); //数九
for(SolarTerm s : values()) if(s.day(year) == day) solar = s.name();
return solar;
}
int minute(int year){
year -= 1900;
int second = 160 + 613*year;
return minute + 48*year + second/800;
}
int hour(int year){
return hour + 5*(year - 1900) + minute(year)/60;
}
int day(int year){
return day + 365*(year - 1900) + hour(year)/24;
}
Stem stem(int year){
return stem.add(5*year + hour(year)/24);
}
Branch branch(int year){
return branch.add(5*year + hour(year)/24 - 8);
}
int day(Stem stem, int year){
int δ = stem.compareTo(stem(year));
return day(year) + δ + (δ < 0 ? 10 : 0);
}
int day(int num, int year){
return day(year) + num - 1;
}
int day(Stem stem, int num, int year){
return day(stem, year) + 10*num - 10;
}
int day(Branch branch, int year){
int δ = branch.compareTo(branch(year));
return day(year) + δ + (δ < 0 ? 12 : 0);
}
int day(Branch branch, int num, int year){
return day(branch, year) + 12*num - 12;
}
}
package cn.dairy;
import java.util.Calendar;
import java.util.TimeZone;
import static cn.dairy.Time.FORMAT;
import static java.lang.String.valueOf;
class Gregorian{
private final static String Month = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
int day;
int year;
int month;
int date;
int length;
static String DATE_TEXT(){
return DATE_TEXT(day());
}
static String DATE_TEXT(int day){
Gregorian gd=new Gregorian(day);
return valueOf(gd.year) + "-" + FORMAT(gd.month) + "-" + FORMAT(gd.date);}
Gregorian(int day){
this.day = day;
year = day/366;
while(day > day(year + 1)) year++;
month = 1;
while(day > day(year, month + 1)) month++;
date = day - day(year, month);
length = length(year, month);
}
Gregorian(){
Calendar da = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
year = da.get(Calendar.YEAR);
month = da.get(Calendar.MONTH) + 1;
date = da.get(Calendar.DATE);
day = day(year, month, date);
length = length(year, month);
}
static int day(){
return new Gregorian().day;
}
private static int day(int year){
return day(year, 0);
}
private static int day(int year, int month){
return day(year, month, 0);
}
static int day(int year, int month, int date){
while(month > 1) date += length(year, --month);
while(year > 1899) date += length(--year);
return date - 356;
}
private static int length(int year, int month){
if(month == 2) return 28 + leap(year);
else return 31 - (month - 1)%7%2;
}
private static int length(int year){
return 365 + leap(year);
}
private static int leap(int year){
return year%4 == 0 ? year%100 == 0 ? year%400 == 0 ? 1 : 0 : 1 : 0;
}
private String MONTH(int month){
return Month.substring(3*month - 3, 3*month);
}
static String FESTIVAL(){
return FESTIVAL(day());
}
static String FESTIVAL(int day){
String FESTIVAL=null;
Gregorian gd = new Gregorian(day);
int da = 100*gd.month + gd.date;
for(Festival f : Festival.values())
if(da==f.date) FESTIVAL = f.name();
if(Nongli.weekday(day) == 0) {
if(507 < da && da < 515) FESTIVAL = "母亲节";
if(614 < da && da < 622) FESTIVAL = "父亲节";
}
return FESTIVAL;
}
static boolean IS_REST_DAY(){
return IS_REST_DAY(day());
}
private static boolean IS_REST_DAY(int day){
boolean IS_REST_DAY = (Nongli.weekday(day)%6 == 0) || SolarTerm.IS_REST_DAY(day);
Gregorian gd = new Gregorian(day);
int da = 100*gd.month + gd.date;
if(1000 < da && da < 1004) IS_REST_DAY = true;
for(Festival f : Festival.values())
if(da== f.date && f.isRestDay) IS_REST_DAY = true;
return IS_REST_DAY;
}
private enum Festival{
元旦(101, true),
情人节(215, false),
劳动节(501, true),
儿童节(601, false),
国庆(1001, true),
光棍节(1111, false),
圣诞(1225, false);
int date;
boolean isRestDay;
Festival(int date, boolean isRestDay){
this.date = date;
this.isRestDay = isRestDay;
}
}
}
package cn.dairy;
import java.util.Calendar;
import java.util.TimeZone;
import static cn.dairy.Nongli.Branch.BRANCH;
import static cn.dairy.Nongli.TEXT;
import static java.util.Calendar.HOUR_OF_DAY;
import static java.util.Calendar.MILLISECOND;
import static java.util.Calendar.MINUTE;
import static java.util.Calendar.SECOND;
import static java.util.Calendar.getInstance;
class Time{
int hour;
int minute;
int second;
String SCALE;
String TIME;
Time(){
Calendar da = getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
hour = da.get(HOUR_OF_DAY);
minute = da.get(MINUTE);
second = da.get(SECOND);
int time = 90000*hour + 1500*minute + 25*second + da.get(MILLISECOND)/40;
SCALE = scaleText(time);
TIME = timeText(time);
}
Time(int time){
hour = time/90000;
minute = time/1500%60;
second = time/25%60;
SCALE = scaleText(time);
TIME = timeText(time);
}
static String FORMAT(int i){
return i < 10 ? "0" + String.valueOf(i) : String.valueOf(i);
}
static String FORMAT(int i, int j){
return FORMAT(i) + ":" + FORMAT(j);
}
static String FORMAT(int i, int j, int k){
return FORMAT(i, j) + ":" + FORMAT(k);
}
private String timeText(int time){
int deci = time/3600, shift = deci/60 + 3, point = deci/10%6;
if(120 <= deci && deci < 480) return scaleText(time)+FORMAT(time/360%60,time/6%60);
else return TEXT(shift%10, 18, point, 19)+FORMAT(time/360%100,time/6%60);
}
private String scaleText(int time){
int deci = time/3600;
int scale = (deci%25 + hour%6)/6;
return BRANCH((hour + hour%2)/2) + TEXT(11 - hour%2*11, scale == 0 ? 16 : scale, 17);
}
}