summer.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /*
  2. summer.c
  3. stm32l0 daylight savings time (DST) calculation
  4. US:
  5. Der Energy Policy Act of 2005 (Public Law 109-58) bestimmt in Sec. 110 mit dem Titel Daylight Savings,
  6. dass ab 2007 die Sommerzeit am zweiten Sonntag im März beginnt und am ersten Sonntag im November endet.
  7. EU:
  8. Central European Summer Time (CEST, britisch) oder Central European Daylight Saving Time
  9. (applies to almost all european countries)
  10. Die mitteleuropäische Sommerzeit beginnt jeweils am letzten Sonntag im März um 2:00 Uhr MEZ,
  11. indem die Stundenzählung um eine Stunde von 2:00 Uhr auf 3:00 Uhr vorgestellt wird. Sie endet jeweils am letzten Sonntag im Oktober um
  12. 3:00 Uhr MESZ, indem die Stundenzählung um eine Stunde von 3:00 Uhr auf 2:00 Uhr zurückgestellt wird. Die Stunde von 2:00 Uhr bis 3:00 Uhr erscheint im Herbst also zweimal.
  13. */
  14. #ifdef __unix
  15. #include <stdio.h>
  16. #include <stdint.h>
  17. static uint8_t is_leap_year(uint16_t y)
  18. {
  19. if (
  20. ((y % 4 == 0) && (y % 100 != 0)) ||
  21. (y % 400 == 0)
  22. )
  23. return 1;
  24. return 0;
  25. }
  26. uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d)
  27. {
  28. uint8_t tmp1;
  29. uint16_t tmp2;
  30. tmp1 = 0;
  31. if ( m >= 3 )
  32. tmp1++;
  33. tmp2 = m;
  34. tmp2 +=2;
  35. tmp2 *=611;
  36. tmp2 /= 20;
  37. tmp2 += d;
  38. tmp2 -= 91;
  39. tmp1 <<=1;
  40. tmp2 -= tmp1;
  41. if ( tmp1 != 0 )
  42. tmp2 += is_leap_year(y);
  43. return tmp2;
  44. }
  45. uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn)
  46. {
  47. uint8_t j, c, tmp8;
  48. uint16_t tmp16;
  49. y--;
  50. j = y % 100;
  51. c = y / 100;
  52. tmp16 = c;
  53. tmp16 *= 5;
  54. tmp16 += ydn;
  55. tmp8 = j;
  56. j >>= 2;
  57. c >>= 2;
  58. tmp8 += j;
  59. tmp8 += c;
  60. tmp8 += 28;
  61. tmp16 += tmp8;
  62. tmp16 %= 7;
  63. return tmp16;
  64. }
  65. uint16_t year;
  66. uint16_t month;
  67. uint16_t day;
  68. uint16_t hour;
  69. #else
  70. #include "datecalc.h"
  71. #include "stm32l031xx.h"
  72. #endif
  73. /*
  74. this function does not require gui_date_adjust() to be called
  75. 0: no DST (Wintertime)
  76. 1: DST (Summertime)
  77. -1: unknown (last sunday in oct, between 2am and 3am )
  78. region
  79. 0: do nothing
  80. 1: EU
  81. 2: US
  82. */
  83. int is_dst_by_date(uint8_t region)
  84. {
  85. uint16_t ydn;
  86. uint16_t ydn_change_to_wintertime;
  87. uint16_t ydn_change_to_summertime;
  88. #ifndef __unix
  89. uint16_t year;
  90. uint16_t month;
  91. uint16_t day;
  92. uint16_t hour;
  93. /* read date from date register */
  94. year = ((RTC->DR >> 20) & 15);
  95. year *= 10;
  96. year += ((RTC->DR >> 16) & 15);
  97. year += 2000;
  98. month = ((RTC->DR >> 12) & 1);
  99. month *= 10;
  100. month += ((RTC->DR >> 8) & 15);
  101. day = ((RTC->DR >> 4) & 3);
  102. day *= 10;
  103. day += ((RTC->DR) & 15);
  104. /* also get the hour for later comparison */
  105. hour = ((RTC->TR >> 20) & 3); /* assuming 24-hour clock, not sure about PM flag */
  106. hour *= 10;
  107. hour += ((RTC->TR >> 16) & 15);
  108. #endif
  109. /* convert all dates to date numbers of the current year*/
  110. if ( region == 1 ) /* EU */
  111. {
  112. /* European Summer Time: Last Sunday in March and last Sunday in October */
  113. ydn_change_to_summertime = get_year_day_number(year, 3, 31);
  114. ydn_change_to_wintertime = get_year_day_number(year, 10, 31);
  115. }
  116. else if ( region == 2 ) /* US */
  117. {
  118. /* US DST rules: Second Sunday in March and First Sunday in November */
  119. ydn_change_to_summertime = get_year_day_number(year, 2, 28+is_leap_year(year));
  120. ydn_change_to_summertime += 14;
  121. ydn_change_to_wintertime = get_year_day_number(year, 10, 31);
  122. ydn_change_to_wintertime += 7;
  123. }
  124. else
  125. {
  126. return 0;
  127. }
  128. ydn_change_to_summertime -= get_weekday_by_year_day_number(year, ydn_change_to_summertime);
  129. ydn_change_to_wintertime -= get_weekday_by_year_day_number(year, ydn_change_to_wintertime);
  130. ydn = get_year_day_number(year, month, day);
  131. if ( ydn == ydn_change_to_summertime )
  132. if ( hour >= 2 )
  133. return 1; /* yes, it is DST */
  134. if ( ydn == ydn_change_to_wintertime )
  135. {
  136. if ( hour < 2 )
  137. return 1; /* still DST */
  138. if ( hour > 3 )
  139. return 0; /* not DST any more */
  140. return -1; /* not sure whether DST or not */
  141. }
  142. if ( ydn > ydn_change_to_summertime && ydn < ydn_change_to_wintertime )
  143. return 1; /* within DST */
  144. return 0;
  145. }
  146. #ifdef __unix
  147. int main()
  148. {
  149. int dd;
  150. int d = -2;
  151. for( year = 2000; year < 2030; year++ )
  152. {
  153. printf("%d: ", year);
  154. for( month = 3; month <= 11; month ++ )
  155. {
  156. for( day = 1; day<= 31; day++ )
  157. {
  158. hour = 2;
  159. dd = is_dst_by_date(1);
  160. if ( d == 0 && dd == 1 )
  161. {
  162. printf("%d.%d.%d", day, month, year);
  163. }
  164. switch(dd)
  165. {
  166. //case -1: printf("%d.%d.%d", day, month, year); break;
  167. //case 0: printf("w"); break;
  168. //case 1: printf("s"); break;
  169. }
  170. d = dd;
  171. }
  172. }
  173. printf("\n");
  174. }
  175. }
  176. #else
  177. /*
  178. region
  179. 0: do nothing
  180. 1: EU
  181. 2: US
  182. */
  183. void adjustDST(uint8_t region)
  184. {
  185. int is_dst;
  186. int dst_state;
  187. is_dst = is_dst_by_date(region);
  188. if ( is_dst >= 0 )
  189. {
  190. dst_state = 0;
  191. if ( RTC->CR & RTC_CR_BCK ) /* BKP flag in the CR register */
  192. dst_state = 1;
  193. if ( is_dst != dst_state )
  194. {
  195. __disable_irq();
  196. PWR->CR |= PWR_CR_DBP; /* disable write protection (step 1) */
  197. RTC->WPR = 0x0ca; /* disable RTC write protection (step 2) */
  198. RTC->WPR = 0x053;
  199. if ( is_dst != 0 )
  200. {
  201. RTC->CR |= RTC_CR_BCK; /* the RTC will now run in summer time (set DST flag) */
  202. RTC->CR |= RTC_CR_ADD1H; /* change to DST (Summertime): add one hour */
  203. }
  204. else
  205. {
  206. RTC->CR &= ~RTC_CR_BCK; /* the RTC will now run in winter time (turn off DST flag) */
  207. RTC->CR |= RTC_CR_SUB1H; /* change back to none DST (Wintertime): subtract one hour */
  208. }
  209. RTC->WPR = 0; /* enable RTC write protection (step 2) */
  210. RTC->WPR = 0;
  211. __enable_irq();
  212. }
  213. }
  214. }
  215. #endif