Annotation of embedaddon/php/ext/calendar/gregor.c, revision 1.1.1.1

1.1       misho       1: /* $selId: gregor.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:  *     SdnToGregorian(
                     14:  *         long int  sdn,
                     15:  *         int      *pYear,
                     16:  *         int      *pMonth,
                     17:  *         int      *pDay);
                     18:  *
                     19:  * Convert a SDN to a Gregorian calendar date.  If the input SDN is less
                     20:  * than 1, the three output values will all be set to zero, otherwise
                     21:  * *pYear will be >= -4714 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:  *     long int
                     25:  *     GregorianToSdn(
                     26:  *         int inputYear,
                     27:  *         int inputMonth,
                     28:  *         int inputDay);
                     29:  *
                     30:  * Convert a Gregorian 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:  *     char *MonthNameShort[13];
                     38:  *
                     39:  * Convert a Gregorian month number (1 to 12) to the abbreviated (three
                     40:  * character) name of the Gregorian month (null terminated).  An index of
                     41:  * zero will return a zero length string.
                     42:  *
                     43:  *     char *MonthNameLong[13];
                     44:  *
                     45:  * Convert a Gregorian month number (1 to 12) to the name of the Gregorian
                     46:  * month (null terminated).  An index of zero will return a zero length
                     47:  * string.
                     48:  *
                     49:  * VALID RANGE
                     50:  *
                     51:  *     4714 B.C. to at least 10000 A.D.
                     52:  *
                     53:  *     Although this software can handle dates all the way back to 4714
                     54:  *     B.C., such use may not be meaningful.  The Gregorian calendar was
                     55:  *     not instituted until October 15, 1582 (or October 5, 1582 in the
                     56:  *     Julian calendar).  Some countries did not accept it until much
                     57:  *     later.  For example, Britain converted in 1752, The USSR in 1918 and
                     58:  *     Greece in 1923.  Most European countries used the Julian calendar
                     59:  *     prior to the Gregorian.
                     60:  *
                     61:  * CALENDAR OVERVIEW
                     62:  *
                     63:  *     The Gregorian calendar is a modified version of the Julian calendar.
                     64:  *     The only difference being the specification of leap years.  The
                     65:  *     Julian calendar specifies that every year that is a multiple of 4
                     66:  *     will be a leap year.  This leads to a year that is 365.25 days long,
                     67:  *     but the current accepted value for the tropical year is 365.242199
                     68:  *     days.
                     69:  *
                     70:  *     To correct this error in the length of the year and to bring the
                     71:  *     vernal equinox back to March 21, Pope Gregory XIII issued a papal
                     72:  *     bull declaring that Thursday October 4, 1582 would be followed by
                     73:  *     Friday October 15, 1582 and that centennial years would only be a
                     74:  *     leap year if they were a multiple of 400.  This shortened the year
                     75:  *     by 3 days per 400 years, giving a year of 365.2425 days.
                     76:  *
                     77:  *     Another recently proposed change in the leap year rule is to make
                     78:  *     years that are multiples of 4000 not a leap year, but this has never
                     79:  *     been officially accepted and this rule is not implemented in these
                     80:  *     algorithms.
                     81:  *
                     82:  * ALGORITHMS
                     83:  *
                     84:  *     The calculations are based on three different cycles: a 400 year
                     85:  *     cycle of leap years, a 4 year cycle of leap years and a 5 month
                     86:  *     cycle of month lengths.
                     87:  *
                     88:  *     The 5 month cycle is used to account for the varying lengths of
                     89:  *     months.  You will notice that the lengths alternate between 30
                     90:  *     and 31 days, except for three anomalies: both July and August
                     91:  *     have 31 days, both December and January have 31, and February
                     92:  *     is less than 30.  Starting with March, the lengths are in a
                     93:  *     cycle of 5 months (31, 30, 31, 30, 31):
                     94:  *
                     95:  *         Mar   31 days  \
                     96:  *         Apr   30 days   |
                     97:  *         May   31 days    > First cycle
                     98:  *         Jun   30 days   |
                     99:  *         Jul   31 days  /
                    100:  *
                    101:  *         Aug   31 days  \
                    102:  *         Sep   30 days   |
                    103:  *         Oct   31 days    > Second cycle
                    104:  *         Nov   30 days   |
                    105:  *         Dec   31 days  /
                    106:  *
                    107:  *         Jan   31 days  \
                    108:  *         Feb 28/9 days   |
                    109:  *                          > Third cycle (incomplete)
                    110:  *
                    111:  *     For this reason the calculations (internally) assume that the
                    112:  *     year starts with March 1.
                    113:  *
                    114:  * TESTING
                    115:  *
                    116:  *     This algorithm has been tested from the year 4714 B.C. to 10000
                    117:  *     A.D.  The source code of the verification program is included in
                    118:  *     this package.
                    119:  *
                    120:  * REFERENCES
                    121:  *
                    122:  *     Conversions Between Calendar Date and Julian Day Number by Robert J.
                    123:  *     Tantzen, Communications of the Association for Computing Machinery
                    124:  *     August 1963.  (Also published in Collected Algorithms from CACM,
                    125:  *     algorithm number 199).
                    126:  *
                    127:  **************************************************************************/
                    128: 
                    129: #include "sdncal.h"
                    130: #include <limits.h>
                    131: 
                    132: #define GREGOR_SDN_OFFSET         32045
                    133: #define DAYS_PER_5_MONTHS  153
                    134: #define DAYS_PER_4_YEARS   1461
                    135: #define DAYS_PER_400_YEARS 146097
                    136: 
                    137: void SdnToGregorian(
                    138:                                           long int sdn,
                    139:                                           int *pYear,
                    140:                                           int *pMonth,
                    141:                                           int *pDay)
                    142: {
                    143:        int century;
                    144:        int year;
                    145:        int month;
                    146:        int day;
                    147:        long int temp;
                    148:        int dayOfYear;
                    149: 
                    150:        if (sdn <= 0 ||
                    151:                        sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) {
                    152:                goto fail;
                    153:        }
                    154:        temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1;
                    155: 
                    156:        /* Calculate the century (year/100). */
                    157:        century = temp / DAYS_PER_400_YEARS;
                    158: 
                    159:        /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
                    160:        temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
                    161:        year = (century * 100) + (temp / DAYS_PER_4_YEARS);
                    162:        dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
                    163: 
                    164:        /* Calculate the month and day of month. */
                    165:        temp = dayOfYear * 5 - 3;
                    166:        month = temp / DAYS_PER_5_MONTHS;
                    167:        day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
                    168: 
                    169:        /* Convert to the normal beginning of the year. */
                    170:        if (month < 10) {
                    171:                month += 3;
                    172:        } else {
                    173:                year += 1;
                    174:                month -= 9;
                    175:        }
                    176: 
                    177:        /* Adjust to the B.C./A.D. type numbering. */
                    178:        year -= 4800;
                    179:        if (year <= 0)
                    180:                year--;
                    181: 
                    182:        *pYear = year;
                    183:        *pMonth = month;
                    184:        *pDay = day;
                    185:        return;
                    186: 
                    187: fail:
                    188:        *pYear = 0;
                    189:        *pMonth = 0;
                    190:        *pDay = 0;
                    191: }
                    192: 
                    193: long int GregorianToSdn(
                    194:                                                   int inputYear,
                    195:                                                   int inputMonth,
                    196:                                                   int inputDay)
                    197: {
                    198:        int year;
                    199:        int month;
                    200: 
                    201:        /* check for invalid dates */
                    202:        if (inputYear == 0 || inputYear < -4714 ||
                    203:                inputMonth <= 0 || inputMonth > 12 ||
                    204:                inputDay <= 0 || inputDay > 31) {
                    205:                return (0);
                    206:        }
                    207:        /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */
                    208:        if (inputYear == -4714) {
                    209:                if (inputMonth < 11) {
                    210:                        return (0);
                    211:                }
                    212:                if (inputMonth == 11 && inputDay < 25) {
                    213:                        return (0);
                    214:                }
                    215:        }
                    216:        /* Make year always a positive number. */
                    217:        if (inputYear < 0) {
                    218:                year = inputYear + 4801;
                    219:        } else {
                    220:                year = inputYear + 4800;
                    221:        }
                    222: 
                    223:        /* Adjust the start of the year. */
                    224:        if (inputMonth > 2) {
                    225:                month = inputMonth - 3;
                    226:        } else {
                    227:                month = inputMonth + 9;
                    228:                year--;
                    229:        }
                    230: 
                    231:        return (((year / 100) * DAYS_PER_400_YEARS) / 4
                    232:                        + ((year % 100) * DAYS_PER_4_YEARS) / 4
                    233:                        + (month * DAYS_PER_5_MONTHS + 2) / 5
                    234:                        + inputDay
                    235:                        - GREGOR_SDN_OFFSET);
                    236: }
                    237: 
                    238: char *MonthNameShort[13] =
                    239: {
                    240:        "",
                    241:        "Jan",
                    242:        "Feb",
                    243:        "Mar",
                    244:        "Apr",
                    245:        "May",
                    246:        "Jun",
                    247:        "Jul",
                    248:        "Aug",
                    249:        "Sep",
                    250:        "Oct",
                    251:        "Nov",
                    252:        "Dec"
                    253: };
                    254: 
                    255: char *MonthNameLong[13] =
                    256: {
                    257:        "",
                    258:        "January",
                    259:        "February",
                    260:        "March",
                    261:        "April",
                    262:        "May",
                    263:        "June",
                    264:        "July",
                    265:        "August",
                    266:        "September",
                    267:        "October",
                    268:        "November",
                    269:        "December"
                    270: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>