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