PHP 儒略日期转换器算法
PHP Julian date converter algorithms
以下算法:
京东1099114.5
PHP:3 月 -1704 12:00:00(PHP 内部转换器)
echo jdtogregorian($int_jd);
FLN:0 Mar -1703 12:00:00(Fliegel-Van Flandern)
function jd_to_gd_fln ($J) {
$p = $J + 68569;
$q = floor(4*$p/146097);
$r = floor($p - (146097*$q + 3)/4);
$s = floor(4000*($r+1)/1461001);
$t = floor($r - (1461*$s/4) + 31);
$u = floor(80*$t/2447);
$v = floor($u/11);
$Y = floor(100*($q-49)+$s+$v);
$M = floor($u + 2 - 12*$v);
$D = floor($t - 2447*$u/80);
return "$M/$D/$Y";
}
MEU:3 月 18 日 -1703 12:00:00(Jean Meeus“天文算法,第 2 版,更正)
function jd_to_greg_meeus($julian) {
$Z = intval($J+0.5);
$F = ($J+0.5)-$Z;
if ($Z < 2299161) {$A = $Z;}
if ($Z >= 2299161) {
$alpha = floor(($Z-1867216.25)/36524.25);
$A = $Z + 1 + $alpha - floor($alpha/4);
}
$B = $A + 1524;
$C = floor( ($B-122.1)/365.25);
$D = floor( 365.25*$C );
$E = floor( ($B-$D)/30.6001 );
$day = floor($B - $D - floor(30.6001*$E));
if ($E < 13.5) { $month = $E - 1;}
if ($E > 13.5) { $month = $E - 13;}
if ($month > 2.5) { $year = $C - 4716; }
if ($month < 2.5) { $year = $C - 4715; }
return "$month/$day/$year";
}
RICH:4 Mar -1703 12:00:00(Richards in Explanatory Supplement 3rd Ed.)
function jd_to_gd_rchrds ($J) {
$y=4716;
$v=3;
$j=1401;
$u=5;
$m=2;
$s=153;
$n=12;
$w=2;
$r=4;
$B=274277;
$p=1461;
$C=-38;
$f = $J + $j + (((4 * $J + $B) / 146097) * 3) / 4 + $C;
$e = $r * $f + $v;
$g = ($e % $p) / $r;
$h = $u * $g + $w;
$D = floor((($h%$s)) / $u + 1);
$M = floor((($h / $s + $m) % $n) + 1);
$Y = floor(($e / $p) - $y + ($n + $m - $M) / $n);
return "$M/$D/$Y";
}
每种算法都有其问题。
我的代码哪里做错了?
供参考的测试数据应包括:
京东: 2399301.5
京东: 1721420
京东: 1721425
京东: 1721426
京东: 2422444
京东: 2222444
Meeus 假设有公历 0 年,即使这在天文学上是正确的,但从日历的角度来看却毫无意义。从技术上讲,所有转换为公历日期的结果都是错误的?从日历上讲,如果没有 0,但从天文学上讲,有一个 0,那如何转化为同一日期?
我没有看 Fliegel-Van Flandern 算法。我针对 Danny F 要求的测试用例测试了剩余的算法,并添加了罗马儒略历的最后一个中午 (JD 2299160) 和罗马公历的第一个中午 (JD 2299161)。对于非整数儒略日,我只使用了 Meeus 算法,因为这是唯一一个说它支持非整数的算法。
内部算法是正确的,当您考虑到 0 年不存在时,它总是转换为公历。 Meeus 算法是正确的,当你允许它假设存在 0 年,并在 1582 年 10 月 4 日及更早的时候转换为 Julian,并在之后的日期转换为 Gregorian。
Richards 算法需要更正。我没有在某些点使用 floor 函数,而是使用 intdiv 函数代替每个“/”运算符,这更接近于 Richard 对算法的描述。在这些更正之后,测试用例是成功的。 (Richards 认为 0 年存在并且总是转换为 Gregorian。)
function jd_to_gd_rchrds ($J) {
$y=4716;
$v=3;
$j=1401;
$u=5;
$m=2;
$s=153;
$n=12;
$w=2;
$r=4;
$B=274277;
$p=1461;
$C=-38;
$f = $J + $j + intdiv(intdiv(4 * $J + $B,146097) * 3,4) + $C;
$e = $r * $f + $v;
$g = intdiv(($e % $p) , $r);
$h = $u * $g + $w;
$D = intdiv((($h%$s)) , $u) + 1;
$M = ((intdiv($h , $s) + $m) % $n) + 1;
$Y = intdiv($e , $p) - $y + intdiv($n + $m - $M,$n);
return "$M/$D/$Y";
}
测试用例结果:
Julian day PHP internal Meeus Richards
0 0/0/0 1/1/-4712 11/24/-4713
1 11/25/-4714 1/2/-4712 11/25/-4713
1507231.5 bad input 7/30/-586 bad input
1721420 12/26/-1 12/28/0 12/26/0
1721425 12/31/-1 1/2/1 12/31/0
1741426 10/5/55 10/7/55 10/5/55
2222444 9/28/1372 9/20/1372 9/28/1372
2299160 10/14/1582 10/4/1582 10/14/1582
2299161 10/15/1582 10/15/1582 10/15/1582
2399301.5 bad input 12/18/1856 bad input
2422444 4/29/1920 4/29/1920 4/29/1920
不,没有公历 0 年这样的东西,因为公历直到 1582 年才开始。天文算法在公历改革之前使用儒略历。是天文算法 return 零年。 Meeus 是绝对正确的。 -years 与 BC years 不同。查一下。任何使用 BC-AD 的算法都在使用天文学家未使用的完全虚构的公历。查一下。
void JDToDateMeeus(double jDNum, int *month, double *day, int *year) //converts a Julian day to a calendar Date
{
int alpha, A, B, C, D, E, Z;
double F;
jDNum += 0.5;
Z = jDNum; //Z == int so I = int part
F = jDNum - Z; //F = fractional part
if(Z < 2299161) //Julian?
A = Z;
else{ //Gregorian
alpha = (int)floor((Z - 1867216.25) / 36524.25);
A = Z + 1 + alpha - (int)floor(alpha / 4.0);
}
B = A + 1524;
C = (int)floor((B - 122.1) /365.25);
D = (int)floor(365.25 * C);
E = (int)floor((B - D) /30.6001);
*day = B - D - (int)floor(30.6001 * E) + F;
if( E < 14)
*month = E - 1;
else
*month = E - 13;
if(*month > 2)
*year = C - 4716;
else
*year = C - 4715;
}
double DateToJulianDayNumber(int month, double day, int year) //after Meeus
{
int M, Y, A, B;
double JD;
if(month > 2){
Y = year;
M = month;
}
else{
Y = year - 1;
M = month + 12;
}
if((year > 1582) || ((year == 1582) && ((month == 10 && day >= 5.0) || (month > 10)))){ //Gregorian
A = (int)floor(Y / 100.0);
B = 2 - A + (int)floor(A / 4.0);
}
else
B = 0;
JD = (int)floor(365.25 * (Y + 4716)) + (int)floor(30.6001 * (M + 1)) + day + B - 1524.5;
return JD;
}
这些函数不使用虚构的公历。根据教皇格雷戈里的历法改革,10 月 4 日之后是 1582 年的 15 日。 1582年以后使用公历闰年,有0年,之前的年写负号。
我使用下面的 php 代码将儒略日期转换为公历格式。
$ts=mktime(0, 0, 0, 1, 136, "20" . 20);
$mydate=date ('m-d-Y', $ts);
echo $mydate;
也可以参考https://www.juliandate.net进行日期转换
以下算法:
京东1099114.5
PHP:3 月 -1704 12:00:00(PHP 内部转换器)
echo jdtogregorian($int_jd);
FLN:0 Mar -1703 12:00:00(Fliegel-Van Flandern)
function jd_to_gd_fln ($J) {
$p = $J + 68569;
$q = floor(4*$p/146097);
$r = floor($p - (146097*$q + 3)/4);
$s = floor(4000*($r+1)/1461001);
$t = floor($r - (1461*$s/4) + 31);
$u = floor(80*$t/2447);
$v = floor($u/11);
$Y = floor(100*($q-49)+$s+$v);
$M = floor($u + 2 - 12*$v);
$D = floor($t - 2447*$u/80);
return "$M/$D/$Y";
}
MEU:3 月 18 日 -1703 12:00:00(Jean Meeus“天文算法,第 2 版,更正)
function jd_to_greg_meeus($julian) {
$Z = intval($J+0.5);
$F = ($J+0.5)-$Z;
if ($Z < 2299161) {$A = $Z;}
if ($Z >= 2299161) {
$alpha = floor(($Z-1867216.25)/36524.25);
$A = $Z + 1 + $alpha - floor($alpha/4);
}
$B = $A + 1524;
$C = floor( ($B-122.1)/365.25);
$D = floor( 365.25*$C );
$E = floor( ($B-$D)/30.6001 );
$day = floor($B - $D - floor(30.6001*$E));
if ($E < 13.5) { $month = $E - 1;}
if ($E > 13.5) { $month = $E - 13;}
if ($month > 2.5) { $year = $C - 4716; }
if ($month < 2.5) { $year = $C - 4715; }
return "$month/$day/$year";
}
RICH:4 Mar -1703 12:00:00(Richards in Explanatory Supplement 3rd Ed.)
function jd_to_gd_rchrds ($J) {
$y=4716;
$v=3;
$j=1401;
$u=5;
$m=2;
$s=153;
$n=12;
$w=2;
$r=4;
$B=274277;
$p=1461;
$C=-38;
$f = $J + $j + (((4 * $J + $B) / 146097) * 3) / 4 + $C;
$e = $r * $f + $v;
$g = ($e % $p) / $r;
$h = $u * $g + $w;
$D = floor((($h%$s)) / $u + 1);
$M = floor((($h / $s + $m) % $n) + 1);
$Y = floor(($e / $p) - $y + ($n + $m - $M) / $n);
return "$M/$D/$Y";
}
每种算法都有其问题。
我的代码哪里做错了?
供参考的测试数据应包括:
京东: 2399301.5
京东: 1721420
京东: 1721425
京东: 1721426
京东: 2422444
京东: 2222444
Meeus 假设有公历 0 年,即使这在天文学上是正确的,但从日历的角度来看却毫无意义。从技术上讲,所有转换为公历日期的结果都是错误的?从日历上讲,如果没有 0,但从天文学上讲,有一个 0,那如何转化为同一日期?
我没有看 Fliegel-Van Flandern 算法。我针对 Danny F 要求的测试用例测试了剩余的算法,并添加了罗马儒略历的最后一个中午 (JD 2299160) 和罗马公历的第一个中午 (JD 2299161)。对于非整数儒略日,我只使用了 Meeus 算法,因为这是唯一一个说它支持非整数的算法。
内部算法是正确的,当您考虑到 0 年不存在时,它总是转换为公历。 Meeus 算法是正确的,当你允许它假设存在 0 年,并在 1582 年 10 月 4 日及更早的时候转换为 Julian,并在之后的日期转换为 Gregorian。
Richards 算法需要更正。我没有在某些点使用 floor 函数,而是使用 intdiv 函数代替每个“/”运算符,这更接近于 Richard 对算法的描述。在这些更正之后,测试用例是成功的。 (Richards 认为 0 年存在并且总是转换为 Gregorian。)
function jd_to_gd_rchrds ($J) {
$y=4716;
$v=3;
$j=1401;
$u=5;
$m=2;
$s=153;
$n=12;
$w=2;
$r=4;
$B=274277;
$p=1461;
$C=-38;
$f = $J + $j + intdiv(intdiv(4 * $J + $B,146097) * 3,4) + $C;
$e = $r * $f + $v;
$g = intdiv(($e % $p) , $r);
$h = $u * $g + $w;
$D = intdiv((($h%$s)) , $u) + 1;
$M = ((intdiv($h , $s) + $m) % $n) + 1;
$Y = intdiv($e , $p) - $y + intdiv($n + $m - $M,$n);
return "$M/$D/$Y";
}
测试用例结果:
Julian day PHP internal Meeus Richards 0 0/0/0 1/1/-4712 11/24/-4713 1 11/25/-4714 1/2/-4712 11/25/-4713 1507231.5 bad input 7/30/-586 bad input 1721420 12/26/-1 12/28/0 12/26/0 1721425 12/31/-1 1/2/1 12/31/0 1741426 10/5/55 10/7/55 10/5/55 2222444 9/28/1372 9/20/1372 9/28/1372 2299160 10/14/1582 10/4/1582 10/14/1582 2299161 10/15/1582 10/15/1582 10/15/1582 2399301.5 bad input 12/18/1856 bad input 2422444 4/29/1920 4/29/1920 4/29/1920
不,没有公历 0 年这样的东西,因为公历直到 1582 年才开始。天文算法在公历改革之前使用儒略历。是天文算法 return 零年。 Meeus 是绝对正确的。 -years 与 BC years 不同。查一下。任何使用 BC-AD 的算法都在使用天文学家未使用的完全虚构的公历。查一下。
void JDToDateMeeus(double jDNum, int *month, double *day, int *year) //converts a Julian day to a calendar Date
{
int alpha, A, B, C, D, E, Z;
double F;
jDNum += 0.5;
Z = jDNum; //Z == int so I = int part
F = jDNum - Z; //F = fractional part
if(Z < 2299161) //Julian?
A = Z;
else{ //Gregorian
alpha = (int)floor((Z - 1867216.25) / 36524.25);
A = Z + 1 + alpha - (int)floor(alpha / 4.0);
}
B = A + 1524;
C = (int)floor((B - 122.1) /365.25);
D = (int)floor(365.25 * C);
E = (int)floor((B - D) /30.6001);
*day = B - D - (int)floor(30.6001 * E) + F;
if( E < 14)
*month = E - 1;
else
*month = E - 13;
if(*month > 2)
*year = C - 4716;
else
*year = C - 4715;
}
double DateToJulianDayNumber(int month, double day, int year) //after Meeus
{
int M, Y, A, B;
double JD;
if(month > 2){
Y = year;
M = month;
}
else{
Y = year - 1;
M = month + 12;
}
if((year > 1582) || ((year == 1582) && ((month == 10 && day >= 5.0) || (month > 10)))){ //Gregorian
A = (int)floor(Y / 100.0);
B = 2 - A + (int)floor(A / 4.0);
}
else
B = 0;
JD = (int)floor(365.25 * (Y + 4716)) + (int)floor(30.6001 * (M + 1)) + day + B - 1524.5;
return JD;
}
这些函数不使用虚构的公历。根据教皇格雷戈里的历法改革,10 月 4 日之后是 1582 年的 15 日。 1582年以后使用公历闰年,有0年,之前的年写负号。
我使用下面的 php 代码将儒略日期转换为公历格式。
$ts=mktime(0, 0, 0, 1, 136, "20" . 20);
$mydate=date ('m-d-Y', $ts);
echo $mydate;
也可以参考https://www.juliandate.net进行日期转换