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