datecalc.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. datecalc.c
  3. Written 1996/96 by Oliver Kraus
  4. Published by Heinz Heise Verlag 1997 (c't 15/97)
  5. Completly rewritten and put under GPL 2011 by Oliver Kraus
  6. (c) 2011 by Oliver Kraus (olikraus@gmail.com)
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation, either version 3 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. Development goals:
  18. - English version
  19. - Optimized for 8 bit microcontroller
  20. Definitions:
  21. Short Name: y
  22. Long Name: year
  23. Range: 2000...
  24. Short Name: ydn
  25. Long Name: year day number
  26. Range: 1..366
  27. Short Name: cdn
  28. Long Name: century day number
  29. Range: 1...65535
  30. Short Name: ymd
  31. Long Name: Year, Month, Day
  32. Range: 2000...65535, 1..12, 1..31
  33. Conversions
  34. ymd --> y, ydn
  35. get_year_day_number()
  36. y, ydn --> ymd
  37. get_month_by_year_day_number()
  38. get_day_by_year_day_number()
  39. y, ydn --> cdn
  40. to_century_day_number();
  41. cdn --> y, ydn
  42. from_century_day_number();
  43. */
  44. #include <stdint.h>
  45. /*
  46. Prototype:
  47. uint8_t is_leap_year(uint16_t y)
  48. Description:
  49. Calculate leap year
  50. Arguments:
  51. y year, e.g. 2011 for year 2011
  52. Result:
  53. 0 not a leap year
  54. 1 leap year
  55. */
  56. uint8_t is_leap_year(uint16_t y)
  57. {
  58. if (
  59. ((y % 4 == 0) && (y % 100 != 0)) ||
  60. (y % 400 == 0)
  61. )
  62. return 1;
  63. return 0;
  64. }
  65. /*
  66. Prototype:
  67. uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d)
  68. Description:
  69. Calculate the day number within a year. 1st of Jan has the number 1.
  70. "Robertson" Algorithm IDAY (CACM Vol 15/#10/Oct 1972)
  71. Arguments:
  72. y year, e.g. 2011 for year 2011
  73. m month with 1 = january to 12 = december
  74. d day starting with 1
  75. Result:
  76. The "day number" within the year: 1 for the 1st of Jan.
  77. See also:
  78. get_month_by_day_number()
  79. */
  80. uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d)
  81. {
  82. uint8_t tmp1;
  83. uint16_t tmp2;
  84. tmp1 = 0;
  85. if ( m >= 3 )
  86. tmp1++;
  87. tmp2 = m;
  88. tmp2 +=2;
  89. tmp2 *=611;
  90. tmp2 /= 20;
  91. tmp2 += d;
  92. tmp2 -= 91;
  93. tmp1 <<=1;
  94. tmp2 -= tmp1;
  95. if ( tmp1 != 0 )
  96. tmp2 += is_leap_year(y);
  97. return tmp2;
  98. }
  99. /*
  100. Prototype:
  101. uint8_t get_month_by_year_day_number(uint16_t y, uint16_t ydn)
  102. Description:
  103. Get the month from year and day number within a year.
  104. "R. A. Stone" Algorithm (CACM Vol 13/#10/Oct 1970)
  105. Arguments:
  106. y year, e.g. 2011 for year 2011
  107. ydn year day number (1st of Jan has the number 1)
  108. Result:
  109. The month within the year: 1 for January.
  110. See also:
  111. get_year_day_number()
  112. */
  113. static uint16_t corrected_year_day_number(uint16_t y, uint16_t ydn)
  114. {
  115. uint8_t a;
  116. a = is_leap_year(y);
  117. if ( ydn > 59+a )
  118. {
  119. ydn += 2;
  120. ydn -= a;
  121. }
  122. ydn += 91;
  123. return ydn;
  124. }
  125. uint8_t get_month_by_year_day_number(uint16_t y, uint16_t ydn)
  126. {
  127. uint8_t a;
  128. ydn = corrected_year_day_number(y, ydn);
  129. ydn *= 20;
  130. ydn /= 611;
  131. a = ydn;
  132. a -= 2;
  133. return a;
  134. }
  135. /*
  136. Prototype:
  137. uint8_t get_day_by_year_day_number(uint16_t y, uint16_t ydn)
  138. Description:
  139. Get the day within month from year and day number within a year.
  140. "R. A. Stone" Algorithm (CACM Vol 13/#10/Oct 1970)
  141. Arguments:
  142. y year, e.g. 2011 for year 2011
  143. ydn year day number (1st of Jan has the number 1)
  144. Result:
  145. The day within a month: 1 for the first day of a month.
  146. See also:
  147. get_year_day_number()
  148. */
  149. uint8_t get_day_by_year_day_number(uint16_t y, uint16_t ydn)
  150. {
  151. uint8_t m;
  152. uint16_t tmp;
  153. m = get_month_by_year_day_number(y, ydn);
  154. m += 2;
  155. ydn = corrected_year_day_number(y, ydn);
  156. tmp = 611;
  157. tmp *= m;
  158. tmp /= 20;
  159. ydn -= tmp;
  160. return ydn;
  161. }
  162. /*
  163. Prototype:
  164. uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn)
  165. Description:
  166. Get the day within week from year and day number within a year.
  167. "Zeller" Algorithm
  168. https://de.wikisource.org/wiki/Index:Acta_Mathematica_vol._009_(1886)
  169. https://ia801407.us.archive.org/8/items/actamathematica09upps/actamathematica09upps.pdf
  170. Arguments:
  171. y year, e.g. 2011 for year 2011
  172. ydn year day number (1st of Jan has the number 1)
  173. Result:
  174. The day within a week: 0..6 with 0 = Sunday, 1 = Monday, ...
  175. See also:
  176. get_year_day_number()
  177. */
  178. uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn)
  179. {
  180. uint8_t j, c, tmp8;
  181. uint16_t tmp16;
  182. y--;
  183. j = y % 100;
  184. c = y / 100;
  185. tmp16 = c;
  186. tmp16 *= 5;
  187. tmp16 += ydn;
  188. tmp8 = j;
  189. j >>= 2;
  190. c >>= 2;
  191. tmp8 += j;
  192. tmp8 += c;
  193. tmp8 += 28;
  194. tmp16 += tmp8;
  195. tmp16 %= 7;
  196. return tmp16;
  197. }
  198. /*
  199. Prototype:
  200. uint16_t to_century_day_number(uint16_t y, uint16_t ydn)
  201. Description:
  202. Calculate days since January, 1st, 2000
  203. Arguments:
  204. y year, e.g. 2011 for year 2011
  205. ydn year day number (1st of Jan has the number 1)
  206. */
  207. uint16_t to_century_day_number(uint16_t y, uint16_t ydn)
  208. {
  209. uint16_t cdn;
  210. cdn = ydn;
  211. cdn--;
  212. while( y > 2000 )
  213. {
  214. y--;
  215. cdn += 365;
  216. cdn += is_leap_year(y);
  217. }
  218. return cdn;
  219. }
  220. void from_century_day_number(uint16_t cdn, uint16_t *year, uint16_t *ydn)
  221. {
  222. uint16_t y, days_per_year;
  223. y = 2000;
  224. for(;;)
  225. {
  226. days_per_year = 365;
  227. days_per_year += is_leap_year(y);
  228. if ( cdn >= days_per_year )
  229. {
  230. cdn -= days_per_year;
  231. y++;
  232. }
  233. else
  234. break;
  235. }
  236. cdn++;
  237. *year = y;
  238. *ydn = cdn;
  239. }
  240. /*
  241. Calculate the seconds after 2000-01-01 00:00. The largest possible
  242. time is 2136-02-07 06:28:15
  243. */
  244. uint32_t to_time(uint16_t cdn, uint8_t h, uint8_t m, uint8_t s)
  245. {
  246. uint32_t t;
  247. t = cdn;
  248. t *= 24;
  249. t += h;
  250. t *= 60;
  251. t += m;
  252. t *= 60;
  253. t += s;
  254. return t;
  255. }
  256. void from_time(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m, uint8_t *s)
  257. {
  258. *s = t % 60;
  259. t /= 60;
  260. *m = t % 60;
  261. t /= 60;
  262. *h = t % 24;
  263. t /= 24;
  264. *cdn = t;
  265. }
  266. uint32_t to_sec_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi, uint8_t s)
  267. {
  268. uint16_t ydn = get_year_day_number(y, mo, d);
  269. uint16_t cdn = to_century_day_number(y, ydn);
  270. return to_time(cdn, h, mi, s);
  271. }
  272. /*
  273. Calculate the minutes after 2000-01-01 00:00.
  274. */
  275. uint32_t to_minutes(uint16_t cdn, uint8_t h, uint8_t m)
  276. {
  277. uint32_t t;
  278. t = cdn;
  279. t *= 24;
  280. t += h;
  281. t *= 60;
  282. t += m;
  283. return t;
  284. }
  285. void from_minutes(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m)
  286. {
  287. *m = t % 60;
  288. t /= 60;
  289. *h = t % 24;
  290. t /= 24;
  291. *cdn = t;
  292. }
  293. uint32_t to_minutes_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi)
  294. {
  295. uint16_t ydn = get_year_day_number(y, mo, d);
  296. uint16_t cdn = to_century_day_number(y, ydn);
  297. return to_minutes(cdn, h, mi);
  298. }
  299. /*
  300. Eingabe: Jahr
  301. Ausgabe: Tagesnummer des Ostersonntag,
  302. rel. zum Jahresanfang.
  303. Algorithmus "Computus ecclesiasticus"
  304. 325 n.Chr. wurde Ostern auf den Sonntag nach
  305. dem ersten Fruehlingsvollmond festgelegt. Damit
  306. liegt Ostern zwischen dem 22. Maerz und
  307. dem 25. April.
  308. */
  309. /*
  310. int ostersonntag(int jahr)
  311. {
  312. int gz, jhd, ksj, korr, so, epakte, n;
  313. gz = (jahr%19)+1;
  314. jhd = jahr/100+1;
  315. ksj = (3*jhd)/4-12;
  316. korr = (8*jhd+5)/25-5;
  317. so = (5*jahr)/4-ksj-10;
  318. epakte = (11*gz+20+korr-ksj) % 30;
  319. if ( (epakte == 25 && gz > 11) || epakte == 24 )
  320. epakte++;
  321. n = 44-epakte;
  322. if ( n < 21 )
  323. n = n + 30;
  324. n = n + 7 - (so+n) % 7;
  325. n += schaltjahr(jahr);
  326. return n+59;
  327. }
  328. */