| #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: 2008-02-27 15:00:24 -0500 (Wed, 27 Feb 2008) $ |
| */ |
| |
| /*! @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 |