PageRenderTime 28ms CodeModel.GetById 6ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Dependencies/Boost/boost/date_time/tz_db_base.hpp

http://hadesmem.googlecode.com/
C++ Header | 385 lines | 194 code | 43 blank | 148 comment | 11 complexity | 36804f1243bdccdabd6249e98b84003a MD5 | raw file
  1#ifndef DATE_TIME_TZ_DB_BASE_HPP__
  2#define DATE_TIME_TZ_DB_BASE_HPP__
  3
  4/* Copyright (c) 2003-2005 CrystalClear Software, Inc.
  5 * Subject to the Boost Software License, Version 1.0. 
  6 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  7 * Author: Jeff Garland, Bart Garst
  8 * $Date: 2011-07-07 14:57:37 +1000 (Thu, 07 Jul 2011) $
  9 */
 10
 11#include <map>
 12#include <vector>
 13#include <string>
 14#include <sstream>
 15#include <fstream>
 16#include <stdexcept>
 17#include <boost/tokenizer.hpp>
 18#include <boost/shared_ptr.hpp>
 19#include <boost/throw_exception.hpp>
 20#include <boost/date_time/compiler_config.hpp>
 21#include <boost/date_time/time_zone_names.hpp>
 22#include <boost/date_time/time_zone_base.hpp>
 23#include <boost/date_time/time_parsing.hpp>
 24
 25namespace boost {
 26  namespace date_time {
 27
 28    //! Exception thrown when tz database cannot locate requested data file
 29    class data_not_accessible : public std::logic_error
 30    {
 31     public:
 32       data_not_accessible() : 
 33         std::logic_error(std::string("Unable to locate or access the required datafile.")) 
 34       {}
 35       data_not_accessible(const std::string& filespec) : 
 36         std::logic_error(std::string("Unable to locate or access the required datafile. Filespec: " + filespec)) 
 37       {}
 38    };
 39    
 40    //! Exception thrown when tz database locates incorrect field structure in data file
 41    class bad_field_count : public std::out_of_range
 42    {
 43     public:
 44       bad_field_count(const std::string& s) : 
 45         std::out_of_range(s) 
 46      {}
 47    };
 48
 49    //! Creates a database of time_zones from csv datafile
 50    /*! The csv file containing the zone_specs used by the
 51     * tz_db_base is intended to be customized by the
 52     * library user. When customizing this file (or creating your own) the
 53     * file must follow a specific format.
 54     * 
 55     * This first line is expected to contain column headings and is therefore
 56     * not processed by the tz_db_base.
 57     *
 58     * Each record (line) must have eleven fields. Some of those fields can
 59     * be empty. Every field (even empty ones) must be enclosed in 
 60     * double-quotes.
 61     * Ex:
 62     * @code
 63     * "America/Phoenix" <- string enclosed in quotes
 64     * ""                <- empty field
 65     * @endcode
 66     * 
 67     * Some fields represent a length of time. The format of these fields 
 68     * must be:
 69     * @code
 70     * "{+|-}hh:mm[:ss]" <- length-of-time format
 71     * @endcode
 72     * Where the plus or minus is mandatory and the seconds are optional.
 73     * 
 74     * Since some time zones do not use daylight savings it is not always 
 75     * necessary for every field in a zone_spec to contain a value. All 
 76     * zone_specs must have at least ID and GMT offset. Zones that use 
 77     * daylight savings must have all fields filled except: 
 78     * STD ABBR, STD NAME, DST NAME. You should take note 
 79     * that DST ABBR is mandatory for zones that use daylight savings 
 80     * (see field descriptions for further details).
 81     *
 82     * ******* Fields and their description/details ********* 
 83     *     
 84     * ID: 
 85     * Contains the identifying string for the zone_spec. Any string will
 86     * do as long as it's unique. No two ID's can be the same. 
 87     *
 88     * STD ABBR:
 89     * STD NAME:
 90     * DST ABBR:
 91     * DST NAME:
 92     * These four are all the names and abbreviations used by the time 
 93     * zone being described. While any string will do in these fields, 
 94     * care should be taken. These fields hold the strings that will be 
 95     * used in the output of many of the local_time classes. 
 96     * Ex:
 97     * @code
 98     * time_zone nyc = tz_db.time_zone_from_region("America/New_York");
 99     * local_time ny_time(date(2004, Aug, 30), IS_DST, nyc);
100     * cout << ny_time.to_long_string() << endl;
101     * // 2004-Aug-30 00:00:00 Eastern Daylight Time
102     * cout << ny_time.to_short_string() << endl;
103     * // 2004-Aug-30 00:00:00 EDT
104     * @endcode
105     *
106     * NOTE: The exact format/function names may vary - see local_time 
107     * documentation for further details.
108     *
109     * GMT offset:
110     * This is the number of hours added to utc to get the local time 
111     * before any daylight savings adjustments are made. Some examples 
112     * are: America/New_York offset -5 hours, & Africa/Cairo offset +2 hours.
113     * The format must follow the length-of-time format described above.
114     *
115     * DST adjustment:
116     * The amount of time added to gmt_offset when daylight savings is in 
117     * effect. The format must follow the length-of-time format described
118     * above.
119     *
120     * DST Start Date rule:
121     * This is a specially formatted string that describes the day of year
122     * in which the transition take place. It holds three fields of it's own,
123     * separated by semicolons. 
124     * The first field indicates the "nth" weekday of the month. The possible 
125     * values are: 1 (first), 2 (second), 3 (third), 4 (fourth), 5 (fifth), 
126     * and -1 (last).
127     * The second field indicates the day-of-week from 0-6 (Sun=0).
128     * The third field indicates the month from 1-12 (Jan=1).
129     * 
130     * Examples are: "-1;5;9"="Last Friday of September", 
131     * "2;1;3"="Second Monday of March"
132     *
133     * Start time:
134     * Start time is the number of hours past midnight, on the day of the
135     * start transition, the transition takes place. More simply put, the 
136     * time of day the transition is made (in 24 hours format). The format
137     * must follow the length-of-time format described above with the 
138     * exception that it must always be positive.
139     *
140     * DST End date rule:
141     * See DST Start date rule. The difference here is this is the day 
142     * daylight savings ends (transition to STD).
143     *
144     * End time:
145     * Same as Start time.
146     */
147    template<class time_zone_type, class rule_type>
148    class tz_db_base {
149    public:
150      /* Having CharT as a template parameter created problems 
151       * with posix_time::duration_from_string. Templatizing 
152       * duration_from_string was not possible at this time, however, 
153       * it should be possible in the future (when poor compilers get 
154       * fixed or stop being used). 
155       * Since this class was designed to use CharT as a parameter it 
156       * is simply typedef'd here to ease converting in back to a 
157       * parameter the future */
158      typedef char char_type;
159
160      typedef typename time_zone_type::base_type time_zone_base_type;
161      typedef typename time_zone_type::time_duration_type time_duration_type;
162      typedef time_zone_names_base<char_type> time_zone_names;
163      typedef boost::date_time::dst_adjustment_offsets<time_duration_type> dst_adjustment_offsets;
164      typedef std::basic_string<char_type> string_type;
165
166      //! Constructs an empty database
167      tz_db_base() {}
168
169      //! Process csv data file, may throw exceptions
170      /*! May throw bad_field_count exceptions */
171      void load_from_stream(std::istream &in)
172      {
173        std::string  buff;
174        while( std::getline(in, buff)) {
175          parse_string(buff);
176        }
177      }
178
179      //! Process csv data file, may throw exceptions
180      /*! May throw data_not_accessible, or bad_field_count exceptions */
181      void load_from_file(const std::string& pathspec)
182      {
183        string_type in_str;
184        std::string  buff;
185        
186        std::ifstream ifs(pathspec.c_str());
187        if(!ifs){
188          boost::throw_exception(data_not_accessible(pathspec));
189        }
190        std::getline(ifs, buff); // first line is column headings
191        this->load_from_stream(ifs);
192      }
193
194      //! returns true if record successfully added to map
195      /*! Takes a region name in the form of "America/Phoenix", and a 
196       * time_zone object for that region. The id string must be a unique 
197       * name that does not already exist in the database. */
198      bool add_record(const string_type& region, 
199                      boost::shared_ptr<time_zone_base_type> tz)
200      {
201        typename map_type::value_type p(region, tz); 
202        return (m_zone_map.insert(p)).second;
203      }
204
205      //! Returns a time_zone object built from the specs for the given region
206      /*! Returns a time_zone object built from the specs for the given 
207       * region. If region does not exist a local_time::record_not_found 
208       * exception will be thrown */
209      boost::shared_ptr<time_zone_base_type> 
210      time_zone_from_region(const string_type& region) const 
211      {
212        // get the record
213        typename map_type::const_iterator record = m_zone_map.find(region);
214        if(record == m_zone_map.end()){
215          return boost::shared_ptr<time_zone_base_type>(); //null pointer
216        }
217        return record->second;
218      }
219
220      //! Returns a vector of strings holding the time zone regions in the database
221      std::vector<std::string> region_list() const
222      {
223        typedef std::vector<std::string> vector_type;
224        vector_type regions;
225        typename map_type::const_iterator itr = m_zone_map.begin();
226        while(itr != m_zone_map.end()) {
227          regions.push_back(itr->first);
228          ++itr;
229        }
230        return regions;
231      }
232    
233    private:
234      typedef std::map<string_type, boost::shared_ptr<time_zone_base_type> > map_type;
235      map_type m_zone_map;
236
237      // start and end rule are of the same type
238      typedef typename rule_type::start_rule::week_num week_num;
239
240      /* TODO: mechanisms need to be put in place to handle different
241       * types of rule specs. parse_rules() only handles nth_kday
242       * rule types. */
243      
244      //! parses rule specs for transition day rules
245      rule_type* parse_rules(const string_type& sr, const string_type& er) const
246      {
247        using namespace gregorian;
248        // start and end rule are of the same type, 
249        // both are included here for readability
250        typedef typename rule_type::start_rule start_rule;
251        typedef typename rule_type::end_rule end_rule;
252       
253        // these are: [start|end] nth, day, month
254        int s_nth = 0, s_d = 0, s_m = 0;
255        int e_nth = 0, e_d = 0, e_m = 0;
256        split_rule_spec(s_nth, s_d, s_m, sr);
257        split_rule_spec(e_nth, e_d, e_m, er);
258        
259        typename start_rule::week_num s_wn, e_wn;
260        s_wn = get_week_num(s_nth);
261        e_wn = get_week_num(e_nth);
262        
263        
264        return new rule_type(start_rule(s_wn, s_d, s_m),
265                             end_rule(e_wn, e_d, e_m));
266      }
267      //! helper function for parse_rules()
268      week_num get_week_num(int nth) const
269      {
270        typedef typename rule_type::start_rule start_rule;
271        switch(nth){
272        case 1:
273          return start_rule::first;
274        case 2:
275          return start_rule::second;
276        case 3:
277          return start_rule::third;
278        case 4:
279          return start_rule::fourth;
280        case 5:
281        case -1:
282          return start_rule::fifth;
283        default:
284          // shouldn't get here - add error handling later
285          break;
286        }
287        return start_rule::fifth; // silence warnings
288      }
289          
290      //! splits the [start|end]_date_rule string into 3 ints
291      void split_rule_spec(int& nth, int& d, int& m, string_type rule) const
292      {
293        typedef boost::char_separator<char_type, std::char_traits<char_type> > char_separator_type;
294        typedef boost::tokenizer<char_separator_type,
295                                 std::basic_string<char_type>::const_iterator,
296                                 std::basic_string<char_type> > tokenizer;
297        typedef boost::tokenizer<char_separator_type,
298                                 std::basic_string<char_type>::const_iterator,
299                                 std::basic_string<char_type> >::iterator tokenizer_iterator;
300        
301        const char_type sep_char[] = { ';', '\0'};
302        char_separator_type sep(sep_char);
303        tokenizer tokens(rule, sep); // 3 fields
304        
305        tokenizer_iterator tok_iter = tokens.begin(); 
306        nth = std::atoi(tok_iter->c_str()); ++tok_iter;
307        d   = std::atoi(tok_iter->c_str()); ++tok_iter;
308        m   = std::atoi(tok_iter->c_str());
309      }
310
311     
312      //! Take a line from the csv, turn it into a time_zone_type.
313      /*! Take a line from the csv, turn it into a time_zone_type,
314       * and add it to the map. Zone_specs in csv file are expected to 
315       * have eleven fields that describe the time zone. Returns true if 
316       * zone_spec successfully added to database */
317      bool parse_string(string_type& s)
318      {
319        std::vector<string_type> result;
320        typedef boost::token_iterator_generator<boost::escaped_list_separator<char_type>, string_type::const_iterator, string_type >::type token_iter_type;
321
322        token_iter_type i = boost::make_token_iterator<string_type>(s.begin(), s.end(),boost::escaped_list_separator<char_type>());
323
324        token_iter_type end;
325        while (i != end) {
326          result.push_back(*i);
327          i++;
328        }
329
330        enum db_fields { ID, STDABBR, STDNAME, DSTABBR, DSTNAME, GMTOFFSET,
331                         DSTADJUST, START_DATE_RULE, START_TIME, END_DATE_RULE,
332                         END_TIME, FIELD_COUNT };
333
334        //take a shot at fixing gcc 4.x error
335        const unsigned int expected_fields = static_cast<unsigned int>(FIELD_COUNT);
336        if (result.size() != expected_fields) { 
337          std::ostringstream msg;
338          msg << "Expecting " << FIELD_COUNT << " fields, got " 
339            << result.size() << " fields in line: " << s;
340          boost::throw_exception(bad_field_count(msg.str()));
341          BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return false); // should never reach
342        }
343
344        // initializations
345        bool has_dst = true; 
346        if(result[DSTABBR] == std::string()){
347          has_dst = false;
348        }
349
350
351        // start building components of a time_zone
352        time_zone_names names(result[STDNAME], result[STDABBR],
353                              result[DSTNAME], result[DSTABBR]);
354
355        time_duration_type utc_offset = 
356          str_from_delimited_time_duration<time_duration_type,char_type>(result[GMTOFFSET]);
357        
358        dst_adjustment_offsets adjust(time_duration_type(0,0,0),
359                                      time_duration_type(0,0,0),
360                                      time_duration_type(0,0,0));
361
362        boost::shared_ptr<rule_type> rules;
363
364        if(has_dst){
365          adjust = dst_adjustment_offsets(
366                                          str_from_delimited_time_duration<time_duration_type,char_type>(result[DSTADJUST]),
367                                          str_from_delimited_time_duration<time_duration_type,char_type>(result[START_TIME]),
368                                          str_from_delimited_time_duration<time_duration_type,char_type>(result[END_TIME])
369                                          );
370
371          rules = 
372            boost::shared_ptr<rule_type>(parse_rules(result[START_DATE_RULE],
373                                                     result[END_DATE_RULE]));
374        }
375        string_type id(result[ID]);
376        boost::shared_ptr<time_zone_base_type> zone(new time_zone_type(names, utc_offset, adjust, rules));
377        return (add_record(id, zone));
378        
379      } 
380     
381    };
382
383} } // namespace
384
385#endif // DATE_TIME_TZ_DB_BASE_HPP__