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
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>
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
> {
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.
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
),
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
)),
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
)),
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
){
171 construction_adjustment(utc_time_type(d
, td
), tz
, true);
172 this->time_
= posix_time::posix_time_system::get_time_rep(t
.date(),
177 construction_adjustment(utc_time_type(d
, td
), tz
, false);
178 this->time_
= posix_time::posix_time_system::get_time_rep(t
.date(),
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(
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(),
205 return is_not_in_dst
;
209 //! Simple destructor, releases time zone if last referrer
210 ~local_date_time_base() {};
213 local_date_time_base(const local_date_time_base
& rhs
) :
214 date_time::base_time
<utc_time_type
, time_system_type
>(rhs
),
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
)),
225 //! returns time zone associated with calling instance
226 boost::shared_ptr
<tz_type
> zone() const
230 //! returns false is time_zone is NULL and if time value is a special_value
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_
)){
246 if(lt
+ zone_
->dst_offset() < zone_
->dst_local_end_time(lt
.date().year())) {
250 case invalid_time_label
:
251 if(lt
>= zone_
->dst_local_start_time(lt
.date().year())) {
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();
270 lt
+= zone_
->dst_offset();
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()){
288 if(zone_
== boost::shared_ptr
<tz_type
>()) {
289 ss
<< utc_time() << " UTC";
292 bool is_dst_
= is_dst();
293 utc_time_type lt
= this->utc_time() + zone_
->base_utc_offset();
295 lt
+= zone_
->dst_offset();
297 ss
<< local_time() << " ";
299 ss
<< zone_
->dst_zone_abbrev();
302 ss
<< zone_
->std_zone_abbrev();
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
>()) {
322 return std::string("Z");
325 return std::string("Coordinated Universal Time");
330 time_duration_type td
= zone_
->base_utc_offset();
331 td
+= zone_
->dst_offset();
332 return zone_as_offset(td
, ":");
335 return zone_
->dst_zone_name();
340 time_duration_type td
= zone_
->base_utc_offset();
341 return zone_as_offset(td
, ":");
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
>()) {
356 return std::string("Z");
359 return std::string("UTC");
364 time_duration_type td
= zone_
->base_utc_offset();
365 td
+= zone_
->dst_offset();
366 return zone_as_offset(td
, "");
369 return zone_
->dst_zone_abbrev();
374 time_duration_type td
= zone_
->base_utc_offset();
375 return zone_as_offset(td
, "");
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
);
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
);
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
);
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
);
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_
);
479 boost::shared_ptr
<tz_type
> zone_
;
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
,
488 if(z
!= boost::shared_ptr
<tz_type
>()) {
489 if(dst_flag
&& z
->has_dst()) {
490 t
-= z
->dst_offset();
492 t
-= z
->base_utc_offset();
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
512 ss
<< std::setw(2) << std::setfill('0')
513 << date_time::absolute_value(td
.hours())
515 << std::setw(2) << std::setfill('0')
516 << date_time::absolute_value(td
.minutes());
521 //!Use the default parameters to define local_date_time
522 typedef local_date_time_base
<> local_date_time
;