Back to index

php5  5.3.10
gregor.c
Go to the documentation of this file.
00001 /* $selId: gregor.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  *     SdnToGregorian(
00014  *         long int  sdn,
00015  *         int      *pYear,
00016  *         int      *pMonth,
00017  *         int      *pDay);
00018  *
00019  * Convert a SDN to a Gregorian calendar date.  If the input SDN is less
00020  * than 1, the three output values will all be set to zero, otherwise
00021  * *pYear will be >= -4714 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  *     GregorianToSdn(
00026  *         int inputYear,
00027  *         int inputMonth,
00028  *         int inputDay);
00029  *
00030  * Convert a Gregorian 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  *     char *MonthNameShort[13];
00038  *
00039  * Convert a Gregorian month number (1 to 12) to the abbreviated (three
00040  * character) name of the Gregorian month (null terminated).  An index of
00041  * zero will return a zero length string.
00042  *
00043  *     char *MonthNameLong[13];
00044  *
00045  * Convert a Gregorian month number (1 to 12) to the name of the Gregorian
00046  * month (null terminated).  An index of zero will return a zero length
00047  * string.
00048  *
00049  * VALID RANGE
00050  *
00051  *     4714 B.C. to at least 10000 A.D.
00052  *
00053  *     Although this software can handle dates all the way back to 4714
00054  *     B.C., such use may not be meaningful.  The Gregorian calendar was
00055  *     not instituted until October 15, 1582 (or October 5, 1582 in the
00056  *     Julian calendar).  Some countries did not accept it until much
00057  *     later.  For example, Britain converted in 1752, The USSR in 1918 and
00058  *     Greece in 1923.  Most European countries used the Julian calendar
00059  *     prior to the Gregorian.
00060  *
00061  * CALENDAR OVERVIEW
00062  *
00063  *     The Gregorian calendar is a modified version of the Julian calendar.
00064  *     The only difference being the specification of leap years.  The
00065  *     Julian calendar specifies that every year that is a multiple of 4
00066  *     will be a leap year.  This leads to a year that is 365.25 days long,
00067  *     but the current accepted value for the tropical year is 365.242199
00068  *     days.
00069  *
00070  *     To correct this error in the length of the year and to bring the
00071  *     vernal equinox back to March 21, Pope Gregory XIII issued a papal
00072  *     bull declaring that Thursday October 4, 1582 would be followed by
00073  *     Friday October 15, 1582 and that centennial years would only be a
00074  *     leap year if they were a multiple of 400.  This shortened the year
00075  *     by 3 days per 400 years, giving a year of 365.2425 days.
00076  *
00077  *     Another recently proposed change in the leap year rule is to make
00078  *     years that are multiples of 4000 not a leap year, but this has never
00079  *     been officially accepted and this rule is not implemented in these
00080  *     algorithms.
00081  *
00082  * ALGORITHMS
00083  *
00084  *     The calculations are based on three different cycles: a 400 year
00085  *     cycle of leap years, a 4 year cycle of leap years and a 5 month
00086  *     cycle of month lengths.
00087  *
00088  *     The 5 month cycle is used to account for the varying lengths of
00089  *     months.  You will notice that the lengths alternate between 30
00090  *     and 31 days, except for three anomalies: both July and August
00091  *     have 31 days, both December and January have 31, and February
00092  *     is less than 30.  Starting with March, the lengths are in a
00093  *     cycle of 5 months (31, 30, 31, 30, 31):
00094  *
00095  *         Mar   31 days  \
00096  *         Apr   30 days   |
00097  *         May   31 days    > First cycle
00098  *         Jun   30 days   |
00099  *         Jul   31 days  /
00100  *
00101  *         Aug   31 days  \
00102  *         Sep   30 days   |
00103  *         Oct   31 days    > Second cycle
00104  *         Nov   30 days   |
00105  *         Dec   31 days  /
00106  *
00107  *         Jan   31 days  \
00108  *         Feb 28/9 days   |
00109  *                          > Third cycle (incomplete)
00110  *
00111  *     For this reason the calculations (internally) assume that the
00112  *     year starts with March 1.
00113  *
00114  * TESTING
00115  *
00116  *     This algorithm has been tested from the year 4714 B.C. to 10000
00117  *     A.D.  The source code of the verification program is included in
00118  *     this package.
00119  *
00120  * REFERENCES
00121  *
00122  *     Conversions Between Calendar Date and Julian Day Number by Robert J.
00123  *     Tantzen, Communications of the Association for Computing Machinery
00124  *     August 1963.  (Also published in Collected Algorithms from CACM,
00125  *     algorithm number 199).
00126  *
00127  **************************************************************************/
00128 
00129 #include "sdncal.h"
00130 #include <limits.h>
00131 
00132 #define GREGOR_SDN_OFFSET         32045
00133 #define DAYS_PER_5_MONTHS  153
00134 #define DAYS_PER_4_YEARS   1461
00135 #define DAYS_PER_400_YEARS 146097
00136 
00137 void SdnToGregorian(
00138                                       long int sdn,
00139                                       int *pYear,
00140                                       int *pMonth,
00141                                       int *pDay)
00142 {
00143        int century;
00144        int year;
00145        int month;
00146        int day;
00147        long int temp;
00148        int dayOfYear;
00149 
00150        if (sdn <= 0 ||
00151                      sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) {
00152               goto fail;
00153        }
00154        temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1;
00155 
00156        /* Calculate the century (year/100). */
00157        century = temp / DAYS_PER_400_YEARS;
00158 
00159        /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
00160        temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
00161        year = (century * 100) + (temp / DAYS_PER_4_YEARS);
00162        dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
00163 
00164        /* Calculate the month and day of month. */
00165        temp = dayOfYear * 5 - 3;
00166        month = temp / DAYS_PER_5_MONTHS;
00167        day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
00168 
00169        /* Convert to the normal beginning of the year. */
00170        if (month < 10) {
00171               month += 3;
00172        } else {
00173               year += 1;
00174               month -= 9;
00175        }
00176 
00177        /* Adjust to the B.C./A.D. type numbering. */
00178        year -= 4800;
00179        if (year <= 0)
00180               year--;
00181 
00182        *pYear = year;
00183        *pMonth = month;
00184        *pDay = day;
00185        return;
00186 
00187 fail:
00188        *pYear = 0;
00189        *pMonth = 0;
00190        *pDay = 0;
00191 }
00192 
00193 long int GregorianToSdn(
00194                                              int inputYear,
00195                                              int inputMonth,
00196                                              int inputDay)
00197 {
00198        int year;
00199        int month;
00200 
00201        /* check for invalid dates */
00202        if (inputYear == 0 || inputYear < -4714 ||
00203               inputMonth <= 0 || inputMonth > 12 ||
00204               inputDay <= 0 || inputDay > 31) {
00205               return (0);
00206        }
00207        /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */
00208        if (inputYear == -4714) {
00209               if (inputMonth < 11) {
00210                      return (0);
00211               }
00212               if (inputMonth == 11 && inputDay < 25) {
00213                      return (0);
00214               }
00215        }
00216        /* Make year always a positive number. */
00217        if (inputYear < 0) {
00218               year = inputYear + 4801;
00219        } else {
00220               year = inputYear + 4800;
00221        }
00222 
00223        /* Adjust the start of the year. */
00224        if (inputMonth > 2) {
00225               month = inputMonth - 3;
00226        } else {
00227               month = inputMonth + 9;
00228               year--;
00229        }
00230 
00231        return (((year / 100) * DAYS_PER_400_YEARS) / 4
00232                      + ((year % 100) * DAYS_PER_4_YEARS) / 4
00233                      + (month * DAYS_PER_5_MONTHS + 2) / 5
00234                      + inputDay
00235                      - GREGOR_SDN_OFFSET);
00236 }
00237 
00238 char *MonthNameShort[13] =
00239 {
00240        "",
00241        "Jan",
00242        "Feb",
00243        "Mar",
00244        "Apr",
00245        "May",
00246        "Jun",
00247        "Jul",
00248        "Aug",
00249        "Sep",
00250        "Oct",
00251        "Nov",
00252        "Dec"
00253 };
00254 
00255 char *MonthNameLong[13] =
00256 {
00257        "",
00258        "January",
00259        "February",
00260        "March",
00261        "April",
00262        "May",
00263        "June",
00264        "July",
00265        "August",
00266        "September",
00267        "October",
00268        "November",
00269        "December"
00270 };