gregor.c 7.5 KB

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