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