Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #ifndef LOCAL_TIME_LOCAL_DATE_TIME_HPP__
0002 #define LOCAL_TIME_LOCAL_DATE_TIME_HPP__
0003 
0004 /* Copyright (c) 2003-2005 CrystalClear Software, Inc.
0005  * Subject to the Boost Software License, Version 1.0.
0006  * (See accompanying 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 <iomanip>
0013 #include <sstream>
0014 #include <stdexcept>
0015 #include <boost/shared_ptr.hpp>
0016 #include <boost/throw_exception.hpp>
0017 #include <boost/date_time/time.hpp>
0018 #include <boost/date_time/posix_time/posix_time.hpp> //todo remove?
0019 #include <boost/date_time/compiler_config.hpp>
0020 #include <boost/date_time/dst_rules.hpp>
0021 #include <boost/date_time/time_zone_base.hpp>
0022 #include <boost/date_time/special_defs.hpp>
0023 #include <boost/date_time/time_resolution_traits.hpp> // absolute_value
0024 
0025 namespace boost {
0026 namespace local_time {
0027 
0028   //! simple exception for reporting when STD or DST cannot be determined
0029   struct BOOST_SYMBOL_VISIBLE ambiguous_result : public std::logic_error
0030   {
0031     ambiguous_result (std::string const& msg = std::string()) :
0032       std::logic_error(std::string("Daylight Savings Results are ambiguous: " + msg)) {}
0033   };
0034   //! simple exception for when time label given cannot exist
0035   struct BOOST_SYMBOL_VISIBLE time_label_invalid : public std::logic_error
0036   {
0037     time_label_invalid (std::string const& msg = std::string()) :
0038       std::logic_error(std::string("Time label given is invalid: " + msg)) {}
0039   };
0040   struct BOOST_SYMBOL_VISIBLE dst_not_valid: public std::logic_error
0041   {
0042     dst_not_valid(std::string const& msg = std::string()) :
0043       std::logic_error(std::string("is_dst flag does not match resulting dst for time label given: " + msg)) {}
0044   };
0045 
0046   //TODO: I think these should be in local_date_time_base and not
0047   // necessarily brought into the namespace
0048   using date_time::time_is_dst_result;
0049   using date_time::is_in_dst;
0050   using date_time::is_not_in_dst;
0051   using date_time::ambiguous;
0052   using date_time::invalid_time_label;
0053 
0054   //! Representation of "wall-clock" time in a particular time zone
0055   /*! Representation of "wall-clock" time in a particular time zone
0056    * Local_date_time_base holds a time value (date and time offset from 00:00)
0057    * along with a time zone. The time value is stored as UTC and conversions
0058    * to wall clock time are made as needed. This approach allows for
0059    * operations between wall-clock times in different time zones, and
0060    * daylight savings time considerations, to be made. Time zones are
0061    * required to be in the form of a boost::shared_ptr<time_zone_base>.
0062    */
0063   template<class utc_time_=posix_time::ptime,
0064            class tz_type=date_time::time_zone_base<utc_time_,char> >
0065   class BOOST_SYMBOL_VISIBLE local_date_time_base :  public date_time::base_time<utc_time_,
0066                                                             boost::posix_time::posix_time_system> {
0067   public:
0068     typedef utc_time_ utc_time_type;
0069     typedef typename utc_time_type::time_duration_type time_duration_type;
0070     typedef typename utc_time_type::date_type date_type;
0071     typedef typename date_type::duration_type date_duration_type;
0072     typedef typename utc_time_type::time_system_type time_system_type;
0073     /*! This constructor interprets the passed time as a UTC time.
0074      *  So, for example, if the passed timezone is UTC-5 then the
0075      *  time will be adjusted back 5 hours.  The time zone allows for
0076      *  automatic calculation of whether the particular time is adjusted for
0077      *  daylight savings, etc.
0078      *  If the time zone shared pointer is null then time stays unadjusted.
0079      *@param t A UTC time
0080      *@param tz Timezone for to adjust the UTC time to.
0081      */
0082     local_date_time_base(utc_time_type t,
0083                          boost::shared_ptr<tz_type> tz) :
0084       date_time::base_time<utc_time_type, time_system_type>(t),
0085       zone_(tz)
0086     {
0087       // param was already utc so nothing more to do
0088     }
0089 
0090     /*! This constructs a local time -- the passed time information
0091      * understood to be in the passed tz. The DST flag must be passed
0092      * to indicate whether the time is in daylight savings or not.
0093      *  @throws -- time_label_invalid if the time passed does not exist in
0094      *             the given locale. The non-existent case occurs typically
0095      *             during the shift-back from daylight savings time.  When
0096      *             the clock is shifted forward a range of times
0097      *             (2 am to 3 am in the US) is skipped and hence is invalid.
0098      *  @throws -- dst_not_valid if the DST flag is passed for a period
0099      *             where DST is not active.
0100      */
0101     local_date_time_base(date_type d,
0102                          time_duration_type td,
0103                          boost::shared_ptr<tz_type> tz,
0104                          bool dst_flag) : //necessary for constr_adj()
0105       date_time::base_time<utc_time_type,time_system_type>(construction_adjustment(utc_time_type(d, td), tz, dst_flag)),
0106       zone_(tz)
0107     {
0108       if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()){
0109 
0110         // d & td are already local so we use them
0111         time_is_dst_result result = check_dst(d, td, tz);
0112         bool in_dst = (result == is_in_dst); // less processing than is_dst()
0113 
0114         // ambig occurs at end, invalid at start
0115         if(result == invalid_time_label){
0116           // Ex: 2:15am local on trans-in day in nyc, dst_flag irrelevant
0117           std::ostringstream ss;
0118           ss << "time given: " << d << ' ' << td;
0119           boost::throw_exception(time_label_invalid(ss.str()));
0120         }
0121         else if(result != ambiguous && in_dst != dst_flag){
0122           // is dst_flag accurate?
0123           // Ex: false flag in NYC in June
0124           std::ostringstream ss;
0125           ss.setf(std::ios_base::boolalpha);
0126           ss << "flag given: dst=" << dst_flag << ", dst calculated: dst=" << in_dst;
0127           boost::throw_exception(dst_not_valid(ss.str()));
0128         }
0129 
0130         // everything checks out and conversion to utc already done
0131       }
0132     }
0133 
0134     //TODO maybe not the right set...Ignore the last 2 for now...
0135     enum DST_CALC_OPTIONS { EXCEPTION_ON_ERROR, NOT_DATE_TIME_ON_ERROR };
0136                             //ASSUME_DST_ON_ERROR, ASSUME_NOT_DST_ON_ERROR };
0137 
0138     /*! This constructs a local time -- the passed time information
0139      * understood to be in the passed tz.  The DST flag is calculated
0140      * according to the specified rule.
0141      */
0142     local_date_time_base(date_type d,
0143                          time_duration_type td,
0144                          boost::shared_ptr<tz_type> tz,
0145                          DST_CALC_OPTIONS calc_option) :
0146       // dummy value - time_ is set in constructor code
0147       date_time::base_time<utc_time_type,time_system_type>(utc_time_type(d,td)),
0148       zone_(tz)
0149     {
0150       time_is_dst_result result = check_dst(d, td, tz);
0151       if(result == ambiguous) {
0152         if(calc_option == EXCEPTION_ON_ERROR){
0153           std::ostringstream ss;
0154           ss << "time given: " << d << ' ' << td;
0155           boost::throw_exception(ambiguous_result(ss.str()));
0156         }
0157         else{ // NADT on error
0158           this->time_ = posix_time::posix_time_system::get_time_rep(date_type(date_time::not_a_date_time), time_duration_type(date_time::not_a_date_time));
0159         }
0160       }
0161       else if(result == invalid_time_label){
0162         if(calc_option == EXCEPTION_ON_ERROR){
0163           std::ostringstream ss;
0164           ss << "time given: " << d << ' ' << td;
0165           boost::throw_exception(time_label_invalid(ss.str()));
0166         }
0167         else{ // NADT on error
0168           this->time_ = posix_time::posix_time_system::get_time_rep(date_type(date_time::not_a_date_time), time_duration_type(date_time::not_a_date_time));
0169         }
0170       }
0171       else if(result == is_in_dst){
0172         utc_time_type t =
0173           construction_adjustment(utc_time_type(d, td), tz, true);
0174         this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
0175                                                             t.time_of_day());
0176       }
0177       else{
0178         utc_time_type t =
0179           construction_adjustment(utc_time_type(d, td), tz, false);
0180         this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
0181                                                             t.time_of_day());
0182       }
0183     }
0184 
0185 
0186     //! Determines if given time label is in daylight savings for given zone
0187     /*! Determines if given time label is in daylight savings for given zone.
0188      * Takes a date and time_duration representing a local time, along
0189      * with time zone, and returns a time_is_dst_result object as result.
0190      */
0191     static time_is_dst_result check_dst(date_type d,
0192                                         time_duration_type td,
0193                                         boost::shared_ptr<tz_type> tz)
0194     {
0195       if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()) {
0196         typedef typename date_time::dst_calculator<date_type, time_duration_type> dst_calculator;
0197         return dst_calculator::local_is_dst(
0198             d, td,
0199             tz->dst_local_start_time(d.year()).date(),
0200             tz->dst_local_start_time(d.year()).time_of_day(),
0201             tz->dst_local_end_time(d.year()).date(),
0202             tz->dst_local_end_time(d.year()).time_of_day(),
0203             tz->dst_offset()
0204         );
0205       }
0206       else{
0207         return is_not_in_dst;
0208       }
0209     }
0210 
0211     //! Simple destructor, releases time zone if last referrer
0212     ~local_date_time_base() {}
0213 
0214     //! Copy constructor
0215     local_date_time_base(const local_date_time_base& rhs) :
0216       date_time::base_time<utc_time_type, time_system_type>(rhs),
0217       zone_(rhs.zone_)
0218     {}
0219 
0220     //! Special values constructor
0221     explicit local_date_time_base(const boost::date_time::special_values sv,
0222                                   boost::shared_ptr<tz_type> tz = boost::shared_ptr<tz_type>()) :
0223       date_time::base_time<utc_time_type, time_system_type>(utc_time_type(sv)),
0224       zone_(tz)
0225     {}
0226 
0227     //! returns time zone associated with calling instance
0228     boost::shared_ptr<tz_type> zone() const
0229     {
0230       return zone_;
0231     }
0232     //! returns false is time_zone is NULL and if time value is a special_value
0233     bool is_dst() const
0234     {
0235       if(zone_ != boost::shared_ptr<tz_type>() && zone_->has_dst() && !this->is_special()) {
0236         // check_dst takes a local time, *this is utc
0237         utc_time_type lt(this->time_);
0238         lt += zone_->base_utc_offset();
0239         // dst_offset only needs to be considered with ambiguous time labels
0240         // make that adjustment there
0241 
0242         switch(check_dst(lt.date(), lt.time_of_day(), zone_)){
0243           case is_not_in_dst:
0244             return false;
0245           case is_in_dst:
0246             return true;
0247           case ambiguous:
0248             if(lt + zone_->dst_offset() < zone_->dst_local_end_time(lt.date().year())) {
0249               return true;
0250             }
0251             break;
0252           case invalid_time_label:
0253             if(lt >= zone_->dst_local_start_time(lt.date().year())) {
0254               return true;
0255             }
0256             break;
0257         }
0258       }
0259       return false;
0260     }
0261     //! Returns object's time value as a utc representation
0262     utc_time_type utc_time() const
0263     {
0264       return utc_time_type(this->time_);
0265     }
0266     //! Returns object's time value as a local representation
0267     utc_time_type local_time() const
0268     {
0269       if(zone_ != boost::shared_ptr<tz_type>()){
0270         utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
0271         if (is_dst()) {
0272           lt += zone_->dst_offset();
0273         }
0274         return lt;
0275       }
0276       return utc_time_type(this->time_);
0277     }
0278     //! Returns string in the form "2003-Aug-20 05:00:00 EDT"
0279     /*! Returns string in the form "2003-Aug-20 05:00:00 EDT". If
0280      * time_zone is NULL the time zone abbreviation will be "UTC". The time
0281      * zone abbrev will not be included if calling object is a special_value*/
0282     std::string to_string() const
0283     {
0284       //TODO is this a temporary function ???
0285       std::ostringstream ss;
0286       if(this->is_special()){
0287         ss << utc_time();
0288         return ss.str();
0289       }
0290       if(zone_ == boost::shared_ptr<tz_type>()) {
0291         ss << utc_time() << " UTC";
0292         return ss.str();
0293       }
0294       bool is_dst_ = is_dst();
0295       utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
0296       if (is_dst_) {
0297         lt += zone_->dst_offset();
0298       }
0299       ss << local_time() << " ";
0300       if (is_dst()) {
0301         ss << zone_->dst_zone_abbrev();
0302       }
0303       else {
0304         ss << zone_->std_zone_abbrev();
0305       }
0306       return ss.str();
0307     }
0308     /*! returns a local_date_time_base in the given time zone with the
0309      * optional time_duration added. */
0310     local_date_time_base local_time_in(boost::shared_ptr<tz_type> new_tz,
0311                                        time_duration_type td=time_duration_type(0,0,0)) const
0312     {
0313       return local_date_time_base(utc_time_type(this->time_) + td, new_tz);
0314     }
0315 
0316     //! Returns name of associated time zone or "Coordinated Universal Time".
0317     /*! Optional bool parameter will return time zone as an offset
0318      * (ie "+07:00" extended ISO 8601 format). Empty string is returned for
0319      * classes that do not use a time_zone */
0320     std::string zone_name(bool as_offset=false) const
0321     {
0322       if(zone_ == boost::shared_ptr<tz_type>()) {
0323         if(as_offset) {
0324           return std::string("Z");
0325         }
0326         else {
0327           return std::string("Coordinated Universal Time");
0328         }
0329       }
0330       if (is_dst()) {
0331         if(as_offset) {
0332           time_duration_type td = zone_->base_utc_offset();
0333           td += zone_->dst_offset();
0334           return zone_as_offset(td, ":");
0335         }
0336         else {
0337           return zone_->dst_zone_name();
0338         }
0339       }
0340       else {
0341         if(as_offset) {
0342           time_duration_type td = zone_->base_utc_offset();
0343           return zone_as_offset(td, ":");
0344         }
0345         else {
0346           return zone_->std_zone_name();
0347         }
0348       }
0349     }
0350     //! Returns abbreviation of associated time zone or "UTC".
0351     /*! Optional bool parameter will return time zone as an offset
0352      * (ie "+0700" ISO 8601 format). Empty string is returned for classes
0353      * that do not use a time_zone */
0354     std::string zone_abbrev(bool as_offset=false) const
0355     {
0356       if(zone_ == boost::shared_ptr<tz_type>()) {
0357         if(as_offset) {
0358           return std::string("Z");
0359         }
0360         else {
0361           return std::string("UTC");
0362         }
0363       }
0364       if (is_dst()) {
0365         if(as_offset) {
0366           time_duration_type td = zone_->base_utc_offset();
0367           td += zone_->dst_offset();
0368           return zone_as_offset(td, "");
0369         }
0370         else {
0371           return zone_->dst_zone_abbrev();
0372         }
0373       }
0374       else {
0375         if(as_offset) {
0376           time_duration_type td = zone_->base_utc_offset();
0377           return zone_as_offset(td, "");
0378         }
0379         else {
0380           return zone_->std_zone_abbrev();
0381         }
0382       }
0383     }
0384 
0385     //! returns a posix_time_zone string for the associated time_zone. If no time_zone, "UTC+00" is returned.
0386     std::string zone_as_posix_string() const
0387     {
0388       if(zone_ == shared_ptr<tz_type>()) {
0389         return std::string("UTC+00");
0390       }
0391       return zone_->to_posix_string();
0392     }
0393 
0394     //! Equality comparison operator
0395     /*bool operator==(const date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>& rhs) const
0396     { // fails due to rhs.time_ being protected
0397       return date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>::operator==(rhs);
0398       //return this->time_ == rhs.time_;
0399     }*/
0400     //! Equality comparison operator
0401     bool operator==(const local_date_time_base& rhs) const
0402     {
0403       return time_system_type::is_equal(this->time_, rhs.time_);
0404     }
0405     //! Non-Equality comparison operator
0406     bool operator!=(const local_date_time_base& rhs) const
0407     {
0408       return !(*this == rhs);
0409     }
0410     //! Less than comparison operator
0411     bool operator<(const local_date_time_base& rhs) const
0412     {
0413       return time_system_type::is_less(this->time_, rhs.time_);
0414     }
0415     //! Less than or equal to comparison operator
0416     bool operator<=(const local_date_time_base& rhs) const
0417     {
0418       return (*this < rhs || *this == rhs);
0419     }
0420     //! Greater than comparison operator
0421     bool operator>(const local_date_time_base& rhs) const
0422     {
0423       return !(*this <= rhs);
0424     }
0425     //! Greater than or equal to comparison operator
0426     bool operator>=(const local_date_time_base& rhs) const
0427     {
0428       return (*this > rhs || *this == rhs);
0429     }
0430 
0431     //! Local_date_time + date_duration
0432     local_date_time_base operator+(const date_duration_type& dd) const
0433     {
0434       return local_date_time_base(time_system_type::add_days(this->time_,dd), zone_);
0435     }
0436     //! Local_date_time += date_duration
0437     local_date_time_base operator+=(const date_duration_type& dd)
0438     {
0439       this->time_ = time_system_type::add_days(this->time_,dd);
0440       return *this;
0441     }
0442     //! Local_date_time - date_duration
0443     local_date_time_base operator-(const date_duration_type& dd) const
0444     {
0445       return local_date_time_base(time_system_type::subtract_days(this->time_,dd), zone_);
0446     }
0447     //! Local_date_time -= date_duration
0448     local_date_time_base operator-=(const date_duration_type& dd)
0449     {
0450       this->time_ = time_system_type::subtract_days(this->time_,dd);
0451       return *this;
0452     }
0453     //! Local_date_time + time_duration
0454     local_date_time_base operator+(const time_duration_type& td) const
0455     {
0456       return local_date_time_base(time_system_type::add_time_duration(this->time_,td), zone_);
0457     }
0458     //! Local_date_time += time_duration
0459     local_date_time_base operator+=(const time_duration_type& td)
0460     {
0461       this->time_ = time_system_type::add_time_duration(this->time_,td);
0462       return *this;
0463     }
0464     //! Local_date_time - time_duration
0465     local_date_time_base operator-(const time_duration_type& td) const
0466     {
0467       return local_date_time_base(time_system_type::subtract_time_duration(this->time_,td), zone_);
0468     }
0469     //! Local_date_time -= time_duration
0470     local_date_time_base operator-=(const time_duration_type& td)
0471     {
0472       this->time_ = time_system_type::subtract_time_duration(this->time_,td);
0473       return *this;
0474     }
0475     //! local_date_time -= local_date_time --> time_duration_type
0476     time_duration_type operator-(const local_date_time_base& rhs) const
0477     {
0478       return utc_time_type(this->time_) - utc_time_type(rhs.time_);
0479     }
0480   private:
0481     boost::shared_ptr<tz_type> zone_;
0482     //bool is_dst_;
0483 
0484     /*! Adjust the passed in time to UTC?
0485      */
0486     utc_time_type construction_adjustment(utc_time_type t,
0487                                           boost::shared_ptr<tz_type> z,
0488                                           bool dst_flag)
0489     {
0490       if(z != boost::shared_ptr<tz_type>()) {
0491         if(dst_flag && z->has_dst()) {
0492           t -= z->dst_offset();
0493         } // else no adjust
0494         t -= z->base_utc_offset();
0495       }
0496       return t;
0497     }
0498 
0499     /*! Simple formatting code -- todo remove this?
0500      */
0501     std::string zone_as_offset(const time_duration_type& td,
0502                                const std::string& separator) const
0503     {
0504       std::ostringstream ss;
0505       if(td.is_negative()) {
0506         // a negative duration is represented as "-[h]h:mm"
0507         // we require two digits for the hour. A positive duration
0508         // with the %H flag will always give two digits
0509         ss << "-";
0510       }
0511       else {
0512         ss << "+";
0513       }
0514       ss  << std::setw(2) << std::setfill('0')
0515           << date_time::absolute_value(td.hours())
0516           << separator
0517           << std::setw(2) << std::setfill('0')
0518           << date_time::absolute_value(td.minutes());
0519       return ss.str();
0520     }
0521   };
0522 
0523   //!Use the default parameters to define local_date_time
0524   typedef local_date_time_base<> local_date_time;
0525 
0526 } }
0527 
0528 
0529 #endif