Back to index

php5  5.3.10
julian.c
Go to the documentation of this file.
00001 /* $selId: julian.c,v 2.0 1995/10/24 01:13:06 lees Exp $
00002  * Copyright 1993-1995, Scott E. Lee, all rights reserved.
00003  * Permission granted to use, copy, modify, distribute and sell so long as
00004  * the above copyright and this permission statement are retained in all
00005  * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
00006  */
00007 
00008 /**************************************************************************
00009  *
00010  * These are the externally visible components of this file:
00011  *
00012  *     void
00013  *     SdnToJulian(
00014  *         long int  sdn,
00015  *         int      *pYear,
00016  *         int      *pMonth,
00017  *         int      *pDay);
00018  *
00019  * Convert a SDN to a Julian calendar date.  If the input SDN is less than
00020  * 1, the three output values will all be set to zero, otherwise *pYear
00021  * will be >= -4713 and != 0; *pMonth will be in the range 1 to 12
00022  * inclusive; *pDay will be in the range 1 to 31 inclusive.
00023  *
00024  *     long int
00025  *     JulianToSdn(
00026  *         int inputYear,
00027  *         int inputMonth,
00028  *         int inputDay);
00029  *
00030  * Convert a Julian calendar date to a SDN.  Zero is returned when the
00031  * input date is detected as invalid or out of the supported range.  The
00032  * return value will be > 0 for all valid, supported dates, but there are
00033  * some invalid dates that will return a positive value.  To verify that a
00034  * date is valid, convert it to SDN and then back and compare with the
00035  * original.
00036  *
00037  * VALID RANGE
00038  *
00039  *     4713 B.C. to at least 10000 A.D.
00040  *
00041  *     Although this software can handle dates all the way back to 4713
00042  *     B.C., such use may not be meaningful.  The calendar was created in
00043  *     46 B.C., but the details did not stabilize until at least 8 A.D.,
00044  *     and perhaps as late at the 4th century.  Also, the beginning of a
00045  *     year varied from one culture to another - not all accepted January
00046  *     as the first month.
00047  *
00048  * CALENDAR OVERVIEW
00049  *
00050  *     Julias Ceasar created the calendar in 46 B.C. as a modified form of
00051  *     the old Roman republican calendar which was based on lunar cycles.
00052  *     The new Julian calendar set fixed lengths for the months, abandoning
00053  *     the lunar cycle.  It also specified that there would be exactly 12
00054  *     months per year and 365.25 days per year with every 4th year being a
00055  *     leap year.
00056  *
00057  *     Note that the current accepted value for the tropical year is
00058  *     365.242199 days, not 365.25.  This lead to an 11 day shift in the
00059  *     calendar with respect to the seasons by the 16th century when the
00060  *     Gregorian calendar was created to replace the Julian calendar.
00061  *
00062  *     The difference between the Julian and today's Gregorian calendar is
00063  *     that the Gregorian does not make centennial years leap years unless
00064  *     they are a multiple of 400, which leads to a year of 365.2425 days.
00065  *     In other words, in the Gregorian calendar, 1700, 1800 and 1900 are
00066  *     not leap years, but 2000 is.  All centennial years are leap years in
00067  *     the Julian calendar.
00068  *
00069  *     The details are unknown, but the lengths of the months were adjusted
00070  *     until they finally stablized in 8 A.D. with their current lengths:
00071  *
00072  *         January          31
00073  *         February         28/29
00074  *         March            31
00075  *         April            30
00076  *         May              31
00077  *         June             30
00078  *         Quintilis/July   31
00079  *         Sextilis/August  31
00080  *         September        30
00081  *         October          31
00082  *         November         30
00083  *         December         31
00084  *
00085  *     In the early days of the calendar, the days of the month were not
00086  *     numbered as we do today.  The numbers ran backwards (decreasing) and
00087  *     were counted from the Ides (15th of the month - which in the old
00088  *     Roman republican lunar calendar would have been the full moon) or
00089  *     from the Nonae (9th day before the Ides) or from the beginning of
00090  *     the next month.
00091  *
00092  *     In the early years, the beginning of the year varied, sometimes
00093  *     based on the ascension of rulers.  It was not always the first of
00094  *     January.
00095  *
00096  *     Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not
00097  *     come into use until several centuries later when Christianity became
00098  *     a dominant religion.
00099  *
00100  * ALGORITHMS
00101  *
00102  *     The calculations are based on two different cycles: a 4 year cycle
00103  *     of leap years and a 5 month cycle of month lengths.
00104  *
00105  *     The 5 month cycle is used to account for the varying lengths of
00106  *     months.  You will notice that the lengths alternate between 30 and
00107  *     31 days, except for three anomalies: both July and August have 31
00108  *     days, both December and January have 31, and February is less than
00109  *     30.  Starting with March, the lengths are in a cycle of 5 months
00110  *     (31, 30, 31, 30, 31):
00111  *
00112  *         Mar   31 days  \
00113  *         Apr   30 days   |
00114  *         May   31 days    > First cycle
00115  *         Jun   30 days   |
00116  *         Jul   31 days  /
00117  *
00118  *         Aug   31 days  \
00119  *         Sep   30 days   |
00120  *         Oct   31 days    > Second cycle
00121  *         Nov   30 days   |
00122  *         Dec   31 days  /
00123  *
00124  *         Jan   31 days  \
00125  *         Feb 28/9 days   |
00126  *                          > Third cycle (incomplete)
00127  *
00128  *     For this reason the calculations (internally) assume that the year
00129  *     starts with March 1.
00130  *
00131  * TESTING
00132  *
00133  *     This algorithm has been tested from the year 4713 B.C. to 10000 A.D.
00134  *     The source code of the verification program is included in this
00135  *     package.
00136  *
00137  * REFERENCES
00138  *
00139  *     Conversions Between Calendar Date and Julian Day Number by Robert J.
00140  *     Tantzen, Communications of the Association for Computing Machinery
00141  *     August 1963.  (Also published in Collected Algorithms from CACM,
00142  *     algorithm number 199).  [Note: the published algorithm is for the
00143  *     Gregorian calendar, but was adjusted to use the Julian calendar's
00144  *     simpler leap year rule.]
00145  *
00146  **************************************************************************/
00147 
00148 #include "sdncal.h"
00149 #include <limits.h>
00150 
00151 #define JULIAN_SDN_OFFSET         32083
00152 #define DAYS_PER_5_MONTHS  153
00153 #define DAYS_PER_4_YEARS   1461
00154 
00155 void SdnToJulian(
00156                                    long int sdn,
00157                                    int *pYear,
00158                                    int *pMonth,
00159                                    int *pDay)
00160 {
00161        int year;
00162        int month;
00163        int day;
00164        long int temp;
00165        int dayOfYear;
00166 
00167        if (sdn <= 0) {
00168               goto fail;
00169        }
00170        /* Check for overflow */
00171        if (sdn > (LONG_MAX - JULIAN_SDN_OFFSET * 4 + 1) / 4 || sdn < LONG_MIN / 4) {
00172               goto fail;
00173        }
00174        temp = sdn * 4 + (JULIAN_SDN_OFFSET * 4 - 1);
00175 
00176        /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
00177        {
00178               long yearl = temp / DAYS_PER_4_YEARS;
00179               if (yearl > INT_MAX || yearl < INT_MIN) {
00180                      goto fail;
00181               }
00182               year = (int) yearl;
00183        }
00184        dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
00185 
00186        /* Calculate the month and day of month. */
00187        temp = dayOfYear * 5 - 3;
00188        month = temp / DAYS_PER_5_MONTHS;
00189        day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
00190 
00191        /* Convert to the normal beginning of the year. */
00192        if (month < 10) {
00193               month += 3;
00194        } else {
00195               year += 1;
00196               month -= 9;
00197        }
00198 
00199        /* Adjust to the B.C./A.D. type numbering. */
00200        year -= 4800;
00201        if (year <= 0)
00202               year--;
00203 
00204        *pYear = year;
00205        *pMonth = month;
00206        *pDay = day;
00207        return;
00208 
00209 fail:
00210        *pYear = 0;
00211        *pMonth = 0;
00212        *pDay = 0;
00213 }
00214 
00215 long int JulianToSdn(
00216                                           int inputYear,
00217                                           int inputMonth,
00218                                           int inputDay)
00219 {
00220        int year;
00221        int month;
00222 
00223        /* check for invalid dates */
00224        if (inputYear == 0 || inputYear < -4713 ||
00225               inputMonth <= 0 || inputMonth > 12 ||
00226               inputDay <= 0 || inputDay > 31) {
00227               return (0);
00228        }
00229        /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */
00230        if (inputYear == -4713) {
00231               if (inputMonth == 1 && inputDay == 1) {
00232                      return (0);
00233               }
00234        }
00235        /* Make year always a positive number. */
00236        if (inputYear < 0) {
00237               year = inputYear + 4801;
00238        } else {
00239               year = inputYear + 4800;
00240        }
00241 
00242        /* Adjust the start of the year. */
00243        if (inputMonth > 2) {
00244               month = inputMonth - 3;
00245        } else {
00246               month = inputMonth + 9;
00247               year--;
00248        }
00249 
00250        return ((year * DAYS_PER_4_YEARS) / 4
00251                      + (month * DAYS_PER_5_MONTHS + 2) / 5
00252                      + inputDay
00253                      - JULIAN_SDN_OFFSET);
00254 }
00255 
00256 /*
00257  * Local variables:
00258  * tab-width: 4
00259  * c-basic-offset: 4
00260  * End:
00261  * vim600: sw=4 ts=4 fdm=marker
00262  * vim<600: sw=4 ts=4
00263  */