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