fix doc example typo
[boost.git] / boost / date_time / local_time / local_date_time.hpp
blob3f1a17cd5db9e6c3d026a569f93d48f2f1c8dd36
1 #ifndef LOCAL_TIME_LOCAL_DATE_TIME_HPP__
2 #define LOCAL_TIME_LOCAL_DATE_TIME_HPP__
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 */
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/dst_rules.hpp>
20 #include <boost/date_time/time_zone_base.hpp>
21 #include <boost/date_time/special_defs.hpp>
23 namespace boost {
24 namespace local_time {
26 //! simple exception for reporting when STD or DST cannot be determined
27 struct ambiguous_result : public std::logic_error
29 ambiguous_result (std::string const& msg = std::string()) :
30 std::logic_error(std::string("Daylight Savings Results are ambiguous: " + msg)) {}
32 //! simple exception for when time label given cannot exist
33 struct time_label_invalid : public std::logic_error
35 time_label_invalid (std::string const& msg = std::string()) :
36 std::logic_error(std::string("Time label given is invalid: " + msg)) {}
38 struct dst_not_valid: public std::logic_error
40 dst_not_valid(std::string const& msg = std::string()) :
41 std::logic_error(std::string("is_dst flag does not match resulting dst for time label given: " + msg)) {}
44 //TODO: I think these should be in local_date_time_base and not
45 // necessarily brought into the namespace
46 using date_time::time_is_dst_result;
47 using date_time::is_in_dst;
48 using date_time::is_not_in_dst;
49 using date_time::ambiguous;
50 using date_time::invalid_time_label;
52 //! Representation of "wall-clock" time in a particular time zone
53 /*! Representation of "wall-clock" time in a particular time zone
54 * Local_date_time_base holds a time value (date and time offset from 00:00)
55 * along with a time zone. The time value is stored as UTC and conversions
56 * to wall clock time are made as needed. This approach allows for
57 * operations between wall-clock times in different time zones, and
58 * daylight savings time considerations, to be made. Time zones are
59 * required to be in the form of a boost::shared_ptr<time_zone_base>.
61 template<class utc_time_=posix_time::ptime,
62 class tz_type=date_time::time_zone_base<utc_time_,char> >
63 class local_date_time_base : public date_time::base_time<utc_time_,
64 boost::posix_time::posix_time_system> {
65 public:
66 typedef utc_time_ utc_time_type;
67 typedef typename utc_time_type::time_duration_type time_duration_type;
68 typedef typename utc_time_type::date_type date_type;
69 typedef typename date_type::duration_type date_duration_type;
70 typedef typename utc_time_type::time_system_type time_system_type;
71 /*! This constructor interprets the passed time as a UTC time.
72 * So, for example, if the passed timezone is UTC-5 then the
73 * time will be adjusted back 5 hours. The time zone allows for
74 * automatic calculation of whether the particular time is adjusted for
75 * daylight savings, etc.
76 * If the time zone shared pointer is null then time stays unadjusted.
77 *@param t A UTC time
78 *@param tz Timezone for to adjust the UTC time to.
80 local_date_time_base(utc_time_type t,
81 boost::shared_ptr<tz_type> tz) :
82 date_time::base_time<utc_time_type, time_system_type>(t),
83 zone_(tz)
85 // param was already utc so nothing more to do
88 /*! This constructs a local time -- the passed time information
89 * understood to be in the passed tz. The DST flag must be passed
90 * to indicate whether the time is in daylight savings or not.
91 * @throws -- time_label_invalid if the time passed does not exist in
92 * the given locale. The non-existent case occurs typically
93 * during the shift-back from daylight savings time. When
94 * the clock is shifted forward a range of times
95 * (2 am to 3 am in the US) is skipped and hence is invalid.
96 * @throws -- dst_not_valid if the DST flag is passed for a period
97 * where DST is not active.
99 local_date_time_base(date_type d,
100 time_duration_type td,
101 boost::shared_ptr<tz_type> tz,
102 bool dst_flag) : //necessary for constr_adj()
103 date_time::base_time<utc_time_type,time_system_type>(construction_adjustment(utc_time_type(d, td), tz, dst_flag)),
104 zone_(tz)
106 if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()){
108 // d & td are already local so we use them
109 time_is_dst_result result = check_dst(d, td, tz);
110 bool in_dst = (result == is_in_dst); // less processing than is_dst()
112 // ambig occurs at end, invalid at start
113 if(result == invalid_time_label){
114 // Ex: 2:15am local on trans-in day in nyc, dst_flag irrelevant
115 std::ostringstream ss;
116 ss << "time given: " << d << ' ' << td;
117 boost::throw_exception(time_label_invalid(ss.str()));
119 else if(result != ambiguous && in_dst != dst_flag){
120 // is dst_flag accurate?
121 // Ex: false flag in NYC in June
122 std::ostringstream ss;
123 ss.setf(std::ios_base::boolalpha);
124 ss << "flag given: dst=" << dst_flag << ", dst calculated: dst=" << in_dst;
125 boost::throw_exception(dst_not_valid(ss.str()));
128 // everything checks out and conversion to utc already done
132 //TODO maybe not the right set...Ignore the last 2 for now...
133 enum DST_CALC_OPTIONS { EXCEPTION_ON_ERROR, NOT_DATE_TIME_ON_ERROR };
134 //ASSUME_DST_ON_ERROR, ASSUME_NOT_DST_ON_ERROR };
136 /*! This constructs a local time -- the passed time information
137 * understood to be in the passed tz. The DST flag is calculated
138 * according to the specified rule.
140 local_date_time_base(date_type d,
141 time_duration_type td,
142 boost::shared_ptr<tz_type> tz,
143 DST_CALC_OPTIONS calc_option) :
144 // dummy value - time_ is set in constructor code
145 date_time::base_time<utc_time_type,time_system_type>(utc_time_type(d,td)),
146 zone_(tz)
148 time_is_dst_result result = check_dst(d, td, tz);
149 if(result == ambiguous) {
150 if(calc_option == EXCEPTION_ON_ERROR){
151 std::ostringstream ss;
152 ss << "time given: " << d << ' ' << td;
153 boost::throw_exception(ambiguous_result(ss.str()));
155 else{ // NADT on error
156 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 else if(result == invalid_time_label){
160 if(calc_option == EXCEPTION_ON_ERROR){
161 std::ostringstream ss;
162 ss << "time given: " << d << ' ' << td;
163 boost::throw_exception(time_label_invalid(ss.str()));
165 else{ // NADT on error
166 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 else if(result == is_in_dst){
170 utc_time_type t =
171 construction_adjustment(utc_time_type(d, td), tz, true);
172 this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
173 t.time_of_day());
175 else{
176 utc_time_type t =
177 construction_adjustment(utc_time_type(d, td), tz, false);
178 this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
179 t.time_of_day());
184 //! Determines if given time label is in daylight savings for given zone
185 /*! Determines if given time label is in daylight savings for given zone.
186 * Takes a date and time_duration representing a local time, along
187 * with time zone, and returns a time_is_dst_result object as result.
189 static time_is_dst_result check_dst(date_type d,
190 time_duration_type td,
191 boost::shared_ptr<tz_type> tz)
193 if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()) {
194 typedef typename date_time::dst_calculator<date_type, time_duration_type> dst_calculator;
195 return dst_calculator::local_is_dst(
196 d, td,
197 tz->dst_local_start_time(d.year()).date(),
198 tz->dst_local_start_time(d.year()).time_of_day(),
199 tz->dst_local_end_time(d.year()).date(),
200 tz->dst_local_end_time(d.year()).time_of_day(),
201 tz->dst_offset()
204 else{
205 return is_not_in_dst;
209 //! Simple destructor, releases time zone if last referrer
210 ~local_date_time_base() {};
212 //! Copy constructor
213 local_date_time_base(const local_date_time_base& rhs) :
214 date_time::base_time<utc_time_type, time_system_type>(rhs),
215 zone_(rhs.zone_)
218 //! Special values constructor
219 explicit local_date_time_base(const boost::date_time::special_values sv,
220 boost::shared_ptr<tz_type> tz = boost::shared_ptr<tz_type>()) :
221 date_time::base_time<utc_time_type, time_system_type>(utc_time_type(sv)),
222 zone_(tz)
225 //! returns time zone associated with calling instance
226 boost::shared_ptr<tz_type> zone() const
228 return zone_;
230 //! returns false is time_zone is NULL and if time value is a special_value
231 bool is_dst() const
233 if(zone_ != boost::shared_ptr<tz_type>() && zone_->has_dst() && !this->is_special()) {
234 // check_dst takes a local time, *this is utc
235 utc_time_type lt(this->time_);
236 lt += zone_->base_utc_offset();
237 // dst_offset only needs to be considered with ambiguous time labels
238 // make that adjustment there
240 switch(check_dst(lt.date(), lt.time_of_day(), zone_)){
241 case is_not_in_dst:
242 return false;
243 case is_in_dst:
244 return true;
245 case ambiguous:
246 if(lt + zone_->dst_offset() < zone_->dst_local_end_time(lt.date().year())) {
247 return true;
249 break;
250 case invalid_time_label:
251 if(lt >= zone_->dst_local_start_time(lt.date().year())) {
252 return true;
254 break;
257 return false;
259 //! Returns object's time value as a utc representation
260 utc_time_type utc_time() const
262 return utc_time_type(this->time_);
264 //! Returns object's time value as a local representation
265 utc_time_type local_time() const
267 if(zone_ != boost::shared_ptr<tz_type>()){
268 utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
269 if (is_dst()) {
270 lt += zone_->dst_offset();
272 return lt;
274 return utc_time_type(this->time_);
276 //! Returns string in the form "2003-Aug-20 05:00:00 EDT"
277 /*! Returns string in the form "2003-Aug-20 05:00:00 EDT". If
278 * time_zone is NULL the time zone abbreviation will be "UTC". The time
279 * zone abbrev will not be included if calling object is a special_value*/
280 std::string to_string() const
282 //TODO is this a temporary function ???
283 std::ostringstream ss;
284 if(this->is_special()){
285 ss << utc_time();
286 return ss.str();
288 if(zone_ == boost::shared_ptr<tz_type>()) {
289 ss << utc_time() << " UTC";
290 return ss.str();
292 bool is_dst_ = is_dst();
293 utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
294 if (is_dst_) {
295 lt += zone_->dst_offset();
297 ss << local_time() << " ";
298 if (is_dst()) {
299 ss << zone_->dst_zone_abbrev();
301 else {
302 ss << zone_->std_zone_abbrev();
304 return ss.str();
306 /*! returns a local_date_time_base in the given time zone with the
307 * optional time_duration added. */
308 local_date_time_base local_time_in(boost::shared_ptr<tz_type> new_tz,
309 time_duration_type td=time_duration_type(0,0,0)) const
311 return local_date_time_base(utc_time_type(this->time_) + td, new_tz);
314 //! Returns name of associated time zone or "Coordinated Universal Time".
315 /*! Optional bool parameter will return time zone as an offset
316 * (ie "+07:00" extended iso format). Empty string is returned for
317 * classes that do not use a time_zone */
318 std::string zone_name(bool as_offset=false) const
320 if(zone_ == boost::shared_ptr<tz_type>()) {
321 if(as_offset) {
322 return std::string("Z");
324 else {
325 return std::string("Coordinated Universal Time");
328 if (is_dst()) {
329 if(as_offset) {
330 time_duration_type td = zone_->base_utc_offset();
331 td += zone_->dst_offset();
332 return zone_as_offset(td, ":");
334 else {
335 return zone_->dst_zone_name();
338 else {
339 if(as_offset) {
340 time_duration_type td = zone_->base_utc_offset();
341 return zone_as_offset(td, ":");
343 else {
344 return zone_->std_zone_name();
348 //! Returns abbreviation of associated time zone or "UTC".
349 /*! Optional bool parameter will return time zone as an offset
350 * (ie "+0700" iso format). Empty string is returned for classes
351 * that do not use a time_zone */
352 std::string zone_abbrev(bool as_offset=false) const
354 if(zone_ == boost::shared_ptr<tz_type>()) {
355 if(as_offset) {
356 return std::string("Z");
358 else {
359 return std::string("UTC");
362 if (is_dst()) {
363 if(as_offset) {
364 time_duration_type td = zone_->base_utc_offset();
365 td += zone_->dst_offset();
366 return zone_as_offset(td, "");
368 else {
369 return zone_->dst_zone_abbrev();
372 else {
373 if(as_offset) {
374 time_duration_type td = zone_->base_utc_offset();
375 return zone_as_offset(td, "");
377 else {
378 return zone_->std_zone_abbrev();
383 //! returns a posix_time_zone string for the associated time_zone. If no time_zone, "UTC+00" is returned.
384 std::string zone_as_posix_string() const
386 if(zone_ == shared_ptr<tz_type>()) {
387 return std::string("UTC+00");
389 return zone_->to_posix_string();
392 //! Equality comparison operator
393 /*bool operator==(const date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>& rhs) const
394 { // fails due to rhs.time_ being protected
395 return date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>::operator==(rhs);
396 //return this->time_ == rhs.time_;
398 //! Equality comparison operator
399 bool operator==(const local_date_time_base& rhs) const
401 return time_system_type::is_equal(this->time_, rhs.time_);
403 //! Non-Equality comparison operator
404 bool operator!=(const local_date_time_base& rhs) const
406 return !(*this == rhs);
408 //! Less than comparison operator
409 bool operator<(const local_date_time_base& rhs) const
411 return time_system_type::is_less(this->time_, rhs.time_);
413 //! Less than or equal to comparison operator
414 bool operator<=(const local_date_time_base& rhs) const
416 return (*this < rhs || *this == rhs);
418 //! Greater than comparison operator
419 bool operator>(const local_date_time_base& rhs) const
421 return !(*this <= rhs);
423 //! Greater than or equal to comparison operator
424 bool operator>=(const local_date_time_base& rhs) const
426 return (*this > rhs || *this == rhs);
429 //! Local_date_time + date_duration
430 local_date_time_base operator+(const date_duration_type& dd) const
432 return local_date_time_base(time_system_type::add_days(this->time_,dd), zone_);
434 //! Local_date_time += date_duration
435 local_date_time_base operator+=(const date_duration_type& dd)
437 this->time_ = time_system_type::add_days(this->time_,dd);
438 return *this;
440 //! Local_date_time - date_duration
441 local_date_time_base operator-(const date_duration_type& dd) const
443 return local_date_time_base(time_system_type::subtract_days(this->time_,dd), zone_);
445 //! Local_date_time -= date_duration
446 local_date_time_base operator-=(const date_duration_type& dd)
448 this->time_ = time_system_type::subtract_days(this->time_,dd);
449 return *this;
451 //! Local_date_time + time_duration
452 local_date_time_base operator+(const time_duration_type& td) const
454 return local_date_time_base(time_system_type::add_time_duration(this->time_,td), zone_);
456 //! Local_date_time += time_duration
457 local_date_time_base operator+=(const time_duration_type& td)
459 this->time_ = time_system_type::add_time_duration(this->time_,td);
460 return *this;
462 //! Local_date_time - time_duration
463 local_date_time_base operator-(const time_duration_type& td) const
465 return local_date_time_base(time_system_type::subtract_time_duration(this->time_,td), zone_);
467 //! Local_date_time -= time_duration
468 local_date_time_base operator-=(const time_duration_type& td)
470 this->time_ = time_system_type::subtract_time_duration(this->time_,td);
471 return *this;
473 //! local_date_time -= local_date_time --> time_duration_type
474 time_duration_type operator-(const local_date_time_base& rhs) const
476 return utc_time_type(this->time_) - utc_time_type(rhs.time_);
478 private:
479 boost::shared_ptr<tz_type> zone_;
480 //bool is_dst_;
482 /*! Adjust the passed in time to UTC?
484 utc_time_type construction_adjustment(utc_time_type t,
485 boost::shared_ptr<tz_type> z,
486 bool dst_flag)
488 if(z != boost::shared_ptr<tz_type>()) {
489 if(dst_flag && z->has_dst()) {
490 t -= z->dst_offset();
491 } // else no adjust
492 t -= z->base_utc_offset();
494 return t;
497 /*! Simple formatting code -- todo remove this?
499 std::string zone_as_offset(const time_duration_type& td,
500 const std::string& separator) const
502 std::ostringstream ss;
503 if(td.is_negative()) {
504 // a negative duration is represented as "-[h]h:mm"
505 // we require two digits for the hour. A positive duration
506 // with the %H flag will always give two digits
507 ss << "-";
509 else {
510 ss << "+";
512 ss << std::setw(2) << std::setfill('0')
513 << date_time::absolute_value(td.hours())
514 << separator
515 << std::setw(2) << std::setfill('0')
516 << date_time::absolute_value(td.minutes());
517 return ss.str();
521 //!Use the default parameters to define local_date_time
522 typedef local_date_time_base<> local_date_time;
527 #endif