New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
posix_time_zone.hpp in vendors/XIOS/current/extern/boost/include/boost/date_time/local_time – NEMO

source: vendors/XIOS/current/extern/boost/include/boost/date_time/local_time/posix_time_zone.hpp @ 3408

Last change on this file since 3408 was 3408, checked in by rblod, 12 years ago

importing initial XIOS vendor drop

  • Property svn:keywords set to Id
File size: 17.6 KB
Line 
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
24namespace boost{
25namespace 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__
Note: See TracBrowser for help on using the repository browser.