]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/date_time/local_time/posix_time_zone.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / date_time / local_time / posix_time_zone.hpp
1 #ifndef _DATE_TIME_POSIX_TIME_ZONE__
2 #define _DATE_TIME_POSIX_TIME_ZONE__
3
4 /* Copyright (c) 2003-2005 CrystalClear Software, Inc.
5 * Subject to the Boost Software License, Version 1.0. (See accompanying
6 * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
7 * Author: Jeff Garland, Bart Garst
8 * $Date$
9 */
10
11 #include <string>
12 #include <sstream>
13 #include <stdexcept>
14 #include <boost/tokenizer.hpp>
15 #include <boost/throw_exception.hpp>
16 #include <boost/date_time/compiler_config.hpp>
17 #include <boost/date_time/gregorian/gregorian.hpp>
18 #include <boost/date_time/time_zone_names.hpp>
19 #include <boost/date_time/time_zone_base.hpp>
20 #include <boost/date_time/local_time/dst_transition_day_rules.hpp>
21 #include <boost/date_time/posix_time/posix_time.hpp>
22 #include <boost/date_time/string_convert.hpp>
23 #include <boost/date_time/time_parsing.hpp>
24
25 namespace boost{
26 namespace local_time{
27
28 //! simple exception for UTC and Daylight savings start/end offsets
29 struct BOOST_SYMBOL_VISIBLE bad_offset : public std::out_of_range
30 {
31 bad_offset(std::string const& msg = std::string()) :
32 std::out_of_range(std::string("Offset out of range: " + msg)) {}
33 };
34 //! simple exception for UTC daylight savings adjustment
35 struct BOOST_SYMBOL_VISIBLE bad_adjustment : public std::out_of_range
36 {
37 bad_adjustment(std::string const& msg = std::string()) :
38 std::out_of_range(std::string("Adjustment out of range: " + msg)) {}
39 };
40
41 typedef boost::date_time::dst_adjustment_offsets<boost::posix_time::time_duration> dst_adjustment_offsets;
42
43 //! A time zone class constructed from a POSIX time zone string
44 /*! A POSIX time zone string takes the form of:<br>
45 * "std offset dst [offset],start[/time],end[/time]" (w/no spaces)
46 * 'std' specifies the abbrev of the time zone.<br>
47 * 'offset' is the offset from UTC.<br>
48 * 'dst' specifies the abbrev of the time zone during daylight savings time.<br>
49 * The second offset is how many hours changed during DST. Default=1<br>
50 * 'start' and'end' are the dates when DST goes into (and out of) effect.<br>
51 * 'offset' takes the form of: [+|-]hh[:mm[:ss]] {h=0-23, m/s=0-59}<br>
52 * 'time' and 'offset' take the same form. Time defaults=02:00:00<br>
53 * 'start' and 'end' can be one of three forms:<br>
54 * Mm.w.d {month=1-12, week=1-5 (5 is always last), day=0-6}<br>
55 * Jn {n=1-365 Feb29 is never counted}<br>
56 * n {n=0-365 Feb29 is counted in leap years}<br>
57 * Example "PST-5PDT01:00:00,M4.1.0/02:00:00,M10.1.0/02:00:00"
58 * <br>
59 * Exceptions will be thrown under these conditions:<br>
60 * An invalid date spec (see date class)<br>
61 * A boost::local_time::bad_offset exception will be thrown for:<br>
62 * A DST start or end offset that is negative or more than 24 hours<br>
63 * A UTC zone that is greater than +14 or less than -12 hours<br>
64 * A boost::local_time::bad_adjustment exception will be thrown for:<br>
65 * A DST adjustment that is 24 hours or more (positive or negative)<br>
66 *
67 * Note that UTC zone offsets can be greater than +12:
68 * http://www.worldtimezone.com/utc/utc+1200.html
69 */
70 template<class CharT>
71 class BOOST_SYMBOL_VISIBLE posix_time_zone_base : public date_time::time_zone_base<posix_time::ptime,CharT> {
72 public:
73 typedef boost::posix_time::time_duration time_duration_type;
74 typedef date_time::time_zone_names_base<CharT> time_zone_names;
75 typedef date_time::time_zone_base<posix_time::ptime,CharT> base_type;
76 typedef typename base_type::string_type string_type;
77 typedef CharT char_type;
78 typedef typename base_type::stringstream_type stringstream_type;
79 typedef boost::char_separator<char_type, std::char_traits<char_type> > char_separator_type;
80 typedef boost::tokenizer<char_separator_type,
81 typename string_type::const_iterator,
82 string_type> tokenizer_type;
83 typedef typename tokenizer_type::iterator tokenizer_iterator_type;
84
85 //! Construct from a POSIX time zone string
86 posix_time_zone_base(const string_type& s) :
87 //zone_names_("std_name","std_abbrev","no-dst","no-dst"),
88 zone_names_(),
89 has_dst_(false),
90 base_utc_offset_(posix_time::hours(0)),
91 dst_offsets_(posix_time::hours(0),posix_time::hours(0),posix_time::hours(0)),
92 dst_calc_rules_()
93 {
94 #ifdef __HP_aCC
95 // Work around bug in aC++ compiler: see QXCR1000880488 in the
96 // HP bug tracking system
97 const char_type sep_chars[2] = {',',0};
98 #else
99 const char_type sep_chars[2] = {','};
100 #endif
101 char_separator_type sep(sep_chars);
102 tokenizer_type tokens(s, sep);
103 tokenizer_iterator_type it = tokens.begin(), end = tokens.end();
104 if (it == end)
105 BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse time zone name"));
106 calc_zone(*it++);
107 if(has_dst_)
108 {
109 if (it == end)
110 BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse DST begin time"));
111 string_type dst_begin = *it++;
112
113 if (it == end)
114 BOOST_THROW_EXCEPTION(std::invalid_argument("Could not parse DST end time"));
115 string_type dst_end = *it;
116 calc_rules(dst_begin, dst_end);
117 }
118 }
119 virtual ~posix_time_zone_base() {}
120 //!String for the zone when not in daylight savings (eg: EST)
121 virtual string_type std_zone_abbrev()const
122 {
123 return zone_names_.std_zone_abbrev();
124 }
125 //!String for the timezone when in daylight savings (eg: EDT)
126 /*! For those time zones that have no DST, an empty string is used */
127 virtual string_type dst_zone_abbrev() const
128 {
129 return zone_names_.dst_zone_abbrev();
130 }
131 //!String for the zone when not in daylight savings (eg: Eastern Standard Time)
132 /*! The full STD name is not extracted from the posix time zone string.
133 * Therefore, the STD abbreviation is used in it's place */
134 virtual string_type std_zone_name()const
135 {
136 return zone_names_.std_zone_name();
137 }
138 //!String for the timezone when in daylight savings (eg: Eastern Daylight Time)
139 /*! The full DST name is not extracted from the posix time zone string.
140 * Therefore, the STD abbreviation is used in it's place. For time zones
141 * that have no DST, an empty string is used */
142 virtual string_type dst_zone_name()const
143 {
144 return zone_names_.dst_zone_name();
145 }
146 //! True if zone uses daylight savings adjustments otherwise false
147 virtual bool has_dst()const
148 {
149 return has_dst_;
150 }
151 //! Local time that DST starts -- NADT if has_dst is false
152 virtual posix_time::ptime dst_local_start_time(gregorian::greg_year y)const
153 {
154 gregorian::date d(gregorian::not_a_date_time);
155 if(has_dst_)
156 {
157 d = dst_calc_rules_->start_day(y);
158 }
159 return posix_time::ptime(d, dst_offsets_.dst_start_offset_);
160 }
161 //! Local time that DST ends -- NADT if has_dst is false
162 virtual posix_time::ptime dst_local_end_time(gregorian::greg_year y)const
163 {
164 gregorian::date d(gregorian::not_a_date_time);
165 if(has_dst_)
166 {
167 d = dst_calc_rules_->end_day(y);
168 }
169 return posix_time::ptime(d, dst_offsets_.dst_end_offset_);
170 }
171 //! Base offset from UTC for zone (eg: -07:30:00)
172 virtual time_duration_type base_utc_offset()const
173 {
174 return base_utc_offset_;
175 }
176 //! Adjustment forward or back made while DST is in effect
177 virtual time_duration_type dst_offset()const
178 {
179 return dst_offsets_.dst_adjust_;
180 }
181
182 //! Returns a POSIX time_zone string for this object
183 virtual string_type to_posix_string() const
184 {
185 // std offset dst [offset],start[/time],end[/time] - w/o spaces
186 stringstream_type ss;
187 ss.fill('0');
188 boost::shared_ptr<dst_calc_rule> no_rules;
189 // std
190 ss << std_zone_abbrev();
191 // offset
192 if(base_utc_offset().is_negative()) {
193 // inverting the sign guarantees we get two digits
194 ss << '-' << std::setw(2) << base_utc_offset().invert_sign().hours();
195 }
196 else {
197 ss << '+' << std::setw(2) << base_utc_offset().hours();
198 }
199 if(base_utc_offset().minutes() != 0 || base_utc_offset().seconds() != 0) {
200 ss << ':' << std::setw(2) << base_utc_offset().minutes();
201 if(base_utc_offset().seconds() != 0) {
202 ss << ':' << std::setw(2) << base_utc_offset().seconds();
203 }
204 }
205 if(dst_calc_rules_ != no_rules) {
206 // dst
207 ss << dst_zone_abbrev();
208 // dst offset
209 if(dst_offset().is_negative()) {
210 // inverting the sign guarantees we get two digits
211 ss << '-' << std::setw(2) << dst_offset().invert_sign().hours();
212 }
213 else {
214 ss << '+' << std::setw(2) << dst_offset().hours();
215 }
216 if(dst_offset().minutes() != 0 || dst_offset().seconds() != 0) {
217 ss << ':' << std::setw(2) << dst_offset().minutes();
218 if(dst_offset().seconds() != 0) {
219 ss << ':' << std::setw(2) << dst_offset().seconds();
220 }
221 }
222 // start/time
223 ss << ',' << date_time::convert_string_type<char, char_type>(dst_calc_rules_->start_rule_as_string()) << '/'
224 << std::setw(2) << dst_offsets_.dst_start_offset_.hours() << ':'
225 << std::setw(2) << dst_offsets_.dst_start_offset_.minutes();
226 if(dst_offsets_.dst_start_offset_.seconds() != 0) {
227 ss << ':' << std::setw(2) << dst_offsets_.dst_start_offset_.seconds();
228 }
229 // end/time
230 ss << ',' << date_time::convert_string_type<char, char_type>(dst_calc_rules_->end_rule_as_string()) << '/'
231 << std::setw(2) << dst_offsets_.dst_end_offset_.hours() << ':'
232 << std::setw(2) << dst_offsets_.dst_end_offset_.minutes();
233 if(dst_offsets_.dst_end_offset_.seconds() != 0) {
234 ss << ':' << std::setw(2) << dst_offsets_.dst_end_offset_.seconds();
235 }
236 }
237
238 return ss.str();
239 }
240 private:
241 time_zone_names zone_names_;
242 bool has_dst_;
243 time_duration_type base_utc_offset_;
244 dst_adjustment_offsets dst_offsets_;
245 boost::shared_ptr<dst_calc_rule> dst_calc_rules_;
246
247 /*! Extract time zone abbreviations for STD & DST as well
248 * as the offsets for the time shift that occurs and how
249 * much of a shift. At this time full time zone names are
250 * NOT extracted so the abbreviations are used in their place */
251 void calc_zone(const string_type& obj){
252 const char_type empty_string[2] = {'\0'};
253 stringstream_type ss(empty_string);
254 typename string_type::const_pointer sit = obj.c_str(), obj_end = sit + obj.size();
255 string_type l_std_zone_abbrev, l_dst_zone_abbrev;
256
257 // get 'std' name/abbrev
258 while(std::isalpha(*sit)){
259 ss << *sit++;
260 }
261 l_std_zone_abbrev = ss.str();
262 ss.str(empty_string);
263
264 // get UTC offset
265 if(sit != obj_end){
266 // get duration
267 while(sit != obj_end && !std::isalpha(*sit)){
268 ss << *sit++;
269 }
270 base_utc_offset_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(ss.str());
271 ss.str(empty_string);
272
273 // base offset must be within range of -12 hours to +14 hours
274 if(base_utc_offset_ < time_duration_type(-12,0,0) ||
275 base_utc_offset_ > time_duration_type(14,0,0))
276 {
277 boost::throw_exception(bad_offset(posix_time::to_simple_string(base_utc_offset_)));
278 }
279 }
280
281 // get DST data if given
282 if(sit != obj_end){
283 has_dst_ = true;
284
285 // get 'dst' name/abbrev
286 while(sit != obj_end && std::isalpha(*sit)){
287 ss << *sit++;
288 }
289 l_dst_zone_abbrev = ss.str();
290 ss.str(empty_string);
291
292 // get DST offset if given
293 if(sit != obj_end){
294 // get duration
295 while(sit != obj_end && !std::isalpha(*sit)){
296 ss << *sit++;
297 }
298 dst_offsets_.dst_adjust_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(ss.str());
299 ss.str(empty_string);
300 }
301 else{ // default DST offset
302 dst_offsets_.dst_adjust_ = posix_time::hours(1);
303 }
304
305 // adjustment must be within +|- 1 day
306 if(dst_offsets_.dst_adjust_ <= time_duration_type(-24,0,0) ||
307 dst_offsets_.dst_adjust_ >= time_duration_type(24,0,0))
308 {
309 boost::throw_exception(bad_adjustment(posix_time::to_simple_string(dst_offsets_.dst_adjust_)));
310 }
311 }
312 // full names not extracted so abbrevs used in their place
313 zone_names_ = time_zone_names(l_std_zone_abbrev, l_std_zone_abbrev, l_dst_zone_abbrev, l_dst_zone_abbrev);
314 }
315
316 void calc_rules(const string_type& start, const string_type& end){
317 #ifdef __HP_aCC
318 // Work around bug in aC++ compiler: see QXCR1000880488 in the
319 // HP bug tracking system
320 const char_type sep_chars[2] = {'/',0};
321 #else
322 const char_type sep_chars[2] = {'/'};
323 #endif
324 char_separator_type sep(sep_chars);
325 tokenizer_type st_tok(start, sep);
326 tokenizer_type et_tok(end, sep);
327 tokenizer_iterator_type sit = st_tok.begin();
328 tokenizer_iterator_type eit = et_tok.begin();
329
330 // generate date spec
331 char_type x = string_type(*sit).at(0);
332 if(x == 'M'){
333 M_func(*sit, *eit);
334 }
335 else if(x == 'J'){
336 julian_no_leap(*sit, *eit);
337 }
338 else{
339 julian_day(*sit, *eit);
340 }
341
342 ++sit;
343 ++eit;
344 // generate durations
345 // starting offset
346 if(sit != st_tok.end()){
347 dst_offsets_.dst_start_offset_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(*sit);
348 }
349 else{
350 // default
351 dst_offsets_.dst_start_offset_ = posix_time::hours(2);
352 }
353 // start/end offsets must fall on given date
354 if(dst_offsets_.dst_start_offset_ < time_duration_type(0,0,0) ||
355 dst_offsets_.dst_start_offset_ >= time_duration_type(24,0,0))
356 {
357 boost::throw_exception(bad_offset(posix_time::to_simple_string(dst_offsets_.dst_start_offset_)));
358 }
359
360 // ending offset
361 if(eit != et_tok.end()){
362 dst_offsets_.dst_end_offset_ = date_time::str_from_delimited_time_duration<time_duration_type,char_type>(*eit);
363 }
364 else{
365 // default
366 dst_offsets_.dst_end_offset_ = posix_time::hours(2);
367 }
368 // start/end offsets must fall on given date
369 if(dst_offsets_.dst_end_offset_ < time_duration_type(0,0,0) ||
370 dst_offsets_.dst_end_offset_ >= time_duration_type(24,0,0))
371 {
372 boost::throw_exception(bad_offset(posix_time::to_simple_string(dst_offsets_.dst_end_offset_)));
373 }
374 }
375
376 /* Parses out a start/end date spec from a posix time zone string.
377 * Date specs come in three possible formats, this function handles
378 * the 'M' spec. Ex "M2.2.4" => 2nd month, 2nd week, 4th day .
379 */
380 void M_func(const string_type& s, const string_type& e){
381 typedef gregorian::nth_kday_of_month nkday;
382 unsigned short sm=0,sw=0,sd=0,em=0,ew=0,ed=0; // start/end month,week,day
383 #ifdef __HP_aCC
384 // Work around bug in aC++ compiler: see QXCR1000880488 in the
385 // HP bug tracking system
386 const char_type sep_chars[3] = {'M','.',0};
387 #else
388 const char_type sep_chars[3] = {'M','.'};
389 #endif
390 char_separator_type sep(sep_chars);
391 tokenizer_type stok(s, sep), etok(e, sep);
392
393 tokenizer_iterator_type it = stok.begin();
394 sm = lexical_cast<unsigned short>(*it++);
395 sw = lexical_cast<unsigned short>(*it++);
396 sd = lexical_cast<unsigned short>(*it);
397
398 it = etok.begin();
399 em = lexical_cast<unsigned short>(*it++);
400 ew = lexical_cast<unsigned short>(*it++);
401 ed = lexical_cast<unsigned short>(*it);
402
403 dst_calc_rules_ = shared_ptr<dst_calc_rule>(
404 new nth_kday_dst_rule(
405 nth_last_dst_rule::start_rule(
406 static_cast<nkday::week_num>(sw),sd,sm),
407 nth_last_dst_rule::start_rule(
408 static_cast<nkday::week_num>(ew),ed,em)
409 )
410 );
411 }
412
413 //! Julian day. Feb29 is never counted, even in leap years
414 // expects range of 1-365
415 void julian_no_leap(const string_type& s, const string_type& e){
416 typedef gregorian::gregorian_calendar calendar;
417 const unsigned short year = 2001; // Non-leap year
418 unsigned short sm=1;
419 int sd=0;
420 sd = lexical_cast<int>(s.substr(1)); // skip 'J'
421 while(sd >= calendar::end_of_month_day(year,sm)){
422 sd -= calendar::end_of_month_day(year,sm++);
423 }
424 unsigned short em=1;
425 int ed=0;
426 ed = lexical_cast<int>(e.substr(1)); // skip 'J'
427 while(ed > calendar::end_of_month_day(year,em)){
428 ed -= calendar::end_of_month_day(year,em++);
429 }
430
431 dst_calc_rules_ = shared_ptr<dst_calc_rule>(
432 new partial_date_dst_rule(
433 partial_date_dst_rule::start_rule(
434 static_cast<unsigned short>(sd), static_cast<date_time::months_of_year>(sm)),
435 partial_date_dst_rule::end_rule(
436 static_cast<unsigned short>(ed), static_cast<date_time::months_of_year>(em))
437 )
438 );
439 }
440
441 //! Julian day. Feb29 is always counted, but exception thrown in non-leap years
442 // expects range of 0-365
443 void julian_day(const string_type& s, const string_type& e){
444 int sd=0, ed=0;
445 sd = lexical_cast<int>(s);
446 ed = lexical_cast<int>(e);
447 dst_calc_rules_ = shared_ptr<dst_calc_rule>(
448 new partial_date_dst_rule(
449 partial_date_dst_rule::start_rule(++sd),// args are 0-365
450 partial_date_dst_rule::end_rule(++ed) // pd expects 1-366
451 )
452 );
453 }
454
455 //! helper function used when throwing exceptions
456 static std::string td_as_string(const time_duration_type& td)
457 {
458 std::string s;
459 #if defined(USE_DATE_TIME_PRE_1_33_FACET_IO)
460 s = posix_time::to_simple_string(td);
461 #else
462 std::stringstream ss;
463 ss << td;
464 s = ss.str();
465 #endif
466 return s;
467 }
468 };
469
470 typedef posix_time_zone_base<char> posix_time_zone;
471
472 } } // namespace boost::local_time
473
474
475 #endif // _DATE_TIME_POSIX_TIME_ZONE__