/*
 * geoTime.c --
 *
 *	This file defines functions for manipulate date and times
 * 
 * Copyright (c) 2004 Gordon D. Carrie
 *
 * Licensed under the Open Software License version 2.1
 *
 * Please send feedback to user0@tkgeomap.org
 *
 * @(#) $Id: geoTime.c,v 1.6 2007/04/20 15:08:58 tkgeomap Exp $
 *
 **********************************************************************
 */

#include <stdlib.h>
#include <math.h>
#include <tcl.h>

#include "geography.h"

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_CalSet --
 *
 * 	This function assigns values to a GeoTime_Cal structure.
 *
 * Results:
 * 	Assigns values to a calendar time structure.
 *
 * Side effects:
 * 	None.
 *------------------------------------------------------------------------
 */

struct GeoTime_Cal
GeoTime_CalSet(year, month, day, hour, minute, second)
    int year;
    int month;
    int day;
    int hour;
    int minute;
    double second;
{
    struct GeoTime_Cal cal;
    int s;
    double fsec;

    s = floor(second);
    fsec = second - s;
    minute += s / 60;
    s %= 60;
    hour += minute / 60;
    minute %= 60;
    day += hour / 24;
    hour %= 24;

    cal.year = year;
    cal.month = month;
    cal.day = day;
    cal.hour = hour;
    cal.minute = minute;
    cal.second = s + fsec;
    return cal;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_JulSet --
 *
 * 	This function assigns values to a GeoTime_Jul structure.
 *
 * Results:
 * 	Assigns values to a Julian time structure.
 *
 * Side effects:
 * 	None.
 *
 *------------------------------------------------------------------------
 */

struct GeoTime_Jul
GeoTime_JulSet(day, second)
    int day;
    double second;
{
    struct GeoTime_Jul jul;
    int s;
    double fsec;

    s = floor(second);
    fsec = second - s;
    day += s / 86400;
    s %= 86400;
    jul.day = day;
    jul.second = s + fsec;
    return jul;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_CalToJul --
 *
 *	This function converts a calendar time representation to Julian date.
 *
 * Results:
 * 	Return value gives the Julian representation of a calendar time.
 * Side effects:
 *------------------------------------------------------------------------
 */

struct GeoTime_Jul
GeoTime_CalToJul(cal)
    struct GeoTime_Cal cal;	/* Time instant in calendar format */
{
    int year = cal.year;
    int month = cal.month;
    int day = cal.day;
    int hour = cal.hour;
    int minute = cal.minute;
    double second = cal.second;
    struct GeoTime_Jul jul;	/* Return value */
    int d;			/* Number of days in second */

    /*
     * Compute Julian day.
     * Ref. Henry F. Fliegel and Thomas C. Van Flandern, from
     * http://serendipity.magnet.ch/hermetic/cal_stud/jdn.htm or
     * http://aa.usno.navy.mil/faq/docs/JD_Formula.html
     */

    jul.day =   (1461 * (year + 4800 + (month - 14) / 12)) / 4
	+ (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12
	- (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4
	+ day - 32075;

    /*
     * Compute total seconds.  If greater than 1 day, increment days.
     */

    second = 3600.0 * hour + 60.0 * minute + second;
    d = floor(second / 86400.0);
    jul.day += d;
    jul.second = second - d * 86400.0;
    return jul;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_GetCal --
 *
 *	This function retrieves a calendar date and time from a GeoTime.
 *
 * Results:
 * 	Return value gives the calendar representation of a Julian time.
 * Side effects:
 * 	None.
 *
 *------------------------------------------------------------------------
 */

struct GeoTime_Cal
GeoTime_JulToCal(jul)
    struct GeoTime_Jul jul;	/* Time instant in calendar form */
{
    struct GeoTime_Cal cal;	/* Return value, calendar equivalent of jul */
    int l, n, i, j;		/* Intermediaries */
    int h, m;			/* Hour, minute */
    int s;			/* Second */
    double fsec;

    /*
     * Compute month, day, and year from Julian date
     * Ref. Henry F. Fliegel and Thomas C. Van Flandern,
     * http://serendipity.magnet.ch/hermetic/cal_stud/jdn.htm
     */

    l = jul.day + 68569;
    n = (4 * l) / 146097;
    l = l - (146097 * n + 3) / 4;
    i = (4000 * (l + 1)) / 1461001;
    l = l - (1461 * i) / 4 + 31;
    j = (80 * l) / 2447;
    cal.day = l - (2447 * j) / 80;
    l = j / 11;
    cal.month = j + 2 - (12 * l);
    cal.year = 100 * (n - 49) + i + l;

    /*
     * Convert seconds to clock time
     */

    s = floor(jul.second);
    fsec = jul.second - s;
    h = s / 3600;
    s %= 3600;
    m = s / 60;
    s %= 60;
    if (h > 23) {
	cal.day += h / 24;
	h %= 24;
    }
    cal.hour = h;
    cal.minute = m;
    cal.second = s + fsec;
    return cal;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_Incr --
 *
 *	This function increments a time instant.
 *
 * Results:
 * 	This given time is incremented.
 *
 * Side effects:
 * 	None.
 *
 *------------------------------------------------------------------------
 */

void
GeoTime_Incr(jul, ds)
    struct GeoTime_Jul *jul;	/* GeoTime to increment */
    double ds;			/* Increment, in seconds */
{
    int d;
    double s;

    s = jul->second + ds;
    d = floor(s / 86400.0);
    jul->day += d;
    jul->second = s - d * 86400.0;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_Cmp --
 *
 *	Compare two times.
 *
 * Results:
 * 	Return value is a collating code.
 *	    -1 if jul1 is before jul2
 *	     0 if jul1 is same time as jul2
 *	     1 if jul1 is after jul2
 *
 * Side effects:
 * 	None.
 *
 *------------------------------------------------------------------------
 */

int
GeoTime_Cmp(jul1, jul2)
    struct GeoTime_Jul jul1, jul2;	/* GeoTimes to compare */
{

    jul1 = GeoTime_JulSet(jul1.day, jul1.second);
    jul2 = GeoTime_JulSet(jul2.day, jul2.second);
    if (jul1.day > jul2.day) {
	return 1;
    }
    if (jul1.day < jul2.day) {
	return -1;
    }
    if (jul1.second > jul2.second) {
	return 1;
    }
    if (jul1.second < jul2.second) {
	return -1;
    }
    return 0;
}

/*
 *------------------------------------------------------------------------
 *
 * GeoTime_Diff --
 *
 *	Subtract one time from another.
 *
 * Results:
 * 	Return value is the difference in seconds between two times.
 *
 * Side effects:
 * 	None.
 *
 *------------------------------------------------------------------------
 */

double
GeoTime_Diff(jul1, jul2)
    struct GeoTime_Jul jul1, jul2;	/* GeoTimes to compare */
{

    jul1 = GeoTime_JulSet(jul1.day, jul1.second);
    jul2 = GeoTime_JulSet(jul2.day, jul2.second);
    return (jul1.day - jul2.day) * 86400.0 + jul1.second - jul2.second;
}


syntax highlighted by Code2HTML, v. 0.9.1