]> git.sesse.net Git - casparcg/blob - dependencies/boost/boost/date_time/dst_rules.hpp
Manually merged pull request #222
[casparcg] / dependencies / boost / boost / date_time / dst_rules.hpp
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: 2008-02-27 15:00:24 -0500 (Wed, 27 Feb 2008) $
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       {
109         unsigned int start_minutes = 
110           dst_start_offset.hours() * 60 + dst_start_offset.minutes();
111         unsigned int end_minutes = 
112           dst_end_offset.hours() * 60 + dst_end_offset.minutes();
113         long length_minutes =  
114           dst_length_minutes.hours() * 60 + dst_length_minutes.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 
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