1 #ifndef DATE_TIME_DST_RULES_HPP__
2 #define DATE_TIME_DST_RULES_HPP__
4 /* Copyright (c) 2002,2003, 2007 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, Bart Garst
12 /*! @file dst_rules.hpp
13 Contains template class to provide static dst rule calculations
16 #include "boost/date_time/date_generators.hpp"
17 #include "boost/date_time/period.hpp"
18 #include "boost/date_time/date_defs.hpp"
24 enum time_is_dst_result
{is_not_in_dst
, is_in_dst
,
25 ambiguous
, invalid_time_label
};
28 //! Dynamic class used to caluclate dst transition information
29 template<class date_type_
,
30 class time_duration_type_
>
34 typedef time_duration_type_ time_duration_type
;
35 typedef date_type_ date_type
;
37 //! Check the local time offset when on dst start day
38 /*! On this dst transition, the time label between
39 * the transition boundary and the boudary + the offset
40 * are invalid times. If before the boundary then still
42 *@param time_of_day Time offset in the day for the local time
43 *@param dst_start_offset_minutes Local day offset for start of dst
44 *@param dst_length_minutes Number of minutes to adjust clock forward
45 *@retval status of time label w.r.t. dst
47 static time_is_dst_result
48 process_local_dst_start_day(const time_duration_type
& time_of_day
,
49 unsigned int dst_start_offset_minutes
,
50 long dst_length_minutes
)
52 //std::cout << "here" << std::endl;
53 if (time_of_day
< time_duration_type(0,dst_start_offset_minutes
,0)) {
56 long offset
= dst_start_offset_minutes
+ dst_length_minutes
;
57 if (time_of_day
>= time_duration_type(0,offset
,0)) {
60 return invalid_time_label
;
63 //! Check the local time offset when on the last day of dst
64 /*! This is the calculation for the DST end day. On that day times
65 * prior to the conversion time - dst_length (1 am in US) are still
66 * in dst. Times between the above and the switch time are
67 * ambiguous. Times after the start_offset are not in dst.
68 *@param time_of_day Time offset in the day for the local time
69 *@param dst_end_offset_minutes Local time of day for end of dst
70 *@retval status of time label w.r.t. dst
72 static time_is_dst_result
73 process_local_dst_end_day(const time_duration_type
& time_of_day
,
74 unsigned int dst_end_offset_minutes
,
75 long dst_length_minutes
)
77 //in US this will be 60 so offset in day is 1,0,0
78 int offset
= dst_end_offset_minutes
-dst_length_minutes
;
79 if (time_of_day
< time_duration_type(0,offset
,0)) {
82 if (time_of_day
>= time_duration_type(0,dst_end_offset_minutes
,0)) {
88 //! Calculates if the given local time is dst or not
89 /*! Determines if the time is really in DST or not. Also checks for
90 * invalid and ambiguous.
91 * @param current_day The day to check for dst
92 * @param time_of_day Time offset within the day to check
93 * @param dst_start_day Starting day of dst for the given locality
94 * @param dst_start_offset Time offset within day for dst boundary
95 * @param dst_end_day Ending day of dst for the given locality
96 * @param dst_end_offset Time offset within day given in dst for dst boundary
97 * @param dst_length lenght of dst adjusment
98 * @retval The time is either ambiguous, invalid, in dst, or not in dst
100 static time_is_dst_result
101 local_is_dst(const date_type
& current_day
,
102 const time_duration_type
& time_of_day
,
103 const date_type
& dst_start_day
,
104 const time_duration_type
& dst_start_offset
,
105 const date_type
& dst_end_day
,
106 const time_duration_type
& dst_end_offset
,
107 const time_duration_type
& dst_length_minutes
)
109 unsigned int start_minutes
=
110 dst_start_offset
.hours() * 60 + dst_start_offset
.minutes();
111 unsigned int end_minutes
=
112 dst_end_offset
.hours() * 60 + dst_end_offset
.minutes();
113 long length_minutes
=
114 dst_length_minutes
.hours() * 60 + dst_length_minutes
.minutes();
116 return local_is_dst(current_day
, time_of_day
,
117 dst_start_day
, start_minutes
,
118 dst_end_day
, end_minutes
,
122 //! Calculates if the given local time is dst or not
123 /*! Determines if the time is really in DST or not. Also checks for
124 * invalid and ambiguous.
125 * @param current_day The day to check for dst
126 * @param time_of_day Time offset within the day to check
127 * @param dst_start_day Starting day of dst for the given locality
128 * @param dst_start_offset_minutes Offset within day for dst
129 * boundary (eg 120 for US which is 02:00:00)
130 * @param dst_end_day Ending day of dst for the given locality
131 * @param dst_end_offset_minutes Offset within day given in dst for dst
132 * boundary (eg 120 for US which is 02:00:00)
133 * @param dst_length_minutes Length of dst adjusment (eg: 60 for US)
134 * @retval The time is either ambiguous, invalid, in dst, or not in dst
136 static time_is_dst_result
137 local_is_dst(const date_type
& current_day
,
138 const time_duration_type
& time_of_day
,
139 const date_type
& dst_start_day
,
140 unsigned int dst_start_offset_minutes
,
141 const date_type
& dst_end_day
,
142 unsigned int dst_end_offset_minutes
,
143 long dst_length_minutes
)
145 //in northern hemisphere dst is in the middle of the year
146 if (dst_start_day
< dst_end_day
) {
147 if ((current_day
> dst_start_day
) && (current_day
< dst_end_day
)) {
150 if ((current_day
< dst_start_day
) || (current_day
> dst_end_day
)) {
151 return is_not_in_dst
;
154 else {//southern hemisphere dst is at begining /end of year
155 if ((current_day
< dst_start_day
) && (current_day
> dst_end_day
)) {
156 return is_not_in_dst
;
158 if ((current_day
> dst_start_day
) || (current_day
< dst_end_day
)) {
163 if (current_day
== dst_start_day
) {
164 return process_local_dst_start_day(time_of_day
,
165 dst_start_offset_minutes
,
169 if (current_day
== dst_end_day
) {
170 return process_local_dst_end_day(time_of_day
,
171 dst_end_offset_minutes
,
174 //you should never reach this statement
175 return invalid_time_label
;
181 //! Compile-time configurable daylight savings time calculation engine
182 /* This template provides the ability to configure a daylight savings
183 * calculation at compile time covering all the cases. Unfortunately
184 * because of the number of dimensions related to daylight savings
185 * calculation the number of parameters is high. In addition, the
186 * start and end transition rules are complex types that specify
187 * an algorithm for calculation of the starting day and ending
188 * day of daylight savings time including the month and day
189 * specifications (eg: last sunday in October).
191 * @param date_type A type that represents dates, typically gregorian::date
192 * @param time_duration_type Used for the offset in the day calculations
193 * @param dst_traits A set of traits that define the rules of dst
194 * calculation. The dst_trait must include the following:
195 * start_rule_functor - Rule to calculate the starting date of a
196 * dst transition (eg: last_kday_of_month).
197 * start_day - static function that returns month of dst start for
199 * start_month -static function that returns day or day of week for
201 * end_rule_functor - Rule to calculate the end of dst day.
202 * end_day - static fucntion that returns end day for end_rule_functor
203 * end_month - static function that returns end month for end_rule_functor
204 * dst_start_offset_minutes - number of minutes from start of day to transition to dst -- 120 (or 2:00 am) is typical for the U.S. and E.U.
205 * dst_start_offset_minutes - number of minutes from start of day to transition off of dst -- 180 (or 3:00 am) is typical for E.U.
206 * dst_length_minutes - number of minutes that dst shifts clock
208 template<class date_type
,
209 class time_duration_type
,
211 class dst_calc_engine
214 typedef typename
date_type::year_type year_type
;
215 typedef typename
date_type::calendar_type calendar_type
;
216 typedef dst_calculator
<date_type
, time_duration_type
> dstcalc
;
218 //! Calculates if the given local time is dst or not
219 /*! Determines if the time is really in DST or not. Also checks for
220 * invalid and ambiguous.
221 * @retval The time is either ambiguous, invalid, in dst, or not in dst
223 static time_is_dst_result
local_is_dst(const date_type
& d
,
224 const time_duration_type
& td
)
227 year_type y
= d
.year();
228 date_type dst_start
= local_dst_start_day(y
);
229 date_type dst_end
= local_dst_end_day(y
);
230 return dstcalc::local_is_dst(d
,td
,
232 dst_traits::dst_start_offset_minutes(),
234 dst_traits::dst_end_offset_minutes(),
235 dst_traits::dst_shift_length_minutes());
239 static bool is_dst_boundary_day(date_type d
)
241 year_type y
= d
.year();
242 return ((d
== local_dst_start_day(y
)) ||
243 (d
== local_dst_end_day(y
)));
246 //! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00)
247 static time_duration_type
dst_offset()
249 return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0);
252 static date_type
local_dst_start_day(year_type year
)
254 return dst_traits::local_dst_start_day(year
);
257 static date_type
local_dst_end_day(year_type year
)
259 return dst_traits::local_dst_end_day(year
);
265 //! Depricated: Class to calculate dst boundaries for US time zones
266 /* Use dst_calc_engine instead.
267 * In 2007 US/Canada DST rules changed
268 * (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time).
270 template<class date_type_
,
271 class time_duration_type_
,
272 unsigned int dst_start_offset_minutes
=120, //from start of day
273 short dst_length_minutes
=60> //1 hour == 60 min in US
277 typedef time_duration_type_ time_duration_type
;
278 typedef date_type_ date_type
;
279 typedef typename
date_type::year_type year_type
;
280 typedef typename
date_type::calendar_type calendar_type
;
281 typedef date_time::last_kday_of_month
<date_type
> lkday
;
282 typedef date_time::first_kday_of_month
<date_type
> fkday
;
283 typedef date_time::nth_kday_of_month
<date_type
> nkday
;
284 typedef dst_calculator
<date_type
, time_duration_type
> dstcalc
;
286 //! Calculates if the given local time is dst or not
287 /*! Determines if the time is really in DST or not. Also checks for
288 * invalid and ambiguous.
289 * @retval The time is either ambiguous, invalid, in dst, or not in dst
291 static time_is_dst_result
local_is_dst(const date_type
& d
,
292 const time_duration_type
& td
)
295 year_type y
= d
.year();
296 date_type dst_start
= local_dst_start_day(y
);
297 date_type dst_end
= local_dst_end_day(y
);
298 return dstcalc::local_is_dst(d
,td
,
299 dst_start
,dst_start_offset_minutes
,
300 dst_end
, dst_start_offset_minutes
,
306 static bool is_dst_boundary_day(date_type d
)
308 year_type y
= d
.year();
309 return ((d
== local_dst_start_day(y
)) ||
310 (d
== local_dst_end_day(y
)));
313 static date_type
local_dst_start_day(year_type year
)
315 if (year
>= year_type(2007)) {
316 //second sunday in march
317 nkday
ssim(nkday::second
, Sunday
, gregorian::Mar
);
318 return ssim
.get_date(year
);
320 //first sunday in april
321 fkday
fsia(Sunday
, gregorian::Apr
);
322 return fsia
.get_date(year
);
326 static date_type
local_dst_end_day(year_type year
)
328 if (year
>= year_type(2007)) {
329 //first sunday in november
330 fkday
fsin(Sunday
, gregorian::Nov
);
331 return fsin
.get_date(year
);
333 //last sunday in october
334 lkday
lsio(Sunday
, gregorian::Oct
);
335 return lsio
.get_date(year
);
339 static time_duration_type
dst_offset()
341 return time_duration_type(0,dst_length_minutes
,0);
349 //! Used for local time adjustments in places that don't use dst
350 template<class date_type_
, class time_duration_type_
>
354 typedef time_duration_type_ time_duration_type
;
355 typedef date_type_ date_type
;
358 //! Calculates if the given local time is dst or not
359 /*! @retval Always is_not_in_dst since this is for zones without dst
361 static time_is_dst_result
local_is_dst(const date_type
&,
362 const time_duration_type
&)
364 return is_not_in_dst
;
367 //! Calculates if the given utc time is in dst
368 static time_is_dst_result
utc_is_dst(const date_type
&,
369 const time_duration_type
&)
371 return is_not_in_dst
;
374 static bool is_dst_boundary_day(date_type d
)
379 static time_duration_type
dst_offset()
381 return time_duration_type(0,0,0);
387 } } //namespace date_time