寿星万年历是一款采用现代天文算法制作的农历历算程序,含有公历与回历信息,可以很方便的进行公、农、回三历之间的转换。提供公元-4712年到公元9999年的日期查询功能。其中1500年到1940农历数据已经与陈垣的《二十史朔闰表》核对;含有从公420元(南北朝/宋武帝元年)到今的基本年号。在过去几百年中,寿星万年历的误差是非常小的,节气时刻计算及日月合朔时刻的平均误差小于1秒,太阳坐标的最大可能误差为0.2角秒,月亮坐标的最大可能误差为3角秒,平均误差为误差的1/6。万年历中含有几百个国内城市的经纬度,并且用户可根据自已的需要扩展经纬度数据。
这是网上见到的精度较高的万年历,其采用先进的算法实现,其精度堪比刘安国教授为中国科学院国家授时中心制作的日梭万年历。但网络上只有javascript版本。后来找到一份别人从javascript转未JAVA的代码,测试有点小问题,所以自己又重新转了一次,同时也转了一个VB NET的代码。源码如下:
JAVA部分:
package com.konshine.health.aide.util;
import android.util.Log;
public class SolarTerm {
//========角度变换===============
/**每弧度的角秒数*/
private static final double ANGULAR_SECONDS = (float)(180*3600)/Math.PI;
/**每弧度的角度数*/
private static final double ANGULAR_DEGREE = 180.0f/Math.PI;
/**
* 取整数部分
* @param v
* @return
*/
public static double int2(double v){
v=Math.floor(v);
if(v<0)
return v+=1;
else
return v;
}
/**
* 对超过0-2PI的角度转为0-2PI
* @param v
* @return
*/
public static double rad2mrad(double v){
v=v % (2*Math.PI);
if(v<0) return v+2*Math.PI;
return v;
}
/**
* 将弧度转为字串
* tim=0输出格式示例: -23°59' 48.23"
* tim=1输出格式示例: 18h 29m 44.52s
* @param d 需要转换的弧度
* @param tim
* @return
*/
public static String rad2str(double d,boolean tim) {
String s = "+";
String w1 = "°", w2 = "’", w3 = "”";
if (d < 0) {
d = -d;
s = "-";
}
if (tim) {
d *= 12 / Math.PI;
w1 = "h ";
w2 = "m ";
w3 = "s ";
} else {
d *= 180f / Math.PI;
}
double a = Math.floor(d);
d = (d - a) * 60f;
double b = Math.floor(d);
d = (d - b) * 60f;
double c = Math.floor(d);
d = (d - c) * 100f;
d = Math.floor(d + 0.5);
if (d >= 100f) {
d -= 100f;
c++;
}
if (c >= 60){
c -= 60f;
b++;
}
if(b>=60){
b-=60f;
a++;
};
String a1=" "+a,b1="0"+b,c1="0"+c,d1="0"+d;
s+=a1.substring(a1.length()-3,3)+w1;
s+=b1.substring(b1.length()-2,2)+w2;
s+=c1.substring(c1.length()-2,2)+".";
s+=d1.substring(d1.length()-2,2)+w3;
return s;
}
//================日历计算===============
private static final double J2000=2451545; //2000年前儒略日数(2000-1-1 12:00:00格林威治平时)
/**年*/
private double year=2000;
/**月*/
private double month=1;
/**日*/
private double diy=1;
/**时*/
private double hour=12;
/**分*/
private double minute=0;
/**秒*/
private double second=0;
/**世界时与原子时之差计算表*/
private static final double[] dts={
-4000,108371.7,-13036.80,392.000, 0.0000, -500,
17201.0, -627.82, 16.170,-0.3413,-150, 12200.6, -346.41, 5.403,-0.1593, 150,
9113.8, -328.13, -1.647, 0.0377, 500, 5707.5, -391.41, 0.915, 0.3145, 900,
2203.4, -283.45, 13.034,-0.1778,1300, 490.1, -57.35, 2.085,-0.0072, 1600,
120.0, -9.81, -1.532, 0.1403,1700, 10.2, -0.91, 0.510,-0.0370, 1800, 13.4,
-0.72, 0.202,-0.0193,1830, 7.8, -1.81, 0.416,-0.0247, 1860, 8.3, -0.13,
-0.406, 0.0292, 1880, -5.4, 0.32, -0.183, 0.0173, 1900, -2.3, 2.06, 0.169,
-0.0135,1920, 21.2, 1.69, -0.304, 0.0167, 1940, 24.2, 1.22, -0.064, 0.0031,
1960, 33.2, 0.51, 0.231,-0.0109, 1980, 51.0, 1.29, -0.026, 0.0032,2000, 64.7,
-1.66, 5.224,-0.2905, 2150, 279.4, 732.95,429.579, 0.0158, 6000
};
/**
* 计算世界时与原子时之差,传入年
* @param year 年
* @return
*/
public double deltatT(double year){
int i=0;
for(i=0;i<100;i+=5)
if(year<this.dts[i+5]||i==95) break;
double t1=(year-this.dts[i])/(this.dts[i+5]-this.dts[i])*10, t2=t1*t1, t3=t2*t1;
return this.dts[i+1] +this.dts[i+2]*t1 +this.dts[i+3]*t2 +this.dts[i+4]*t3;
}
/**
* 传入儒略日(J2000起算),计算UTC与原子时的差(单位:日)
* @param julianDay 儒略日
* @return
*/
public double deltatT2(double julianDay){
return this.deltatT(julianDay/365.2425f+2000f)/86400.0f;
}
/**
* 公历转儒略日,UTC=1表示原日期是UTC
* @param UTC
* @return
*/
public double toJD(boolean UTC){
double y=this.year, m=this.month, n=0; //取出年月
if(m<=2) {
m+=12f;
y--;
}
//判断是否为格里高利历日1582*372+10*31+15
if(this.year*372f+this.month*31f+this.diy>=588829f) {
//加百年闰
n = int2(y / 100f);
n = 2 - n + int2(n / 4);
}
n +=int2(365.2500001d*(y+4716)) ; //加上年引起的偏移日数
n +=int2(30.6*(m+1))+this.diy; //加上月引起的偏移日数及日偏移数
n +=((this.second/60f+this.minute)/60+this.hour)/24f - 1524.5f;
if(UTC) return n+this.deltatT2(n-J2000);
return n;
}
/**
* 儒略日数转公历,UTC=1表示目标公历是UTC
* @param jd
* @param UTC
*/
public void setFromJD(double jd,boolean UTC){
if(UTC) jd-=this.deltatT2(jd-J2000);
jd+=0.5f;
double A=int2(jd), F=jd-A, D; //取得日数的整数部份A及小数部分F
if(A>2299161){
D=int2((A-1867216.25f)/36524.25f);
A+=1+D-int2(D/4);
};
A +=1524f; //向前移4年零2个月
this.year =int2((A-122.1f)/365.25f);//年
D =A-int2(365.25f*this.year); //去除整年日数后余下日数
this.month =int2(D/30.6001f); //月数
this.diy =D-int2(this.month*30.6001f);//去除整月日数后余下日数
this.year-=4716f;
this.month--;
if(this.month>12) this.month-=12f;
if(this.month<=2) this.year++;
//日的小数转为时分秒
F*=24f;
this.hour=int2(F);
F-=this.hour;
F*=60f;
this.minute=int2(F);
F-=this.minute;
F*=60f;
this.second=F;
}
/**
* 设置时间
* @param s 参数例:"20000101 120000"或"20000101"
*/
public void setFromStr(String s){
this.year=Double.parseDouble(s.substring(0,4));
this.month=Double.parseDouble(s.substring(4, 2));
this.diy=Double.parseDouble(s.substring(6, 2));
this.hour=Double.parseDouble(s.substring(9,2));
this.minute=Double.parseDouble(s.substring(11,2));
this.second=Double.parseDouble(s.substring(13,2)); /* 将5改为了2 */
}
/**
* 日期转为串
* @return
*/
public String toStr(){
String Y;
if(this.year>999) {
Y= String.valueOf((int)this.year);
}else {
if(this.year>99){
Y= "0"+ (int)this.year;
}else{
if(this.year>9){
Y= "00"+ (int)this.year;
}else{
Y= "000"+ (int)this.year;
}
}
}
String M=this.month<10?"0"+(int)this.month:String.valueOf((int)this.month),
D=this.diy<10?"0"+(int)this.diy:String.valueOf((int)this.diy);
double h=this.hour,m=this.minute,s=Math.floor(this.second+.5);
if(s>=60) {
s-=60;
m++;
}
if(m>=60) {
m-=60;
h++;
}
String sh= h<10?"0"+(int)h: String.valueOf((int)h),
sm=m<10?"0"+(int)m:String.valueOf((int)m),
ss=s<10?"0"+(int)s:String.valueOf((int)s);
return Y+"-"+M+"-"+D+" "+sh+":"+sm+":"+ss;
}
/**
* 算出:jd转到当地UTC后,UTC日数的整数部分或小数部分<br/>
* 基于J2000力学时jd的起算点是12:00:00时,所以跳日时刻发生在12:00:00,这与日历计算发生矛盾<br/>
* 把jd改正为00:00:00起算,这样儒略日的跳日动作就与日期的跳日同步</br>
* 改正方法为jd=jd+0.5-deltatT+shiqu/24
* 把儒略日的起点移动-0.5(即前移12小时)
* 式中shiqu是时区,北京的起算点是-8小时,shiqu取8
* @param jd
* @param shiqu
* @param dec
* @return
*/
public double Dint_dec(double jd,int shiqu,boolean dec){
double u=jd+0.5-this.deltatT2(jd)+shiqu/24;
if(dec)
return Math.floor(u); //返回整数部分
else
return u-Math.floor(u); //返回小数部分
}
/**
* 计算两个日期的相差的天数,输入字串格式日期,如:"20080101"
* @param d1
* @param d2
* @return
*/
public double d1_d2 (String d1,String d2){
//备份原来的数据
double Y=this.year,M=this.month,D=this.diy,h=this.hour,m=this.minute,s=this.second;
this.setFromStr(d1.substring(0,8)+" 120000");
double jd1=this.toJD(false);
this.setFromStr(d2.substring(0,8)+" 120000");
double jd2=this.toJD(false);
//还原
this.year=Y;
this.month=M;
this.diy=D;
this.hour=h;
this.minute=m;
this.second=s;
if(jd1>jd2)
return Math.floor(jd1-jd2+0.0001f);
else
return -Math.floor(jd2-jd1+0.0001f);
}
//=========黄赤交角及黄赤坐标变换===========
/**黄赤交角系数表*/
private static final double[] hcjjB = {
84381.448, -46.8150, -0.00059, 0.001813};
/**Date黄道上的岁差p*/
private static final double[] preceB= {
0,50287.92262,111.24406,0.07699,-0.23479,-0.00178,0.00018,0.00001};
/**
* 返回黄赤交角(常规精度),短期精度很高
* @param t
* @return
*/
public static double hcjj1 (double t){
double t1=t/36525f;
double t2=t1*t1;
double t3=t2*t1;
return (hcjjB[0] +hcjjB[1]*t1 +hcjjB[2]*t2 +hcjjB[3]*t3)/ANGULAR_SECONDS;
}
/**
* 黄赤转换(黄赤坐标旋转)
* @param JW
* @param E
*/
public static void HCconv(double[] JW,double E){
//黄道赤道坐标变换,赤到黄E取负
double HJ=rad2mrad(JW[0]), HW=JW[1];
double sinE =Math.sin(E),cosE =Math.cos(E);
double sinW=cosE*Math.sin(HW)+sinE*Math.cos(HW)*Math.sin(HJ);
double J=Math.atan2( Math.sin(HJ)*cosE-Math.tan(HW)*sinE, Math.cos(HJ) );
JW[0]=rad2mrad(J);
JW[1]=Math.asin(sinW);
}
/**
* 补岁差
* @param jd
* @param zb
*/
public static void addPrece(double jd,double[] zb){
int i;
double t=1,v=0, t1=jd/365250f;
for(i=1;i<8;i++) {
t*=t1;
v+=preceB[i]*t;
}
zb[0]=rad2mrad(zb[0]+(v+2.9965f*t1)/ANGULAR_SECONDS);
}
/**===============光行差==================**/
/**离心率*/
private static final double[] GXC_e= {
0.016708634, -0.000042037,-0.0000001267};
/**近点*/
private static final double[] GXC_p={
102.93735/ANGULAR_DEGREE,1.71946/ANGULAR_DEGREE, 0.00046/ANGULAR_DEGREE};
/**太平黄经*/
private static final double[] GXC_l={
280.4664567/ANGULAR_DEGREE,36000.76982779/ANGULAR_DEGREE,
0.0003032028/ANGULAR_DEGREE,1/49931000/ANGULAR_DEGREE,-1/153000000/ANGULAR_DEGREE};
/**光行差常数*/
private static final double GXC_k=20.49552/ANGULAR_SECONDS;
/**
* 恒星周年光行差计算(黄道坐标中)
* @param t
* @param zb
*/
public static void addGxc(double t,double[]zb){
double t1=t/36525f, t2=t1*t1, t3=t2*t1,t4=t3*t1;
double L=GXC_l[0] +GXC_l[1]*t1 +GXC_l[2]*t2 +GXC_l[3]*t3 +GXC_l[4]*t4;
double p=GXC_p[0] +GXC_p[1]*t1 +GXC_p[2]*t2;
double e=GXC_e[0] +GXC_e[1]*t1 +GXC_e[2]*t2;
double dL=L-zb[0], dP=p-zb[0];
zb[0]-=GXC_k * (Math.cos(dL)-e*Math.cos(dP)) / Math.cos(zb[1]);
zb[1]-=GXC_k * Math.sin(zb[1]) * (Math.sin(dL)-e*Math.sin(dP));
zb[0]=rad2mrad(zb[0]);
}
/**===============章动计算==================**/
/**章动表*/
private static final double[] nutB={
2.1824391966, -33.757045954, 0.0000362262, 3.7340E-08,-2.8793E-10,
-171996,-1742,92025, 89,3.5069406862, 1256.663930738, 0.0000105845, 6.9813E-10,-2.2815E-10,
-13187, -16, 5736,-31,1.3375032491, 16799.418221925,-0.0000511866, 6.4626E-08,-5.3543E-10,
-2274, -2, 977, -5,4.3648783932, -67.514091907, 0.0000724525, 7.4681E-08,-5.7586E-10, 2062,
2, -895, 5,0.0431251803, -628.301955171, 0.0000026820, 6.5935E-10, 5.5705E-11, -1426, 34, 54,
-1,2.3555557435, 8328.691425719, 0.0001545547, 2.5033E-07,-1.1863E-09, 712, 1, -7, 0,3.4638155059,
1884.965885909, 0.0000079025, 3.8785E-11,-2.8386E-10, -517, 12, 224, -6,5.4382493597, 16833.175267879,
-0.0000874129, 2.7285E-08,-2.4750E-10, -386, -4, 200, 0,3.6930589926, 25128.109647645, 0.0001033681,
3.1496E-07,-1.7218E-09, -301, 0, 129, -1,3.5500658664, 628.361975567, 0.0000132664, 1.3575E-09,
-1.7245E-10, 217, -5, -95, 3
};
/**章动*/
public static class ZD {
public double Lon;
public double Obl;
public ZD(){
};
public ZD(double lon,double obl){
this.Lon=lon;
this.Obl=obl;
}
}
/**
* 计算黄经章动及交角章动
* @param t
* @return
*/
public static ZD nutation(double t){
ZD d=new ZD(0,0);
t/=36525.0f;
double c,t1=t, t2=t1*t1, t3=t2*t1, t4=t3*t1;// t5=t4*t1;
for(int i=0;i<nutB.length;i+=9){
c=nutB[i] +nutB[i+1]*t1 +nutB[i+2]*t2 +nutB[i+3]*t3 +nutB[i+4]*t4;
d.Lon+=(nutB[i+5]+nutB[i+6]*t/10.0f)*Math.sin(c); //黄经章动
d.Obl+=(nutB[i+7]+nutB[i+8]*t/10.0f)*Math.cos(c); //交角章动
}
d.Lon/=ANGULAR_SECONDS*10000.0f; //黄经章动
d.Obl/=ANGULAR_SECONDS*10000.0f; //交角章动
return d;
}
/**
* 本函数计算赤经章动及赤纬章动
* @param t
* @param zb
*/
public static void nutationRaDec(double t,double[]zb){
double Ra=zb[0],Dec=zb[1];
double E=hcjj1(t), sinE=Math.sin(E), cosE=Math.cos(E); //计算黄赤交角
ZD d=nutation(t); //计算黄经章动及交角章动
double cosRa=Math.cos(Ra), sinRa=Math.sin(Ra);
double tanDec=Math.tan(Dec);
zb[0]+=(cosE+sinE*sinRa*tanDec)*d.Lon-cosRa*tanDec*d.Obl; //赤经章动
zb[1]+= sinE*cosRa*d.Lon+sinRa*d.Obl; //赤纬章动
zb[0]=rad2mrad(zb[0]);
}
/**=================以下是月球及地球运动参数表===================**/
/***************************************
* 如果用记事本查看此代码,请在"格式"菜单中去除"自动换行"
* E10是关于地球的,格式如下:
* 它是一个数组,每3个数看作一条记录,每条记录的3个数记为A,B,C
* rec=A*cos(B+C*t); 式中t是J2000起算的儒略千年数
* 每条记录的计算结果(即rec)取和即得地球的日心黄经的周期量L0
* E11格式如下: rec = A*cos*(B+C*t) *t, 取和后得泊松量L1
* E12格式如下: rec = A*cos*(B+C*t) *t*t, 取和后得泊松量L2
* E13格式如下: rec = A*cos*(B+C*t) *t*t*t, 取和后得泊松量L3
* 最后地球的地心黄经:L = L0+L1+L2+L3+...
* E20,E21,E22,E23...用于计算黄纬
* M10,M11等是关于月球的,参数的用法请阅读Mnn()函数
*****************************************/
//地球运动VSOP87参数
/**黄经周期项*/
private static final double[] E10={
1.75347045673, 0.00000000000, 0.0000000000, 0.03341656456, 4.66925680417, 6283.0758499914, 0.00034894275, 4.62610241759, 12566.1516999828, 0.00003417571, 2.82886579606, 3.5231183490,
0.00003497056, 2.74411800971, 5753.3848848968, 0.00003135896, 3.62767041758, 77713.7714681205, 0.00002676218, 4.41808351397, 7860.4193924392, 0.00002342687, 6.13516237631, 3930.2096962196,
0.00001273166, 2.03709655772, 529.6909650946, 0.00001324292, 0.74246356352, 11506.7697697936, 0.00000901855, 2.04505443513, 26.2983197998, 0.00001199167, 1.10962944315, 1577.3435424478,
0.00000857223, 3.50849156957, 398.1490034082, 0.00000779786, 1.17882652114, 5223.6939198022, 0.00000990250, 5.23268129594, 5884.9268465832, 0.00000753141, 2.53339053818, 5507.5532386674,
0.00000505264, 4.58292563052, 18849.2275499742, 0.00000492379, 4.20506639861, 775.5226113240, 0.00000356655, 2.91954116867, 0.0673103028, 0.00000284125, 1.89869034186, 796.2980068164,
0.00000242810, 0.34481140906, 5486.7778431750, 0.00000317087, 5.84901952218, 11790.6290886588, 0.00000271039, 0.31488607649, 10977.0788046990, 0.00000206160, 4.80646606059, 2544.3144198834,
0.00000205385, 1.86947813692, 5573.1428014331, 0.00000202261, 2.45767795458, 6069.7767545534, 0.00000126184, 1.08302630210, 20.7753954924, 0.00000155516, 0.83306073807, 213.2990954380,
0.00000115132, 0.64544911683, 0.9803210682, 0.00000102851, 0.63599846727, 4694.0029547076, 0.00000101724, 4.26679821365, 7.1135470008, 0.00000099206, 6.20992940258, 2146.1654164752,
0.00000132212, 3.41118275555, 2942.4634232916, 0.00000097607, 0.68101272270, 155.4203994342, 0.00000085128, 1.29870743025, 6275.9623029906, 0.00000074651, 1.75508916159, 5088.6288397668,
0.00000101895, 0.97569221824, 15720.8387848784, 0.00000084711, 3.67080093025, 71430.6956181291, 0.00000073547, 4.67926565481, 801.8209311238, 0.00000073874, 3.50319443167, 3154.6870848956,
0.00000078756, 3.03698313141, 12036.4607348882, 0.00000079637, 1.80791330700, 17260.1546546904, 0.00000085803, 5.98322631256,161000.6857376741, 0.00000056963, 2.78430398043, 6286.5989683404,
0.00000061148, 1.81839811024, 7084.8967811152, 0.00000069627, 0.83297596966, 9437.7629348870, 0.00000056116, 4.38694880779, 14143.4952424306, 0.00000062449, 3.97763880587, 8827.3902698748,
0.00000051145, 0.28306864501, 5856.4776591154, 0.00000055577, 3.47006009062, 6279.5527316424, 0.00000041036, 5.36817351402, 8429.2412664666, 0.00000051605, 1.33282746983, 1748.0164130670,
0.00000051992, 0.18914945834, 12139.5535091068, 0.00000049000, 0.48735065033, 1194.4470102246, 0.00000039200, 6.16832995016, 10447.3878396044, 0.00000035566, 1.77597314691, 6812.7668150860,
0.00000036770, 6.04133859347, 10213.2855462110, 0.00000036596, 2.56955238628, 1059.3819301892, 0.00000033291, 0.59309499459, 17789.8456197850, 0.00000035954, 1.70876111898, 2352.8661537718
};
/**黄经泊松1项*/
private static final double[] E11={
6283.31966747491,0.00000000000, 0.0000000000, 0.00206058863, 2.67823455584, 6283.0758499914, 0.00004303430, 2.63512650414, 12566.1516999828, 0.00000425264, 1.59046980729, 3.5231183490,
0.00000108977, 2.96618001993, 1577.3435424478, 0.00000093478, 2.59212835365, 18849.2275499742, 0.00000119261, 5.79557487799, 26.2983197998, 0.00000072122, 1.13846158196, 529.6909650946,
0.00000067768, 1.87472304791, 398.1490034082, 0.00000067327, 4.40918235168, 5507.5532386674, 0.00000059027, 2.88797038460, 5223.6939198022

本文分享了万年历的源代码实现,分别使用JAVA和VB.NET编程语言。通过代码解析,读者可以理解如何在两种不同环境下开发万年历功能,涉及日期处理和用户界面展示。
472

被折叠的 条评论
为什么被折叠?



