julian.c 7.9 KB

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