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