1 /*
2  *  This library is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU Lesser General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This library is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  Lesser General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  *  Copyright (C) 2000 - 2005 Liam Girdwood
17  */
18 
19 #include <time.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <math.h>
23 #include <libnova/julian_day.h>
24 #include <libnova/utility.h>
25 
26 /* Standard Win32 apps do not have POSIX support. */
27 #ifndef __WIN32__
28 #include <sys/time.h>
29 #endif
30 
31 /*! \fn double ln_get_julian_day (struct ln_date * date)
32 * \param date Date required.
33 * \return Julian day
34 *
35 * Calculate the julian day from a calendar day.
36 * Valid for positive and negative years but not for negative JD.
37 */
38 /* Formula 7.1 on pg 61
39 */
ln_get_julian_day(struct ln_date * date)40 double ln_get_julian_day (struct ln_date * date)
41 {
42     double JD;
43     double days;
44     int a,b;
45     struct ln_date local_date;
46 
47 	/* create local copy */
48     memcpy (&local_date, date, sizeof (struct ln_date));
49 
50     /* check for month = January or February */
51     if (local_date.months < 3 ) {
52         local_date.years--;
53 	    local_date.months += 12;
54 	}
55 
56 	a = local_date.years / 100;
57 
58 	/* check for Julian or Gregorian calendar (starts Oct 4th 1582) */
59 	if (local_date.years > 1582 ||
60 		(local_date.years == 1582 &&
61 		(local_date.months > 10 ||
62 		(local_date.months == 10 && local_date.days >= 4)))) {
63 	    /* Gregorian calendar */
64 	    b = 2 - a + (a / 4);
65 	} else {
66 	    /* Julian calendar */
67 	    b = 0;
68 	}
69 
70 	/* add a fraction of hours, minutes and secs to days*/
71 	days = local_date.days + (double)(local_date.hours / 24.0) + (double)(local_date.minutes / 1440.0) + (double)(local_date.seconds /  86400.0);
72 
73 	/* now get the JD */
74 	JD = (int)(365.25 * (local_date.years + 4716)) +
75 	    (int)(30.6001 * (local_date.months + 1)) + days + b - 1524.5;
76 
77 	return JD;
78 }
79 
80 
81 /*! \fn unsigned int ln_get_day_of_week (struct ln_date *date)
82 * \param date Date required
83 * \return Day of the week
84 *
85 * Calculate the day of the week.
86 * Returns 0 = Sunday .. 6 = Saturday
87 */
ln_get_day_of_week(struct ln_date * date)88 unsigned int ln_get_day_of_week (struct ln_date *date)
89 {
90     unsigned int day;
91     double JD;
92 
93     /* get julian day */
94     JD = ln_get_julian_day (date);
95     JD += 1.5;
96     day = (int)JD % 7;
97 
98     return day;
99 }
100 
101 /*! \fn void ln_get_date (double JD, struct ln_date * date)
102 * \param JD Julian day
103 * \param date Pointer to new calendar date.
104 *
105 * Calculate the date from the Julian day
106 */
ln_get_date(double JD,struct ln_date * date)107 void ln_get_date (double JD, struct ln_date * date)
108 {
109    int A,a,B,C,D,E;
110    double F,Z;
111 
112    JD += 0.5;
113    Z = (int) JD;
114    F = JD - Z;
115 
116    if (Z < 2299161)
117        A = (int) Z;
118    else {
119        a = (int) ((Z - 1867216.25) / 36524.25);
120        A = (int) (Z + 1 + a - (int)(a / 4));
121    }
122 
123    B = A + 1524;
124    C = (int) ((B - 122.1) / 365.25);
125    D = (int) (365.25 * C);
126    E = (int) ((B - D) / 30.6001);
127 
128    /* get the hms */
129    date->hours = (int) (F * 24);
130    F -= (double)date->hours / 24;
131    date->minutes = (int) (F * 1440);
132    F -= (double)date->minutes / 1440;
133    date->seconds = F * 86400;
134 
135    /* get the day */
136    date->days = B - D - (int)(30.6001 * E);
137 
138    /* get the month */
139    if (E < 14)
140        date->months = E - 1;
141    else
142        date->months = E - 13;
143 
144    /* get the year */
145    if (date->months > 2)
146        date->years = C - 4716;
147    else
148        date->years = C - 4715;
149 }
150 
151 /*! \fn void ln_get_date_from_timet (time_t * t, struct ln_date * date)
152 * \param t system time
153 * \param date Pointer to new calendar date.
154 *
155 * Set date from system time
156 */
ln_get_date_from_timet(time_t * t,struct ln_date * date)157 void ln_get_date_from_timet (time_t * t, struct ln_date * date)
158 {
159 	struct tm gmt;
160 
161 	/* convert to UTC time representation */
162 	gmtime_r (t, &gmt);
163 
164 	ln_get_date_from_tm (&gmt, date);
165 }
166 
167 /*! \fn void ln_get_date_from_tm (struct tm * t, struct ln_date * date)
168 * \param tm system tm structure
169 * \param date Pointer to new calendar date.
170 *
171 * Set date from system tm structure
172 */
ln_get_date_from_tm(struct tm * t,struct ln_date * date)173 void ln_get_date_from_tm (struct tm * t, struct ln_date * date)
174 {
175 	/* fill in date struct */
176 	date->seconds = t->tm_sec;
177 	date->minutes = t->tm_min;
178 	date->hours = t->tm_hour;
179 	date->days = t->tm_mday;
180 	date->months = t->tm_mon + 1;
181 	date->years = t->tm_year + 1900;
182 }
183 
184 /*! \fn void ln_get_date_from_sys (struct ln_date * date)
185 * \param date Pointer to store date.
186 *
187 * Calculate local date from system date.
188 */
ln_get_date_from_sys(struct ln_date * date)189 void ln_get_date_from_sys (struct ln_date * date)
190 {
191 	struct tm * gmt;
192 	struct timeval tv;
193 	struct timezone tz;
194 
195 	/* get current time with microseconds precission*/
196 	gettimeofday (&tv, &tz);
197 
198 	/* convert to UTC time representation */
199 	gmt = gmtime(&tv.tv_sec);
200 
201 	/* fill in date struct */
202 	date->seconds = gmt->tm_sec + ((double)tv.tv_usec / 1000000);
203 	date->minutes = gmt->tm_min;
204 	date->hours = gmt->tm_hour;
205 	date->days = gmt->tm_mday;
206 	date->months = gmt->tm_mon + 1;
207 	date->years = gmt->tm_year + 1900;
208 }
209 
210 
211 /*! \fn double ln_get_julian_from_timet (time_t * in_time)
212 * \param time The time_t.
213 * \return Julian day.
214 *
215 * Calculate Julian day from time_t.
216 */
ln_get_julian_from_timet(time_t * in_time)217 double ln_get_julian_from_timet (time_t * in_time)
218 {
219 	// 1.1.1970 = JD 2440587.5
220 	return (double)(2440587.5 + (double)(*in_time / (double) 86400.0));
221 }
222 
223 #ifndef HAVE_ROUND
224 
225 /* Simple round to nearest */
round(double x)226 double round (double x)
227 {
228 	return floor(x + 0.5);
229 }
230 
231 #endif /* ! HAVE_ROUND */
232 
233 /*! \fn void ln_get_timet_from_julian (double JD, time_t * in_time)
234 * \param JD Julian day
235 * \param in_time Pointer to store time_t
236 *
237 * Calculate time_t from julian day
238 */
ln_get_timet_from_julian(double JD,time_t * in_time)239 void ln_get_timet_from_julian (double JD, time_t * in_time)
240 {
241 	*in_time = (time_t)round((JD - (double) 2440587.5) * (double) 86400.0);
242 }
243 
244 /*! \fn double ln_get_julian_from_sys()
245 * \return Julian day (UT)
246 *
247 * Calculate the julian day (UT) from the local system time
248 */
ln_get_julian_from_sys()249 double ln_get_julian_from_sys()
250 {
251 	double JD;
252 	struct ln_date date;
253 
254 	/* get sys date */
255 	ln_get_date_from_sys (&date);
256 	JD = ln_get_julian_day (&date);
257 
258 	return JD;
259 }
260 
261 /*! \fn double ln_get_julian_local_date(struct ln_zonedate* zonedate)
262 * \param zonedate Local date
263 * \return Julian day (UT)
264 *
265 * Calculate Julian day (UT) from zone date
266 */
ln_get_julian_local_date(struct ln_zonedate * zonedate)267 double ln_get_julian_local_date(struct ln_zonedate* zonedate)
268 {
269 	struct ln_date date;
270 
271 	ln_zonedate_to_date (zonedate, &date);
272 
273 	return ln_get_julian_day (&date);
274 }
275 
276 /*! \fn void ln_get_local_date (double JD, struct ln_zonedate * zonedate)
277 * \param JD Julian day
278 * \param zonedate Pointer to new calendar date.
279 *
280 * Calculate the zone date from the Julian day (UT). Gets zone info from
281 * system using either _timezone or tm_gmtoff fields.
282 */
ln_get_local_date(double JD,struct ln_zonedate * zonedate)283 void ln_get_local_date (double JD, struct ln_zonedate * zonedate)
284 {
285 	struct ln_date date;
286 	time_t curtime;
287 	struct tm *loctime;
288 	long gmtoff;
289 
290 	ln_get_date (JD, &date);
291 
292 	/* add day light savings time and hour angle */
293 #ifdef __WIN32__
294  	_tzset();
295  	gmtoff = _timezone;
296  	if (_daylight)
297  		gmtoff += 3600;
298 #else
299  	curtime = time (NULL);
300  	loctime = localtime(&curtime);
301  	gmtoff = loctime->tm_gmtoff;
302 	// otherwise there is no reasonable way how to get that:(
303 	// tm_gmtoff already included DST
304 #endif
305 	ln_date_to_zonedate (&date, zonedate, gmtoff);
306 }
307 
308 /*! \fn int ln_get_date_from_mpc (struct ln_date* date, char* mpc_date)
309 * \param date Pointer to new calendar date.
310 * \param mpc_date Pointer to string MPC date
311 * \return 0 for valid date
312 *
313 * Calculate the local date from the a MPC packed date.
314 * See http://cfa-www.harvard.edu/iau/info/PackedDates.html for info.
315 */
ln_get_date_from_mpc(struct ln_date * date,char * mpc_date)316 int ln_get_date_from_mpc (struct ln_date* date, char* mpc_date)
317 {
318 	char year[3];
319 	char month[2];
320 	char day[2];
321 
322 	/* is mpc_date correct length */
323 	if (strlen(mpc_date) !=5)
324 		return -1;
325 
326 	/* get the century */
327 	switch (*mpc_date) {
328 		case 'I':
329 			date->years = 1800;
330 		break;
331 		case 'J':
332 			date->years = 1900;
333 		break;
334 		case 'K':
335 			date->years = 2000;
336 		break;
337 		default:
338 			return -1;
339 	}
340 
341 	/* get the year */
342 	year[0] = *(mpc_date + 1);
343 	year[1] = *(mpc_date + 2);
344 	year[2] = 0;
345 	date->years += strtol (year,0,10);
346 
347 	/* month */
348 	month[0] = *(mpc_date + 3);
349 	month[1] = 0;
350 	date->months = strtol (month, 0, 16);
351 
352 	/* day */
353 	day[0] = *(mpc_date + 4);
354 	day[1] = 0;
355 	date->days = strtol (day, 0, 31);
356 
357 	/* reset hours,min,secs to 0 */
358 	date->hours = 0;
359 	date->minutes = 0;
360 	date->seconds = 0;
361 	return 0;
362 }
363 
364 /*! \fn double ln_get_julian_from_mpc (char* mpc_date)
365 * \param mpc_date Pointer to string MPC date
366 * \return Julian day.
367 *
368 * Calculate the julian day from the a MPC packed date.
369 * See http://cfa-www.harvard.edu/iau/info/PackedDates.html for info.
370 */
ln_get_julian_from_mpc(char * mpc_date)371 double ln_get_julian_from_mpc (char* mpc_date)
372 {
373 	struct ln_date date;
374 	double JD;
375 
376 	ln_get_date_from_mpc(&date, mpc_date);
377 	JD = ln_get_julian_day(&date);
378 
379 	return JD;
380 }
381 
382 /*! \fn void ln_date_to_zonedate (struct ln_date * date, struct ln_zonedate * zonedate, long gmtoff)
383 * \param zonedate Ptr to zonedate
384 * \param gmtoff Offset in seconds from UT
385 * \param date Ptr to date
386 *
387 * Converts a ln_date (UT) to a ln_zonedate (local time).
388 */
ln_date_to_zonedate(struct ln_date * date,struct ln_zonedate * zonedate,long gmtoff)389 void ln_date_to_zonedate (struct ln_date * date, struct ln_zonedate * zonedate, long gmtoff)
390 {
391 	double jd;
392 	struct ln_date dat;
393 
394 	jd = ln_get_julian_day (date);
395 	jd += gmtoff / 86400.0;
396 	ln_get_date (jd, &dat);
397 
398 	zonedate->years   = dat.years;
399 	zonedate->months  = dat.months;
400 	zonedate->days    = dat.days;
401 	zonedate->hours   = dat.hours;
402 	zonedate->minutes = dat.minutes;
403 	zonedate->seconds = dat.seconds;
404 
405 	zonedate->gmtoff  = gmtoff;
406 }
407 
408 /*! \fn void ln_zonedate_to_date (struct ln_zonedate * zonedate, struct ln_date * date)
409 * \param zonedate Ptr to zonedate
410 * \param date Ptr to date
411 *
412 * Converts a ln_zonedate (local time) to a ln_date (UT).
413 */
ln_zonedate_to_date(struct ln_zonedate * zonedate,struct ln_date * date)414 void ln_zonedate_to_date (struct ln_zonedate * zonedate, struct ln_date * date)
415 {
416 	double jd;
417 	struct ln_date dat;
418 
419 	dat.years   = zonedate->years;
420 	dat.months  = zonedate->months;
421 	dat.days    = zonedate->days;
422 	dat.hours   = zonedate->hours;
423 	dat.minutes = zonedate->minutes;
424 	dat.seconds = zonedate->seconds;
425 
426 	jd = ln_get_julian_day (&dat);
427 	jd -= zonedate->gmtoff / 86400.0;
428 	ln_get_date (jd, date);
429 }
430