datecalc.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. static 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
  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
  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
  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. Arguments:
  169. y year, e.g. 2011 for year 2011
  170. ydn year day number (1st of Jan has the number 1)
  171. Result:
  172. The day within a week: 0..6 with 0 = Sunday, 1 = Monday, ...
  173. See also:
  174. get_year_day_number()
  175. */
  176. uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn)
  177. {
  178. uint8_t j, c, tmp8;
  179. uint16_t tmp16;
  180. y--;
  181. j = y % 100;
  182. c = y / 100;
  183. tmp16 = c;
  184. tmp16 *= 5;
  185. tmp16 += ydn;
  186. tmp8 = j;
  187. j >>= 2;
  188. c >>= 2;
  189. tmp8 += j;
  190. tmp8 += c;
  191. tmp8 += 28;
  192. tmp16 += tmp8;
  193. tmp16 %= 7;
  194. return tmp16;
  195. }
  196. /*
  197. Prototype:
  198. uint16_t to_century_day_number(uint16_t y, uint16_t ydn)
  199. Description:
  200. Calculate days since January, 1st, 2000
  201. Arguments:
  202. y year, e.g. 2011 for year 2011
  203. ydn year day number (1st of Jan has the number 1)
  204. */
  205. uint16_t to_century_day_number(uint16_t y, uint16_t ydn)
  206. {
  207. uint16_t cdn;
  208. cdn = ydn;
  209. cdn--;
  210. while( y > 2000 )
  211. {
  212. y--;
  213. cdn += 365;
  214. cdn += is_leap_year(y);
  215. }
  216. return cdn;
  217. }
  218. void from_century_day_number(uint16_t cdn, uint16_t *year, uint16_t *ydn)
  219. {
  220. uint16_t y, days_per_year;
  221. y = 2000;
  222. for(;;)
  223. {
  224. days_per_year = 365;
  225. days_per_year += is_leap_year(y);
  226. if ( cdn >= days_per_year )
  227. {
  228. cdn -= days_per_year;
  229. y++;
  230. }
  231. else
  232. break;
  233. }
  234. cdn++;
  235. *year = y;
  236. *ydn = cdn;
  237. }
  238. /*
  239. Calculate the seconds after 2000-01-01 00:00. The largest possible
  240. time is 2136-02-07 06:28:15
  241. */
  242. uint32_t to_time(uint16_t cdn, uint8_t h, uint8_t m, uint8_t s)
  243. {
  244. uint32_t t;
  245. t = cdn;
  246. t *= 24;
  247. t += h;
  248. t *= 60;
  249. t += m;
  250. t *= 60;
  251. t += s;
  252. return t;
  253. }
  254. void from_time(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m, uint8_t *s)
  255. {
  256. *s = t % 60;
  257. t /= 60;
  258. *m = t % 60;
  259. t /= 60;
  260. *h = t % 24;
  261. t /= 24;
  262. *cdn = t;
  263. }
  264. uint32_t to_sec_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi, uint8_t s)
  265. {
  266. uint16_t ydn = get_year_day_number(y, mo, d);
  267. uint16_t cdn = to_century_day_number(y, ydn);
  268. return to_time(cdn, h, mi, s);
  269. }
  270. /*
  271. Calculate the minutes after 2000-01-01 00:00.
  272. */
  273. uint32_t to_minutes(uint16_t cdn, uint8_t h, uint8_t m)
  274. {
  275. uint32_t t;
  276. t = cdn;
  277. t *= 24;
  278. t += h;
  279. t *= 60;
  280. t += m;
  281. return t;
  282. }
  283. void from_minutes(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m)
  284. {
  285. *m = t % 60;
  286. t /= 60;
  287. *h = t % 24;
  288. t /= 24;
  289. *cdn = t;
  290. }
  291. uint32_t to_minutes_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi)
  292. {
  293. uint16_t ydn = get_year_day_number(y, mo, d);
  294. uint16_t cdn = to_century_day_number(y, ydn);
  295. return to_minutes(cdn, h, mi);
  296. }