Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:30:38

0001 #ifndef DATE_TIME_DST_RULES_HPP__
0002 #define DATE_TIME_DST_RULES_HPP__
0003 
0004 /* Copyright (c) 2002,2003, 2007 CrystalClear Software, Inc.
0005  * Use, modification and distribution is subject to the
0006  * Boost Software License, Version 1.0. (See accompanying
0007  * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
0008  * Author: Jeff Garland, Bart Garst
0009  * $Date$
0010  */
0011 
0012 /*! @file dst_rules.hpp
0013   Contains template class to provide static dst rule calculations
0014 */
0015 
0016 #include "boost/date_time/date_generators.hpp"
0017 #include "boost/date_time/period.hpp"
0018 #include "boost/date_time/date_defs.hpp"
0019 #include <stdexcept>
0020 
0021 namespace boost {
0022   namespace date_time {
0023 
0024     enum time_is_dst_result {is_not_in_dst, is_in_dst,
0025                              ambiguous, invalid_time_label};
0026 
0027 
0028     //! Dynamic class used to caluclate dst transition information
0029     template<class date_type_,
0030              class time_duration_type_>
0031     class dst_calculator
0032     {
0033     public:
0034       typedef time_duration_type_ time_duration_type;
0035       typedef date_type_ date_type;
0036 
0037       //! Check the local time offset when on dst start day
0038       /*! On this dst transition, the time label between
0039        *  the transition boundary and the boudary + the offset
0040        *  are invalid times.  If before the boundary then still
0041        *  not in dst.
0042        *@param time_of_day Time offset in the day for the local time
0043        *@param dst_start_offset_minutes Local day offset for start of dst
0044        *@param dst_length_minutes Number of minutes to adjust clock forward
0045        *@retval status of time label w.r.t. dst
0046        */
0047       static time_is_dst_result
0048       process_local_dst_start_day(const time_duration_type& time_of_day,
0049                                   unsigned int dst_start_offset_minutes,
0050                                   long dst_length_minutes)
0051       {
0052         //std::cout << "here" << std::endl;
0053         if (time_of_day < time_duration_type(0,dst_start_offset_minutes,0)) {
0054           return is_not_in_dst;
0055         }
0056         long offset = dst_start_offset_minutes + dst_length_minutes;
0057         if (time_of_day >= time_duration_type(0,offset,0)) {
0058           return is_in_dst;
0059         }
0060         return invalid_time_label;
0061       }
0062 
0063       //! Check the local time offset when on the last day of dst
0064       /*! This is the calculation for the DST end day.  On that day times
0065        *  prior to the conversion time - dst_length (1 am in US) are still
0066        *  in dst.  Times between the above and the switch time are
0067        *  ambiguous.  Times after the start_offset are not in dst.
0068        *@param time_of_day Time offset in the day for the local time
0069        *@param dst_end_offset_minutes Local time of day for end of dst
0070        *@retval status of time label w.r.t. dst
0071        */
0072       static time_is_dst_result
0073       process_local_dst_end_day(const time_duration_type& time_of_day,
0074                                 unsigned int dst_end_offset_minutes,
0075                                 long dst_length_minutes)
0076       {
0077         //in US this will be 60 so offset in day is 1,0,0
0078         int offset = dst_end_offset_minutes-dst_length_minutes;
0079         if (time_of_day < time_duration_type(0,offset,0)) {
0080           return is_in_dst;
0081         }
0082         if (time_of_day >= time_duration_type(0,dst_end_offset_minutes,0)) {
0083           return is_not_in_dst;
0084         }
0085         return ambiguous;
0086       }
0087 
0088       //! Calculates if the given local time is dst or not
0089       /*! Determines if the time is really in DST or not.  Also checks for
0090        *  invalid and ambiguous.
0091        *  @param current_day The day to check for dst
0092        *  @param time_of_day Time offset within the day to check
0093        *  @param dst_start_day  Starting day of dst for the given locality
0094        *  @param dst_start_offset Time offset within day for dst boundary
0095        *  @param dst_end_day    Ending day of dst for the given locality
0096        *  @param dst_end_offset Time offset within day given in dst for dst boundary
0097        *  @param dst_length_minutes length of dst adjusment
0098        *  @retval The time is either ambiguous, invalid, in dst, or not in dst
0099        */
0100       static time_is_dst_result
0101       local_is_dst(const date_type& current_day,
0102                    const time_duration_type& time_of_day,
0103                    const date_type& dst_start_day,
0104                    const time_duration_type& dst_start_offset,
0105                    const date_type& dst_end_day,
0106                    const time_duration_type& dst_end_offset,
0107                    const time_duration_type& dst_length)
0108       {
0109         unsigned int start_minutes = static_cast<unsigned>(
0110           dst_start_offset.hours() * 60 + dst_start_offset.minutes());
0111         unsigned int end_minutes = static_cast<unsigned>(
0112           dst_end_offset.hours() * 60 + dst_end_offset.minutes());
0113         long length_minutes = static_cast<long>(
0114           dst_length.hours() * 60 + dst_length.minutes());
0115 
0116         return local_is_dst(current_day, time_of_day,
0117                             dst_start_day, start_minutes,
0118                             dst_end_day, end_minutes,
0119                             length_minutes);
0120       }
0121 
0122       //! Calculates if the given local time is dst or not
0123       /*! Determines if the time is really in DST or not.  Also checks for
0124        *  invalid and ambiguous.
0125        *  @param current_day The day to check for dst
0126        *  @param time_of_day Time offset within the day to check
0127        *  @param dst_start_day  Starting day of dst for the given locality
0128        *  @param dst_start_offset_minutes Offset within day for dst
0129        *         boundary (eg 120 for US which is 02:00:00)
0130        *  @param dst_end_day    Ending day of dst for the given locality
0131        *  @param dst_end_offset_minutes Offset within day given in dst for dst
0132        *         boundary (eg 120 for US which is 02:00:00)
0133        *  @param dst_length_minutes Length of dst adjusment (eg: 60 for US)
0134        *  @retval The time is either ambiguous, invalid, in dst, or not in dst
0135        */
0136       static time_is_dst_result
0137       local_is_dst(const date_type& current_day,
0138                    const time_duration_type& time_of_day,
0139                    const date_type& dst_start_day,
0140                    unsigned int dst_start_offset_minutes,
0141                    const date_type& dst_end_day,
0142                    unsigned int dst_end_offset_minutes,
0143                    long dst_length_minutes)
0144       {
0145         //in northern hemisphere dst is in the middle of the year
0146         if (dst_start_day < dst_end_day) {
0147           if ((current_day > dst_start_day) && (current_day < dst_end_day)) {
0148             return is_in_dst;
0149           }
0150           if ((current_day < dst_start_day) || (current_day > dst_end_day)) {
0151             return is_not_in_dst;
0152           }
0153         }
0154         else {//southern hemisphere dst is at begining /end of year
0155           if ((current_day < dst_start_day) && (current_day > dst_end_day)) {
0156             return is_not_in_dst;
0157           }
0158           if ((current_day > dst_start_day) || (current_day < dst_end_day)) {
0159             return is_in_dst;
0160           }
0161         }
0162 
0163         if (current_day == dst_start_day) {
0164           return process_local_dst_start_day(time_of_day,
0165                                              dst_start_offset_minutes,
0166                                              dst_length_minutes);
0167         }
0168 
0169         if (current_day == dst_end_day) {
0170           return process_local_dst_end_day(time_of_day,
0171                                            dst_end_offset_minutes,
0172                                            dst_length_minutes);
0173         }
0174         //you should never reach this statement
0175         return invalid_time_label;
0176       }
0177 
0178     };
0179 
0180 
0181     //! Compile-time configurable daylight savings time calculation engine
0182     /* This template provides the ability to configure a daylight savings
0183      * calculation at compile time covering all the cases.  Unfortunately
0184      * because of the number of dimensions related to daylight savings
0185      * calculation the number of parameters is high.  In addition, the
0186      * start and end transition rules are complex types that specify
0187      * an algorithm for calculation of the starting day and ending
0188      * day of daylight savings time including the month and day
0189      * specifications (eg: last sunday in October).
0190      *
0191      * @param date_type A type that represents dates, typically gregorian::date
0192      * @param time_duration_type Used for the offset in the day calculations
0193      * @param dst_traits A set of traits that define the rules of dst
0194      *        calculation.  The dst_trait must include the following:
0195      * start_rule_functor - Rule to calculate the starting date of a
0196      *                      dst transition (eg: last_kday_of_month).
0197      * start_day - static function that returns month of dst start for
0198      *             start_rule_functor
0199      * start_month -static function that returns day or day of week for
0200      *              dst start of dst
0201      * end_rule_functor - Rule to calculate the end of dst day.
0202      * end_day - static fucntion that returns end day for end_rule_functor
0203      * end_month - static function that returns end month for end_rule_functor
0204      * 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.
0205      * 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.
0206      * dst_length_minutes - number of minutes that dst shifts clock
0207      */
0208     template<class date_type,
0209              class time_duration_type,
0210              class dst_traits>
0211     class dst_calc_engine
0212     {
0213     public:
0214       typedef typename date_type::year_type year_type;
0215       typedef typename date_type::calendar_type calendar_type;
0216       typedef dst_calculator<date_type, time_duration_type> dstcalc;
0217 
0218       //! Calculates if the given local time is dst or not
0219       /*! Determines if the time is really in DST or not.  Also checks for
0220        *  invalid and ambiguous.
0221        *  @retval The time is either ambiguous, invalid, in dst, or not in dst
0222        */
0223       static time_is_dst_result local_is_dst(const date_type& d,
0224                                              const time_duration_type& td)
0225       {
0226 
0227         year_type y = d.year();
0228         date_type dst_start = local_dst_start_day(y);
0229         date_type dst_end   = local_dst_end_day(y);
0230         return dstcalc::local_is_dst(d,td,
0231                                      dst_start,
0232                                      dst_traits::dst_start_offset_minutes(),
0233                                      dst_end,
0234                                      dst_traits::dst_end_offset_minutes(),
0235                                      dst_traits::dst_shift_length_minutes());
0236 
0237       }
0238 
0239       static bool is_dst_boundary_day(date_type d)
0240       {
0241         year_type y = d.year();
0242         return ((d == local_dst_start_day(y)) ||
0243                 (d == local_dst_end_day(y)));
0244       }
0245 
0246       //! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00)
0247       static time_duration_type dst_offset()
0248       {
0249         return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0);
0250       }
0251 
0252       static date_type local_dst_start_day(year_type year)
0253       {
0254         return dst_traits::local_dst_start_day(year);
0255       }
0256 
0257       static date_type local_dst_end_day(year_type year)
0258       {
0259         return dst_traits::local_dst_end_day(year);
0260       }
0261 
0262 
0263     };
0264 
0265     //! Depricated: Class to calculate dst boundaries for US time zones
0266     /* Use dst_calc_engine instead.
0267      * In 2007 US/Canada DST rules changed
0268      * (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time).
0269      */
0270     template<class date_type_,
0271              class time_duration_type_,
0272              unsigned int dst_start_offset_minutes=120, //from start of day
0273              short dst_length_minutes=60>  //1 hour == 60 min in US
0274     class us_dst_rules
0275     {
0276     public:
0277       typedef time_duration_type_ time_duration_type;
0278       typedef date_type_ date_type;
0279       typedef typename date_type::year_type year_type;
0280       typedef typename date_type::calendar_type calendar_type;
0281       typedef date_time::last_kday_of_month<date_type> lkday;
0282       typedef date_time::first_kday_of_month<date_type> fkday;
0283       typedef date_time::nth_kday_of_month<date_type> nkday;
0284       typedef dst_calculator<date_type, time_duration_type> dstcalc;
0285 
0286       //! Calculates if the given local time is dst or not
0287       /*! Determines if the time is really in DST or not.  Also checks for
0288        *  invalid and ambiguous.
0289        *  @retval The time is either ambiguous, invalid, in dst, or not in dst
0290        */
0291       static time_is_dst_result local_is_dst(const date_type& d,
0292                                              const time_duration_type& td)
0293       {
0294 
0295         year_type y = d.year();
0296         date_type dst_start = local_dst_start_day(y);
0297         date_type dst_end   = local_dst_end_day(y);
0298         return dstcalc::local_is_dst(d,td,
0299                                      dst_start,dst_start_offset_minutes,
0300                                      dst_end, dst_start_offset_minutes,
0301                                      dst_length_minutes);
0302 
0303       }
0304 
0305 
0306       static bool is_dst_boundary_day(date_type d)
0307       {
0308         year_type y = d.year();
0309         return ((d == local_dst_start_day(y)) ||
0310                 (d == local_dst_end_day(y)));
0311       }
0312 
0313       static date_type local_dst_start_day(year_type year)
0314       {
0315         if (year >= year_type(2007)) {
0316           //second sunday in march
0317           nkday ssim(nkday::second, Sunday, date_time::Mar);
0318           return ssim.get_date(year);
0319         } else {
0320           //first sunday in april
0321           fkday fsia(Sunday, date_time::Apr);
0322           return fsia.get_date(year);
0323         }
0324       }
0325 
0326       static date_type local_dst_end_day(year_type year)
0327       {
0328         if (year >= year_type(2007)) {
0329           //first sunday in november
0330           fkday fsin(Sunday, date_time::Nov);
0331           return fsin.get_date(year);
0332         } else {
0333           //last sunday in october
0334           lkday lsio(Sunday, date_time::Oct);
0335           return lsio.get_date(year);
0336         }
0337       }
0338 
0339       static time_duration_type dst_offset()
0340       {
0341         return time_duration_type(0,dst_length_minutes,0);
0342       }
0343 
0344      private:
0345 
0346 
0347     };
0348 
0349     //! Used for local time adjustments in places that don't use dst
0350     template<class date_type_, class time_duration_type_>
0351     class null_dst_rules
0352     {
0353     public:
0354       typedef time_duration_type_ time_duration_type;
0355       typedef date_type_ date_type;
0356 
0357 
0358       //! Calculates if the given local time is dst or not
0359       /*! @retval Always is_not_in_dst since this is for zones without dst
0360        */
0361       static time_is_dst_result local_is_dst(const date_type&,
0362                                              const time_duration_type&)
0363       {
0364         return is_not_in_dst;
0365       }
0366 
0367       //! Calculates if the given utc time is in dst
0368       static time_is_dst_result utc_is_dst(const date_type&,
0369                                            const time_duration_type&)
0370       {
0371         return is_not_in_dst;
0372       }
0373 
0374       static bool is_dst_boundary_day(date_type /*d*/)
0375       {
0376         return false;
0377       }
0378 
0379       static time_duration_type dst_offset()
0380       {
0381         return time_duration_type(0,0,0);
0382       }
0383 
0384     };
0385 
0386 
0387   } } //namespace date_time
0388 
0389 
0390 
0391 #endif