source: NonGTP/Boost/boost/date_time/local_time/local_date_time.hpp @ 857

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