]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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: | |
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 | } | |
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 | ||
55 | dynamic_local_time_adjustor(time_duration_type utc_offset) : | |
56 | utc_offset_(utc_offset) | |
57 | {} | |
58 | ||
59 | //! Presumes local time | |
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 | */ | |
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 | |
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 | |
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 | |
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 |