1 #ifndef DATE_TIME_LOCAL_TIME_ADJUSTOR_HPP__ 2 #define DATE_TIME_LOCAL_TIME_ADJUSTOR_HPP__ 3 4 /* Copyright (c) 2002,2003 CrystalClear Software, Inc. 5 * Use, modification and distribution is subject to the 6 * Boost Software License, Version 1.0. (See accompanying 7 * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 8 * Author: Jeff Garland 9 * $Date$ 10 */ 11 12 /*! @file local_time_adjustor.hpp 13 Time adjustment calculations for local times 14 */ 15 16 #include <stdexcept> 17 #include <boost/throw_exception.hpp> 18 #include <boost/date_time/compiler_config.hpp> 19 #include <boost/date_time/date_generators.hpp> 20 #include <boost/date_time/dst_rules.hpp> 21 #include <boost/date_time/time_defs.hpp> // boost::date_time::dst_flags 22 #include <boost/date_time/special_defs.hpp> // not_a_date_time 23 24 namespace boost { 25 namespace date_time { 26 27 28 //! Provides a base offset adjustment from utc 29 template<class time_duration_type, 30 short hours, unsigned short minutes = 0> 31 class utc_adjustment 32 { 33 public: local_to_utc_base_offset()34 static time_duration_type local_to_utc_base_offset() 35 { 36 time_duration_type td(hours,minutes,0); 37 return td.invert_sign(); 38 } utc_to_local_base_offset()39 static time_duration_type utc_to_local_base_offset() 40 { 41 return time_duration_type(hours,minutes,0); 42 } 43 }; 44 45 46 47 //! Allow sliding utc adjustment with fixed dst rules 48 template<class time_type, class dst_rules> 49 class dynamic_local_time_adjustor : public dst_rules 50 { 51 public: 52 typedef typename time_type::time_duration_type time_duration_type; 53 typedef typename time_type::date_type date_type; 54 dynamic_local_time_adjustor(time_duration_type utc_offset)55 dynamic_local_time_adjustor(time_duration_type utc_offset) : 56 utc_offset_(utc_offset) 57 {} 58 59 //! Presumes local time utc_offset(bool is_dst)60 time_duration_type utc_offset(bool is_dst) 61 { 62 if (is_dst) { 63 return utc_offset_ + this->dst_offset(); 64 } 65 else { 66 return utc_offset_; 67 } 68 69 } 70 private: 71 time_duration_type utc_offset_; 72 73 }; 74 75 76 77 //! Embed the rules for local time adjustments at compile time 78 template<class time_type, class dst_rules, class utc_offset_rules> 79 class static_local_time_adjustor: public dst_rules, public utc_offset_rules 80 { 81 public: 82 typedef typename time_type::time_duration_type time_duration_type; 83 typedef typename time_type::date_type date_type; 84 85 //! Calculates the offset from a utc time to local based on dst and utc offset 86 /*! @param t UTC time to calculate offset to local time 87 * This adjustment depends on the following observations about the 88 * workings of the DST boundary offset. Since UTC time labels are 89 * monotonically increasing we can determine if a given local time 90 * is in DST or not and therefore adjust the offset appropriately. 91 * 92 * The logic is as follows. Starting with UTC time use the offset to 93 * create a label for an non-dst adjusted local time. Then call 94 * dst_rules::local_is_dst with the non adjust local time. The 95 * results of this function will either unabiguously decide that 96 * the initial local time is in dst or return an illegal or 97 * ambiguous result. An illegal result only occurs at the end 98 * of dst (where labels are skipped) and indicates that dst has 99 * ended. An ambiguous result means that we need to recheck by 100 * making a dst adjustment and then rechecking. If the dst offset 101 * is added to the utc time and the recheck proves non-ambiguous 102 * then we are past the boundary. If it is still ambiguous then 103 * we are ahead of the boundary and dst is still in effect. 104 * 105 * TODO -- check if all dst offsets are positive. If not then 106 * the algorithm needs to check for this and reverse the 107 * illegal/ambiguous logic. 108 */ utc_to_local_offset(const time_type & t)109 static time_duration_type utc_to_local_offset(const time_type& t) 110 { 111 //get initial local time guess by applying utc offset 112 time_type initial = t + utc_offset_rules::utc_to_local_base_offset(); 113 time_is_dst_result dst_flag = 114 dst_rules::local_is_dst(initial.date(), initial.time_of_day()); 115 switch(dst_flag) { 116 case is_in_dst: return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); 117 case is_not_in_dst: return utc_offset_rules::utc_to_local_base_offset(); 118 case invalid_time_label:return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); 119 case ambiguous: { 120 time_type retry = initial + dst_rules::dst_offset(); 121 dst_flag = dst_rules::local_is_dst(retry.date(), retry.time_of_day()); 122 //if still ambibuous then the utc time still translates to a dst time 123 if (dst_flag == ambiguous) { 124 return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); 125 } 126 // we are past the dst boundary 127 else { 128 return utc_offset_rules::utc_to_local_base_offset(); 129 } 130 } 131 }//case 132 //TODO better exception type 133 boost::throw_exception(std::out_of_range("Unreachable case")); 134 BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return time_duration_type(not_a_date_time)); // should never reach 135 } 136 137 //! Get the offset to UTC given a local time local_to_utc_offset(const time_type & t,date_time::dst_flags dst=date_time::calculate)138 static time_duration_type local_to_utc_offset(const time_type& t, 139 date_time::dst_flags dst=date_time::calculate) 140 { 141 switch (dst) { 142 case is_dst: 143 return utc_offset_rules::local_to_utc_base_offset() - dst_rules::dst_offset(); 144 case not_dst: 145 return utc_offset_rules::local_to_utc_base_offset(); 146 case calculate: 147 time_is_dst_result res = 148 dst_rules::local_is_dst(t.date(), t.time_of_day()); 149 switch(res) { 150 case is_in_dst: return utc_offset_rules::local_to_utc_base_offset() - dst_rules::dst_offset(); 151 case is_not_in_dst: return utc_offset_rules::local_to_utc_base_offset(); 152 case ambiguous: return utc_offset_rules::local_to_utc_base_offset(); 153 case invalid_time_label: break; 154 } 155 } 156 boost::throw_exception(std::out_of_range("Time label invalid")); 157 BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return time_duration_type(not_a_date_time)); // should never reach 158 } 159 160 161 private: 162 163 }; 164 165 void dummy_to_prevent_msvc6_ice(); //why ask why? 166 167 //! Template that simplifies the creation of local time calculator 168 /*! Use this template to create the timezone to utc convertors as required. 169 * 170 * This class will also work for other regions that don't use dst and 171 * have a utc offset which is an integral number of hours. 172 * 173 * <b>Template Parameters</b> 174 * -time_type -- Time class to use 175 * -utc_offset -- Number hours local time is adjust from utc 176 * -use_dst -- true (default) if region uses dst, false otherwise 177 * For example: 178 * @code 179 * //eastern timezone is utc-5 180 typedef date_time::local_adjustor<ptime, -5, us_dst> us_eastern; 181 typedef date_time::local_adjustor<ptime, -6, us_dst> us_central; 182 typedef date_time::local_adjustor<ptime, -7, us_dst> us_mountain; 183 typedef date_time::local_adjustor<ptime, -8, us_dst> us_pacific; 184 typedef date_time::local_adjustor<ptime, -7, no_dst> us_arizona; 185 @endcode 186 187 */ 188 template<class time_type, short utc_offset, class dst_rule> 189 class local_adjustor 190 { 191 public: 192 typedef typename time_type::time_duration_type time_duration_type; 193 typedef typename time_type::date_type date_type; 194 typedef static_local_time_adjustor<time_type, 195 dst_rule, 196 utc_adjustment<time_duration_type, 197 utc_offset> > dst_adjustor; 198 //! Convert a utc time to local time utc_to_local(const time_type & t)199 static time_type utc_to_local(const time_type& t) 200 { 201 time_duration_type td = dst_adjustor::utc_to_local_offset(t); 202 return t + td; 203 } 204 //! Convert a local time to utc local_to_utc(const time_type & t,date_time::dst_flags dst=date_time::calculate)205 static time_type local_to_utc(const time_type& t, 206 date_time::dst_flags dst=date_time::calculate) 207 { 208 time_duration_type td = dst_adjustor::local_to_utc_offset(t, dst); 209 return t + td; 210 } 211 }; 212 213 214 } } //namespace date_time 215 216 217 218 #endif 219