• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef LOCAL_TIME_LOCAL_DATE_TIME_HPP__
2 #define LOCAL_TIME_LOCAL_DATE_TIME_HPP__
3 
4 /* Copyright (c) 2003-2005 CrystalClear Software, Inc.
5  * Subject to the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
7  * Author: Jeff Garland, Bart Garst
8  * $Date$
9  */
10 
11 #include <string>
12 #include <iomanip>
13 #include <sstream>
14 #include <stdexcept>
15 #include <boost/shared_ptr.hpp>
16 #include <boost/throw_exception.hpp>
17 #include <boost/date_time/time.hpp>
18 #include <boost/date_time/posix_time/posix_time.hpp> //todo remove?
19 #include <boost/date_time/compiler_config.hpp>
20 #include <boost/date_time/dst_rules.hpp>
21 #include <boost/date_time/time_zone_base.hpp>
22 #include <boost/date_time/special_defs.hpp>
23 #include <boost/date_time/time_resolution_traits.hpp> // absolute_value
24 
25 namespace boost {
26 namespace local_time {
27 
28   //! simple exception for reporting when STD or DST cannot be determined
29   struct BOOST_SYMBOL_VISIBLE ambiguous_result : public std::logic_error
30   {
ambiguous_resultboost::local_time::ambiguous_result31     ambiguous_result (std::string const& msg = std::string()) :
32       std::logic_error(std::string("Daylight Savings Results are ambiguous: " + msg)) {}
33   };
34   //! simple exception for when time label given cannot exist
35   struct BOOST_SYMBOL_VISIBLE time_label_invalid : public std::logic_error
36   {
time_label_invalidboost::local_time::time_label_invalid37     time_label_invalid (std::string const& msg = std::string()) :
38       std::logic_error(std::string("Time label given is invalid: " + msg)) {}
39   };
40   struct BOOST_SYMBOL_VISIBLE dst_not_valid: public std::logic_error
41   {
dst_not_validboost::local_time::dst_not_valid42     dst_not_valid(std::string const& msg = std::string()) :
43       std::logic_error(std::string("is_dst flag does not match resulting dst for time label given: " + msg)) {}
44   };
45 
46   //TODO: I think these should be in local_date_time_base and not
47   // necessarily brought into the namespace
48   using date_time::time_is_dst_result;
49   using date_time::is_in_dst;
50   using date_time::is_not_in_dst;
51   using date_time::ambiguous;
52   using date_time::invalid_time_label;
53 
54   //! Representation of "wall-clock" time in a particular time zone
55   /*! Representation of "wall-clock" time in a particular time zone
56    * Local_date_time_base holds a time value (date and time offset from 00:00)
57    * along with a time zone. The time value is stored as UTC and conversions
58    * to wall clock time are made as needed. This approach allows for
59    * operations between wall-clock times in different time zones, and
60    * daylight savings time considerations, to be made. Time zones are
61    * required to be in the form of a boost::shared_ptr<time_zone_base>.
62    */
63   template<class utc_time_=posix_time::ptime,
64            class tz_type=date_time::time_zone_base<utc_time_,char> >
65   class BOOST_SYMBOL_VISIBLE local_date_time_base :  public date_time::base_time<utc_time_,
66                                                             boost::posix_time::posix_time_system> {
67   public:
68     typedef utc_time_ utc_time_type;
69     typedef typename utc_time_type::time_duration_type time_duration_type;
70     typedef typename utc_time_type::date_type date_type;
71     typedef typename date_type::duration_type date_duration_type;
72     typedef typename utc_time_type::time_system_type time_system_type;
73     /*! This constructor interprets the passed time as a UTC time.
74      *  So, for example, if the passed timezone is UTC-5 then the
75      *  time will be adjusted back 5 hours.  The time zone allows for
76      *  automatic calculation of whether the particular time is adjusted for
77      *  daylight savings, etc.
78      *  If the time zone shared pointer is null then time stays unadjusted.
79      *@param t A UTC time
80      *@param tz Timezone for to adjust the UTC time to.
81      */
local_date_time_base(utc_time_type t,boost::shared_ptr<tz_type> tz)82     local_date_time_base(utc_time_type t,
83                          boost::shared_ptr<tz_type> tz) :
84       date_time::base_time<utc_time_type, time_system_type>(t),
85       zone_(tz)
86     {
87       // param was already utc so nothing more to do
88     }
89 
90     /*! This constructs a local time -- the passed time information
91      * understood to be in the passed tz. The DST flag must be passed
92      * to indicate whether the time is in daylight savings or not.
93      *  @throws -- time_label_invalid if the time passed does not exist in
94      *             the given locale. The non-existent case occurs typically
95      *             during the shift-back from daylight savings time.  When
96      *             the clock is shifted forward a range of times
97      *             (2 am to 3 am in the US) is skipped and hence is invalid.
98      *  @throws -- dst_not_valid if the DST flag is passed for a period
99      *             where DST is not active.
100      */
local_date_time_base(date_type d,time_duration_type td,boost::shared_ptr<tz_type> tz,bool dst_flag)101     local_date_time_base(date_type d,
102                          time_duration_type td,
103                          boost::shared_ptr<tz_type> tz,
104                          bool dst_flag) : //necessary for constr_adj()
105       date_time::base_time<utc_time_type,time_system_type>(construction_adjustment(utc_time_type(d, td), tz, dst_flag)),
106       zone_(tz)
107     {
108       if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()){
109 
110         // d & td are already local so we use them
111         time_is_dst_result result = check_dst(d, td, tz);
112         bool in_dst = (result == is_in_dst); // less processing than is_dst()
113 
114         // ambig occurs at end, invalid at start
115         if(result == invalid_time_label){
116           // Ex: 2:15am local on trans-in day in nyc, dst_flag irrelevant
117           std::ostringstream ss;
118           ss << "time given: " << d << ' ' << td;
119           boost::throw_exception(time_label_invalid(ss.str()));
120         }
121         else if(result != ambiguous && in_dst != dst_flag){
122           // is dst_flag accurate?
123           // Ex: false flag in NYC in June
124           std::ostringstream ss;
125           ss.setf(std::ios_base::boolalpha);
126           ss << "flag given: dst=" << dst_flag << ", dst calculated: dst=" << in_dst;
127           boost::throw_exception(dst_not_valid(ss.str()));
128         }
129 
130         // everything checks out and conversion to utc already done
131       }
132     }
133 
134     //TODO maybe not the right set...Ignore the last 2 for now...
135     enum DST_CALC_OPTIONS { EXCEPTION_ON_ERROR, NOT_DATE_TIME_ON_ERROR };
136                             //ASSUME_DST_ON_ERROR, ASSUME_NOT_DST_ON_ERROR };
137 
138     /*! This constructs a local time -- the passed time information
139      * understood to be in the passed tz.  The DST flag is calculated
140      * according to the specified rule.
141      */
local_date_time_base(date_type d,time_duration_type td,boost::shared_ptr<tz_type> tz,DST_CALC_OPTIONS calc_option)142     local_date_time_base(date_type d,
143                          time_duration_type td,
144                          boost::shared_ptr<tz_type> tz,
145                          DST_CALC_OPTIONS calc_option) :
146       // dummy value - time_ is set in constructor code
147       date_time::base_time<utc_time_type,time_system_type>(utc_time_type(d,td)),
148       zone_(tz)
149     {
150       time_is_dst_result result = check_dst(d, td, tz);
151       if(result == ambiguous) {
152         if(calc_option == EXCEPTION_ON_ERROR){
153           std::ostringstream ss;
154           ss << "time given: " << d << ' ' << td;
155           boost::throw_exception(ambiguous_result(ss.str()));
156         }
157         else{ // NADT on error
158           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));
159         }
160       }
161       else if(result == invalid_time_label){
162         if(calc_option == EXCEPTION_ON_ERROR){
163           std::ostringstream ss;
164           ss << "time given: " << d << ' ' << td;
165           boost::throw_exception(time_label_invalid(ss.str()));
166         }
167         else{ // NADT on error
168           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));
169         }
170       }
171       else if(result == is_in_dst){
172         utc_time_type t =
173           construction_adjustment(utc_time_type(d, td), tz, true);
174         this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
175                                                             t.time_of_day());
176       }
177       else{
178         utc_time_type t =
179           construction_adjustment(utc_time_type(d, td), tz, false);
180         this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
181                                                             t.time_of_day());
182       }
183     }
184 
185 
186     //! Determines if given time label is in daylight savings for given zone
187     /*! Determines if given time label is in daylight savings for given zone.
188      * Takes a date and time_duration representing a local time, along
189      * with time zone, and returns a time_is_dst_result object as result.
190      */
check_dst(date_type d,time_duration_type td,boost::shared_ptr<tz_type> tz)191     static time_is_dst_result check_dst(date_type d,
192                                         time_duration_type td,
193                                         boost::shared_ptr<tz_type> tz)
194     {
195       if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()) {
196         typedef typename date_time::dst_calculator<date_type, time_duration_type> dst_calculator;
197         return dst_calculator::local_is_dst(
198             d, td,
199             tz->dst_local_start_time(d.year()).date(),
200             tz->dst_local_start_time(d.year()).time_of_day(),
201             tz->dst_local_end_time(d.year()).date(),
202             tz->dst_local_end_time(d.year()).time_of_day(),
203             tz->dst_offset()
204         );
205       }
206       else{
207         return is_not_in_dst;
208       }
209     }
210 
211     //! Simple destructor, releases time zone if last referrer
~local_date_time_base()212     ~local_date_time_base() {}
213 
214     //! Copy constructor
local_date_time_base(const local_date_time_base & rhs)215     local_date_time_base(const local_date_time_base& rhs) :
216       date_time::base_time<utc_time_type, time_system_type>(rhs),
217       zone_(rhs.zone_)
218     {}
219 
220     //! Special values constructor
local_date_time_base(const boost::date_time::special_values sv,boost::shared_ptr<tz_type> tz=boost::shared_ptr<tz_type> ())221     explicit local_date_time_base(const boost::date_time::special_values sv,
222                                   boost::shared_ptr<tz_type> tz = boost::shared_ptr<tz_type>()) :
223       date_time::base_time<utc_time_type, time_system_type>(utc_time_type(sv)),
224       zone_(tz)
225     {}
226 
227     //! returns time zone associated with calling instance
zone() const228     boost::shared_ptr<tz_type> zone() const
229     {
230       return zone_;
231     }
232     //! returns false is time_zone is NULL and if time value is a special_value
is_dst() const233     bool is_dst() const
234     {
235       if(zone_ != boost::shared_ptr<tz_type>() && zone_->has_dst() && !this->is_special()) {
236         // check_dst takes a local time, *this is utc
237         utc_time_type lt(this->time_);
238         lt += zone_->base_utc_offset();
239         // dst_offset only needs to be considered with ambiguous time labels
240         // make that adjustment there
241 
242         switch(check_dst(lt.date(), lt.time_of_day(), zone_)){
243           case is_not_in_dst:
244             return false;
245           case is_in_dst:
246             return true;
247           case ambiguous:
248             if(lt + zone_->dst_offset() < zone_->dst_local_end_time(lt.date().year())) {
249               return true;
250             }
251             break;
252           case invalid_time_label:
253             if(lt >= zone_->dst_local_start_time(lt.date().year())) {
254               return true;
255             }
256             break;
257         }
258       }
259       return false;
260     }
261     //! Returns object's time value as a utc representation
utc_time() const262     utc_time_type utc_time() const
263     {
264       return utc_time_type(this->time_);
265     }
266     //! Returns object's time value as a local representation
local_time() const267     utc_time_type local_time() const
268     {
269       if(zone_ != boost::shared_ptr<tz_type>()){
270         utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
271         if (is_dst()) {
272           lt += zone_->dst_offset();
273         }
274         return lt;
275       }
276       return utc_time_type(this->time_);
277     }
278     //! Returns string in the form "2003-Aug-20 05:00:00 EDT"
279     /*! Returns string in the form "2003-Aug-20 05:00:00 EDT". If
280      * time_zone is NULL the time zone abbreviation will be "UTC". The time
281      * zone abbrev will not be included if calling object is a special_value*/
to_string() const282     std::string to_string() const
283     {
284       //TODO is this a temporary function ???
285       std::ostringstream ss;
286       if(this->is_special()){
287         ss << utc_time();
288         return ss.str();
289       }
290       if(zone_ == boost::shared_ptr<tz_type>()) {
291         ss << utc_time() << " UTC";
292         return ss.str();
293       }
294       bool is_dst_ = is_dst();
295       utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
296       if (is_dst_) {
297         lt += zone_->dst_offset();
298       }
299       ss << local_time() << " ";
300       if (is_dst()) {
301         ss << zone_->dst_zone_abbrev();
302       }
303       else {
304         ss << zone_->std_zone_abbrev();
305       }
306       return ss.str();
307     }
308     /*! returns a local_date_time_base in the given time zone with the
309      * optional time_duration added. */
local_time_in(boost::shared_ptr<tz_type> new_tz,time_duration_type td=time_duration_type (0,0,0)) const310     local_date_time_base local_time_in(boost::shared_ptr<tz_type> new_tz,
311                                        time_duration_type td=time_duration_type(0,0,0)) const
312     {
313       return local_date_time_base(utc_time_type(this->time_) + td, new_tz);
314     }
315 
316     //! Returns name of associated time zone or "Coordinated Universal Time".
317     /*! Optional bool parameter will return time zone as an offset
318      * (ie "+07:00" extended iso format). Empty string is returned for
319      * classes that do not use a time_zone */
zone_name(bool as_offset=false) const320     std::string zone_name(bool as_offset=false) const
321     {
322       if(zone_ == boost::shared_ptr<tz_type>()) {
323         if(as_offset) {
324           return std::string("Z");
325         }
326         else {
327           return std::string("Coordinated Universal Time");
328         }
329       }
330       if (is_dst()) {
331         if(as_offset) {
332           time_duration_type td = zone_->base_utc_offset();
333           td += zone_->dst_offset();
334           return zone_as_offset(td, ":");
335         }
336         else {
337           return zone_->dst_zone_name();
338         }
339       }
340       else {
341         if(as_offset) {
342           time_duration_type td = zone_->base_utc_offset();
343           return zone_as_offset(td, ":");
344         }
345         else {
346           return zone_->std_zone_name();
347         }
348       }
349     }
350     //! Returns abbreviation of associated time zone or "UTC".
351     /*! Optional bool parameter will return time zone as an offset
352      * (ie "+0700" iso format). Empty string is returned for classes
353      * that do not use a time_zone */
zone_abbrev(bool as_offset=false) const354     std::string zone_abbrev(bool as_offset=false) const
355     {
356       if(zone_ == boost::shared_ptr<tz_type>()) {
357         if(as_offset) {
358           return std::string("Z");
359         }
360         else {
361           return std::string("UTC");
362         }
363       }
364       if (is_dst()) {
365         if(as_offset) {
366           time_duration_type td = zone_->base_utc_offset();
367           td += zone_->dst_offset();
368           return zone_as_offset(td, "");
369         }
370         else {
371           return zone_->dst_zone_abbrev();
372         }
373       }
374       else {
375         if(as_offset) {
376           time_duration_type td = zone_->base_utc_offset();
377           return zone_as_offset(td, "");
378         }
379         else {
380           return zone_->std_zone_abbrev();
381         }
382       }
383     }
384 
385     //! returns a posix_time_zone string for the associated time_zone. If no time_zone, "UTC+00" is returned.
zone_as_posix_string() const386     std::string zone_as_posix_string() const
387     {
388       if(zone_ == shared_ptr<tz_type>()) {
389         return std::string("UTC+00");
390       }
391       return zone_->to_posix_string();
392     }
393 
394     //! Equality comparison operator
395     /*bool operator==(const date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>& rhs) const
396     { // fails due to rhs.time_ being protected
397       return date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>::operator==(rhs);
398       //return this->time_ == rhs.time_;
399     }*/
400     //! Equality comparison operator
operator ==(const local_date_time_base & rhs) const401     bool operator==(const local_date_time_base& rhs) const
402     {
403       return time_system_type::is_equal(this->time_, rhs.time_);
404     }
405     //! Non-Equality comparison operator
operator !=(const local_date_time_base & rhs) const406     bool operator!=(const local_date_time_base& rhs) const
407     {
408       return !(*this == rhs);
409     }
410     //! Less than comparison operator
operator <(const local_date_time_base & rhs) const411     bool operator<(const local_date_time_base& rhs) const
412     {
413       return time_system_type::is_less(this->time_, rhs.time_);
414     }
415     //! Less than or equal to comparison operator
operator <=(const local_date_time_base & rhs) const416     bool operator<=(const local_date_time_base& rhs) const
417     {
418       return (*this < rhs || *this == rhs);
419     }
420     //! Greater than comparison operator
operator >(const local_date_time_base & rhs) const421     bool operator>(const local_date_time_base& rhs) const
422     {
423       return !(*this <= rhs);
424     }
425     //! Greater than or equal to comparison operator
operator >=(const local_date_time_base & rhs) const426     bool operator>=(const local_date_time_base& rhs) const
427     {
428       return (*this > rhs || *this == rhs);
429     }
430 
431     //! Local_date_time + date_duration
operator +(const date_duration_type & dd) const432     local_date_time_base operator+(const date_duration_type& dd) const
433     {
434       return local_date_time_base(time_system_type::add_days(this->time_,dd), zone_);
435     }
436     //! Local_date_time += date_duration
operator +=(const date_duration_type & dd)437     local_date_time_base operator+=(const date_duration_type& dd)
438     {
439       this->time_ = time_system_type::add_days(this->time_,dd);
440       return *this;
441     }
442     //! Local_date_time - date_duration
operator -(const date_duration_type & dd) const443     local_date_time_base operator-(const date_duration_type& dd) const
444     {
445       return local_date_time_base(time_system_type::subtract_days(this->time_,dd), zone_);
446     }
447     //! Local_date_time -= date_duration
operator -=(const date_duration_type & dd)448     local_date_time_base operator-=(const date_duration_type& dd)
449     {
450       this->time_ = time_system_type::subtract_days(this->time_,dd);
451       return *this;
452     }
453     //! Local_date_time + time_duration
operator +(const time_duration_type & td) const454     local_date_time_base operator+(const time_duration_type& td) const
455     {
456       return local_date_time_base(time_system_type::add_time_duration(this->time_,td), zone_);
457     }
458     //! Local_date_time += time_duration
operator +=(const time_duration_type & td)459     local_date_time_base operator+=(const time_duration_type& td)
460     {
461       this->time_ = time_system_type::add_time_duration(this->time_,td);
462       return *this;
463     }
464     //! Local_date_time - time_duration
operator -(const time_duration_type & td) const465     local_date_time_base operator-(const time_duration_type& td) const
466     {
467       return local_date_time_base(time_system_type::subtract_time_duration(this->time_,td), zone_);
468     }
469     //! Local_date_time -= time_duration
operator -=(const time_duration_type & td)470     local_date_time_base operator-=(const time_duration_type& td)
471     {
472       this->time_ = time_system_type::subtract_time_duration(this->time_,td);
473       return *this;
474     }
475     //! local_date_time -= local_date_time --> time_duration_type
operator -(const local_date_time_base & rhs) const476     time_duration_type operator-(const local_date_time_base& rhs) const
477     {
478       return utc_time_type(this->time_) - utc_time_type(rhs.time_);
479     }
480   private:
481     boost::shared_ptr<tz_type> zone_;
482     //bool is_dst_;
483 
484     /*! Adjust the passed in time to UTC?
485      */
construction_adjustment(utc_time_type t,boost::shared_ptr<tz_type> z,bool dst_flag)486     utc_time_type construction_adjustment(utc_time_type t,
487                                           boost::shared_ptr<tz_type> z,
488                                           bool dst_flag)
489     {
490       if(z != boost::shared_ptr<tz_type>()) {
491         if(dst_flag && z->has_dst()) {
492           t -= z->dst_offset();
493         } // else no adjust
494         t -= z->base_utc_offset();
495       }
496       return t;
497     }
498 
499     /*! Simple formatting code -- todo remove this?
500      */
zone_as_offset(const time_duration_type & td,const std::string & separator) const501     std::string zone_as_offset(const time_duration_type& td,
502                                const std::string& separator) const
503     {
504       std::ostringstream ss;
505       if(td.is_negative()) {
506         // a negative duration is represented as "-[h]h:mm"
507         // we require two digits for the hour. A positive duration
508         // with the %H flag will always give two digits
509         ss << "-";
510       }
511       else {
512         ss << "+";
513       }
514       ss  << std::setw(2) << std::setfill('0')
515           << date_time::absolute_value(td.hours())
516           << separator
517           << std::setw(2) << std::setfill('0')
518           << date_time::absolute_value(td.minutes());
519       return ss.str();
520     }
521   };
522 
523   //!Use the default parameters to define local_date_time
524   typedef local_date_time_base<> local_date_time;
525 
526 } }
527 
528 
529 #endif
530