dst_rules.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #ifndef DATE_TIME_DST_RULES_HPP__
  2. #define DATE_TIME_DST_RULES_HPP__
  3. /* Copyright (c) 2002,2003, 2007 CrystalClear Software, Inc.
  4. * Use, modification and distribution is subject to the
  5. * Boost Software License, Version 1.0. (See accompanying
  6. * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  7. * Author: Jeff Garland, Bart Garst
  8. * $Date$
  9. */
  10. /*! @file dst_rules.hpp
  11. Contains template class to provide static dst rule calculations
  12. */
  13. #include "boost/date_time/date_generators.hpp"
  14. #include "boost/date_time/period.hpp"
  15. #include "boost/date_time/date_defs.hpp"
  16. #include <stdexcept>
  17. namespace boost {
  18. namespace date_time {
  19. enum time_is_dst_result {is_not_in_dst, is_in_dst,
  20. ambiguous, invalid_time_label};
  21. //! Dynamic class used to caluclate dst transition information
  22. template<class date_type_,
  23. class time_duration_type_>
  24. class dst_calculator
  25. {
  26. public:
  27. typedef time_duration_type_ time_duration_type;
  28. typedef date_type_ date_type;
  29. //! Check the local time offset when on dst start day
  30. /*! On this dst transition, the time label between
  31. * the transition boundary and the boudary + the offset
  32. * are invalid times. If before the boundary then still
  33. * not in dst.
  34. *@param time_of_day Time offset in the day for the local time
  35. *@param dst_start_offset_minutes Local day offset for start of dst
  36. *@param dst_length_minutes Number of minutes to adjust clock forward
  37. *@retval status of time label w.r.t. dst
  38. */
  39. static time_is_dst_result
  40. process_local_dst_start_day(const time_duration_type& time_of_day,
  41. unsigned int dst_start_offset_minutes,
  42. long dst_length_minutes)
  43. {
  44. //std::cout << "here" << std::endl;
  45. if (time_of_day < time_duration_type(0,dst_start_offset_minutes,0)) {
  46. return is_not_in_dst;
  47. }
  48. long offset = dst_start_offset_minutes + dst_length_minutes;
  49. if (time_of_day >= time_duration_type(0,offset,0)) {
  50. return is_in_dst;
  51. }
  52. return invalid_time_label;
  53. }
  54. //! Check the local time offset when on the last day of dst
  55. /*! This is the calculation for the DST end day. On that day times
  56. * prior to the conversion time - dst_length (1 am in US) are still
  57. * in dst. Times between the above and the switch time are
  58. * ambiguous. Times after the start_offset are not in dst.
  59. *@param time_of_day Time offset in the day for the local time
  60. *@param dst_end_offset_minutes Local time of day for end of dst
  61. *@retval status of time label w.r.t. dst
  62. */
  63. static time_is_dst_result
  64. process_local_dst_end_day(const time_duration_type& time_of_day,
  65. unsigned int dst_end_offset_minutes,
  66. long dst_length_minutes)
  67. {
  68. //in US this will be 60 so offset in day is 1,0,0
  69. int offset = dst_end_offset_minutes-dst_length_minutes;
  70. if (time_of_day < time_duration_type(0,offset,0)) {
  71. return is_in_dst;
  72. }
  73. if (time_of_day >= time_duration_type(0,dst_end_offset_minutes,0)) {
  74. return is_not_in_dst;
  75. }
  76. return ambiguous;
  77. }
  78. //! Calculates if the given local time is dst or not
  79. /*! Determines if the time is really in DST or not. Also checks for
  80. * invalid and ambiguous.
  81. * @param current_day The day to check for dst
  82. * @param time_of_day Time offset within the day to check
  83. * @param dst_start_day Starting day of dst for the given locality
  84. * @param dst_start_offset Time offset within day for dst boundary
  85. * @param dst_end_day Ending day of dst for the given locality
  86. * @param dst_end_offset Time offset within day given in dst for dst boundary
  87. * @param dst_length_minutes length of dst adjusment
  88. * @retval The time is either ambiguous, invalid, in dst, or not in dst
  89. */
  90. static time_is_dst_result
  91. local_is_dst(const date_type& current_day,
  92. const time_duration_type& time_of_day,
  93. const date_type& dst_start_day,
  94. const time_duration_type& dst_start_offset,
  95. const date_type& dst_end_day,
  96. const time_duration_type& dst_end_offset,
  97. const time_duration_type& dst_length)
  98. {
  99. unsigned int start_minutes = static_cast<unsigned>(
  100. dst_start_offset.hours() * 60 + dst_start_offset.minutes());
  101. unsigned int end_minutes = static_cast<unsigned>(
  102. dst_end_offset.hours() * 60 + dst_end_offset.minutes());
  103. long length_minutes = static_cast<long>(
  104. dst_length.hours() * 60 + dst_length.minutes());
  105. return local_is_dst(current_day, time_of_day,
  106. dst_start_day, start_minutes,
  107. dst_end_day, end_minutes,
  108. length_minutes);
  109. }
  110. //! Calculates if the given local time is dst or not
  111. /*! Determines if the time is really in DST or not. Also checks for
  112. * invalid and ambiguous.
  113. * @param current_day The day to check for dst
  114. * @param time_of_day Time offset within the day to check
  115. * @param dst_start_day Starting day of dst for the given locality
  116. * @param dst_start_offset_minutes Offset within day for dst
  117. * boundary (eg 120 for US which is 02:00:00)
  118. * @param dst_end_day Ending day of dst for the given locality
  119. * @param dst_end_offset_minutes Offset within day given in dst for dst
  120. * boundary (eg 120 for US which is 02:00:00)
  121. * @param dst_length_minutes Length of dst adjusment (eg: 60 for US)
  122. * @retval The time is either ambiguous, invalid, in dst, or not in dst
  123. */
  124. static time_is_dst_result
  125. local_is_dst(const date_type& current_day,
  126. const time_duration_type& time_of_day,
  127. const date_type& dst_start_day,
  128. unsigned int dst_start_offset_minutes,
  129. const date_type& dst_end_day,
  130. unsigned int dst_end_offset_minutes,
  131. long dst_length_minutes)
  132. {
  133. //in northern hemisphere dst is in the middle of the year
  134. if (dst_start_day < dst_end_day) {
  135. if ((current_day > dst_start_day) && (current_day < dst_end_day)) {
  136. return is_in_dst;
  137. }
  138. if ((current_day < dst_start_day) || (current_day > dst_end_day)) {
  139. return is_not_in_dst;
  140. }
  141. }
  142. else {//southern hemisphere dst is at begining /end of year
  143. if ((current_day < dst_start_day) && (current_day > dst_end_day)) {
  144. return is_not_in_dst;
  145. }
  146. if ((current_day > dst_start_day) || (current_day < dst_end_day)) {
  147. return is_in_dst;
  148. }
  149. }
  150. if (current_day == dst_start_day) {
  151. return process_local_dst_start_day(time_of_day,
  152. dst_start_offset_minutes,
  153. dst_length_minutes);
  154. }
  155. if (current_day == dst_end_day) {
  156. return process_local_dst_end_day(time_of_day,
  157. dst_end_offset_minutes,
  158. dst_length_minutes);
  159. }
  160. //you should never reach this statement
  161. return invalid_time_label;
  162. }
  163. };
  164. //! Compile-time configurable daylight savings time calculation engine
  165. /* This template provides the ability to configure a daylight savings
  166. * calculation at compile time covering all the cases. Unfortunately
  167. * because of the number of dimensions related to daylight savings
  168. * calculation the number of parameters is high. In addition, the
  169. * start and end transition rules are complex types that specify
  170. * an algorithm for calculation of the starting day and ending
  171. * day of daylight savings time including the month and day
  172. * specifications (eg: last sunday in October).
  173. *
  174. * @param date_type A type that represents dates, typically gregorian::date
  175. * @param time_duration_type Used for the offset in the day calculations
  176. * @param dst_traits A set of traits that define the rules of dst
  177. * calculation. The dst_trait must include the following:
  178. * start_rule_functor - Rule to calculate the starting date of a
  179. * dst transition (eg: last_kday_of_month).
  180. * start_day - static function that returns month of dst start for
  181. * start_rule_functor
  182. * start_month -static function that returns day or day of week for
  183. * dst start of dst
  184. * end_rule_functor - Rule to calculate the end of dst day.
  185. * end_day - static fucntion that returns end day for end_rule_functor
  186. * end_month - static function that returns end month for end_rule_functor
  187. * dst_start_offset_minutes - number of minutes from start of day to transition to dst -- 120 (or 2:00 am) is typical for the U.S. and E.U.
  188. * dst_start_offset_minutes - number of minutes from start of day to transition off of dst -- 180 (or 3:00 am) is typical for E.U.
  189. * dst_length_minutes - number of minutes that dst shifts clock
  190. */
  191. template<class date_type,
  192. class time_duration_type,
  193. class dst_traits>
  194. class dst_calc_engine
  195. {
  196. public:
  197. typedef typename date_type::year_type year_type;
  198. typedef typename date_type::calendar_type calendar_type;
  199. typedef dst_calculator<date_type, time_duration_type> dstcalc;
  200. //! Calculates if the given local time is dst or not
  201. /*! Determines if the time is really in DST or not. Also checks for
  202. * invalid and ambiguous.
  203. * @retval The time is either ambiguous, invalid, in dst, or not in dst
  204. */
  205. static time_is_dst_result local_is_dst(const date_type& d,
  206. const time_duration_type& td)
  207. {
  208. year_type y = d.year();
  209. date_type dst_start = local_dst_start_day(y);
  210. date_type dst_end = local_dst_end_day(y);
  211. return dstcalc::local_is_dst(d,td,
  212. dst_start,
  213. dst_traits::dst_start_offset_minutes(),
  214. dst_end,
  215. dst_traits::dst_end_offset_minutes(),
  216. dst_traits::dst_shift_length_minutes());
  217. }
  218. static bool is_dst_boundary_day(date_type d)
  219. {
  220. year_type y = d.year();
  221. return ((d == local_dst_start_day(y)) ||
  222. (d == local_dst_end_day(y)));
  223. }
  224. //! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00)
  225. static time_duration_type dst_offset()
  226. {
  227. return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0);
  228. }
  229. static date_type local_dst_start_day(year_type year)
  230. {
  231. return dst_traits::local_dst_start_day(year);
  232. }
  233. static date_type local_dst_end_day(year_type year)
  234. {
  235. return dst_traits::local_dst_end_day(year);
  236. }
  237. };
  238. //! Depricated: Class to calculate dst boundaries for US time zones
  239. /* Use dst_calc_engine instead.
  240. * In 2007 US/Canada DST rules changed
  241. * (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time).
  242. */
  243. template<class date_type_,
  244. class time_duration_type_,
  245. unsigned int dst_start_offset_minutes=120, //from start of day
  246. short dst_length_minutes=60> //1 hour == 60 min in US
  247. class us_dst_rules
  248. {
  249. public:
  250. typedef time_duration_type_ time_duration_type;
  251. typedef date_type_ date_type;
  252. typedef typename date_type::year_type year_type;
  253. typedef typename date_type::calendar_type calendar_type;
  254. typedef date_time::last_kday_of_month<date_type> lkday;
  255. typedef date_time::first_kday_of_month<date_type> fkday;
  256. typedef date_time::nth_kday_of_month<date_type> nkday;
  257. typedef dst_calculator<date_type, time_duration_type> dstcalc;
  258. //! Calculates if the given local time is dst or not
  259. /*! Determines if the time is really in DST or not. Also checks for
  260. * invalid and ambiguous.
  261. * @retval The time is either ambiguous, invalid, in dst, or not in dst
  262. */
  263. static time_is_dst_result local_is_dst(const date_type& d,
  264. const time_duration_type& td)
  265. {
  266. year_type y = d.year();
  267. date_type dst_start = local_dst_start_day(y);
  268. date_type dst_end = local_dst_end_day(y);
  269. return dstcalc::local_is_dst(d,td,
  270. dst_start,dst_start_offset_minutes,
  271. dst_end, dst_start_offset_minutes,
  272. dst_length_minutes);
  273. }
  274. static bool is_dst_boundary_day(date_type d)
  275. {
  276. year_type y = d.year();
  277. return ((d == local_dst_start_day(y)) ||
  278. (d == local_dst_end_day(y)));
  279. }
  280. static date_type local_dst_start_day(year_type year)
  281. {
  282. if (year >= year_type(2007)) {
  283. //second sunday in march
  284. nkday ssim(nkday::second, Sunday, date_time::Mar);
  285. return ssim.get_date(year);
  286. } else {
  287. //first sunday in april
  288. fkday fsia(Sunday, date_time::Apr);
  289. return fsia.get_date(year);
  290. }
  291. }
  292. static date_type local_dst_end_day(year_type year)
  293. {
  294. if (year >= year_type(2007)) {
  295. //first sunday in november
  296. fkday fsin(Sunday, date_time::Nov);
  297. return fsin.get_date(year);
  298. } else {
  299. //last sunday in october
  300. lkday lsio(Sunday, date_time::Oct);
  301. return lsio.get_date(year);
  302. }
  303. }
  304. static time_duration_type dst_offset()
  305. {
  306. return time_duration_type(0,dst_length_minutes,0);
  307. }
  308. private:
  309. };
  310. //! Used for local time adjustments in places that don't use dst
  311. template<class date_type_, class time_duration_type_>
  312. class null_dst_rules
  313. {
  314. public:
  315. typedef time_duration_type_ time_duration_type;
  316. typedef date_type_ date_type;
  317. //! Calculates if the given local time is dst or not
  318. /*! @retval Always is_not_in_dst since this is for zones without dst
  319. */
  320. static time_is_dst_result local_is_dst(const date_type&,
  321. const time_duration_type&)
  322. {
  323. return is_not_in_dst;
  324. }
  325. //! Calculates if the given utc time is in dst
  326. static time_is_dst_result utc_is_dst(const date_type&,
  327. const time_duration_type&)
  328. {
  329. return is_not_in_dst;
  330. }
  331. static bool is_dst_boundary_day(date_type /*d*/)
  332. {
  333. return false;
  334. }
  335. static time_duration_type dst_offset()
  336. {
  337. return time_duration_type(0,0,0);
  338. }
  339. };
  340. } } //namespace date_time
  341. #endif