123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /* $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"
- #include <limits.h>
- #define GREGOR_SDN_OFFSET 32045
- #define DAYS_PER_5_MONTHS 153
- #define DAYS_PER_4_YEARS 1461
- #define DAYS_PER_400_YEARS 146097
- void SdnToGregorian(
- zend_long sdn,
- int *pYear,
- int *pMonth,
- int *pDay)
- {
- int century;
- int year;
- int month;
- int day;
- zend_long temp;
- int dayOfYear;
- if (sdn <= 0 ||
- sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) {
- goto fail;
- }
- temp = (sdn + GREGOR_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;
- return;
- fail:
- *pYear = 0;
- *pMonth = 0;
- *pDay = 0;
- }
- zend_long GregorianToSdn(
- int inputYear,
- int inputMonth,
- int inputDay)
- {
- zend_long 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
- - GREGOR_SDN_OFFSET);
- }
- const char * const MonthNameShort[13] =
- {
- "",
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec"
- };
- const char * const MonthNameLong[13] =
- {
- "",
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- };
|