mirror of
https://github.com/infinet/lunar-calendar.git
synced 2026-01-12 21:17:00 +08:00
367 lines
10 KiB
C
367 lines
10 KiB
C
/*
|
|
* functions convert between Gregorian date and Julian date
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "astro.h"
|
|
|
|
/* convert a Gregorian date to JD from AA, p61 */
|
|
double g2jd(int year, int month, double day)
|
|
{
|
|
if (month <= 2) {
|
|
year -= 1;
|
|
month += 12;
|
|
}
|
|
|
|
int a, b, isjulian;
|
|
double jd;
|
|
|
|
a = (int) (year / 100);
|
|
isjulian = 0;
|
|
if (year < 1582) {
|
|
isjulian = 1;
|
|
} else if (year == 1582) {
|
|
if (month < 10)
|
|
isjulian = 1;
|
|
|
|
if (month == 10 && day <= 5.0)
|
|
isjulian = 1;
|
|
|
|
if (month == 10 && day > 5.0 && day < 15.0)
|
|
return 2299160.5;
|
|
}
|
|
|
|
if (isjulian)
|
|
b = 0;
|
|
else
|
|
b = 2 - a + (int) (a / 4);
|
|
|
|
/* 30.6001 is a hack Meeus suggested */
|
|
jd = (int) (365.25 * (year + 4716)) + (int) (30.6001 * (month + 1))
|
|
+ day + b - 1524.5;
|
|
return jd;
|
|
}
|
|
|
|
/* convert JD to Gregorian date */
|
|
GregorianDate jd2g(double jd)
|
|
{
|
|
jd += 0.5;
|
|
int a, b, c, d, e, z, alpha;
|
|
double f;
|
|
GregorianDate res;
|
|
|
|
z = (int) (jd);
|
|
f = fmod(jd, 1.0); // decimal part
|
|
if (z < 2299161) {
|
|
a = z;
|
|
} else {
|
|
alpha = (int) ((z - 1867216.25) / 36524.25);
|
|
a = z + 1 + alpha - (alpha / 4);
|
|
}
|
|
b = a + 1524;
|
|
c = (int) ((b - 122.1) / 365.25);
|
|
d = (int) (365.25 * c);
|
|
e = (int) ((b - d) / 30.6001);
|
|
res.day = (float) (b - d - (int) (30.6001 * e) + f);
|
|
if (e < 14)
|
|
res.month = e - 1;
|
|
else
|
|
res.month = e - 13;
|
|
|
|
if (res.month > 2)
|
|
res.year = c - 4716;
|
|
else
|
|
res.year = c - 4715;
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Args:
|
|
* isodt: datetime string in ISO format
|
|
* tz: a integer as timezone, e.g. -8 for UTC-8, 2 for UTC2
|
|
* fmt: like strftime but currently only support three format
|
|
* %y-%m-%d %H:%M:%S
|
|
* %y-%m-%d %H:%M
|
|
* %y-%m-%d
|
|
* ut: convert to UTC(adjust delta T)
|
|
* Return:
|
|
* Julian Day
|
|
*
|
|
*/
|
|
double jdptime(char *isodt, char *fmt, double tz, int isut)
|
|
{
|
|
char inputstr[40];
|
|
char *isodate;
|
|
char *isot;
|
|
|
|
strcpy(inputstr, isodt);
|
|
|
|
if (strcmp(fmt, "%y-%m-%d") == 0) {
|
|
char isodatestr[40];
|
|
char isotstr[40];
|
|
isodate = strcpy(isodatestr, isodt);
|
|
isot = strcpy(isotstr, "00:00:00");
|
|
} else if (strcmp(fmt, "%y-%m-%d %H:%M") == 0) {
|
|
isodate = strtok(inputstr, " ");
|
|
isot = strtok(NULL, " ");
|
|
} else {
|
|
isodate = strtok(inputstr, " ");
|
|
isot = strtok(NULL, " ");
|
|
}
|
|
|
|
char *sy;
|
|
char *sm, *sd, *shour, *sminute, *ssec;
|
|
double d, hour, minute, sec;
|
|
GregorianDate g;
|
|
|
|
sy = strtok(isodate, "-");
|
|
sm = strtok(NULL, "-");
|
|
sd = strtok(NULL, "-");
|
|
g.year = atoi(sy);
|
|
g.month = atoi(sm);
|
|
|
|
d = atof(sd);
|
|
|
|
shour = strtok(isot, ":");
|
|
sminute = strtok(NULL, ":");
|
|
ssec = strtok(NULL, ":");
|
|
hour = atof(shour);
|
|
minute = atof(sminute);
|
|
if (ssec)
|
|
sec = atof(ssec);
|
|
else
|
|
sec = 0;
|
|
|
|
d += (hour * 3600.0 + minute * 60.0 + sec) / 86400.0;
|
|
g.day = d;
|
|
|
|
return g2jd(g.year, g.month, g.day);
|
|
}
|
|
|
|
/* format a Julian Day to ISO format datetime
|
|
|
|
Args:
|
|
jd: time in JDTT
|
|
tz: a integer as timezone, e.g. -8 for UTC-8, 2 for UTC2
|
|
fmt: like strftime but currently only support three format
|
|
%y-%m-%d %H:%M:%S
|
|
%y-%m-%d %H:%M
|
|
%y-%m-%d
|
|
ut: convert to UTC(adjust delta T)
|
|
|
|
Return:
|
|
time string in ISO format, e.g. 1984-01-01 23:59:59
|
|
*/
|
|
size_t jdftime(char *isodt, double jd, char *fmt, double tz, int isut)
|
|
{
|
|
|
|
GregorianDate g;
|
|
double deltat, utsec, secs, jdut;
|
|
int isecs;
|
|
/* char isodt[ISODTLEN]; */
|
|
g = jd2g(jd);
|
|
|
|
deltat = isut ? deltaT(g.year, g.month) : 0;
|
|
|
|
/* convert jd to seconds, then adjust deltat */
|
|
utsec = jd * 86400.0 + tz * 3600.0 - deltat;
|
|
|
|
jdut = utsec / 86400.0;
|
|
|
|
/* get time in seconds directly to minimize round error */
|
|
secs = fmod(utsec + 43200.0, 86400.0);
|
|
|
|
if (strcmp(fmt, "%y-%m-%d %H:%M") == 0) {
|
|
isecs = (int)(floor(0.5 + secs / 60.0) * 60.0);
|
|
} else {
|
|
isecs = (int)(secs);
|
|
}
|
|
if (86400 == secs) {
|
|
jdut = floor(jdut) + 0.5;
|
|
isecs = 0;
|
|
}
|
|
|
|
g = jd2g(jdut);
|
|
|
|
/* use integer math hereafter */
|
|
int y, m, d, H, M, S, ms;
|
|
y = g.year;
|
|
m = g.month;
|
|
d = floor(g.day);
|
|
H = isecs / 3600;
|
|
ms = isecs % 3600;
|
|
M = ms / 60;
|
|
S = ms % 60;
|
|
|
|
if (strcmp(fmt, "%y-%m-%d") == 0) {
|
|
sprintf(isodt, "%04d-%02d-%02d", y, m, d);
|
|
} else if (strcmp(fmt, "%y-%m-%d %H:%M") == 0) {
|
|
sprintf(isodt, "%04d-%02d-%02d %02d:%02d", y, m, d, H, M);
|
|
} else {
|
|
sprintf(isodt, "%04d-%02d-%02d %02d:%02d:%02d", y, m, d, H, M, S);
|
|
}
|
|
|
|
return strlen(isodt);
|
|
}
|
|
|
|
/*
|
|
Polynomial Expressions for Delta T (ΔT) from nasa. Valid for -1999 to
|
|
+3000. http://eclipse.gsfc.nasa.gov/LEcat5/deltatpoly.html
|
|
|
|
Arg:
|
|
year: Gregorian year in integer
|
|
m: Gregorian month in integer
|
|
d: doesn't matter
|
|
Result:
|
|
ΔT in seconds
|
|
|
|
verfify against historical records from NASA
|
|
|
|
year history computed diff
|
|
-500 17190 17195.37 5.4
|
|
-400 15530 15523.84 6.2
|
|
-300 14080 14071.97 8.0
|
|
-200 12790 12786.59 3.4
|
|
-100 11640 11632.52 7.5
|
|
0 10580 10578.95 1.0
|
|
100 9600 9592.45 7.6
|
|
200 8640 8636.34 3.7
|
|
300 7680 7676.67 3.3
|
|
400 6700 6694.67 5.3
|
|
500 5710 5705.50 4.5
|
|
600 4740 4734.89 5.1
|
|
700 3810 3809.04 1.0
|
|
800 2960 2951.97 8.0
|
|
900 2200 2197.11 2.9
|
|
1000 1570 1571.65 1.7
|
|
1100 1090 1086.99 3.0
|
|
1200 740 735.10 4.9
|
|
1300 490 490.98 1.0
|
|
1400 320 321.09 1.1
|
|
1500 200 197.85 2.2
|
|
1600 120 119.55 0.5
|
|
1700 9 8.90 0.1
|
|
1750 13 13.44 0.4
|
|
1800 14 13.57 0.4
|
|
1850 7 7.16 0.2
|
|
1900 -3 -2.12 0.9
|
|
1950 29 29.26 0.3
|
|
1955 31 31.23 0.1
|
|
1960 33 33.31 0.1
|
|
1965 36 36.13 0.4
|
|
1970 40 40.66 0.5
|
|
1975 46 45.94 0.4
|
|
1980 50 50.93 0.4
|
|
1985 54 54.60 0.3
|
|
1990 57 57.20 0.3
|
|
1995 61 61.17 0.4
|
|
2000 64 64.00 0.2
|
|
2005 65 64.85 0.1
|
|
|
|
Also, JPL uses "last known leap-second is used over any future interval".
|
|
It causes the large error when compare apparent sun/moon position with JPL
|
|
Horizon
|
|
|
|
*/
|
|
double deltaT(int year, int month) {
|
|
double y, m, u;
|
|
m = (double) month;
|
|
y = year + (m - 0.5) / 12.0;
|
|
if (year < -500) {
|
|
u = (year - 1820) / 100.0;
|
|
return -20 + 32 * u * u;
|
|
} else if (year < 500) {
|
|
u = y / 100.0;
|
|
return 10583.6 + u * (-1014.41 +
|
|
u * (33.78311 +
|
|
u * (-5.952053 +
|
|
u * (-0.1798452 +
|
|
u * (0.022174192 +
|
|
u * 0.0090316521)))));
|
|
} else if (year < 1600) {
|
|
u = (y -1000) / 100.0;
|
|
return 1574.2 + u * (-556.01 +
|
|
u * (71.23472 +
|
|
u * (0.319781 +
|
|
u * (-0.8503463 +
|
|
u * (-0.005050998 +
|
|
u * 0.0083572073)))));
|
|
} else if (year < 1700) {
|
|
u = y -1600;
|
|
return 120 + u * (-0.9808 +
|
|
u * (- 0.01532 +
|
|
u / 7129));
|
|
} else if (year < 1800) {
|
|
u = y - 1700;
|
|
return 8.83 + u * (0.1603 +
|
|
u * (-0.0059285 +
|
|
u * (0.00013336 +
|
|
u / -1174000)));
|
|
} else if (year < 1860) {
|
|
u = y - 1800;
|
|
return 13.72 + u * (-0.332447 +
|
|
u * (0.0068612 +
|
|
u * (0.0041116 +
|
|
u * (-0.00037436 +
|
|
u * (0.0000121272 +
|
|
u * (-0.0000001699 +
|
|
u * 0.000000000875))))));
|
|
} else if (year < 1900) {
|
|
u = y - 1860;
|
|
return 7.62 + u * (0.5737 +
|
|
u * (-0.251754 +
|
|
u * (0.01680668 +
|
|
u * (-0.0004473624 +
|
|
u / 233174))));
|
|
} else if (year < 1920) {
|
|
u = y - 1900;
|
|
return -2.79 + u * (1.494119 +
|
|
u * (-0.0598939 +
|
|
u * (0.0061966 +
|
|
u * -0.000197)));
|
|
} else if (year < 1941) {
|
|
u = y - 1920;
|
|
return 21.20 + u * (0.84493 +
|
|
u * (-0.076100 +
|
|
u * 0.0020936));
|
|
} else if (year < 1961) {
|
|
u = y - 1950;
|
|
return 29.07 + u * (0.407 +
|
|
u * (-1.0 / 233 +
|
|
u / 2547));
|
|
} else if (year < 1986) {
|
|
u = y - 1975;
|
|
return 45.45 + u * (1.067 +
|
|
u * (-1.0 / 260 +
|
|
u / -718));
|
|
} else if (year < 2005) {
|
|
u = y -2000;
|
|
return 63.86 + u * (0.3345 +
|
|
u * (-0.060374 +
|
|
u * (0.0017275 +
|
|
u * (0.000651814 +
|
|
u * 0.00002373599))));
|
|
/*
|
|
* else:
|
|
* JPL uses "last known leap-second is used over any future interval" .
|
|
* It causes the large error when compare apparent sun/moon position
|
|
* with JPL Horizon
|
|
*
|
|
* return 67.182963
|
|
*
|
|
*/
|
|
} else if (year < 2050) {
|
|
u = y -2000;
|
|
return 62.92 + u * (0.32217 + u * 0.005589);
|
|
} else if (year < 2150) {
|
|
u = (y - 1820.0) / 100.0;
|
|
return -20 + 32 * u * u - 0.5628 * (2150 - y);
|
|
} else {
|
|
u = (y - 1820.0) / 100.0;
|
|
return -20 + 32 * u * u;
|
|
}
|
|
}
|