Calendar manipulation routines from Scott E. Lee.
authorPeter Verthez <Peter.Verthez@advalvas.be>
Fri, 28 Dec 2001 20:40:32 +0000 (20:40 +0000)
committerPeter Verthez <Peter.Verthez@advalvas.be>
Fri, 28 Dec 2001 20:40:32 +0000 (20:40 +0000)
gedcom/calendar/.cvsignore [new file with mode: 0644]
gedcom/calendar/Makefile.am [new file with mode: 0644]
gedcom/calendar/dow.c [new file with mode: 0644]
gedcom/calendar/french.c [new file with mode: 0644]
gedcom/calendar/gregor.c [new file with mode: 0644]
gedcom/calendar/jewish.c [new file with mode: 0644]
gedcom/calendar/julian.c [new file with mode: 0644]
gedcom/calendar/sdncal.h [new file with mode: 0644]

diff --git a/gedcom/calendar/.cvsignore b/gedcom/calendar/.cvsignore
new file mode 100644 (file)
index 0000000..70845e0
--- /dev/null
@@ -0,0 +1 @@
+Makefile.in
diff --git a/gedcom/calendar/Makefile.am b/gedcom/calendar/Makefile.am
new file mode 100644 (file)
index 0000000..84e6c42
--- /dev/null
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+# $Id$
+# $Name$
+
+noinst_LTLIBRARIES = libcalendar.la
+libcalendar_la_SOURCES = dow.c \
+                        french.c \
+                        gregor.c \
+                        jewish.c \
+                        julian.c
+noinst_HEADERS = sdncal.h
diff --git a/gedcom/calendar/dow.c b/gedcom/calendar/dow.c
new file mode 100644 (file)
index 0000000..f738959
--- /dev/null
@@ -0,0 +1,71 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+
+/* $selId: dow.c,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * These are the externally visible components of this file:
+ *
+ *     int
+ *     DayOfWeek(
+ *         long int sdn);
+ *
+ * Convert a SDN to a day-of-week number (0 to 6).  Where 0 stands for
+ * Sunday, 1 for Monday, etc. and 6 stands for Saturday.
+ *
+ *     char *DayNameShort[7];
+ *
+ * Convert a day-of-week number (0 to 6), as returned from DayOfWeek(), to
+ * the abbreviated (three character) name of the day.
+ *
+ *     char *DayNameLong[7];
+ *
+ * Convert a day-of-week number (0 to 6), as returned from DayOfWeek(), to
+ * the name of the day.
+ *
+ **************************************************************************/
+
+#include "sdncal.h"
+
+int
+DayOfWeek(
+    long int sdn)
+{
+    int dow;
+
+    dow = (sdn + 1) % 7;
+    if (dow >= 0) {
+       return(dow);
+    } else {
+       return(dow + 7);
+    }
+}
+
+char *DayNameShort[7] = {
+    "Sun",
+    "Mon",
+    "Tue",
+    "Wed",
+    "Thu",
+    "Fri",
+    "Sat"
+};
+
+char *DayNameLong[7] = {
+    "Sunday",
+    "Monday",
+    "Tuesday",
+    "Wednesday",
+    "Thursday",
+    "Friday",
+    "Saturday"
+};
diff --git a/gedcom/calendar/french.c b/gedcom/calendar/french.c
new file mode 100644 (file)
index 0000000..e4452fe
--- /dev/null
@@ -0,0 +1,160 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+
+/* $selId: french.c,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * These are the externally visible components of this file:
+ *
+ *     void
+ *     SdnToFrench(
+ *         long int  sdn,
+ *         int      *pYear,
+ *         int      *pMonth,
+ *         int      *pDay);
+ *
+ * Convert a SDN to a French republican calendar date.  If the input SDN is
+ * before the first day of year 1 or after the last day of year 14, the
+ * three output values will all be set to zero, otherwise *pYear will be in
+ * the range 1 to 14 inclusive; *pMonth will be in the range 1 to 13
+ * inclusive; *pDay will be in the range 1 to 30 inclusive.  If *pMonth is
+ * 13, the SDN represents one of the holidays at the end of the year and
+ * *pDay will be in the range 1 to 6 inclusive.
+ *
+ *     long int
+ *     FrenchToSdn(
+ *         int year,
+ *         int month,
+ *         int day);
+ *
+ * Convert a French republican calendar date to a SDN.  Zero is returned
+ * when the input date is detected as invalid or out of the supported
+ * range.  The return value will be > 0 for all valid, supported dates, but
+ * there are some invalid dates that will return a positive value.  To
+ * verify that a date is valid, convert it to SDN and then back and compare
+ * with the original.
+ *
+ *     char *FrenchMonthName[14];
+ *
+ * Convert a French republican month number (1 to 13) to the name of the
+ * French republican month (null terminated).  An index of 13 (for the
+ * "extra" days at the end of the year) will return the string "Extra".  An
+ * index of zero will return a zero length string.
+ *
+ * VALID RANGE
+ *
+ *     These routines only convert dates in years 1 through 14 (Gregorian
+ *     dates 22 September 1792 through 22 September 1806).  This more than
+ *     covers the period when the calendar was in use.
+ *
+ *     I would support a wider range of dates, but I have not been able to
+ *     find an authoritative definition of when leap years were to have
+ *     occurred.  There are suggestions that it was to skip a leap year ever
+ *     100 years like the Gregorian calendar.
+ *
+ * CALENDAR OVERVIEW
+ *
+ *     The French republican calendar was adopted in October 1793 during
+ *     the French Revolution and was abandoned in January 1806.  The intent
+ *     was to create a new calendar system that was based on scientific
+ *     principals, not religious traditions.
+ *
+ *     The year is divided into 12 months of 30 days each.  The remaining 5
+ *     to 6 days in the year are grouped at the end and are holidays.  Each
+ *     month is divided into three decades (instead of weeks) of 10 days
+ *     each.
+ *
+ *     The epoch (first day of the first year) is 22 September 1792 in the
+ *     Gregorian calendar.  Leap years are every fourth year (year 3, 7,
+ *     11, etc.)
+ *
+ * TESTING
+ *
+ *     This algorithm has been tested from the year 1 to 14.  The source
+ *     code of the verification program is included in this package.
+ *
+ * REFERENCES
+ *
+ *     I have found no detailed, authoritative reference on this calendar.
+ *     The algorithms are based on a preponderance of less authoritative
+ *     sources.
+ *
+ **************************************************************************/
+
+#include "sdncal.h"
+
+#define SDN_OFFSET         2375474
+#define DAYS_PER_4_YEARS   1461
+#define DAYS_PER_MONTH     30
+#define FIRST_VALID        2375840
+#define LAST_VALID         2380952
+
+void
+SdnToFrench(
+    long int  sdn,
+    int      *pYear,
+    int      *pMonth,
+    int      *pDay)
+{
+    long int  temp;
+    int       dayOfYear;
+
+    if (sdn < FIRST_VALID || sdn > LAST_VALID) {
+       *pYear = 0;
+       *pMonth = 0;
+       *pDay = 0;
+       return;
+    }
+
+    temp = (sdn - SDN_OFFSET) * 4 - 1;
+    *pYear = temp / DAYS_PER_4_YEARS;
+    dayOfYear = (temp % DAYS_PER_4_YEARS) / 4;
+    *pMonth = dayOfYear / DAYS_PER_MONTH + 1;
+    *pDay = dayOfYear % DAYS_PER_MONTH + 1;
+}
+
+long int
+FrenchToSdn(
+    int year,
+    int month,
+    int day)
+{
+    /* check for invalid dates */
+    if (year < 1 || year > 14 ||
+       month < 1 || month > 13 ||
+       day < 1 || day > 30)
+    {
+       return(0);
+    }
+
+    return( (year * DAYS_PER_4_YEARS) / 4
+           + (month - 1) * DAYS_PER_MONTH
+           + day
+           + SDN_OFFSET );
+}
+
+char *FrenchMonthName[14] = {
+    "",
+    "Vendemiaire",
+    "Brumaire",
+    "Frimaire",
+    "Nivose",
+    "Pluviose",
+    "Ventose",
+    "Germinal",
+    "Floreal",
+    "Prairial",
+    "Messidor",
+    "Thermidor",
+    "Fructidor",
+    "Extra"
+};
diff --git a/gedcom/calendar/gregor.c b/gedcom/calendar/gregor.c
new file mode 100644 (file)
index 0000000..60dd10a
--- /dev/null
@@ -0,0 +1,274 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+
+/* $selId: gregor.c,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * These are the externally visible components of this file:
+ *
+ *     void
+ *     SdnToGregorian(
+ *         long int  sdn,
+ *         int      *pYear,
+ *         int      *pMonth,
+ *         int      *pDay);
+ *
+ * Convert a SDN to a Gregorian calendar date.  If the input SDN is less
+ * than 1, the three output values will all be set to zero, otherwise
+ * *pYear will be >= -4714 and != 0; *pMonth will be in the range 1 to 12
+ * inclusive; *pDay will be in the range 1 to 31 inclusive.
+ *
+ *     long int
+ *     GregorianToSdn(
+ *         int inputYear,
+ *         int inputMonth,
+ *         int inputDay);
+ *
+ * Convert a Gregorian calendar date to a SDN.  Zero is returned when the
+ * input date is detected as invalid or out of the supported range.  The
+ * return value will be > 0 for all valid, supported dates, but there are
+ * some invalid dates that will return a positive value.  To verify that a
+ * date is valid, convert it to SDN and then back and compare with the
+ * original.
+ *
+ *     char *MonthNameShort[13];
+ *
+ * Convert a Gregorian month number (1 to 12) to the abbreviated (three
+ * character) name of the Gregorian month (null terminated).  An index of
+ * zero will return a zero length string.
+ *
+ *     char *MonthNameLong[13];
+ *
+ * Convert a Gregorian month number (1 to 12) to the name of the Gregorian
+ * month (null terminated).  An index of zero will return a zero length
+ * string.
+ *
+ * VALID RANGE
+ *
+ *     4714 B.C. to at least 10000 A.D.
+ *
+ *     Although this software can handle dates all the way back to 4714
+ *     B.C., such use may not be meaningful.  The Gregorian calendar was
+ *     not instituted until October 15, 1582 (or October 5, 1582 in the
+ *     Julian calendar).  Some countries did not accept it until much
+ *     later.  For example, Britain converted in 1752, The USSR in 1918 and
+ *     Greece in 1923.  Most European countries used the Julian calendar
+ *     prior to the Gregorian.
+ *
+ * CALENDAR OVERVIEW
+ *
+ *     The Gregorian calendar is a modified version of the Julian calendar.
+ *     The only difference being the specification of leap years.  The
+ *     Julian calendar specifies that every year that is a multiple of 4
+ *     will be a leap year.  This leads to a year that is 365.25 days long,
+ *     but the current accepted value for the tropical year is 365.242199
+ *     days.
+ *
+ *     To correct this error in the length of the year and to bring the
+ *     vernal equinox back to March 21, Pope Gregory XIII issued a papal
+ *     bull declaring that Thursday October 4, 1582 would be followed by
+ *     Friday October 15, 1582 and that centennial years would only be a
+ *     leap year if they were a multiple of 400.  This shortened the year
+ *     by 3 days per 400 years, giving a year of 365.2425 days.
+ *
+ *     Another recently proposed change in the leap year rule is to make
+ *     years that are multiples of 4000 not a leap year, but this has never
+ *     been officially accepted and this rule is not implemented in these
+ *     algorithms.
+ *
+ * ALGORITHMS
+ *
+ *     The calculations are based on three different cycles: a 400 year
+ *     cycle of leap years, a 4 year cycle of leap years and a 5 month
+ *     cycle of month lengths.
+ *
+ *     The 5 month cycle is used to account for the varying lengths of
+ *     months.  You will notice that the lengths alternate between 30
+ *     and 31 days, except for three anomalies: both July and August
+ *     have 31 days, both December and January have 31, and February
+ *     is less than 30.  Starting with March, the lengths are in a
+ *     cycle of 5 months (31, 30, 31, 30, 31):
+ *
+ *         Mar   31 days  \
+ *         Apr   30 days   |
+ *         May   31 days    > First cycle
+ *         Jun   30 days   |
+ *         Jul   31 days  /
+ *
+ *         Aug   31 days  \
+ *         Sep   30 days   |
+ *         Oct   31 days    > Second cycle
+ *         Nov   30 days   |
+ *         Dec   31 days  /
+ *
+ *         Jan   31 days  \
+ *         Feb 28/9 days   |
+ *                          > Third cycle (incomplete)
+ *
+ *     For this reason the calculations (internally) assume that the
+ *     year starts with March 1.
+ *
+ * TESTING
+ *
+ *     This algorithm has been tested from the year 4714 B.C. to 10000
+ *     A.D.  The source code of the verification program is included in
+ *     this package.
+ *
+ * REFERENCES
+ *
+ *     Conversions Between Calendar Date and Julian Day Number by Robert J.
+ *     Tantzen, Communications of the Association for Computing Machinery
+ *     August 1963.  (Also published in Collected Algorithms from CACM,
+ *     algorithm number 199).
+ *
+ **************************************************************************/
+
+#include "sdncal.h"
+
+#define SDN_OFFSET         32045
+#define DAYS_PER_5_MONTHS  153
+#define DAYS_PER_4_YEARS   1461
+#define DAYS_PER_400_YEARS 146097
+
+void
+SdnToGregorian(
+    long int  sdn,
+    int      *pYear,
+    int      *pMonth,
+    int      *pDay)
+{
+    int       century;
+    int       year;
+    int       month;
+    int       day;
+    long int  temp;
+    int       dayOfYear;
+
+    if (sdn <= 0) {
+       *pYear = 0;
+       *pMonth = 0;
+       *pDay = 0;
+       return;
+    }
+
+    temp = (sdn + SDN_OFFSET) * 4 - 1;
+
+    /* Calculate the century (year/100). */
+    century = temp / DAYS_PER_400_YEARS;
+
+    /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
+    temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
+    year = (century * 100) + (temp / DAYS_PER_4_YEARS);
+    dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
+
+    /* Calculate the month and day of month. */
+    temp = dayOfYear * 5 - 3;
+    month = temp / DAYS_PER_5_MONTHS;
+    day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
+
+    /* Convert to the normal beginning of the year. */
+    if (month < 10) {
+       month += 3;
+    } else {
+       year += 1;
+       month -= 9;
+    }
+
+    /* Adjust to the B.C./A.D. type numbering. */
+    year -= 4800;
+    if (year <= 0) year--;
+
+    *pYear = year;
+    *pMonth = month;
+    *pDay = day;
+}
+
+long int
+GregorianToSdn(
+    int inputYear,
+    int inputMonth,
+    int inputDay)
+{
+    int year;
+    int month;
+
+    /* check for invalid dates */
+    if (inputYear == 0 || inputYear < -4714 ||
+       inputMonth <= 0 || inputMonth > 12 ||
+       inputDay <= 0 || inputDay > 31)
+    {
+       return(0);
+    }
+
+    /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */
+    if (inputYear == -4714) {
+       if (inputMonth < 11) {
+           return(0);
+       }
+       if (inputMonth == 11 && inputDay < 25) {
+           return(0);
+       }
+    }
+
+    /* Make year always a positive number. */
+    if (inputYear < 0) {
+       year = inputYear + 4801;
+    } else {
+       year = inputYear + 4800;
+    }
+
+    /* Adjust the start of the year. */
+    if (inputMonth > 2) {
+       month = inputMonth - 3;
+    } else {
+       month = inputMonth + 9;
+       year--;
+    }
+
+    return( ((year / 100) * DAYS_PER_400_YEARS) / 4
+           + ((year % 100) * DAYS_PER_4_YEARS) / 4
+           + (month * DAYS_PER_5_MONTHS + 2) / 5
+           + inputDay
+           - SDN_OFFSET );
+}
+
+char *MonthNameShort[13] = {
+    "",
+    "Jan",
+    "Feb",
+    "Mar",
+    "Apr",
+    "May",
+    "Jun",
+    "Jul",
+    "Aug",
+    "Sep",
+    "Oct",
+    "Nov",
+    "Dec"
+};
+
+char *MonthNameLong[13] = {
+    "",
+    "January",
+    "February",
+    "March",
+    "April",
+    "May",
+    "June",
+    "July",
+    "August",
+    "September",
+    "October",
+    "November",
+    "December"
+};
diff --git a/gedcom/calendar/jewish.c b/gedcom/calendar/jewish.c
new file mode 100644 (file)
index 0000000..23b975a
--- /dev/null
@@ -0,0 +1,741 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+
+/* $selId: jewish.c,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * These are the externally visible components of this file:
+ *
+ *     void
+ *     SdnToJewish(
+ *         long int sdn,
+ *         int *pYear,
+ *         int *pMonth,
+ *         int *pDay);
+ *
+ * Convert a SDN to a Jewish calendar date.  If the input SDN is before the
+ * first day of year 1, the three output values will all be set to zero,
+ * otherwise *pYear will be > 0; *pMonth will be in the range 1 to 13
+ * inclusive; *pDay will be in the range 1 to 30 inclusive.  Note that Adar
+ * II is assigned the month number 7 and Elul is always 13.
+ *
+ *     long int
+ *     JewishToSdn(
+ *         int year,
+ *         int month,
+ *         int day);
+ *
+ * Convert a Jewish calendar date to a SDN.  Zero is returned when the
+ * input date is detected as invalid or out of the supported range.  The
+ * return value will be > 0 for all valid, supported dates, but there are
+ * some invalid dates that will return a positive value.  To verify that a
+ * date is valid, convert it to SDN and then back and compare with the
+ * original.
+ *
+ *     char *JewishMonthName[14];
+ *
+ * Convert a Jewish month number (1 to 13) to the name of the Jewish month
+ * (null terminated).  An index of zero will return a zero length string.
+ *
+ * VALID RANGE
+ *
+ *     Although this software can handle dates all the way back to the year
+ *     1 (3761 B.C.), such use may not be meaningful.
+ *
+ *     The Jewish calendar has been in use for several thousand years, but
+ *     in the early days there was no formula to determine the start of a
+ *     month.  A new month was started when the new moon was first
+ *     observed.
+ *
+ *     It is not clear when the current rule based calendar replaced the
+ *     observation based calendar.  According to the book "Jewish Calendar
+ *     Mystery Dispelled" by George Zinberg, the patriarch Hillel II
+ *     published these rules in 358 A.D.  But, according to The
+ *     Encyclopedia Judaica, Hillel II may have only published the 19 year
+ *     rule for determining the occurrence of leap years.
+ *
+ *     I have yet to find a specific date when the current set of rules
+ *     were known to be in use.
+ *
+ * CALENDAR OVERVIEW
+ *
+ *     The Jewish calendar is based on lunar as well as solar cycles.  A
+ *     month always starts on or near a new moon and has either 29 or 30
+ *     days (a lunar cycle is about 29 1/2 days).  Twelve of these
+ *     alternating 29-30 day months gives a year of 354 days, which is
+ *     about 11 1/4 days short of a solar year.
+ *
+ *     Since a month is defined to be a lunar cycle (new moon to new moon),
+ *     this 11 1/4 day difference cannot be overcome by adding days to a
+ *     month as with the Gregorian calendar, so an entire month is
+ *     periodically added to the year, making some years 13 months long.
+ *
+ *     For astronomical as well as ceremonial reasons, the start of a new
+ *     year may be delayed until a day or two after the new moon causing
+ *     years to vary in length.  Leap years can be from 383 to 385 days and
+ *     common years can be from 353 to 355 days.  These are the months of
+ *     the year and their possible lengths:
+ *
+ *                       COMMON YEAR          LEAP YEAR
+ *          1 Tishri    30   30   30         30   30   30
+ *          2 Heshvan   29   29   30         29   29   30 (variable)
+ *          3 Kislev    29   30   30         29   30   30 (variable)
+ *          4 Tevet     29   29   29         29   29   29
+ *          5 Shevat    30   30   30         30   30   30
+ *          6 Adar I    29   29   29         30   30   30 (variable)
+ *          7 Adar II   --   --   --         29   29   29 (optional)
+ *          8 Nisan     30   30   30         30   30   30
+ *          9 Iyyar     29   29   29         29   29   29
+ *         10 Sivan     30   30   30         30   30   30
+ *         11 Tammuz    29   29   29         29   29   29
+ *         12 Av        30   30   30         30   30   30
+ *         13 Elul      29   29   29         29   29   29
+ *                     ---  ---  ---        ---  ---  ---
+ *                     353  354  355        383  384  385
+ *
+ *     Note that the month names and other words that appear in this file
+ *     have multiple possible spellings in the Roman character set.  I have
+ *     chosen to use the spellings found in the Encyclopedia Judaica.
+ *
+ *     Adar II, the month added for leap years, is sometimes referred to as
+ *     the 13th month, but I have chosen to assign it the number 7 to keep
+ *     the months in chronological order.  This may not be consistent with
+ *     other numbering schemes.
+ *
+ *     Leap years occur in a fixed pattern of 19 years called the metonic
+ *     cycle.  The 3rd, 6th, 8th, 11th, 14th, 17th and 19th years of this
+ *     cycle are leap years.  The first metonic cycle starts with Jewish
+ *     year 1, or 3761/60 B.C.  This is believed to be the year of
+ *     creation.
+ *
+ *     To construct the calendar for a year, you must first find the length
+ *     of the year by determining the first day of the year (Tishri 1, or
+ *     Rosh Ha-Shanah) and the first day of the following year.  This
+ *     selects one of the six possible month length configurations listed
+ *     above.
+ *
+ *     Finding the first day of the year is the most difficult part.
+ *     Finding the date and time of the new moon (or molad) is the first
+ *     step.  For this purpose, the lunar cycle is assumed to be 29 days 12
+ *     hours and 793 halakim.  A halakim is 1/1080th of an hour or 3 1/3
+ *     seconds.  (This assumed value is only about 1/2 second less than the
+ *     value used by modern astronomers -- not bad for a number that was
+ *     determined so long ago.)  The first molad of year 1 occurred on
+ *     Sunday at 11:20:11 P.M.  This would actually be Monday, because the
+ *     Jewish day is considered to begin at sunset.
+ *
+ *     Since sunset varies, the day is assumed to begin at 6:00 P.M.  for
+ *     calendar calculation purposes.  So, the first molad was 5 hours 793
+ *     halakim after the start of Tishri 1, 0001 (which was Monday
+ *     September 7, 4761 B.C. by the Gregorian calendar).  All subsequent
+ *     molads can be calculated from this starting point by adding the
+ *     length of a lunar cycle.
+ *
+ *     Once the molad that starts a year is determined the actual start of
+ *     the year (Tishri 1) can be determined.  Tishri 1 will be the day of
+ *     the molad unless it is delayed by one of the following four rules
+ *     (called dehiyyot).  Each rule can delay the start of the year by one
+ *     day, and since rule #1 can combine with one of the other rules, it
+ *     can be delayed as much as two days.
+ *
+ *         1.  Tishri 1 must never be Sunday, Wednesday or Friday.  (This
+ *             is largely to prevent certain holidays from occurring on the
+ *             day before or after the Sabbath.)
+ *
+ *         2.  If the molad occurs on or after noon, Tishri 1 must be
+ *             delayed.
+ *
+ *         3.  If it is a common (not leap) year and the molad occurs on
+ *             Tuesday at or after 3:11:20 A.M., Tishri 1 must be delayed.
+ *
+ *         4.  If it is the year following a leap year and the molad occurs
+ *             on Monday at or after 9:32:43 and 1/3 sec, Tishri 1 must be
+ *             delayed.
+ *
+ * GLOSSARY
+ *
+ *     dehiyyot         The set of 4 rules that determine when the new year
+ *                      starts relative to the molad.
+ *
+ *     halakim          1/1080th of an hour or 3 1/3 seconds.
+ *
+ *     lunar cycle      The period of time between mean conjunctions of the
+ *                      sun and moon (new moon to new moon).  This is
+ *                      assumed to be 29 days 12 hours and 793 halakim for
+ *                      calendar purposes.
+ *
+ *     metonic cycle    A 19 year cycle which determines which years are
+ *                      leap years and which are common years.  The 3rd,
+ *                      6th, 8th, 11th, 14th, 17th and 19th years of this
+ *                      cycle are leap years.
+ *
+ *     molad            The date and time of the mean conjunction of the
+ *                      sun and moon (new moon).  This is the approximate
+ *                      beginning of a month.
+ *
+ *     Rosh Ha-Shanah   The first day of the Jewish year (Tishri 1).
+ *
+ *     Tishri           The first month of the Jewish year.
+ *
+ * ALGORITHMS
+ *
+ *     SERIAL DAY NUMBER TO JEWISH DATE
+ *
+ *     The simplest approach would be to use the rules stated above to find
+ *     the molad of Tishri before and after the given day number.  Then use
+ *     the molads to find Tishri 1 of the current and following years.
+ *     From this the length of the year can be determined and thus the
+ *     length of each month.  But this method is used as a last resort.
+ *
+ *     The first 59 days of the year are the same regardless of the length
+ *     of the year.  As a result, only the day number of the start of the
+ *     year is required.
+ *
+ *     Similarly, the last 6 months do not change from year to year.  And
+ *     since it can be determined whether the year is a leap year by simple
+ *     division, the lengths of Adar I and II can be easily calculated.  In
+ *     fact, all dates after the 3rd month are consistent from year to year
+ *     (once it is known whether it is a leap year).
+ *
+ *     This means that if the given day number falls in the 3rd month or on
+ *     the 30th day of the 2nd month the length of the year must be found,
+ *     but in no other case.
+ *
+ *     So, the approach used is to take the given day number and round it
+ *     to the closest molad of Tishri (first new moon of the year).  The
+ *     rounding is not really to the *closest* molad, but is such that if
+ *     the day number is before the middle of the 3rd month the molad at
+ *     the start of the year is found, otherwise the molad at the end of
+ *     the year is found.
+ *
+ *     Only if the day number is actually found to be in the ambiguous
+ *     period of 29 to 31 days is the other molad calculated.
+ *
+ *     JEWISH DATE TO SERIAL DAY NUMBER
+ *
+ *     The year number is used to find which 19 year metonic cycle contains
+ *     the date and which year within the cycle (this is a division and
+ *     modulus).  This also determines whether it is a leap year.
+ *
+ *     If the month is 1 or 2, the calculation is simple addition to the
+ *     first of the year.
+ *
+ *     If the month is 8 (Nisan) or greater, the calculation is simple
+ *     subtraction from beginning of the following year.
+ *
+ *     If the month is 4 to 7, it is considered whether it is a leap year
+ *     and then simple subtraction from the beginning of the following year
+ *     is used.
+ *
+ *     Only if it is the 3rd month is both the start and end of the year
+ *     required.
+ *
+ * TESTING
+ *
+ *     This algorithm has been tested in two ways.  First, 510 dates from a
+ *     table in "Jewish Calendar Mystery Dispelled" were calculated and
+ *     compared to the table.  Second, the calculation algorithm described
+ *     in "Jewish Calendar Mystery Dispelled" was coded and used to verify
+ *     all dates from the year 1 (3761 B.C.) to the year 13760 (10000
+ *     A.D.).
+ *
+ *     The source code of the verification program is included in this
+ *     package.
+ *
+ * REFERENCES
+ *
+ *     The Encyclopedia Judaica, the entry for "Calendar"
+ *
+ *     The Jewish Encyclopedia
+ *
+ *     Jewish Calendar Mystery Dispelled by George Zinberg, Vantage Press,
+ *     1963
+ *
+ *     The Comprehensive Hebrew Calendar by Arthur Spier, Behrman House
+ *
+ *     The Book of Calendars [note that this work contains many typos]
+ *
+ **************************************************************************/
+
+#include "sdncal.h"
+
+#define HALAKIM_PER_HOUR 1080
+#define HALAKIM_PER_DAY 25920
+#define HALAKIM_PER_LUNAR_CYCLE ((29 * HALAKIM_PER_DAY) + 13753)
+#define HALAKIM_PER_METONIC_CYCLE (HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7))
+
+#define SDN_OFFSET 347997
+#define NEW_MOON_OF_CREATION 31524
+
+#define SUNDAY    0
+#define MONDAY    1
+#define TUESDAY   2
+#define WEDNESDAY 3
+#define THURSDAY  4
+#define FRIDAY    5
+#define SATURDAY  6
+
+#define NOON (18 * HALAKIM_PER_HOUR)
+#define AM3_11_20 ((9 * HALAKIM_PER_HOUR) + 204)
+#define AM9_32_43 ((15 * HALAKIM_PER_HOUR) + 589)
+
+static int monthsPerYear[19] = {
+    12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13
+};
+
+static int yearOffset[19] = {
+    0, 12, 24, 37, 49, 61, 74, 86, 99, 111, 123,
+    136, 148, 160, 173, 185, 197, 210, 222
+};
+
+char *JewishMonthName[14] = {
+    "",
+    "Tishri",
+    "Heshvan",
+    "Kislev",
+    "Tevet",
+    "Shevat",
+    "AdarI",
+    "AdarII",
+    "Nisan",
+    "Iyyar",
+    "Sivan",
+    "Tammuz",
+    "Av",
+    "Elul"
+};
+
+/************************************************************************
+ * Given the year within the 19 year metonic cycle and the time of a molad
+ * (new moon) which starts that year, this routine will calculate what day
+ * will be the actual start of the year (Tishri 1 or Rosh Ha-Shanah).  This
+ * first day of the year will be the day of the molad unless one of 4 rules
+ * (called dehiyyot) delays it.  These 4 rules can delay the start of the
+ * year by as much as 2 days.
+ */
+static long int
+Tishri1(
+    int      metonicYear,
+    long int moladDay,
+    long int moladHalakim)
+{
+    long int tishri1;
+    int dow;
+    int leapYear;
+    int lastWasLeapYear;
+
+    tishri1 = moladDay;
+    dow = tishri1 % 7;
+    leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7
+       || metonicYear == 10 || metonicYear == 13 || metonicYear == 16
+       || metonicYear == 18;
+    lastWasLeapYear = metonicYear == 3 || metonicYear == 6
+       || metonicYear == 8 || metonicYear == 11 || metonicYear == 14
+       || metonicYear == 17 || metonicYear == 0;
+
+    /* Apply rules 2, 3 and 4. */
+    if ((moladHalakim >= NOON) ||
+       ((!leapYear) && dow == TUESDAY && moladHalakim >= AM3_11_20) ||
+       (lastWasLeapYear && dow == MONDAY && moladHalakim >= AM9_32_43))
+    {
+       tishri1++;
+       dow++;
+       if (dow == 7) {
+           dow = 0;
+       }
+    }
+
+    /* Apply rule 1 after the others because it can cause an additional
+     * delay of one day. */
+    if (dow == WEDNESDAY || dow == FRIDAY || dow == SUNDAY) {
+       tishri1++;
+    }
+
+    return(tishri1);
+}
+
+/************************************************************************
+ * Given a metonic cycle number, calculate the date and time of the molad
+ * (new moon) that starts that cycle.  Since the length of a metonic cycle
+ * is a constant, this is a simple calculation, except that it requires an
+ * intermediate value which is bigger that 32 bits.  Because this
+ * intermediate value only needs 36 to 37 bits and the other numbers are
+ * constants, the process has been reduced to just a few steps.
+ */
+static void
+MoladOfMetonicCycle(
+    int       metonicCycle,
+    long int *pMoladDay,
+    long int *pMoladHalakim)
+{
+    register unsigned long int r1, r2, d1, d2;
+
+    /* Start with the time of the first molad after creation. */
+    r1 = NEW_MOON_OF_CREATION;
+
+    /* Calculate metonicCycle * HALAKIM_PER_METONIC_CYCLE.  The upper 32
+     * bits of the result will be in r2 and the lower 16 bits will be
+     * in r1. */
+    r1 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE & 0xFFFF);
+    r2 = r1 >> 16;
+    r2 += metonicCycle * ((HALAKIM_PER_METONIC_CYCLE >> 16) & 0xFFFF);
+
+    /* Calculate r2r1 / HALAKIM_PER_DAY.  The remainder will be in r1, the
+     * upper 16 bits of the quotient will be in d2 and the lower 16 bits
+     * will be in d1. */
+    d2 = r2 / HALAKIM_PER_DAY;
+    r2 -= d2 * HALAKIM_PER_DAY;
+    r1 = (r2 << 16) | (r1 & 0xFFFF);
+    d1 = r1 / HALAKIM_PER_DAY;
+    r1 -= d1 * HALAKIM_PER_DAY;
+
+    *pMoladDay = (d2 << 16) | d1;
+    *pMoladHalakim = r1;
+}
+
+/************************************************************************
+ * Given a day number, find the molad of Tishri (the new moon at the start
+ * of a year) which is closest to that day number.  It's not really the
+ * *closest* molad that we want here.  If the input day is in the first two
+ * months, we want the molad at the start of the year.  If the input day is
+ * in the fourth to last months, we want the molad at the end of the year.
+ * If the input day is in the third month, it doesn't matter which molad is
+ * returned, because both will be required.  This type of "rounding" allows
+ * us to avoid calculating the length of the year in most cases.
+ */
+static void
+FindTishriMolad(
+    long int inputDay,
+    int *pMetonicCycle,
+    int *pMetonicYear,
+    long int *pMoladDay,
+    long int *pMoladHalakim)
+{
+    long int moladDay;
+    long int moladHalakim;
+    int metonicCycle;
+    int metonicYear;
+
+    /* Estimate the metonic cycle number.  Note that this may be an under
+     * estimate because there are 6939.6896 days in a metonic cycle not
+     * 6940, but it will never be an over estimate.  The loop below will
+     * correct for any error in this estimate. */
+    metonicCycle = (inputDay + 310) / 6940;
+
+    /* Calculate the time of the starting molad for this metonic cycle. */
+    MoladOfMetonicCycle(metonicCycle, &moladDay, &moladHalakim);
+
+    /* If the above was an under estimate, increment the cycle number until
+     * the correct one is found.  For modern dates this loop is about 98.6%
+     * likely to not execute, even once, because the above estimate is
+     * really quite close. */
+    while (moladDay < inputDay - 6940 + 310) {
+       metonicCycle++;
+       moladHalakim += HALAKIM_PER_METONIC_CYCLE;
+       moladDay += moladHalakim / HALAKIM_PER_DAY;
+       moladHalakim = moladHalakim % HALAKIM_PER_DAY;
+    }
+
+    /* Find the molad of Tishri closest to this date. */
+    for (metonicYear = 0; metonicYear < 18; metonicYear++) {
+       if (moladDay > inputDay - 74) {
+           break;
+       }
+       moladHalakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear];
+       moladDay += moladHalakim / HALAKIM_PER_DAY;
+       moladHalakim = moladHalakim % HALAKIM_PER_DAY;
+    }
+
+    *pMetonicCycle = metonicCycle;
+    *pMetonicYear = metonicYear;
+    *pMoladDay = moladDay;
+    *pMoladHalakim = moladHalakim;
+}
+
+/************************************************************************
+ * Given a year, find the number of the first day of that year and the date
+ * and time of the starting molad.
+ */
+static void
+FindStartOfYear(
+    int       year,
+    int      *pMetonicCycle,
+    int      *pMetonicYear,
+    long int *pMoladDay,
+    long int *pMoladHalakim,
+    int      *pTishri1)
+{
+    *pMetonicCycle = (year - 1) / 19;
+    *pMetonicYear = (year - 1) % 19;
+    MoladOfMetonicCycle(*pMetonicCycle, pMoladDay, pMoladHalakim);
+
+    *pMoladHalakim += HALAKIM_PER_LUNAR_CYCLE * yearOffset[*pMetonicYear];
+    *pMoladDay += *pMoladHalakim / HALAKIM_PER_DAY;
+    *pMoladHalakim = *pMoladHalakim % HALAKIM_PER_DAY;
+
+    *pTishri1 = Tishri1(*pMetonicYear, *pMoladDay, *pMoladHalakim);
+}
+
+/************************************************************************
+ * Given a serial day number (SDN), find the corresponding year, month and
+ * day in the Jewish calendar.  The three output values will always be
+ * modified.  If the input SDN is before the first day of year 1, they will
+ * all be set to zero, otherwise *pYear will be > 0; *pMonth will be in the
+ * range 1 to 13 inclusive; *pDay will be in the range 1 to 30 inclusive.
+ */
+void
+SdnToJewish(
+    long int sdn,
+    int *pYear,
+    int *pMonth,
+    int *pDay)
+{
+    long int inputDay;
+    long int day;
+    long int halakim;
+    int metonicCycle;
+    int metonicYear;
+    int tishri1;
+    int tishri1After;
+    int yearLength;
+
+    if (sdn <= SDN_OFFSET) {
+       *pYear = 0;
+       *pMonth = 0;
+       *pDay = 0;
+       return;
+    }
+    inputDay = sdn - SDN_OFFSET;
+
+    FindTishriMolad(inputDay, &metonicCycle, &metonicYear, &day, &halakim);
+    tishri1 = Tishri1(metonicYear, day, halakim);
+
+    if (inputDay >= tishri1) {
+       /* It found Tishri 1 at the start of the year. */
+       *pYear = metonicCycle * 19 + metonicYear + 1;
+       if (inputDay < tishri1 + 59) {
+           if (inputDay < tishri1 + 30) {
+               *pMonth = 1;
+               *pDay = inputDay - tishri1 + 1;
+           } else {
+               *pMonth = 2;
+               *pDay = inputDay - tishri1 - 29;
+           }
+           return;
+       }
+
+       /* We need the length of the year to figure this out, so find
+        * Tishri 1 of the next year. */
+       halakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear];
+       day += halakim / HALAKIM_PER_DAY;
+       halakim = halakim % HALAKIM_PER_DAY;
+       tishri1After = Tishri1((metonicYear + 1) % 19, day, halakim);
+    } else {
+       /* It found Tishri 1 at the end of the year. */
+       *pYear = metonicCycle * 19 + metonicYear;
+       if (inputDay >= tishri1 - 177) {
+           /* It is one of the last 6 months of the year. */
+           if (inputDay > tishri1 - 30) {
+               *pMonth = 13;
+               *pDay = inputDay - tishri1 + 30;
+           } else if (inputDay > tishri1 - 60) {
+               *pMonth = 12;
+               *pDay = inputDay - tishri1 + 60;
+           } else if (inputDay > tishri1 - 89) {
+               *pMonth = 11;
+               *pDay = inputDay - tishri1 + 89;
+           } else if (inputDay > tishri1 - 119) {
+               *pMonth = 10;
+               *pDay = inputDay - tishri1 + 119;
+           } else if (inputDay > tishri1 - 148) {
+               *pMonth = 9;
+               *pDay = inputDay - tishri1 + 148;
+           } else {
+               *pMonth = 8;
+               *pDay = inputDay - tishri1 + 178;
+           }
+           return;
+       } else {
+           if (monthsPerYear[(*pYear - 1) % 19] == 13) {
+               *pMonth = 7;
+               *pDay = inputDay - tishri1 + 207;
+               if (*pDay > 0) return;
+               (*pMonth)--;
+               (*pDay) += 30;
+               if (*pDay > 0) return;
+               (*pMonth)--;
+               (*pDay) += 30;
+           } else {
+               *pMonth = 6;
+               *pDay = inputDay - tishri1 + 207;
+               if (*pDay > 0) return;
+               (*pMonth)--;
+               (*pDay) += 30;
+           }
+           if (*pDay > 0) return;
+           (*pMonth)--;
+           (*pDay) += 29;
+           if (*pDay > 0) return;
+
+           /* We need the length of the year to figure this out, so find
+            * Tishri 1 of this year. */
+           tishri1After = tishri1;
+           FindTishriMolad(day - 365,
+               &metonicCycle, &metonicYear, &day, &halakim);
+           tishri1 = Tishri1(metonicYear, day, halakim);
+       }
+    }
+
+    yearLength = tishri1After - tishri1;
+    day = inputDay - tishri1 - 29;
+    if (yearLength == 355 || yearLength == 385) {
+       /* Heshvan has 30 days */
+       if (day <= 30) {
+           *pMonth = 2;
+           *pDay = day;
+           return;
+       }
+       day -= 30;
+    } else {
+       /* Heshvan has 29 days */
+       if (day <= 29) {
+           *pMonth = 2;
+           *pDay = day;
+           return;
+       }
+       day -= 29;
+    }
+
+    /* It has to be Kislev. */
+    *pMonth = 3;
+    *pDay = day;
+}
+
+/************************************************************************
+ * Given a year, month and day in the Jewish calendar, find the
+ * corresponding serial day number (SDN).  Zero is returned when the input
+ * date is detected as invalid.  The return value will be > 0 for all valid
+ * dates, but there are some invalid dates that will return a positive
+ * value.  To verify that a date is valid, convert it to SDN and then back
+ * and compare with the original.
+ */
+long int
+JewishToSdn(
+    int year,
+    int month,
+    int day)
+{
+    long int sdn;
+    int      metonicCycle;
+    int      metonicYear;
+    int      tishri1;
+    int      tishri1After;
+    long int moladDay;
+    long int moladHalakim;
+    int      yearLength;
+    int      lengthOfAdarIAndII;
+
+    if (year <= 0 || day <= 0 || day > 30) {
+       return(0);
+    }
+
+    switch (month) {
+    case 1:
+    case 2:
+       /* It is Tishri or Heshvan - don't need the year length. */
+       FindStartOfYear(year, &metonicCycle, &metonicYear,
+           &moladDay, &moladHalakim, &tishri1);
+       if (month == 1) {
+           sdn = tishri1 + day - 1;
+       } else {
+           sdn = tishri1 + day + 29;
+       }
+       break;
+
+    case 3:
+       /* It is Kislev - must find the year length. */
+
+       /* Find the start of the year. */
+       FindStartOfYear(year, &metonicCycle, &metonicYear,
+           &moladDay, &moladHalakim, &tishri1);
+
+       /* Find the end of the year. */
+       moladHalakim += HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear];
+       moladDay += moladHalakim / HALAKIM_PER_DAY;
+       moladHalakim = moladHalakim % HALAKIM_PER_DAY;
+       tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim);
+
+       yearLength = tishri1After - tishri1;
+
+       if (yearLength == 355 || yearLength == 385) {
+           sdn = tishri1 + day + 59;
+       } else {
+           sdn = tishri1 + day + 58;
+       }
+       break;
+
+    case 4:
+    case 5:
+    case 6:
+       /* It is Tevet, Shevat or Adar I - don't need the year length. */
+
+       FindStartOfYear(year + 1, &metonicCycle, &metonicYear,
+           &moladDay, &moladHalakim, &tishri1After);
+
+       if (monthsPerYear[(year - 1) % 19] == 12) {
+           lengthOfAdarIAndII = 29;
+       } else {
+           lengthOfAdarIAndII = 59;
+       }
+
+       if (month == 4) {
+           sdn = tishri1After + day - lengthOfAdarIAndII - 237;
+       } else if (month == 5) {
+           sdn = tishri1After + day - lengthOfAdarIAndII - 208;
+       } else {
+           sdn = tishri1After + day - lengthOfAdarIAndII - 178;
+       }
+       break;
+
+    default:
+       /* It is Adar II or later - don't need the year length. */
+       FindStartOfYear(year + 1, &metonicCycle, &metonicYear,
+           &moladDay, &moladHalakim, &tishri1After);
+
+       switch (month) {
+       case  7:
+           sdn = tishri1After + day - 207;
+           break;
+       case  8:
+           sdn = tishri1After + day - 178;
+           break;
+       case  9:
+           sdn = tishri1After + day - 148;
+           break;
+       case 10:
+           sdn = tishri1After + day - 119;
+           break;
+       case 11:
+           sdn = tishri1After + day - 89;
+           break;
+       case 12:
+           sdn = tishri1After + day - 60;
+           break;
+       case 13:
+           sdn = tishri1After + day - 30;
+           break;
+       default:
+           return(0);
+       }
+    }
+    return(sdn + SDN_OFFSET);
+}
diff --git a/gedcom/calendar/julian.c b/gedcom/calendar/julian.c
new file mode 100644 (file)
index 0000000..e2c955a
--- /dev/null
@@ -0,0 +1,251 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+
+/* $selId: julian.c,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * These are the externally visible components of this file:
+ *
+ *     void
+ *     SdnToJulian(
+ *         long int  sdn,
+ *         int      *pYear,
+ *         int      *pMonth,
+ *         int      *pDay);
+ *
+ * Convert a SDN to a Julian calendar date.  If the input SDN is less than
+ * 1, the three output values will all be set to zero, otherwise *pYear
+ * will be >= -4713 and != 0; *pMonth will be in the range 1 to 12
+ * inclusive; *pDay will be in the range 1 to 31 inclusive.
+ *
+ *     long int
+ *     JulianToSdn(
+ *         int inputYear,
+ *         int inputMonth,
+ *         int inputDay);
+ *
+ * Convert a Julian calendar date to a SDN.  Zero is returned when the
+ * input date is detected as invalid or out of the supported range.  The
+ * return value will be > 0 for all valid, supported dates, but there are
+ * some invalid dates that will return a positive value.  To verify that a
+ * date is valid, convert it to SDN and then back and compare with the
+ * original.
+ *
+ * VALID RANGE
+ *
+ *     4713 B.C. to at least 10000 A.D.
+ *
+ *     Although this software can handle dates all the way back to 4713
+ *     B.C., such use may not be meaningful.  The calendar was created in
+ *     46 B.C., but the details did not stabilize until at least 8 A.D.,
+ *     and perhaps as late at the 4th century.  Also, the beginning of a
+ *     year varied from one culture to another - not all accepted January
+ *     as the first month.
+ *
+ * CALENDAR OVERVIEW
+ *
+ *     Julias Ceasar created the calendar in 46 B.C. as a modified form of
+ *     the old Roman republican calendar which was based on lunar cycles.
+ *     The new Julian calendar set fixed lengths for the months, abandoning
+ *     the lunar cycle.  It also specified that there would be exactly 12
+ *     months per year and 365.25 days per year with every 4th year being a
+ *     leap year.
+ *
+ *     Note that the current accepted value for the tropical year is
+ *     365.242199 days, not 365.25.  This lead to an 11 day shift in the
+ *     calendar with respect to the seasons by the 16th century when the
+ *     Gregorian calendar was created to replace the Julian calendar.
+ *
+ *     The difference between the Julian and today's Gregorian calendar is
+ *     that the Gregorian does not make centennial years leap years unless
+ *     they are a multiple of 400, which leads to a year of 365.2425 days.
+ *     In other words, in the Gregorian calendar, 1700, 1800 and 1900 are
+ *     not leap years, but 2000 is.  All centennial years are leap years in
+ *     the Julian calendar.
+ *
+ *     The details are unknown, but the lengths of the months were adjusted
+ *     until they finally stablized in 8 A.D. with their current lengths:
+ *
+ *         January          31
+ *         February         28/29
+ *         March            31
+ *         April            30
+ *         May              31
+ *         June             30
+ *         Quintilis/July   31
+ *         Sextilis/August  31
+ *         September        30
+ *         October          31
+ *         November         30
+ *         December         31
+ *
+ *     In the early days of the calendar, the days of the month were not
+ *     numbered as we do today.  The numbers ran backwards (decreasing) and
+ *     were counted from the Ides (15th of the month - which in the old
+ *     Roman republican lunar calendar would have been the full moon) or
+ *     from the Nonae (9th day before the Ides) or from the beginning of
+ *     the next month.
+ *
+ *     In the early years, the beginning of the year varied, sometimes
+ *     based on the ascension of rulers.  It was not always the first of
+ *     January.
+ *
+ *     Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not
+ *     come into use until several centuries later when Christianity became
+ *     a dominant religion.
+ *
+ * ALGORITHMS
+ *
+ *     The calculations are based on two different cycles: a 4 year cycle
+ *     of leap years and a 5 month cycle of month lengths.
+ *
+ *     The 5 month cycle is used to account for the varying lengths of
+ *     months.  You will notice that the lengths alternate between 30 and
+ *     31 days, except for three anomalies: both July and August have 31
+ *     days, both December and January have 31, and February is less than
+ *     30.  Starting with March, the lengths are in a cycle of 5 months
+ *     (31, 30, 31, 30, 31):
+ *
+ *         Mar   31 days  \
+ *         Apr   30 days   |
+ *         May   31 days    > First cycle
+ *         Jun   30 days   |
+ *         Jul   31 days  /
+ *
+ *         Aug   31 days  \
+ *         Sep   30 days   |
+ *         Oct   31 days    > Second cycle
+ *         Nov   30 days   |
+ *         Dec   31 days  /
+ *
+ *         Jan   31 days  \
+ *         Feb 28/9 days   |
+ *                          > Third cycle (incomplete)
+ *
+ *     For this reason the calculations (internally) assume that the year
+ *     starts with March 1.
+ *
+ * TESTING
+ *
+ *     This algorithm has been tested from the year 4713 B.C. to 10000 A.D.
+ *     The source code of the verification program is included in this
+ *     package.
+ *
+ * REFERENCES
+ *
+ *     Conversions Between Calendar Date and Julian Day Number by Robert J.
+ *     Tantzen, Communications of the Association for Computing Machinery
+ *     August 1963.  (Also published in Collected Algorithms from CACM,
+ *     algorithm number 199).  [Note: the published algorithm is for the
+ *     Gregorian calendar, but was adjusted to use the Julian calendar's
+ *     simpler leap year rule.]
+ *
+ **************************************************************************/
+
+#include "sdncal.h"
+
+#define SDN_OFFSET         32083
+#define DAYS_PER_5_MONTHS  153
+#define DAYS_PER_4_YEARS   1461
+
+void
+SdnToJulian(
+    long int  sdn,
+    int      *pYear,
+    int      *pMonth,
+    int      *pDay)
+{
+    int       year;
+    int       month;
+    int       day;
+    long int  temp;
+    int       dayOfYear;
+
+    if (sdn <= 0) {
+       *pYear = 0;
+       *pMonth = 0;
+       *pDay = 0;
+       return;
+    }
+
+    temp = (sdn + SDN_OFFSET) * 4 - 1;
+
+    /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
+    year = temp / DAYS_PER_4_YEARS;
+    dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
+
+    /* Calculate the month and day of month. */
+    temp = dayOfYear * 5 - 3;
+    month = temp / DAYS_PER_5_MONTHS;
+    day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
+
+    /* Convert to the normal beginning of the year. */
+    if (month < 10) {
+       month += 3;
+    } else {
+       year += 1;
+       month -= 9;
+    }
+
+    /* Adjust to the B.C./A.D. type numbering. */
+    year -= 4800;
+    if (year <= 0) year--;
+
+    *pYear = year;
+    *pMonth = month;
+    *pDay = day;
+}
+
+long int
+JulianToSdn(
+    int inputYear,
+    int inputMonth,
+    int inputDay)
+{
+    int year;
+    int month;
+
+    /* check for invalid dates */
+    if (inputYear == 0 || inputYear < -4713 ||
+       inputMonth <= 0 || inputMonth > 12 ||
+       inputDay <= 0 || inputDay > 31)
+    {
+       return(0);
+    }
+
+    /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */
+    if (inputYear == -4713) {
+       if (inputMonth == 1 && inputDay == 1) {
+           return(0);
+       }
+    }
+
+    /* Make year always a positive number. */
+    if (inputYear < 0) {
+       year = inputYear + 4801;
+    } else {
+       year = inputYear + 4800;
+    }
+
+    /* Adjust the start of the year. */
+    if (inputMonth > 2) {
+       month = inputMonth - 3;
+    } else {
+       month = inputMonth + 9;
+       year--;
+    }
+
+    return( (year * DAYS_PER_4_YEARS) / 4
+           + (month * DAYS_PER_5_MONTHS + 2) / 5
+           + inputDay
+           - SDN_OFFSET );
+}
diff --git a/gedcom/calendar/sdncal.h b/gedcom/calendar/sdncal.h
new file mode 100644 (file)
index 0000000..ef9d932
--- /dev/null
@@ -0,0 +1,95 @@
+/* This file is taken from http://www.genealogy.org/~scottlee/
+   Only this initial comment has been added.  The next comment
+   gives the original copyright notice.
+*/
+
+#ifndef _INCLUDED_SDNCAL_H
+#define _INCLUDED_SDNCAL_H
+
+/* $selId: sdncal.h,v 2.0 1995/10/24 01:13:06 lees Exp $
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
+ * Permission granted to use, copy, modify, distribute and sell so long as
+ * the above copyright and this permission statement are retained in all
+ * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
+ */
+
+/**************************************************************************
+ *
+ * This package defines a set of routines that convert calendar dates to
+ * and from a serial day number (SDN).  The SDN is a serial numbering of
+ * days where SDN 1 is November 25, 4714 BC in the Gregorian calendar and
+ * SDN 2447893 is January 1, 1990.  This system of day numbering is
+ * sometimes referred to as Julian days, but to avoid confusion with the
+ * Julian calendar, it is referred to as serial day numbers here.  The term
+ * Julian days is also used to mean the number of days since the beginning
+ * of the current year.
+ *
+ * The SDN can be used as an intermediate step in converting from one
+ * calendar system to another (such as Gregorian to Jewish).  It can also
+ * be used for date computations such as easily comparing two dates,
+ * determining the day of the week, finding the date of yesterday or
+ * calculating the number of days between two dates.
+ *
+ * When using this software on 16 bit systems, be careful to store SDNs in
+ * a long int, because it will not fit in the 16 bits that some systems
+ * allocate to an int.
+ *
+ * For each calendar, there are two routines provided.  One converts dates
+ * in that calendar to SDN and the other converts SDN to calendar dates.
+ * The routines are named SdnTo<CALENDAR>() and <CALENDAR>ToSdn(), where
+ * <CALENDAR> is the name of the calendar system.
+ *
+ * SDN values less than one are not supported.  If a conversion routine
+ * returns an SDN of zero, this means that the date given is either invalid
+ * or is outside the supported range for that calendar.
+ *
+ * At least some validity checks are performed on input dates.  For
+ * example, a negative month number will result in the return of zero for
+ * the SDN.  A returned SDN greater than one does not necessarily mean that
+ * the input date was valid.  To determine if the date is valid, convert it
+ * to SDN, and if the SDN is greater than zero, convert it back to a date
+ * and compare to the original.  For example:
+ *
+ *    int y1, m1, d1;
+ *    int y2, m2, d2;
+ *    long int sdn;
+ *    ...
+ *    sdn = GregorianToSdn(y1, m1, d1);
+ *    if (sdn > 0) {
+ *        SdnToGregorian(sdn, &y2, &m2, &d2);
+ *        if (y1 == y2 && m1 == m2 && d1 == d2) {
+ *            ... date is valid ...
+ *        }
+ *    }
+ *
+ **************************************************************************/
+
+/* Gregorian calendar conversions. */
+void SdnToGregorian(long int sdn, int *pYear, int *pMonth, int *pDay);
+long int GregorianToSdn(int year, int month, int day);
+extern char *MonthNameShort[13];
+extern char *MonthNameLong[13];
+
+/* Julian calendar conversions. */
+void SdnToJulian(long int sdn, int *pYear, int *pMonth, int *pDay);
+long int JulianToSdn(int year, int month, int day);
+
+/* Jewish calendar conversions. */
+void SdnToJewish(long int sdn, int *pYear, int *pMonth, int *pDay);
+long int JewishToSdn(int year, int month, int day);
+extern char *JewishMonthName[14];
+
+/* French republic calendar conversions. */
+void SdnToFrench(long int sdn, int *pYear, int *pMonth, int *pDay);
+long int FrenchToSdn(int inputYear, int inputMonth, int inputDay);
+extern char *FrenchMonthName[14];
+
+/* Islamic calendar conversions. */
+/* Not implemented yet. */
+
+/* Day of week conversion.  0=Sunday, 6=Saturday */
+int DayOfWeek(long int sdn);
+extern char *DayNameShort[7];
+extern char *DayNameLong[7];
+
+#endif /* _INCLUDED_SDNCAL_H */