392 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			392 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | #ifndef DATE_TIME_DST_RULES_HPP__ | ||
|  | #define DATE_TIME_DST_RULES_HPP__ | ||
|  | 
 | ||
|  | /* Copyright (c) 2002,2003, 2007 CrystalClear Software, Inc. | ||
|  |  * Use, modification and distribution is subject to the  | ||
|  |  * Boost Software License, Version 1.0. (See accompanying | ||
|  |  * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) | ||
|  |  * Author: Jeff Garland, Bart Garst | ||
|  |  * $Date$ | ||
|  |  */ | ||
|  | 
 | ||
|  | /*! @file dst_rules.hpp | ||
|  |   Contains template class to provide static dst rule calculations | ||
|  | */ | ||
|  | 
 | ||
|  | #include "boost/date_time/date_generators.hpp" | ||
|  | #include "boost/date_time/period.hpp" | ||
|  | #include "boost/date_time/date_defs.hpp" | ||
|  | #include <stdexcept> | ||
|  | 
 | ||
|  | namespace boost { | ||
|  |   namespace date_time { | ||
|  | 
 | ||
|  |     enum time_is_dst_result {is_not_in_dst, is_in_dst,  | ||
|  |                              ambiguous, invalid_time_label}; | ||
|  | 
 | ||
|  | 
 | ||
|  |     //! Dynamic class used to caluclate dst transition information | ||
|  |     template<class date_type_,  | ||
|  |              class time_duration_type_> | ||
|  |     class dst_calculator | ||
|  |     { | ||
|  |     public: | ||
|  |       typedef time_duration_type_ time_duration_type; | ||
|  |       typedef date_type_ date_type; | ||
|  | 
 | ||
|  |       //! Check the local time offset when on dst start day | ||
|  |       /*! On this dst transition, the time label between | ||
|  |        *  the transition boundary and the boudary + the offset | ||
|  |        *  are invalid times.  If before the boundary then still  | ||
|  |        *  not in dst.   | ||
|  |        *@param time_of_day Time offset in the day for the local time | ||
|  |        *@param dst_start_offset_minutes Local day offset for start of dst | ||
|  |        *@param dst_length_minutes Number of minutes to adjust clock forward | ||
|  |        *@retval status of time label w.r.t. dst | ||
|  |        */ | ||
|  |       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) | ||
|  |       { | ||
|  |         //std::cout << "here" << std::endl; | ||
|  |         if (time_of_day < time_duration_type(0,dst_start_offset_minutes,0)) { | ||
|  |           return is_not_in_dst; | ||
|  |         } | ||
|  |         long offset = dst_start_offset_minutes + dst_length_minutes; | ||
|  |         if (time_of_day >= time_duration_type(0,offset,0)) { | ||
|  |           return is_in_dst; | ||
|  |         } | ||
|  |         return invalid_time_label;  | ||
|  |       } | ||
|  | 
 | ||
|  |       //! Check the local time offset when on the last day of dst | ||
|  |       /*! This is the calculation for the DST end day.  On that day times | ||
|  |        *  prior to the conversion time - dst_length (1 am in US) are still  | ||
|  |        *  in dst.  Times between the above and the switch time are  | ||
|  |        *  ambiguous.  Times after the start_offset are not in dst. | ||
|  |        *@param time_of_day Time offset in the day for the local time | ||
|  |        *@param dst_end_offset_minutes Local time of day for end of dst | ||
|  |        *@retval status of time label w.r.t. dst | ||
|  |        */ | ||
|  |       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) | ||
|  |       { | ||
|  |         //in US this will be 60 so offset in day is 1,0,0 | ||
|  |         int offset = dst_end_offset_minutes-dst_length_minutes; | ||
|  |         if (time_of_day < time_duration_type(0,offset,0)) { | ||
|  |           return is_in_dst; | ||
|  |         } | ||
|  |         if (time_of_day >= time_duration_type(0,dst_end_offset_minutes,0)) { | ||
|  |           return is_not_in_dst; | ||
|  |         } | ||
|  |         return ambiguous; | ||
|  |       } | ||
|  | 
 | ||
|  |       //! Calculates if the given local time is dst or not | ||
|  |       /*! Determines if the time is really in DST or not.  Also checks for  | ||
|  |        *  invalid and ambiguous. | ||
|  |        *  @param current_day The day to check for dst | ||
|  |        *  @param time_of_day Time offset within the day to check  | ||
|  |        *  @param dst_start_day  Starting day of dst for the given locality | ||
|  |        *  @param dst_start_offset Time offset within day for dst boundary | ||
|  |        *  @param dst_end_day    Ending day of dst for the given locality | ||
|  |        *  @param dst_end_offset Time offset within day given in dst for dst boundary | ||
|  |        *  @param dst_length lenght of dst adjusment | ||
|  |        *  @retval The time is either ambiguous, invalid, in dst, or not in dst | ||
|  |        */ | ||
|  |       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_minutes) | ||
|  |       { | ||
|  |         unsigned int start_minutes =  | ||
|  |           dst_start_offset.hours() * 60 + dst_start_offset.minutes(); | ||
|  |         unsigned int end_minutes =  | ||
|  |           dst_end_offset.hours() * 60 + dst_end_offset.minutes(); | ||
|  |         long length_minutes =   | ||
|  |           dst_length_minutes.hours() * 60 + dst_length_minutes.minutes(); | ||
|  | 
 | ||
|  |         return local_is_dst(current_day, time_of_day, | ||
|  |                             dst_start_day, start_minutes, | ||
|  |                             dst_end_day, end_minutes, | ||
|  |                             length_minutes); | ||
|  |       } | ||
|  | 
 | ||
|  |       //! Calculates if the given local time is dst or not | ||
|  |       /*! Determines if the time is really in DST or not.  Also checks for  | ||
|  |        *  invalid and ambiguous. | ||
|  |        *  @param current_day The day to check for dst | ||
|  |        *  @param time_of_day Time offset within the day to check  | ||
|  |        *  @param dst_start_day  Starting day of dst for the given locality | ||
|  |        *  @param dst_start_offset_minutes Offset within day for dst  | ||
|  |        *         boundary (eg 120 for US which is 02:00:00) | ||
|  |        *  @param dst_end_day    Ending day of dst for the given locality | ||
|  |        *  @param dst_end_offset_minutes Offset within day given in dst for dst  | ||
|  |        *         boundary (eg 120 for US which is 02:00:00) | ||
|  |        *  @param dst_length_minutes Length of dst adjusment (eg: 60 for US) | ||
|  |        *  @retval The time is either ambiguous, invalid, in dst, or not in dst | ||
|  |        */ | ||
|  |       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) | ||
|  |       { | ||
|  |         //in northern hemisphere dst is in the middle of the year | ||
|  |         if (dst_start_day < dst_end_day) { | ||
|  |           if ((current_day > dst_start_day) && (current_day < dst_end_day)) { | ||
|  |             return is_in_dst; | ||
|  |           } | ||
|  |           if ((current_day < dst_start_day) || (current_day > dst_end_day)) { | ||
|  |             return is_not_in_dst; | ||
|  |           } | ||
|  |         } | ||
|  |         else {//southern hemisphere dst is at begining /end of year | ||
|  |           if ((current_day < dst_start_day) && (current_day > dst_end_day)) { | ||
|  |             return is_not_in_dst; | ||
|  |           } | ||
|  |           if ((current_day > dst_start_day) || (current_day < dst_end_day)) { | ||
|  |             return is_in_dst; | ||
|  |           } | ||
|  |         } | ||
|  | 
 | ||
|  |         if (current_day == dst_start_day) { | ||
|  |           return process_local_dst_start_day(time_of_day, | ||
|  |                                              dst_start_offset_minutes, | ||
|  |                                              dst_length_minutes); | ||
|  |         } | ||
|  |        | ||
|  |         if (current_day == dst_end_day) { | ||
|  |           return process_local_dst_end_day(time_of_day, | ||
|  |                                            dst_end_offset_minutes, | ||
|  |                                            dst_length_minutes); | ||
|  |         } | ||
|  |         //you should never reach this statement | ||
|  |         return invalid_time_label; | ||
|  |       } | ||
|  | 
 | ||
|  |     }; | ||
|  | 
 | ||
|  | 
 | ||
|  |     //! Compile-time configurable daylight savings time calculation engine | ||
|  |     /* This template provides the ability to configure a daylight savings | ||
|  |      * calculation at compile time covering all the cases.  Unfortunately | ||
|  |      * because of the number of dimensions related to daylight savings | ||
|  |      * calculation the number of parameters is high.  In addition, the | ||
|  |      * start and end transition rules are complex types that specify  | ||
|  |      * an algorithm for calculation of the starting day and ending | ||
|  |      * day of daylight savings time including the month and day  | ||
|  |      * specifications (eg: last sunday in October). | ||
|  |      * | ||
|  |      * @param date_type A type that represents dates, typically gregorian::date | ||
|  |      * @param time_duration_type Used for the offset in the day calculations | ||
|  |      * @param dst_traits A set of traits that define the rules of dst  | ||
|  |      *        calculation.  The dst_trait must include the following: | ||
|  |      * start_rule_functor - Rule to calculate the starting date of a | ||
|  |      *                      dst transition (eg: last_kday_of_month). | ||
|  |      * start_day - static function that returns month of dst start for  | ||
|  |      *             start_rule_functor | ||
|  |      * start_month -static function that returns day or day of week for  | ||
|  |      *              dst start of dst | ||
|  |      * end_rule_functor - Rule to calculate the end of dst day. | ||
|  |      * end_day - static fucntion that returns end day for end_rule_functor | ||
|  |      * end_month - static function that returns end month for end_rule_functor | ||
|  |      * 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. | ||
|  |      * 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.  | ||
|  |      * dst_length_minutes - number of minutes that dst shifts clock | ||
|  |      */ | ||
|  |     template<class date_type,  | ||
|  |              class time_duration_type, | ||
|  |              class dst_traits> | ||
|  |     class dst_calc_engine | ||
|  |     { | ||
|  |     public: | ||
|  |       typedef typename date_type::year_type year_type; | ||
|  |       typedef typename date_type::calendar_type calendar_type; | ||
|  |       typedef dst_calculator<date_type, time_duration_type> dstcalc; | ||
|  | 
 | ||
|  |       //! Calculates if the given local time is dst or not | ||
|  |       /*! Determines if the time is really in DST or not.  Also checks for  | ||
|  |        *  invalid and ambiguous. | ||
|  |        *  @retval The time is either ambiguous, invalid, in dst, or not in dst | ||
|  |        */ | ||
|  |       static time_is_dst_result local_is_dst(const date_type& d, | ||
|  |                                              const time_duration_type& td)  | ||
|  |       { | ||
|  | 
 | ||
|  |         year_type y = d.year(); | ||
|  |         date_type dst_start = local_dst_start_day(y); | ||
|  |         date_type dst_end   = local_dst_end_day(y); | ||
|  |         return dstcalc::local_is_dst(d,td, | ||
|  |                                      dst_start, | ||
|  |                                      dst_traits::dst_start_offset_minutes(), | ||
|  |                                      dst_end,  | ||
|  |                                      dst_traits::dst_end_offset_minutes(),  | ||
|  |                                      dst_traits::dst_shift_length_minutes()); | ||
|  |        | ||
|  |       } | ||
|  | 
 | ||
|  |       static bool is_dst_boundary_day(date_type d) | ||
|  |       { | ||
|  |         year_type y = d.year(); | ||
|  |         return ((d == local_dst_start_day(y)) || | ||
|  |                 (d == local_dst_end_day(y))); | ||
|  |       } | ||
|  | 
 | ||
|  |       //! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00) | ||
|  |       static time_duration_type dst_offset()  | ||
|  |       { | ||
|  |         return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0); | ||
|  |       } | ||
|  | 
 | ||
|  |       static date_type local_dst_start_day(year_type year) | ||
|  |       { | ||
|  |         return dst_traits::local_dst_start_day(year);       | ||
|  |       } | ||
|  | 
 | ||
|  |       static date_type local_dst_end_day(year_type year) | ||
|  |       { | ||
|  |         return dst_traits::local_dst_end_day(year); | ||
|  |       } | ||
|  | 
 | ||
|  | 
 | ||
|  |     }; | ||
|  | 
 | ||
|  |     //! Depricated: Class to calculate dst boundaries for US time zones | ||
|  |     /* Use dst_calc_engine instead. | ||
|  |      * In 2007 US/Canada DST rules changed | ||
|  |      * (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time). | ||
|  |      */ | ||
|  |     template<class date_type_,  | ||
|  |              class time_duration_type_, | ||
|  |              unsigned int dst_start_offset_minutes=120, //from start of day  | ||
|  |              short dst_length_minutes=60>  //1 hour == 60 min in US | ||
|  |     class us_dst_rules  | ||
|  |     { | ||
|  |     public: | ||
|  |       typedef time_duration_type_ time_duration_type; | ||
|  |       typedef date_type_ date_type; | ||
|  |       typedef typename date_type::year_type year_type; | ||
|  |       typedef typename date_type::calendar_type calendar_type; | ||
|  |       typedef date_time::last_kday_of_month<date_type> lkday; | ||
|  |       typedef date_time::first_kday_of_month<date_type> fkday; | ||
|  |       typedef date_time::nth_kday_of_month<date_type> nkday; | ||
|  |       typedef dst_calculator<date_type, time_duration_type> dstcalc; | ||
|  | 
 | ||
|  |       //! Calculates if the given local time is dst or not | ||
|  |       /*! Determines if the time is really in DST or not.  Also checks for  | ||
|  |        *  invalid and ambiguous. | ||
|  |        *  @retval The time is either ambiguous, invalid, in dst, or not in dst | ||
|  |        */ | ||
|  |       static time_is_dst_result local_is_dst(const date_type& d, | ||
|  |                                              const time_duration_type& td)  | ||
|  |       { | ||
|  | 
 | ||
|  |         year_type y = d.year(); | ||
|  |         date_type dst_start = local_dst_start_day(y); | ||
|  |         date_type dst_end   = local_dst_end_day(y); | ||
|  |         return dstcalc::local_is_dst(d,td, | ||
|  |                                      dst_start,dst_start_offset_minutes, | ||
|  |                                      dst_end, dst_start_offset_minutes,  | ||
|  |                                      dst_length_minutes); | ||
|  |        | ||
|  |       } | ||
|  | 
 | ||
|  | 
 | ||
|  |       static bool is_dst_boundary_day(date_type d) | ||
|  |       { | ||
|  |         year_type y = d.year(); | ||
|  |         return ((d == local_dst_start_day(y)) || | ||
|  |                 (d == local_dst_end_day(y))); | ||
|  |       } | ||
|  | 
 | ||
|  |       static date_type local_dst_start_day(year_type year) | ||
|  |       { | ||
|  |         if (year >= year_type(2007)) { | ||
|  |           //second sunday in march | ||
|  |           nkday ssim(nkday::second, Sunday, gregorian::Mar); | ||
|  |           return ssim.get_date(year);       | ||
|  |         } else { | ||
|  |           //first sunday in april | ||
|  |           fkday fsia(Sunday, gregorian::Apr); | ||
|  |           return fsia.get_date(year);       | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       static date_type local_dst_end_day(year_type year) | ||
|  |       { | ||
|  |         if (year >= year_type(2007)) { | ||
|  |           //first sunday in november | ||
|  |           fkday fsin(Sunday, gregorian::Nov); | ||
|  |           return fsin.get_date(year);       | ||
|  |         } else { | ||
|  |           //last sunday in october | ||
|  |           lkday lsio(Sunday, gregorian::Oct); | ||
|  |           return lsio.get_date(year); | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       static time_duration_type dst_offset() | ||
|  |       { | ||
|  |         return time_duration_type(0,dst_length_minutes,0); | ||
|  |       } | ||
|  | 
 | ||
|  |      private: | ||
|  | 
 | ||
|  | 
 | ||
|  |     }; | ||
|  | 
 | ||
|  |     //! Used for local time adjustments in places that don't use dst | ||
|  |     template<class date_type_, class time_duration_type_> | ||
|  |     class null_dst_rules | ||
|  |     { | ||
|  |     public: | ||
|  |       typedef time_duration_type_ time_duration_type; | ||
|  |       typedef date_type_ date_type; | ||
|  | 
 | ||
|  | 
 | ||
|  |       //! Calculates if the given local time is dst or not | ||
|  |       /*! @retval Always is_not_in_dst since this is for zones without dst | ||
|  |        */ | ||
|  |       static time_is_dst_result local_is_dst(const date_type&,  | ||
|  |                                              const time_duration_type&)  | ||
|  |       { | ||
|  |         return is_not_in_dst; | ||
|  |       } | ||
|  |      | ||
|  |       //! Calculates if the given utc time is in dst | ||
|  |       static time_is_dst_result utc_is_dst(const date_type&,  | ||
|  |                                            const time_duration_type&)  | ||
|  |       { | ||
|  |         return is_not_in_dst; | ||
|  |       } | ||
|  | 
 | ||
|  |       static bool is_dst_boundary_day(date_type /*d*/) | ||
|  |       { | ||
|  |         return false; | ||
|  |       } | ||
|  | 
 | ||
|  |       static time_duration_type dst_offset()  | ||
|  |       { | ||
|  |         return time_duration_type(0,0,0); | ||
|  |       } | ||
|  | 
 | ||
|  |     }; | ||
|  | 
 | ||
|  | 
 | ||
|  |   } } //namespace date_time | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #endif |