1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
|
#ifndef DATE_TIME_DST_RULES_HPP__
#define DATE_TIME_DST_RULES_HPP__
/* Copyright (c) 2002,2003, 2007 CrystalClear Software, Inc.
* Use, modification and distribution is subject to the
* Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
* Author: Jeff Garland, Bart Garst
* $Date: 2008-02-27 15:00:24 -0500 (Wed, 27 Feb 2008) $
*/
/*! @file dst_rules.hpp
Contains template class to provide static dst rule calculations
*/
#include "boost/date_time/date_generators.hpp"
#include "boost/date_time/period.hpp"
#include "boost/date_time/date_defs.hpp"
#include <stdexcept>
namespace boost {
namespace date_time {
enum time_is_dst_result {is_not_in_dst, is_in_dst,
ambiguous, invalid_time_label};
//! Dynamic class used to caluclate dst transition information
template<class date_type_,
class time_duration_type_>
class dst_calculator
{
public:
typedef time_duration_type_ time_duration_type;
typedef date_type_ date_type;
//! Check the local time offset when on dst start day
/*! On this dst transition, the time label between
* the transition boundary and the boudary + the offset
* are invalid times. If before the boundary then still
* not in dst.
*@param time_of_day Time offset in the day for the local time
*@param dst_start_offset_minutes Local day offset for start of dst
*@param dst_length_minutes Number of minutes to adjust clock forward
*@retval status of time label w.r.t. dst
*/
static time_is_dst_result
process_local_dst_start_day(const time_duration_type& time_of_day,
unsigned int dst_start_offset_minutes,
long dst_length_minutes)
{
//std::cout << "here" << std::endl;
if (time_of_day < time_duration_type(0,dst_start_offset_minutes,0)) {
return is_not_in_dst;
}
long offset = dst_start_offset_minutes + dst_length_minutes;
if (time_of_day >= time_duration_type(0,offset,0)) {
return is_in_dst;
}
return invalid_time_label;
}
//! Check the local time offset when on the last day of dst
/*! This is the calculation for the DST end day. On that day times
* prior to the conversion time - dst_length (1 am in US) are still
* in dst. Times between the above and the switch time are
* ambiguous. Times after the start_offset are not in dst.
*@param time_of_day Time offset in the day for the local time
*@param dst_end_offset_minutes Local time of day for end of dst
*@retval status of time label w.r.t. dst
*/
static time_is_dst_result
process_local_dst_end_day(const time_duration_type& time_of_day,
unsigned int dst_end_offset_minutes,
long dst_length_minutes)
{
//in US this will be 60 so offset in day is 1,0,0
int offset = dst_end_offset_minutes-dst_length_minutes;
if (time_of_day < time_duration_type(0,offset,0)) {
return is_in_dst;
}
if (time_of_day >= time_duration_type(0,dst_end_offset_minutes,0)) {
return is_not_in_dst;
}
return ambiguous;
}
//! Calculates if the given local time is dst or not
/*! Determines if the time is really in DST or not. Also checks for
* invalid and ambiguous.
* @param current_day The day to check for dst
* @param time_of_day Time offset within the day to check
* @param dst_start_day Starting day of dst for the given locality
* @param dst_start_offset Time offset within day for dst boundary
* @param dst_end_day Ending day of dst for the given locality
* @param dst_end_offset Time offset within day given in dst for dst boundary
* @param dst_length lenght of dst adjusment
* @retval The time is either ambiguous, invalid, in dst, or not in dst
*/
static time_is_dst_result
local_is_dst(const date_type& current_day,
const time_duration_type& time_of_day,
const date_type& dst_start_day,
const time_duration_type& dst_start_offset,
const date_type& dst_end_day,
const time_duration_type& dst_end_offset,
const time_duration_type& dst_length_minutes)
{
unsigned int start_minutes =
dst_start_offset.hours() * 60 + dst_start_offset.minutes();
unsigned int end_minutes =
dst_end_offset.hours() * 60 + dst_end_offset.minutes();
long length_minutes =
dst_length_minutes.hours() * 60 + dst_length_minutes.minutes();
return local_is_dst(current_day, time_of_day,
dst_start_day, start_minutes,
dst_end_day, end_minutes,
length_minutes);
}
//! Calculates if the given local time is dst or not
/*! Determines if the time is really in DST or not. Also checks for
* invalid and ambiguous.
* @param current_day The day to check for dst
* @param time_of_day Time offset within the day to check
* @param dst_start_day Starting day of dst for the given locality
* @param dst_start_offset_minutes Offset within day for dst
* boundary (eg 120 for US which is 02:00:00)
* @param dst_end_day Ending day of dst for the given locality
* @param dst_end_offset_minutes Offset within day given in dst for dst
* boundary (eg 120 for US which is 02:00:00)
* @param dst_length_minutes Length of dst adjusment (eg: 60 for US)
* @retval The time is either ambiguous, invalid, in dst, or not in dst
*/
static time_is_dst_result
local_is_dst(const date_type& current_day,
const time_duration_type& time_of_day,
const date_type& dst_start_day,
unsigned int dst_start_offset_minutes,
const date_type& dst_end_day,
unsigned int dst_end_offset_minutes,
long dst_length_minutes)
{
//in northern hemisphere dst is in the middle of the year
if (dst_start_day < dst_end_day) {
if ((current_day > dst_start_day) && (current_day < dst_end_day)) {
return is_in_dst;
}
if ((current_day < dst_start_day) || (current_day > dst_end_day)) {
return is_not_in_dst;
}
}
else {//southern hemisphere dst is at begining /end of year
if ((current_day < dst_start_day) && (current_day > dst_end_day)) {
return is_not_in_dst;
}
if ((current_day > dst_start_day) || (current_day < dst_end_day)) {
return is_in_dst;
}
}
if (current_day == dst_start_day) {
return process_local_dst_start_day(time_of_day,
dst_start_offset_minutes,
dst_length_minutes);
}
if (current_day == dst_end_day) {
return process_local_dst_end_day(time_of_day,
dst_end_offset_minutes,
dst_length_minutes);
}
//you should never reach this statement
return invalid_time_label;
}
};
//! Compile-time configurable daylight savings time calculation engine
/* This template provides the ability to configure a daylight savings
* calculation at compile time covering all the cases. Unfortunately
* because of the number of dimensions related to daylight savings
* calculation the number of parameters is high. In addition, the
* start and end transition rules are complex types that specify
* an algorithm for calculation of the starting day and ending
* day of daylight savings time including the month and day
* specifications (eg: last sunday in October).
*
* @param date_type A type that represents dates, typically gregorian::date
* @param time_duration_type Used for the offset in the day calculations
* @param dst_traits A set of traits that define the rules of dst
* calculation. The dst_trait must include the following:
* start_rule_functor - Rule to calculate the starting date of a
* dst transition (eg: last_kday_of_month).
* start_day - static function that returns month of dst start for
* start_rule_functor
* start_month -static function that returns day or day of week for
* dst start of dst
* end_rule_functor - Rule to calculate the end of dst day.
* end_day - static fucntion that returns end day for end_rule_functor
* end_month - static function that returns end month for end_rule_functor
* 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.
* 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.
* dst_length_minutes - number of minutes that dst shifts clock
*/
template<class date_type,
class time_duration_type,
class dst_traits>
class dst_calc_engine
{
public:
typedef typename date_type::year_type year_type;
typedef typename date_type::calendar_type calendar_type;
typedef dst_calculator<date_type, time_duration_type> dstcalc;
//! Calculates if the given local time is dst or not
/*! Determines if the time is really in DST or not. Also checks for
* invalid and ambiguous.
* @retval The time is either ambiguous, invalid, in dst, or not in dst
*/
static time_is_dst_result local_is_dst(const date_type& d,
const time_duration_type& td)
{
year_type y = d.year();
date_type dst_start = local_dst_start_day(y);
date_type dst_end = local_dst_end_day(y);
return dstcalc::local_is_dst(d,td,
dst_start,
dst_traits::dst_start_offset_minutes(),
dst_end,
dst_traits::dst_end_offset_minutes(),
dst_traits::dst_shift_length_minutes());
}
static bool is_dst_boundary_day(date_type d)
{
year_type y = d.year();
return ((d == local_dst_start_day(y)) ||
(d == local_dst_end_day(y)));
}
//! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00)
static time_duration_type dst_offset()
{
return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0);
}
static date_type local_dst_start_day(year_type year)
{
return dst_traits::local_dst_start_day(year);
}
static date_type local_dst_end_day(year_type year)
{
return dst_traits::local_dst_end_day(year);
}
};
//! Depricated: Class to calculate dst boundaries for US time zones
/* Use dst_calc_engine instead.
* In 2007 US/Canada DST rules changed
* (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time).
*/
template<class date_type_,
class time_duration_type_,
unsigned int dst_start_offset_minutes=120, //from start of day
short dst_length_minutes=60> //1 hour == 60 min in US
class us_dst_rules
{
public:
typedef time_duration_type_ time_duration_type;
typedef date_type_ date_type;
typedef typename date_type::year_type year_type;
typedef typename date_type::calendar_type calendar_type;
typedef date_time::last_kday_of_month<date_type> lkday;
typedef date_time::first_kday_of_month<date_type> fkday;
typedef date_time::nth_kday_of_month<date_type> nkday;
typedef dst_calculator<date_type, time_duration_type> dstcalc;
//! Calculates if the given local time is dst or not
/*! Determines if the time is really in DST or not. Also checks for
* invalid and ambiguous.
* @retval The time is either ambiguous, invalid, in dst, or not in dst
*/
static time_is_dst_result local_is_dst(const date_type& d,
const time_duration_type& td)
{
year_type y = d.year();
date_type dst_start = local_dst_start_day(y);
date_type dst_end = local_dst_end_day(y);
return dstcalc::local_is_dst(d,td,
dst_start,dst_start_offset_minutes,
dst_end, dst_start_offset_minutes,
dst_length_minutes);
}
static bool is_dst_boundary_day(date_type d)
{
year_type y = d.year();
return ((d == local_dst_start_day(y)) ||
(d == local_dst_end_day(y)));
}
static date_type local_dst_start_day(year_type year)
{
if (year >= year_type(2007)) {
//second sunday in march
nkday ssim(nkday::second, Sunday, gregorian::Mar);
return ssim.get_date(year);
} else {
//first sunday in april
fkday fsia(Sunday, gregorian::Apr);
return fsia.get_date(year);
}
}
static date_type local_dst_end_day(year_type year)
{
if (year >= year_type(2007)) {
//first sunday in november
fkday fsin(Sunday, gregorian::Nov);
return fsin.get_date(year);
} else {
//last sunday in october
lkday lsio(Sunday, gregorian::Oct);
return lsio.get_date(year);
}
}
static time_duration_type dst_offset()
{
return time_duration_type(0,dst_length_minutes,0);
}
private:
};
//! Used for local time adjustments in places that don't use dst
template<class date_type_, class time_duration_type_>
class null_dst_rules
{
public:
typedef time_duration_type_ time_duration_type;
typedef date_type_ date_type;
//! Calculates if the given local time is dst or not
/*! @retval Always is_not_in_dst since this is for zones without dst
*/
static time_is_dst_result local_is_dst(const date_type&,
const time_duration_type&)
{
return is_not_in_dst;
}
//! Calculates if the given utc time is in dst
static time_is_dst_result utc_is_dst(const date_type&,
const time_duration_type&)
{
return is_not_in_dst;
}
static bool is_dst_boundary_day(date_type d)
{
return false;
}
static time_duration_type dst_offset()
{
return time_duration_type(0,0,0);
}
};
} } //namespace date_time
#endif
|