root/ext/calendar/julian.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SdnToJulian
  2. JulianToSdn

   1 /* $selId: julian.c,v 2.0 1995/10/24 01:13:06 lees Exp $
   2  * Copyright 1993-1995, Scott E. Lee, all rights reserved.
   3  * Permission granted to use, copy, modify, distribute and sell so long as
   4  * the above copyright and this permission statement are retained in all
   5  * copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
   6  */
   7 
   8 /**************************************************************************
   9  *
  10  * These are the externally visible components of this file:
  11  *
  12  *     void
  13  *     SdnToJulian(
  14  *         zend_long  sdn,
  15  *         int      *pYear,
  16  *         int      *pMonth,
  17  *         int      *pDay);
  18  *
  19  * Convert a SDN to a Julian calendar date.  If the input SDN is less than
  20  * 1, the three output values will all be set to zero, otherwise *pYear
  21  * will be >= -4713 and != 0; *pMonth will be in the range 1 to 12
  22  * inclusive; *pDay will be in the range 1 to 31 inclusive.
  23  *
  24  *     zend_long
  25  *     JulianToSdn(
  26  *         int inputYear,
  27  *         int inputMonth,
  28  *         int inputDay);
  29  *
  30  * Convert a Julian calendar date to a SDN.  Zero is returned when the
  31  * input date is detected as invalid or out of the supported range.  The
  32  * return value will be > 0 for all valid, supported dates, but there are
  33  * some invalid dates that will return a positive value.  To verify that a
  34  * date is valid, convert it to SDN and then back and compare with the
  35  * original.
  36  *
  37  * VALID RANGE
  38  *
  39  *     4713 B.C. to at least 10000 A.D.
  40  *
  41  *     Although this software can handle dates all the way back to 4713
  42  *     B.C., such use may not be meaningful.  The calendar was created in
  43  *     46 B.C., but the details did not stabilize until at least 8 A.D.,
  44  *     and perhaps as late at the 4th century.  Also, the beginning of a
  45  *     year varied from one culture to another - not all accepted January
  46  *     as the first month.
  47  *
  48  * CALENDAR OVERVIEW
  49  *
  50  *     Julius Caesar created the calendar in 46 B.C. as a modified form of
  51  *     the old Roman republican calendar which was based on lunar cycles.
  52  *     The new Julian calendar set fixed lengths for the months, abandoning
  53  *     the lunar cycle.  It also specified that there would be exactly 12
  54  *     months per year and 365.25 days per year with every 4th year being a
  55  *     leap year.
  56  *
  57  *     Note that the current accepted value for the tropical year is
  58  *     365.242199 days, not 365.25.  This lead to an 11 day shift in the
  59  *     calendar with respect to the seasons by the 16th century when the
  60  *     Gregorian calendar was created to replace the Julian calendar.
  61  *
  62  *     The difference between the Julian and today's Gregorian calendar is
  63  *     that the Gregorian does not make centennial years leap years unless
  64  *     they are a multiple of 400, which leads to a year of 365.2425 days.
  65  *     In other words, in the Gregorian calendar, 1700, 1800 and 1900 are
  66  *     not leap years, but 2000 is.  All centennial years are leap years in
  67  *     the Julian calendar.
  68  *
  69  *     The details are unknown, but the lengths of the months were adjusted
  70  *     until they finally stablized in 8 A.D. with their current lengths:
  71  *
  72  *         January          31
  73  *         February         28/29
  74  *         March            31
  75  *         April            30
  76  *         May              31
  77  *         June             30
  78  *         Quintilis/July   31
  79  *         Sextilis/August  31
  80  *         September        30
  81  *         October          31
  82  *         November         30
  83  *         December         31
  84  *
  85  *     In the early days of the calendar, the days of the month were not
  86  *     numbered as we do today.  The numbers ran backwards (decreasing) and
  87  *     were counted from the Ides (15th of the month - which in the old
  88  *     Roman republican lunar calendar would have been the full moon) or
  89  *     from the Nonae (9th day before the Ides) or from the beginning of
  90  *     the next month.
  91  *
  92  *     In the early years, the beginning of the year varied, sometimes
  93  *     based on the ascension of rulers.  It was not always the first of
  94  *     January.
  95  *
  96  *     Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not
  97  *     come into use until several centuries later when Christianity became
  98  *     a dominant religion.
  99  *
 100  * ALGORITHMS
 101  *
 102  *     The calculations are based on two different cycles: a 4 year cycle
 103  *     of leap years and a 5 month cycle of month lengths.
 104  *
 105  *     The 5 month cycle is used to account for the varying lengths of
 106  *     months.  You will notice that the lengths alternate between 30 and
 107  *     31 days, except for three anomalies: both July and August have 31
 108  *     days, both December and January have 31, and February is less than
 109  *     30.  Starting with March, the lengths are in a cycle of 5 months
 110  *     (31, 30, 31, 30, 31):
 111  *
 112  *         Mar   31 days  \
 113  *         Apr   30 days   |
 114  *         May   31 days    > First cycle
 115  *         Jun   30 days   |
 116  *         Jul   31 days  /
 117  *
 118  *         Aug   31 days  \
 119  *         Sep   30 days   |
 120  *         Oct   31 days    > Second cycle
 121  *         Nov   30 days   |
 122  *         Dec   31 days  /
 123  *
 124  *         Jan   31 days  \
 125  *         Feb 28/9 days   |
 126  *                          > Third cycle (incomplete)
 127  *
 128  *     For this reason the calculations (internally) assume that the year
 129  *     starts with March 1.
 130  *
 131  * TESTING
 132  *
 133  *     This algorithm has been tested from the year 4713 B.C. to 10000 A.D.
 134  *     The source code of the verification program is included in this
 135  *     package.
 136  *
 137  * REFERENCES
 138  *
 139  *     Conversions Between Calendar Date and Julian Day Number by Robert J.
 140  *     Tantzen, Communications of the Association for Computing Machinery
 141  *     August 1963.  (Also published in Collected Algorithms from CACM,
 142  *     algorithm number 199).  [Note: the published algorithm is for the
 143  *     Gregorian calendar, but was adjusted to use the Julian calendar's
 144  *     simpler leap year rule.]
 145  *
 146  **************************************************************************/
 147 
 148 #include "sdncal.h"
 149 #include <limits.h>
 150 
 151 #define JULIAN_SDN_OFFSET         32083
 152 #define DAYS_PER_5_MONTHS  153
 153 #define DAYS_PER_4_YEARS   1461
 154 
 155 void SdnToJulian(
 156                                         zend_long sdn,
 157                                         int *pYear,
 158                                         int *pMonth,
 159                                         int *pDay)
 160 {
 161         int year;
 162         int month;
 163         int day;
 164         zend_long temp;
 165         int dayOfYear;
 166 
 167         if (sdn <= 0) {
 168                 goto fail;
 169         }
 170         /* Check for overflow */
 171         if (sdn > (LONG_MAX - JULIAN_SDN_OFFSET * 4 + 1) / 4 || sdn < LONG_MIN / 4) {
 172                 goto fail;
 173         }
 174         temp = sdn * 4 + (JULIAN_SDN_OFFSET * 4 - 1);
 175 
 176         /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
 177         {
 178                 long yearl = temp / DAYS_PER_4_YEARS;
 179                 if (yearl > INT_MAX || yearl < INT_MIN) {
 180                         goto fail;
 181                 }
 182                 year = (int) yearl;
 183         }
 184         dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
 185 
 186         /* Calculate the month and day of month. */
 187         temp = dayOfYear * 5 - 3;
 188         month = temp / DAYS_PER_5_MONTHS;
 189         day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
 190 
 191         /* Convert to the normal beginning of the year. */
 192         if (month < 10) {
 193                 month += 3;
 194         } else {
 195                 year += 1;
 196                 month -= 9;
 197         }
 198 
 199         /* Adjust to the B.C./A.D. type numbering. */
 200         year -= 4800;
 201         if (year <= 0)
 202                 year--;
 203 
 204         *pYear = year;
 205         *pMonth = month;
 206         *pDay = day;
 207         return;
 208 
 209 fail:
 210         *pYear = 0;
 211         *pMonth = 0;
 212         *pDay = 0;
 213 }
 214 
 215 zend_long JulianToSdn(
 216                                                 int inputYear,
 217                                                 int inputMonth,
 218                                                 int inputDay)
 219 {
 220         int year;
 221         int month;
 222 
 223         /* check for invalid dates */
 224         if (inputYear == 0 || inputYear < -4713 ||
 225                 inputMonth <= 0 || inputMonth > 12 ||
 226                 inputDay <= 0 || inputDay > 31) {
 227                 return (0);
 228         }
 229         /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */
 230         if (inputYear == -4713) {
 231                 if (inputMonth == 1 && inputDay == 1) {
 232                         return (0);
 233                 }
 234         }
 235         /* Make year always a positive number. */
 236         if (inputYear < 0) {
 237                 year = inputYear + 4801;
 238         } else {
 239                 year = inputYear + 4800;
 240         }
 241 
 242         /* Adjust the start of the year. */
 243         if (inputMonth > 2) {
 244                 month = inputMonth - 3;
 245         } else {
 246                 month = inputMonth + 9;
 247                 year--;
 248         }
 249 
 250         return ((year * DAYS_PER_4_YEARS) / 4
 251                         + (month * DAYS_PER_5_MONTHS + 2) / 5
 252                         + inputDay
 253                         - JULIAN_SDN_OFFSET);
 254 }
 255 
 256 /*
 257  * Local variables:
 258  * tab-width: 4
 259  * c-basic-offset: 4
 260  * End:
 261  * vim600: sw=4 ts=4 fdm=marker
 262  * vim<600: sw=4 ts=4
 263  */

/* [<][>][^][v][top][bottom][index][help] */