]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | |
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 | |
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 lenght of dst adjusment | |
98 | * @retval The time is either ambiguous, invalid, in dst, or not in dst | |
99 | */ | |
100 | static time_is_dst_result | |
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_minutes) | |
108 | { | |
b32b8144 FG |
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_minutes.hours() * 60 + dst_length_minutes.minutes()); | |
7c673cae FG |
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 | |
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 | */ | |
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 | ||
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) | |
247 | static time_duration_type dst_offset() | |
248 | { | |
249 | return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0); | |
250 | } | |
251 | ||
252 | static date_type local_dst_start_day(year_type year) | |
253 | { | |
254 | return dst_traits::local_dst_start_day(year); | |
255 | } | |
256 | ||
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 | */ | |
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 | ||
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 | ||
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, gregorian::Mar); | |
318 | return ssim.get_date(year); | |
319 | } else { | |
320 | //first sunday in april | |
321 | fkday fsia(Sunday, gregorian::Apr); | |
322 | return fsia.get_date(year); | |
323 | } | |
324 | } | |
325 | ||
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, gregorian::Nov); | |
331 | return fsin.get_date(year); | |
332 | } else { | |
333 | //last sunday in october | |
334 | lkday lsio(Sunday, gregorian::Oct); | |
335 | return lsio.get_date(year); | |
336 | } | |
337 | } | |
338 | ||
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 | */ | |
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 | |
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 | ||
374 | static bool is_dst_boundary_day(date_type /*d*/) | |
375 | { | |
376 | return false; | |
377 | } | |
378 | ||
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 |