/*
   Modified BLP 8/28/95 for PSPP.

   Original sources for julcal.c and julcal.h can be found at
   ftp.cdrom.com in /pub/algorithms/c/julcal10/{julcal.c,julcal.h}.
 */
/* 
   Julian (sense 1) date routines, handling both Julian (sense 2) and
   Gregorian calendars

   SYNOPSIS
   long juldn (struct tm *bdt)
   long juldnj (struct tm *bdt, long Transition)
   long juldnd (struct tm *bdt, struct tm *Transition_date)

   struct tm *julcd (long J)
   struct tm *julcdj (long J, long Transition)
   struct tm *julcdd (long J, struct tm *Transition_date)

   extern long jul_transition;

   DESCRIPTION

   juldn* returns the Julian day number (days since Jan 1, 4713 B.C.)
   for the date specified by the struct tm (a broken down time)
   pointed to by 'bdt'.  Only the tm_year, tm_mon, and tm_mday fields
   are examined.  If the month or day fields are out of their normal
   range, they are adjusted.  The tm_wday and tm_yday fields are also
   set.

   julcd* returns a pointer to a struct tm filled in with the date
   corresponding to the Julian day number 'J'.  Five fields are set:
   tm_year, tm_mon, tm_mday, tm_wday, and tm_yday.  The pointer is to
   a static structure which is reused on each call.

   For both juldn and julcd, the Gregorian calendar is assumed
   provided the Julian day number falls on or after the value in the
   global variable 'jul_transition', otherwise the Julian calendar is
   used.  'jul_transition' is initialized to 2361222 (or September 14,
   1752), as in the United Kingdom and the colonies.  A different
   transition can be specified by Julian day number (for juldnj or
   julcdj) or Gregorian date (for juldnd or julcdd).  Alternatively,
   'jul_transition' can be reset.  If the transition is specified by
   date, ensure it is interpreted as a Gregorian date by using julcdj
   with a small number:

   jul_transition = julcdj(&my_transition_date, 1L);

   Algorithm is valid from 4713 B.C. to 19,999 A.D.  For caveats,
   see below.

   Aside: In a struct tm, the tm_year field is "year-1900".  For the
   year 1 A.D., that would be -1899.  There was no year 0 (the number
   zero had not been invented!), so the previous year was 1 B.C.,
   which is represented by -1900.

   HISTORY

   $Id: julcal.c%v 1.10 1992/12/13 03:15:59 jrv Exp jrv $

   $Log: julcal.c%v $
   * Revision 1.10  1992/12/13  03:15:59  jrv
   * default transition date is that for the United Kingdom
   *
   * Revision 1.9  1992/12/13  03:07:07  jrv
   * juldnj gives correct value even if month is outside normal range.
   * juldnj normalizes mday, month, and year.
   * _juldnj introduced to let julcdj set yday without causing infinite recursion.
   *
   * Revision 1.8  1992/12/08  00:23:38  jrv
   * Test program moved to a separate file.
   *
   * Revision 1.7  1992/12/06  01:57:20  jrv
   * julcd* return pointers to a static struct tm, like localtime and gmtime.
   *
   * Revision 1.6  1992/12/05  23:14:58  jrv
   * The default transition date is a global variable, initialized to 15 Oct 1582.
   * Some variable names changed.  All variables are lower case.
   *
   * Revision 1.5  1992/12/05  22:20:11  jrv
   * juldnd accepts a struct tm (a "broken-down time") instead of a 
   * unique struct.  julcdd produces a struct tm (including tm_wday 
   * and tm_yday fields) instead of a unique struct.
   *
   * Revision 1.4  1992/12/05  20:04:51  jrv
   * Test program prints input values, then computed values.
   * Test program is silent by default.
   *
   * Revision 1.3  1992/12/04  02:57:54  jrv
   * Reformatted to more standard C.
   * Eliminated some redundant typedefs.
   * Type Year is now an int rather than a long.
   * Some variables converted to lower case.
   *
   * Revision 1.2  1992/12/04  02:15:58  jrv
   * A Year is no longer unsigned, so years BC work.
   * Regression test driver added.
   *
   * Revision 1.1  1992/12/04  00:21:37  jrv
   * Initial revision
   *

   Translated from Pascal to C by Jim Van Zandt, July 1992.

   Error-free translation based on error-free PL/I source

   Based on Pascal code copyright 1985 by Michael A. Covington,
   published in P.C. Tech Journal, December 1985, based on formulae
   appearing in Astronomical Formulae for Calculators by Jean Meeus

   Reconversion to normal Julian epoch, integer arithmetic and
   4000-year correction by John W. Kennedy

   [The 4000-year adjustment is controversial.  It is not mentioned in
   the paper "Calendrical Calculations" by Nachum Dershowitz and
   Edward M.  Reingold in Software Practice and Experience, v 20 n 9
   pp 899-928 (Sep 1990).  I have left it in mainly because it will
   make no difference for a very long time.  - jvz]

   Historical exceptions _not_ allowed for in this package:

   Until Julius Caesar established the Julian calendar in 45 B.C.,
   calendars were irregular.  This package assumes the Julian calendar
   back to 4713 B.C.

   The Julian calendar was altered in 8 B.C.  From 45 B.C. to 8 B.C.,
   the months were
   Jan=31, Feb=29(30), Mar=31, Apr=30, May=31, Jun=30,
   Jul=31, Aug=30,     Sep=31, Oct=30, Nov=31, Dec=30
   This package assumes the month lengths as we know them.

   Leap years from 45 B.C.  to 8 A.D.  were miscalculated: (45, 42,
   39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, then none at all until 8
   A.D.) This package assumes leap years every four years, as they
   were meant to have been.

   January 1 was not always the first day of the year.  The United
   Kingdom, in particular, started the year on March 25 until 1752. 
   (However, the year ended on December 31, leaving the days between
   in limbo.) This package assumes January 1 is the first day of the
   year.

   Leap-year day was originally done by having February 24 (25 from 45
   to 8 B.C.) twice.  This package assumes Leap-year day is February
   29.

   "Transition" argument is the first Julian date to be considered as
   belonging to the Gregorian calendar.  Usual values are:

   2299161 = October 5/15, 1582,       as in Rome, or
   2361222 = September 3/14, 1752,     as in the United Kingdom 
   and the Colonies 

   For more on the history of calendars, including transition dates in
   other countries, see Bob Douglas' article in dates.txt.

 */

#include <config.h>
#include "str.h"

#include <time.h>
#include "julcal.h"

typedef long Julian;
typedef int Year;
typedef int Month;
typedef int Day;

#if 0
Julian jul_transition = JUL_ENGLAND;
#endif

long julian_offset;

/*
   Julian juldnj (struct tm *bdt, Julian Transition);
   Julian juldn (struct tm *bdt);
   Julian juldnd (struct tm *bdt, struct tm *Transition_date);

   struct tm *julcdj (Julian J, Julian Transition);
   struct tm *julcd (Julian J);
   struct tm *julcdd (Julian J, struct tm *Transition_date);
 */

typedef long Work;

static Julian
_juldnj (struct tm *bdt, Julian Transition)
{
  Year ay, y, dy;
  Month m;
  Julian jd_julian, jd_gregorian, jd;

  y = bdt->tm_year + 1900;
  m = bdt->tm_mon;

  dy = m / 12;
  y += dy;
  m -= dy * 12;

  /* assert( -11 <= m && m <= 12 ) */
  if (m < 0)
    {
      m += 12;
      y--;
    }
  else if (m > 12)
    {
      m -= 12;
      y++;
    }

  /* assert( 0 <= m && m <= 11 ) */
  if (m < 2)
    {
      m += 1 + 12;
      y--;
    }
  else
    m++;

  ay = y + 4716;
  jd_julian = ((1461 * (Work) ay) >> 2) + (153 * (m + 1) / 5)
    + (Work) bdt->tm_mday - 1524;
  jd_gregorian = jd_julian + 2 - y / 100 + y / 400 - y / 4000;
  if (jd_gregorian >= Transition)
    jd = jd_gregorian;
  else
    jd = jd_julian;
  return jd;
}

Julian
juldnj (struct tm * bdt, Julian Transition)
{
  Julian jd;

  jd = _juldnj (bdt, Transition);
  return jd;
}

#if 0
Julian
juldn (bdt)
     struct tm *bdt;
{
  return juldnj (bdt, jul_transition);
}

Julian
juldnd (bdt, Transition_date)
     struct tm *bdt;
     struct tm *Transition_date;
{
  return juldnj (bdt, _juldnj (Transition_date, 1L));
}
#endif

struct tm *
julcdj (Julian jd, Julian Transition)
{
  Julian aa, ab, a;
  Work b, d, ee;
  Year ay;
  Month em, m;
  Year y;
  struct tm newyears;
  static struct tm date;

  memset (&date, 0, sizeof (date));

  if (jd < Transition)		/* Julian Calendar */
    a = (Work) (jd);
  else
    /* Gregorian Calendar */
    {
      aa = jd - 1721120L;
      ab = 31 * (aa / 1460969L);
      aa = aa % 1460969L;
      ab = ab + 3 * (aa / 146097L);
      aa = aa % 146097L;
      if (aa == 146096L)
	ab = ab + 3;
      else
	ab = ab + aa / 36524L;
      a = jd + (ab - 2);
    }
  b = a + 1524;
  ay = (Year) ((20 * b - 2442) / 7305);
  d = (1461 * (Work) (ay)) >> 2;
  ee = b - d;
  em = (Month) (10000L * ee / 306001L);

  date.tm_mday = (Day) (ee - 306001L * em / 10000L);

  m = em - 1;
  if (m > 12)
    m -= 12;
  date.tm_mon = m - 1;

  if (m > 2)
    y = ay - 4716;
  else
    y = ay - 4715;
  date.tm_year = y - 1900;

  date.tm_wday = (jd + 1) % 7;

  newyears = date;
  newyears.tm_mon = 0;
  newyears.tm_mday = 1;
  date.tm_yday = jd - _juldnj (&newyears, Transition);
  return &date;
}

#if 0
struct tm *
julcd (jd)
     Julian jd;
{
  return julcdj (jd, jul_transition);
}

struct tm *
julcdd (jd, Transition_date)
     Julian jd;
     struct tm *Transition_date;
{
  return julcdj (jd, _juldnj (Transition_date, 1L));
}
#endif
