日历工具类(一)——公历农历互相转换

最近一直在做关于日历的开发,对于国内来说,农历和二十四节气是必不可少的。奈何网上的各种资料都不能满足需求,或者多多少少都有点问题,动手查阅各种资料自己实现了一个公历和农历互相转换的工具类。

  1 /**
  2  * 工具类,实现公农历互转
  3  */
  4 public class LunarCalendar {
  5         
  6         /**
  7          * 支持转换的最小农历年份
  8          */
  9         public static final int MIN_YEAR = 1900;
 10         /**
 11          * 支持转换的最大农历年份
 12          */
 13         public static final int MAX_YEAR = 2099;
 14 
 15         /**
 16          * 公历每月前的天数
 17          */
 18         private static final int DAYS_BEFORE_MONTH[] = { 0, 31, 59, 90, 120, 151, 181,
 19                         212, 243, 273, 304, 334, 365 };
 20         
 21         /**
 22          * 用来表示1900年到2099年间农历年份的相关信息,共24位bit的16进制表示,其中:
 23          * 1. 前4位表示该年闰哪个月;
 24          * 2. 5-17位表示农历年份13个月的大小月分布,0表示小,1表示大;
 25          * 3. 最后7位表示农历年首(正月初一)对应的公历日期。
 26          * 
 27          * 以2014年的数据0x955ABF为例说明:
 28          *                 1001 0101 0101 1010 1011 1111
 29          *                 闰九月                                  农历正月初一对应公历1月31号        
 30          */
 31         private static final int LUNAR_INFO[] = {
 32                 0x84B6BF,/*1900*/
 33               0x04AE53,0x0A5748,0x5526BD,0x0D2650,0x0D9544,0x46AAB9,0x056A4D,0x09AD42,0x24AEB6,0x04AE4A,/*1901-1910*/
 34             0x6A4DBE,0x0A4D52,0x0D2546,0x5D52BA,0x0B544E,0x0D6A43,0x296D37,0x095B4B,0x749BC1,0x049754,/*1911-1920*/
 35             0x0A4B48,0x5B25BC,0x06A550,0x06D445,0x4ADAB8,0x02B64D,0x095742,0x2497B7,0x04974A,0x664B3E,/*1921-1930*/
 36             0x0D4A51,0x0EA546,0x56D4BA,0x05AD4E,0x02B644,0x393738,0x092E4B,0x7C96BF,0x0C9553,0x0D4A48,/*1931-1940*/
 37             0x6DA53B,0x0B554F,0x056A45,0x4AADB9,0x025D4D,0x092D42,0x2C95B6,0x0A954A,0x7B4ABD,0x06CA51,/*1941-1950*/
 38             0x0B5546,0x555ABB,0x04DA4E,0x0A5B43,0x352BB8,0x052B4C,0x8A953F,0x0E9552,0x06AA48,0x6AD53C,/*1951-1960*/
 39             0x0AB54F,0x04B645,0x4A5739,0x0A574D,0x052642,0x3E9335,0x0D9549,0x75AABE,0x056A51,0x096D46,/*1961-1970*/
 40             0x54AEBB,0x04AD4F,0x0A4D43,0x4D26B7,0x0D254B,0x8D52BF,0x0B5452,0x0B6A47,0x696D3C,0x095B50,/*1971-1980*/
 41             0x049B45,0x4A4BB9,0x0A4B4D,0xAB25C2,0x06A554,0x06D449,0x6ADA3D,0x0AB651,0x095746,0x5497BB,/*1981-1990*/
 42             0x04974F,0x064B44,0x36A537,0x0EA54A,0x86B2BF,0x05AC53,0x0AB647,0x5936BC,0x092E50,0x0C9645,/*1991-2000*/
 43             0x4D4AB8,0x0D4A4C,0x0DA541,0x25AAB6,0x056A49,0x7AADBD,0x025D52,0x092D47,0x5C95BA,0x0A954E,/*2001-2010*/
 44             0x0B4A43,0x4B5537,0x0AD54A,0x955ABF,0x04BA53,0x0A5B48,0x652BBC,0x052B50,0x0A9345,0x474AB9,/*2011-2020*/
 45             0x06AA4C,0x0AD541,0x24DAB6,0x04B64A,0x6a573D,0x0A4E51,0x0D2646,0x5E933A,0x0D534D,0x05AA43,/*2021-2030*/
 46             0x36B537,0x096D4B,0xB4AEBF,0x04AD53,0x0A4D48,0x6D25BC,0x0D254F,0x0D5244,0x5DAA38,0x0B5A4C,/*2031-2040*/
 47             0x056D41,0x24ADB6,0x049B4A,0x7A4BBE,0x0A4B51,0x0AA546,0x5B52BA,0x06D24E,0x0ADA42,0x355B37,/*2041-2050*/
 48             0x09374B,0x8497C1,0x049753,0x064B48,0x66A53C,0x0EA54F,0x06AA44,0x4AB638,0x0AAE4C,0x092E42,/*2051-2060*/
 49             0x3C9735,0x0C9649,0x7D4ABD,0x0D4A51,0x0DA545,0x55AABA,0x056A4E,0x0A6D43,0x452EB7,0x052D4B,/*2061-2070*/
 50             0x8A95BF,0x0A9553,0x0B4A47,0x6B553B,0x0AD54F,0x055A45,0x4A5D38,0x0A5B4C,0x052B42,0x3A93B6,/*2071-2080*/
 51             0x069349,0x7729BD,0x06AA51,0x0AD546,0x54DABA,0x04B64E,0x0A5743,0x452738,0x0D264A,0x8E933E,/*2081-2090*/
 52             0x0D5252,0x0DAA47,0x66B53B,0x056D4F,0x04AE45,0x4A4EB9,0x0A4D4C,0x0D1541,0x2D92B5          /*2091-2099*/
 53     };
 54         
 55         /**
 56          * 将农历日期转换为公历日期
 57          * @param year                        农历年份
 58          * @param month                        农历月
 59          * @param monthDay                农历日
 60          * @param isLeapMonth        该月是否是闰月
 61          * [url=home.php?mod=space&uid=7300]@return[/url] 返回农历日期对应的公历日期,year0, month1, day2.
 62          */
 63         public static final int[] lunarToSolar(int year, int month, int monthDay,
 64                         boolean isLeapMonth) {
 65                 int dayOffset;
 66                 int leapMonth;
 67                 int i;
 68                 
 69                 if (year < MIN_YEAR || year > MAX_YEAR || month < 1 || month > 12
 70                                 || monthDay < 1 || monthDay > 30) {
 71                         throw new IllegalArgumentException(
 72                                         "Illegal lunar date, must be like that:\n\t" +
 73                                         "year : 1900~2099\n\t" + 
 74                                         "month : 1~12\n\t" +
 75                                         "day : 1~30");
 76                 }
 77                 
 78                 dayOffset = (LUNAR_INFO[year - MIN_YEAR] & 0x001F) - 1;
 79 
 80                 if (((LUNAR_INFO[year - MIN_YEAR] & 0x0060) >> 5) == 2)
 81                         dayOffset += 31;
 82 
 83                 for (i = 1; i < month; i++) {
 84                         if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (i - 1))) == 0)
 85                                 dayOffset += 29;
 86                         else
 87                                 dayOffset += 30;
 88                 }
 89 
 90                 dayOffset += monthDay;
 91                 leapMonth = (LUNAR_INFO[year - MIN_YEAR] & 0xf00000) >> 20;
 92 
 93                 // 这一年有闰月
 94                 if (leapMonth != 0) {
 95                         if (month > leapMonth || (month == leapMonth && isLeapMonth)) {
 96                                 if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (month - 1))) == 0)
 97                                         dayOffset += 29;
 98                                 else
 99                                         dayOffset += 30;
100                         }
101                 }
102 
103                 if (dayOffset > 366 || (year % 4 != 0 && dayOffset > 365)) {
104                         year += 1;
105                         if (year % 4 == 1)
106                                 dayOffset -= 366;
107                         else
108                                 dayOffset -= 365;
109                 }
110                 
111                 int[] solarInfo = new int[3];
112                 for (i = 1; i < 13; i++) {
113                         int iPos = DAYS_BEFORE_MONTH[i];
114                         if (year % 4 == 0 && i > 2) {
115                                 iPos += 1;
116                         }
117 
118                         if (year % 4 == 0 && i == 2 && iPos + 1 == dayOffset) {
119                                 solarInfo[1] = i;
120                                 solarInfo[2] = dayOffset - 31;
121                                 break;
122                         }
123 
124                         if (iPos >= dayOffset) {
125                                 solarInfo[1] = i;
126                                 iPos = DAYS_BEFORE_MONTH[i - 1];
127                                 if (year % 4 == 0 && i > 2) {
128                                         iPos += 1;
129                                 }
130                                 if (dayOffset > iPos)
131                                         solarInfo[2] = dayOffset - iPos;
132                                 else if (dayOffset == iPos) {
133                                         if (year % 4 == 0 && i == 2)
134                                                 solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1] + 1;
135                                         else
136                                                 solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1];
137 
138                                 } else
139                                         solarInfo[2] = dayOffset;
140                                 break;
141                         }
142                 }
143                 solarInfo[0] = year;
144 
145                 return solarInfo;
146         }
147         
148         /**
149          * 将公历日期转换为农历日期,且标识是否是闰月
150          * @param year
151          * @param month
152          * @param monthDay
153          * @return 返回公历日期对应的农历日期,year0,month1,day2,leap3
154          */
155         public static final int[] solarToLunar(int year, int month, int monthDay) {
156                 int[] lunarDate = new int[4];
157                 Date baseDate = new GregorianCalendar(1900, 0, 31).getTime();
158                 Date objDate = new GregorianCalendar(year, month - 1, monthDay).getTime();
159                 int offset = (int) ((objDate.getTime() - baseDate.getTime()) / 86400000L);
160                 
161                 // 用offset减去每农历年的天数计算当天是农历第几天
162         // iYear最终结果是农历的年份, offset是当年的第几天
163                 int iYear, daysOfYear = 0;
164                 for (iYear = MIN_YEAR; iYear <= MAX_YEAR && offset > 0; iYear++) {
165                         daysOfYear = daysInLunarYear(iYear);
166                         offset -= daysOfYear;
167                 }
168                 if (offset < 0) {
169                         offset += daysOfYear;
170                         iYear--;
171                 }
172                 
173                 // 农历年份
174                 lunarDate[0] = iYear;
175                 
176                 int leapMonth = leapMonth(iYear); // 闰哪个月,1-12
177                 boolean isLeap = false;
178                 // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
179         int iMonth, daysOfMonth = 0;
180                 for (iMonth = 1; iMonth <= 13 && offset > 0; iMonth++) {
181                         daysOfMonth = daysInLunarMonth(iYear, iMonth);
182                         offset -= daysOfMonth;
183                 }
184                 // 当前月超过闰月,要校正
185         if (leapMonth != 0 && iMonth > leapMonth) {
186                 --iMonth;
187                 
188                 if (iMonth == leapMonth) {
189                         isLeap = true;
190                 }
191         }
192         // offset小于0时,也要校正
193         if (offset < 0) {
194             offset += daysOfMonth;
195             --iMonth;
196         }
197         
198         lunarDate[1] = iMonth;
199         lunarDate[2] = offset + 1;
200         lunarDate[3] = isLeap ? 1 : 0;
201                 
202                 return lunarDate;
203         }
204         
205         /**
206          * 传回农历year年month月的总天数
207          * @param year 要计算的年份
208          * @param month        要计算的月
209          * @return 传回天数
210          */
211         final public static int daysInMonth(int year, int month) {
212                 return daysInMonth(year, month, false);
213         }
214         
215         /**
216          * 传回农历year年month月的总天数
217          * @param year 要计算的年份
218          * @param month        要计算的月
219          * @param leap 当月是否是闰月
220          * @return 传回天数,如果闰月是错误的,返回0.
221          */
222         public static final int daysInMonth(int year, int month, boolean leap) {
223                 int leapMonth = leapMonth(year);
224                 int offset = 0;
225                 
226                 // 如果本年有闰月且month大于闰月时,需要校正
227                 if (leapMonth != 0 && month > leapMonth) {
228                         offset = 1;
229                 }
230                 
231                 // 不考虑闰月
232                 if (!leap) {
233                         return daysInLunarMonth(year, month + offset);
234                 } else {
235                         // 传入的闰月是正确的月份
236                         if (leapMonth != 0 && leapMonth == month) {
237                                 return daysInLunarMonth(year, month + 1);
238                         }
239                 }
240                 
241                 return 0;
242         }
243         
244         /**
245      * 传回农历 year年的总天数
246      *
247      * @param year 将要计算的年份
248      * @return 返回传入年份的总天数
249      */
250     private static int daysInLunarYear(int year) {
251         int i, sum = 348;
252         if (leapMonth(year) != 0) {
253                 sum = 377;
254         }
255         int monthInfo = LUNAR_INFO[year - MIN_YEAR] & 0x0FFF80;
256         for (i = 0x80000; i > 0x7; i >>= 1) {
257             if ((monthInfo & i) != 0)
258                 sum += 1;
259         }
260         return sum;
261     }
262     
263     /**
264      * 传回农历 year年month月的总天数,总共有13个月包括闰月
265      *
266      * @param year  将要计算的年份
267      * @param month 将要计算的月份
268      * @return 传回农历 year年month月的总天数
269      */
270     private static int daysInLunarMonth(int year, int month) {
271         if ((LUNAR_INFO[year - MIN_YEAR] & (0x100000 >> month)) == 0)
272             return 29;
273         else
274             return 30;
275     }
276         
277         /**
278          * 传回农历 year年闰哪个月 1-12 , 没闰传回 0
279          * 
280          * @param year 将要计算的年份
281          * @return 传回农历 year年闰哪个月1-12, 没闰传回 0
282          */
283         private static int leapMonth(int year) {
284                 return (int) ((LUNAR_INFO[year - MIN_YEAR] & 0xF00000)) >> 20;
285         }
286         
287 }

以上工具类支持农历1900年正月初一到2099年腊月三十之间的农历日期。需要的拿走吧!下期附上二十四节气计算的工具类。

转载于:https://www.cnblogs.com/androidsj/p/3629337.html

LunarCalendar返回农历(阴历)日期的JAR包 根据指定日期计算对应农历日期(这个计算方法是网上找的,最初的作者是谁已经无法考证了,感谢网络资源吧!),本人封装成好用的JAR包后发不出来,供大家免费下载! toString()方法输出阴历日期(例如:癸巳年七月廿) getFullInfo()方法输出包括生肖在内的阴历日期(例如:癸巳年七月廿,生肖:蛇) 构建方法包括以下四种: public LunarCalendar(String year, String month, String date) public LunarCalendar(JComboBox jcYear, JComboBox jcMonth, JComboBox jcDate) public LunarCalendar(int year, int month, int date) public LunarCalendar(Calendar cal)) 使用前两种构建方法时,若文本内容不为数字,getErrorMessage会返回错误信息 方法摘要 java.lang.String getErrorMessage() 返回String类型的错误信息 java.lang.String getFullInfo() 返回String类型的详细阴历信息(例如:癸巳年七月廿,生肖:蛇) java.lang.String getLunarAnimal() 返回String类型的生肖(例如:蛇) java.lang.String getLunarDate() 返回String类型的阴历日期(例如:廿) java.lang.String getLunarMonth() 返回String类型的阴历月份(例如:七) java.lang.String getLunarYear() 返回String类型的阴历年份(天干地支,例如:癸巳) java.lang.String toString() 返回String类型的阴历日期(例如:癸巳年七月廿) JAR包名称:LunarCalendar version 1.0 8/26/2013 作者:Roy, Liu royliu90@live.cn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值