/* julian.c -- functions to handle julian dates 
 *
 * This file is part of TUA.
 * 
 *   Copyright (C) 1991,96  Lele Gaifax
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the license, or (at
 *   your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * NOTE:
 * The original package is a public domain ObjC class, Julian, written
 * by Charles G. Bennett. I simply extracted from there the functions I need
 * and translated them in C.
 * The complete sources, with an example app and manual page, are available
 * at any NeXT archive site (cs.orst.edu or sonata.cc.purdue.edu are the most
 * popular) .
 */

#include "tua.h"

extern double EXFUN(modf, (double, double *));

Julian_t
DEFUN (d_to_julian, (date, julian),
       Date_t date AND Julian_t * julian)
{
  int a,b;
  double retVal;
  float yearCorr;
  int year = date.Year;
  int month = date.Month;
  int day = date.Day;
  
  /* Correct for negative year */

  yearCorr = (year > 0? 0.0 : 0.75);
  if (month <= 2)
    {
      year--;
      month += 12;
    }
  b = 0;

  /* Deal with Gregorian reform */
  if(((year * 10000.0) + (month * 100.0) + day) >= 15821015.0)
    {
      a = year / 100;
      b = (2-a)+(a/4);
    }
  
  retVal = (long) ( (365.25 * year) - yearCorr);
  retVal += (long) (30.6001 * (month+1));
  retVal += (long) day;
  retVal += 1720994L;
  retVal += (long) b;

  if (julian)
    *julian = retVal;
  
  return (retVal);
}
  
Julian_t
DEFUN (dt_to_julian, (date, time, julian),
       Date_t date AND Time_t time AND Julian_t * julian)
{
  double retVal;
  double retFraction;
  int hour = time.Hour;
  int min = time.Min;
  int sec = time.Sec;
  
  retVal = d_to_julian (date, 0);
  
  retFraction = (double) ((double) hour/24.0) + (double) ((double) min/1440.0) + (double) ((double) sec/86400.0);

  if (julian)
    *julian = retVal + retFraction;
  
  return  (retVal + retFraction);
}

Date_t
DEFUN (julian_to_d, (julian, date),
       Julian_t julian AND Date_t * date)
{
  long a,b,c,d,e,z, alpha;
  Date_t dat;
  
  z =  (long) (julian + 1);
  /* Deal with Gregorian reform */
  if( z < 2299161L)
    a = z;
  else
    {
      alpha = (long) (( z - 1867216.25) / 36524.25);
      a = z + 1 + alpha - alpha / 4;
    }
  b = a + 1524;
  c = (long) (( b - 122.1) / 365.25);
  d = (long) (365.25 * c);
  e = (long) (( b - d ) / 30.6001);

  dat.Day = (int) b - d - (long) (30.6001 * e);
  dat.Month = (int) ( e < 13.5) ? e - 1 : e - 13;
  dat.Year = (int) (dat.Month > 2.5) ? (c - 4716) : c - 4715;
  dat.DayOfWeek = julian_dow (julian);
  
  if (date)
    *date = dat;

  return dat;
}
     
void
DEFUN (julian_to_dt, (julian, date, time),
       Julian_t julian AND Date_t * date AND Time_t * time)
{
  double fractionalPart;
  double tmpResult;
  double integerPart;

  julian_to_d (julian, date);

  /* days.fractionalDays */
  fractionalPart = modf ((double)julian, &integerPart);
  
  tmpResult = fractionalPart * 24.0;
  fractionalPart = modf (tmpResult, &integerPart);
  time->Hour = (int) integerPart;
  
  tmpResult  = fractionalPart *  60.0;
  fractionalPart = modf (tmpResult, &integerPart);
  time->Min = (int) integerPart;
  
  tmpResult  = fractionalPart *  60.0;
  fractionalPart = modf (tmpResult, &integerPart);

  /*
   * lelegaifax: adjust seconds rounding the fractional part
   * otherwise you'll sometime get a "one-second-less" time.
   */
  if (fractionalPart > 0.5)
    integerPart++;
  
  time->Sec = (int) integerPart;
}
     
CONST char *
DEFUN (julian_to_asc, (julian),
       Julian_t julian)
{
  static char buffer[sizeof "18 Mar 1968 - 10:14:00"];
  Date_t date;
  CONST char *mnames[] =
    { "ERR", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  Time_t time;

  julian_to_dt (julian, &date, &time);
  sprintf (buffer, "%02d %s %04d - %02d:%02d:%02d",
	   date.Day, mnames[date.Month], date.Year,
	   time.Hour, time.Min, time.Sec);
  return buffer;
}

int
DEFUN (julian_dow, (julian),
       Julian_t julian)
{
  return (int) (((long) (julian + 2) % 7));
}

#include <time.h>

double
DEFUN_VOID (julian_current_date)
{
  Date_t dat;
  Time_t tim;
  time_t now = time(0);
  struct tm *theTime;

  theTime = localtime(&now);
  dat.Year = theTime->tm_year+1900;
  dat.Month = theTime->tm_mon+1;
  dat.Day = theTime->tm_mday;
  tim.Hour = theTime->tm_hour;
  tim.Min = theTime->tm_min;
  tim.Sec = theTime->tm_sec;
  return dt_to_julian (dat, tim, 0);
}
