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