Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #ifndef _DATE_TIME_POSIX_TIME_ZONE__
0002 #define _DATE_TIME_POSIX_TIME_ZONE__
0003 
0004 /* Copyright (c) 2003-2005 CrystalClear Software, Inc.
0005  * Subject to the Boost Software License, Version 1.0. (See accompanying
0006  * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
0007  * Author: Jeff Garland, Bart Garst
0008  * $Date$
0009  */
0010 
0011 #include <string>
0012 #include <sstream>
0013 #include <stdexcept>
0014 #include <boost/tokenizer.hpp>
0015 #include <boost/throw_exception.hpp>
0016 #include <boost/date_time/compiler_config.hpp>
0017 #include <boost/date_time/gregorian/gregorian.hpp>
0018 #include <boost/date_time/time_zone_names.hpp>
0019 #include <boost/date_time/time_zone_base.hpp>
0020 #include <boost/date_time/local_time/dst_transition_day_rules.hpp>
0021 #include <boost/date_time/posix_time/posix_time.hpp>
0022 #include <boost/date_time/string_convert.hpp>
0023 #include <boost/date_time/time_parsing.hpp>
0024 
0025 namespace boost{
0026 namespace local_time{
0027 
0028   //! simple exception for UTC and Daylight savings start/end offsets
0029   struct BOOST_SYMBOL_VISIBLE bad_offset : public std::out_of_range
0030   {
0031     bad_offset(std::string const& msg = std::string()) :
0032       std::out_of_range(std::string("Offset out of range: " + msg)) {}
0033   };
0034   //! simple exception for UTC daylight savings adjustment
0035   struct BOOST_SYMBOL_VISIBLE bad_adjustment : public std::out_of_range
0036   {
0037     bad_adjustment(std::string const& msg = std::string()) :
0038       std::out_of_range(std::string("Adjustment out of range: " + msg)) {}
0039   };
0040 
0041   typedef boost::date_time::dst_adjustment_offsets<boost::posix_time::time_duration> dst_adjustment_offsets;
0042 
0043   //! A time zone class constructed from a POSIX time zone string
0044   /*! A POSIX time zone string takes the form of:<br>
0045    * "std offset dst [offset],start[/time],end[/time]" (w/no spaces)
0046    * 'std' specifies the abbrev of the time zone.<br>
0047    * 'offset' is the offset from UTC.<br>
0048    * 'dst' specifies the abbrev of the time zone during daylight savings time.<br>
0049    * The second offset is how many hours changed during DST. Default=1<br>
0050    * 'start' and'end' are the dates when DST goes into (and out of) effect.<br>
0051    * 'offset' takes the form of: [+|-]hh[:mm[:ss]] {h=0-23, m/s=0-59}<br>
0052    * 'time' and 'offset' take the same form. Time defaults=02:00:00<br>
0053    * 'start' and 'end' can be one of three forms:<br>
0054    * Mm.w.d {month=1-12, week=1-5 (5 is always last), day=0-6}<br>
0055    * Jn {n=1-365 Feb29 is never counted}<br>
0056    * n  {n=0-365 Feb29 is counted in leap years}<br>
0057    * Example "PST-5PDT01:00:00,M4.1.0/02:00:00,M10.1.0/02:00:00"
0058    * <br>
0059    * Exceptions will be thrown under these conditions:<br>
0060    * An invalid date spec (see date class)<br>
0061    * A boost::local_time::bad_offset exception will be thrown for:<br>
0062    * A DST start or end offset that is negative or more than 24 hours<br>
0063    * A UTC zone that is greater than +14 or less than -12 hours<br>
0064    * A boost::local_time::bad_adjustment exception will be thrown for:<br>
0065    * A DST adjustment that is 24 hours or more (positive or negative)<br>
0066    *
0067    * Note that UTC zone offsets can be greater than +12:
0068    * http://www.worldtimezone.com/utc/utc+1200.html
0069    */
0070   template<class CharT>
0071   class BOOST_SYMBOL_VISIBLE posix_time_zone_base : public date_time::time_zone_base<posix_time::ptime,CharT> {
0072   public:
0073     typedef boost::posix_time::time_duration time_duration_type;
0074     typedef date_time::time_zone_names_base<CharT> time_zone_names;
0075     typedef date_time::time_zone_base<posix_time::ptime,CharT> base_type;
0076     typedef typename base_type::string_type string_type;
0077     typedef CharT char_type;
0078     typedef typename base_type::stringstream_type stringstream_type;
0079     typedef boost::char_separator<char_type, std::char_traits<char_type> > char_separator_type;
0080     typedef boost::tokenizer<char_separator_type,
0081                              typename string_type::const_iterator,
0082                              string_type> tokenizer_type;
0083     typedef typename tokenizer_type::iterator tokenizer_iterator_type;
0084 
0085     //! Construct from a POSIX time zone string
0086     posix_time_zone_base(const string_type& s) :
0087       //zone_names_("std_name","std_abbrev","no-dst","no-dst"),
0088       zone_names_(),
0089       has_dst_(false),
0090       base_utc_offset_(posix_time::hours(0)),
0091       dst_offsets_(posix_time::hours(0),posix_time::hours(0),posix_time::hours(0)),
0092       dst_calc_rules_()
0093     {
0094 #ifdef __HP_aCC
0095       // Work around bug in aC++ compiler: see QXCR1000880488 in the
0096       // HP bug tracking system
0097       const char_type sep_chars[2] = {',',0};
0098 #else
0099       const char_type sep_chars[2] = {','};
0100 #endif
0101       char_separator_type sep(sep_chars);
0102       tokenizer_type tokens(s, sep);
0103       tokenizer_iterator_type it = tokens.begin(), end = tokens.end();
0104       if (it == end)
0105         BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse time zone name"));
0106       calc_zone(*it++);
0107       if(has_dst_)
0108       {
0109         if (it == end)
0110           BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse DST begin time"));
0111         string_type dst_begin = *it++;
0112 
0113         if (it == end)
0114           BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse DST end time"));
0115         string_type dst_end = *it;
0116         calc_rules(dst_begin, dst_end);
0117       }
0118     }
0119     virtual ~posix_time_zone_base() {}
0120     //!String for the zone when not in daylight savings (eg: EST)
0121     virtual string_type std_zone_abbrev()const
0122     {
0123       return zone_names_.std_zone_abbrev();
0124     }
0125     //!String for the timezone when in daylight savings (eg: EDT)
0126     /*! For those time zones that have no DST, an empty string is used */
0127     virtual string_type dst_zone_abbrev() const
0128     {
0129       return zone_names_.dst_zone_abbrev();
0130     }
0131     //!String for the zone when not in daylight savings (eg: Eastern Standard Time)
0132     /*! The full STD name is not extracted from the posix time zone string.
0133      * Therefore, the STD abbreviation is used in it's place */
0134     virtual string_type std_zone_name()const
0135     {
0136       return zone_names_.std_zone_name();
0137     }
0138     //!String for the timezone when in daylight savings (eg: Eastern Daylight Time)
0139     /*! The full DST name is not extracted from the posix time zone string.
0140      * Therefore, the STD abbreviation is used in it's place. For time zones
0141      * that have no DST, an empty string is used */
0142     virtual string_type dst_zone_name()const
0143     {
0144       return zone_names_.dst_zone_name();
0145     }
0146     //! True if zone uses daylight savings adjustments otherwise false
0147     virtual bool has_dst()const
0148     {
0149       return has_dst_;
0150     }
0151     //! Local time that DST starts -- NADT if has_dst is false
0152     virtual posix_time::ptime dst_local_start_time(gregorian::greg_year y)const
0153     {
0154       gregorian::date d(gregorian::not_a_date_time);
0155       if(has_dst_)
0156       {
0157         d = dst_calc_rules_->start_day(y);
0158       }
0159       return posix_time::ptime(d, dst_offsets_.dst_start_offset_);
0160     }
0161     //! Local time that DST ends -- NADT if has_dst is false
0162     virtual posix_time::ptime dst_local_end_time(gregorian::greg_year y)const
0163     {
0164       gregorian::date d(gregorian::not_a_date_time);
0165       if(has_dst_)
0166       {
0167         d = dst_calc_rules_->end_day(y);
0168       }
0169       return posix_time::ptime(d, dst_offsets_.dst_end_offset_);
0170     }
0171     //! Base offset from UTC for zone (eg: -07:30:00)
0172     virtual time_duration_type base_utc_offset()const
0173     {
0174       return base_utc_offset_;
0175     }
0176     //! Adjustment forward or back made while DST is in effect
0177     virtual time_duration_type dst_offset()const
0178     {
0179       return dst_offsets_.dst_adjust_;
0180     }
0181 
0182     //! Returns a POSIX time_zone string for this object
0183     virtual string_type to_posix_string() const
0184     {
0185       // std offset dst [offset],start[/time],end[/time] - w/o spaces
0186       stringstream_type ss;
0187       ss.fill('0');
0188       boost::shared_ptr<dst_calc_rule> no_rules;
0189       // std
0190       ss << std_zone_abbrev();
0191       // offset
0192       if(base_utc_offset().is_negative()) {
0193         // inverting the sign guarantees we get two digits
0194         ss << '-' << std::setw(2) << base_utc_offset().invert_sign().hours();
0195       }
0196       else {
0197         ss << '+' << std::setw(2) << base_utc_offset().hours();
0198       }
0199       if(base_utc_offset().minutes() != 0 || base_utc_offset().seconds() != 0) {
0200         ss << ':' << std::setw(2) << base_utc_offset().minutes();
0201         if(base_utc_offset().seconds() != 0) {
0202           ss << ':' << std::setw(2) << base_utc_offset().seconds();
0203         }
0204       }
0205       if(dst_calc_rules_ != no_rules) {
0206         // dst
0207         ss << dst_zone_abbrev();
0208         // dst offset
0209         if(dst_offset().is_negative()) {
0210         // inverting the sign guarantees we get two digits
0211           ss << '-' << std::setw(2) << dst_offset().invert_sign().hours();
0212         }
0213         else {
0214           ss << '+' << std::setw(2) << dst_offset().hours();
0215         }
0216         if(dst_offset().minutes() != 0 || dst_offset().seconds() != 0) {
0217           ss << ':' << std::setw(2) << dst_offset().minutes();
0218           if(dst_offset().seconds() != 0) {
0219             ss << ':' << std::setw(2) << dst_offset().seconds();
0220           }
0221         }
0222         // start/time
0223         ss << ',' << date_time::convert_string_type<char, char_type>(dst_calc_rules_->start_rule_as_string()) << '/'
0224            << std::setw(2) << dst_offsets_.dst_start_offset_.hours() << ':'
0225            << std::setw(2) << dst_offsets_.dst_start_offset_.minutes();
0226         if(dst_offsets_.dst_start_offset_.seconds() != 0) {
0227           ss << ':' << std::setw(2) << dst_offsets_.dst_start_offset_.seconds();
0228         }
0229         // end/time
0230         ss << ',' << date_time::convert_string_type<char, char_type>(dst_calc_rules_->end_rule_as_string()) << '/'
0231            << std::setw(2) << dst_offsets_.dst_end_offset_.hours() << ':'
0232            << std::setw(2) << dst_offsets_.dst_end_offset_.minutes();
0233         if(dst_offsets_.dst_end_offset_.seconds() != 0) {
0234           ss << ':' << std::setw(2) << dst_offsets_.dst_end_offset_.seconds();
0235         }
0236       }
0237 
0238       return ss.str();
0239     }
0240   private:
0241     time_zone_names zone_names_;
0242     bool has_dst_;
0243     time_duration_type base_utc_offset_;
0244     dst_adjustment_offsets dst_offsets_;
0245     boost::shared_ptr<dst_calc_rule> dst_calc_rules_;
0246 
0247     /*! Extract time zone abbreviations for STD & DST as well
0248      * as the offsets for the time shift that occurs and how
0249      * much of a shift. At this time full time zone names are
0250      * NOT extracted so the abbreviations are used in their place */
0251     void calc_zone(const string_type& obj){
0252       const char_type empty_string[2] = {'\0'};
0253       stringstream_type ss(empty_string);
0254       typename string_type::const_pointer sit = obj.c_str(), obj_end = sit + obj.size();
0255       string_type l_std_zone_abbrev, l_dst_zone_abbrev;
0256 
0257       // get 'std' name/abbrev
0258       while(std::isalpha(*sit)){
0259         ss << *sit++;
0260       }
0261       l_std_zone_abbrev = ss.str();
0262       ss.str(empty_string);
0263 
0264       // get UTC offset
0265       if(sit != obj_end){
0266         // get duration
0267         while(sit != obj_end && !std::isalpha(*sit)){
0268           ss << *sit++;
0269         }
0270         base_utc_offset_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(ss.str());
0271         ss.str(empty_string);
0272 
0273         // base offset must be within range of -12 hours to +14 hours
0274         if(base_utc_offset_ < time_duration_type(-12,0,0) ||
0275           base_utc_offset_ > time_duration_type(14,0,0))
0276         {
0277           boost::throw_exception(bad_offset(posix_time::to_simple_string(base_utc_offset_)));
0278         }
0279       }
0280 
0281       // get DST data if given
0282       if(sit != obj_end){
0283         has_dst_ = true;
0284 
0285         // get 'dst' name/abbrev
0286         while(sit != obj_end && std::isalpha(*sit)){
0287           ss << *sit++;
0288         }
0289         l_dst_zone_abbrev = ss.str();
0290         ss.str(empty_string);
0291 
0292         // get DST offset if given
0293         if(sit != obj_end){
0294           // get duration
0295           while(sit != obj_end && !std::isalpha(*sit)){
0296             ss << *sit++;
0297           }
0298           dst_offsets_.dst_adjust_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(ss.str());
0299           ss.str(empty_string);
0300         }
0301         else{ // default DST offset
0302           dst_offsets_.dst_adjust_ = posix_time::hours(1);
0303         }
0304 
0305         // adjustment must be within +|- 1 day
0306         if(dst_offsets_.dst_adjust_ <= time_duration_type(-24,0,0) ||
0307             dst_offsets_.dst_adjust_ >= time_duration_type(24,0,0))
0308         {
0309           boost::throw_exception(bad_adjustment(posix_time::to_simple_string(dst_offsets_.dst_adjust_)));
0310         }
0311       }
0312       // full names not extracted so abbrevs used in their place
0313       zone_names_ = time_zone_names(l_std_zone_abbrev, l_std_zone_abbrev, l_dst_zone_abbrev, l_dst_zone_abbrev);
0314     }
0315 
0316     void calc_rules(const string_type& start, const string_type& end){
0317 #ifdef __HP_aCC
0318       // Work around bug in aC++ compiler: see QXCR1000880488 in the
0319       // HP bug tracking system
0320       const char_type sep_chars[2] = {'/',0};
0321 #else
0322       const char_type sep_chars[2] = {'/'};
0323 #endif
0324       char_separator_type sep(sep_chars);
0325       tokenizer_type st_tok(start, sep);
0326       tokenizer_type et_tok(end, sep);
0327       tokenizer_iterator_type sit = st_tok.begin();
0328       tokenizer_iterator_type eit = et_tok.begin();
0329 
0330       // generate date spec
0331       char_type x = string_type(*sit).at(0);
0332       if(x == 'M'){
0333         M_func(*sit, *eit);
0334       }
0335       else if(x == 'J'){
0336         julian_no_leap(*sit, *eit);
0337       }
0338       else{
0339         julian_day(*sit, *eit);
0340       }
0341 
0342       ++sit;
0343       ++eit;
0344       // generate durations
0345       // starting offset
0346       if(sit != st_tok.end()){
0347         dst_offsets_.dst_start_offset_ =  date_time::str_from_delimited_time_duration<time_duration_type,char_type>(*sit);
0348       }
0349       else{
0350         // default
0351         dst_offsets_.dst_start_offset_ = posix_time::hours(2);
0352       }
0353       // start/end offsets must fall on given date
0354       if(dst_offsets_.dst_start_offset_ < time_duration_type(0,0,0) ||
0355           dst_offsets_.dst_start_offset_ >= time_duration_type(24,0,0))
0356       {
0357         boost::throw_exception(bad_offset(posix_time::to_simple_string(dst_offsets_.dst_start_offset_)));
0358       }
0359 
0360       // ending offset
0361       if(eit != et_tok.end()){
0362         dst_offsets_.dst_end_offset_ =  date_time::str_from_delimited_time_duration<time_duration_type,char_type>(*eit);
0363       }
0364       else{
0365         // default
0366         dst_offsets_.dst_end_offset_ = posix_time::hours(2);
0367       }
0368       // start/end offsets must fall on given date
0369       if(dst_offsets_.dst_end_offset_ < time_duration_type(0,0,0) ||
0370         dst_offsets_.dst_end_offset_ >= time_duration_type(24,0,0))
0371       {
0372         boost::throw_exception(bad_offset(posix_time::to_simple_string(dst_offsets_.dst_end_offset_)));
0373       }
0374     }
0375 
0376     /* Parses out a start/end date spec from a posix time zone string.
0377      * Date specs come in three possible formats, this function handles
0378      * the 'M' spec. Ex "M2.2.4" => 2nd month, 2nd week, 4th day .
0379      */
0380     void M_func(const string_type& s, const string_type& e){
0381       typedef gregorian::nth_kday_of_month nkday;
0382       unsigned short sm=0,sw=0,sd=0,em=0,ew=0,ed=0; // start/end month,week,day
0383 #ifdef __HP_aCC
0384       // Work around bug in aC++ compiler: see QXCR1000880488 in the
0385       // HP bug tracking system
0386       const char_type sep_chars[3] = {'M','.',0};
0387 #else
0388       const char_type sep_chars[3] = {'M','.'};
0389 #endif
0390       char_separator_type sep(sep_chars);
0391       tokenizer_type stok(s, sep), etok(e, sep);
0392 
0393       tokenizer_iterator_type it = stok.begin();
0394       sm = lexical_cast<unsigned short>(*it++);
0395       sw = lexical_cast<unsigned short>(*it++);
0396       sd = lexical_cast<unsigned short>(*it);
0397 
0398       it = etok.begin();
0399       em = lexical_cast<unsigned short>(*it++);
0400       ew = lexical_cast<unsigned short>(*it++);
0401       ed = lexical_cast<unsigned short>(*it);
0402 
0403       dst_calc_rules_ = shared_ptr<dst_calc_rule>(
0404         new nth_kday_dst_rule(
0405           nth_last_dst_rule::start_rule(
0406             static_cast<nkday::week_num>(sw),sd,sm),
0407           nth_last_dst_rule::start_rule(
0408             static_cast<nkday::week_num>(ew),ed,em)
0409           )
0410       );
0411     }
0412 
0413     //! Julian day. Feb29 is never counted, even in leap years
0414     // expects range of 1-365
0415     void julian_no_leap(const string_type& s, const string_type& e){
0416       typedef gregorian::gregorian_calendar calendar;
0417       const unsigned short year = 2001; // Non-leap year
0418       unsigned short sm=1;
0419       int sd=0;
0420       sd = lexical_cast<int>(s.substr(1)); // skip 'J'
0421       while(sd >= calendar::end_of_month_day(year,sm)){
0422         sd -= calendar::end_of_month_day(year,sm++);
0423       }
0424       unsigned short em=1;
0425       int ed=0;
0426       ed = lexical_cast<int>(e.substr(1)); // skip 'J'
0427       while(ed > calendar::end_of_month_day(year,em)){
0428         ed -= calendar::end_of_month_day(year,em++);
0429       }
0430 
0431       dst_calc_rules_ = shared_ptr<dst_calc_rule>(
0432         new partial_date_dst_rule(
0433           partial_date_dst_rule::start_rule(
0434             static_cast<unsigned short>(sd), static_cast<date_time::months_of_year>(sm)),
0435           partial_date_dst_rule::end_rule(
0436             static_cast<unsigned short>(ed), static_cast<date_time::months_of_year>(em))
0437           )
0438       );
0439     }
0440 
0441     //! Julian day. Feb29 is always counted, but exception thrown in non-leap years
0442     // expects range of 0-365
0443     void julian_day(const string_type& s, const string_type& e){
0444       int sd=0, ed=0;
0445       sd = lexical_cast<int>(s);
0446       ed = lexical_cast<int>(e);
0447       dst_calc_rules_ = shared_ptr<dst_calc_rule>(
0448         new partial_date_dst_rule(
0449           partial_date_dst_rule::start_rule(++sd),// args are 0-365
0450           partial_date_dst_rule::end_rule(++ed) // pd expects 1-366
0451           )
0452       );
0453     }
0454 
0455     //! helper function used when throwing exceptions
0456     static std::string td_as_string(const time_duration_type& td)
0457     {
0458       std::string s;
0459 #if defined(USE_DATE_TIME_PRE_1_33_FACET_IO)
0460       s = posix_time::to_simple_string(td);
0461 #else
0462       std::stringstream ss;
0463       ss << td;
0464       s = ss.str();
0465 #endif
0466       return s;
0467     }
0468   };
0469 
0470   typedef posix_time_zone_base<char> posix_time_zone;
0471 
0472 } } // namespace boost::local_time
0473 
0474 
0475 #endif // _DATE_TIME_POSIX_TIME_ZONE__