Ugly, not-to-be-pushed sucking in of all of Boost to get windows to work.
[dyninst.git] / external / boost / date_time / date_generators.hpp
1 #ifndef DATE_TIME_DATE_GENERATORS_HPP__
2 #define DATE_TIME_DATE_GENERATORS_HPP__
3
4 /* Copyright (c) 2002,2003,2005 CrystalClear Software, Inc.
5  * Use, modification and distribution is subject to the 
6  * Boost Software License, Version 1.0. (See accompanying
7  * file LICENSE-1.0 or http://www.boost.org/LICENSE-1.0)
8  * Author: Jeff Garland, Bart Garst 
9  * $Date: 2005/04/17 21:48:19 $
10  */
11
12 /*! @file date_generators.hpp
13   Definition and implementation of date algorithm templates
14 */
15 #include <stdexcept>
16 #include <sstream>
17 #include "boost/date_time/date.hpp"
18 #include "boost/date_time/compiler_config.hpp"
19
20 namespace boost {
21 namespace date_time {
22
23   //! Base class for all generators that take a year and produce a date.
24   /*! This class is a base class for polymorphic function objects that take
25     a year and produce a concrete date.
26     @param date_type The type representing a date.  This type must
27     export a calender_type which defines a year_type.
28   */
29   template<class date_type>
30   class year_based_generator
31   {
32   public:
33     typedef typename date_type::calendar_type calendar_type;
34     typedef typename calendar_type::year_type        year_type;
35     year_based_generator() {};
36     virtual ~year_based_generator() {};
37     virtual date_type get_date(year_type y) const = 0;
38     //! Returns a string for use in a POSIX time_zone string
39     virtual std::string to_string() const =0;
40   };
41   
42   //! Generates a date by applying the year to the given month and day.
43   /*!
44     Example usage: 
45     @code
46     partial_date pd(1, Jan);
47     partial_date pd2(70);
48     date d = pd.get_date(2002); //2002-Jan-01
49     date d2 = pd2.get_date(2002); //2002-Mar-10
50     @endcode
51     \ingroup date_alg
52   */
53   template<class date_type>
54  class partial_date : public year_based_generator<date_type>
55  {
56  public:
57    typedef typename date_type::calendar_type calendar_type;
58    typedef typename calendar_type::day_type         day_type;
59    typedef typename calendar_type::month_type       month_type;
60    typedef typename calendar_type::year_type        year_type;
61    typedef typename date_type::duration_type        duration_type;
62    typedef typename duration_type::duration_rep     duration_rep;
63    partial_date(day_type d, month_type m) :
64      day_(d),
65      month_(m)
66    {}
67    //! Partial date created from number of days into year. Range 1-366
68    /*! Allowable values range from 1 to 366. 1=Jan1, 366=Dec31. If argument
69     * exceeds range, partial_date will be created with closest in-range value.
70     * 60 will always be Feb29, if get_date() is called with a non-leap year
71     * an exception will be thrown */
72    partial_date(duration_rep days) :
73      day_(1), // default values
74      month_(1)
75    {
76      date_type d1(2000,1,1);
77      if(days > 1) {
78        if(days > 366) // prevents wrapping
79        {
80          days = 366;
81        }
82        days = days - 1;
83        duration_type dd(days);
84        d1 = d1 + dd;
85      }
86      day_ = d1.day();
87      month_ = d1.month();
88    }
89    //! Return a concrete date when provided with a year specific year.
90    /*! Will throw an 'invalid_argument' exception if a partial_date object,
91     * instantiated with Feb-29, has get_date called with a non-leap year.
92     * Example:
93     * @code
94     * partial_date pd(29, Feb);
95     * pd.get_date(2003); // throws invalid_argument exception
96     * pg.get_date(2000); // returns 2000-2-29
97     * @endcode
98          */
99    date_type get_date(year_type y) const
100    {
101      if((day_ == 29) && (month_ == 2) && !(calendar_type::is_leap_year(y))) {
102        std::stringstream ss("");
103        ss << "No Feb 29th in given year of " << y << ".";
104        throw std::invalid_argument(ss.str());
105        //return date_type(1,1,1); // should never reach
106      } else {
107        return date_type(y, month_, day_);
108      }
109    }
110    date_type operator()(year_type y) const
111    {
112      return get_date(y);
113      //return date_type(y, month_, day_);
114    }
115    bool operator==(const partial_date& rhs) const
116    {
117      return (month_ == rhs.month_) && (day_ == rhs.day_);
118    }
119    bool operator<(const partial_date& rhs) const
120    {
121      if (month_ < rhs.month_) return true;
122      if (month_ > rhs.month_) return false;
123      //months are equal
124      return (day_ < rhs.day_);
125    }
126    
127    // added for streaming purposes
128    month_type month() const 
129    {
130      return month_;
131    }
132    day_type day() const
133    {
134      return day_;
135    }
136
137    //! Returns string suitable for use in POSIX time zone string
138    /*! Returns string formatted with up to 3 digits: 
139     * Jan-01 == "0" 
140     * Feb-29 == "58"
141     * Dec-31 == "365" */
142    virtual std::string to_string() const
143    {
144      std::stringstream ss;
145      date_type d(2004, month_, day_);
146      unsigned short c = d.day_of_year();     
147      c--; // numbered 0-365 while day_of_year is 1 based...
148      ss << c;
149      return ss.str();
150    }
151  private:
152    day_type day_;
153    month_type month_;
154  };
155
156
157   //! Useful generator functor for finding holidays
158   /*! Based on the idea in Cal. Calc. for finding holidays that are
159    *  the 'first Monday of September'. When instantiated with
160    *  'fifth' kday of month, the result will be the last kday of month
161    *  which can be the fourth or fifth depending on the structure of 
162    *  the month.
163    *
164    *  The algorithm here basically guesses for the first
165    *  day of the month.  Then finds the first day of the correct
166    *  type.  That is, if the first of the month is a Tuesday
167    *  and it needs Wenesday then we simply increment by a day
168    *  and then we can add the length of a week until we get
169    *  to the 'nth kday'.  There are probably more efficient 
170    *  algorithms based on using a mod 7, but this one works 
171    *  reasonably well for basic applications.
172    *  \ingroup date_alg
173    */
174   template<class date_type>
175   class nth_kday_of_month : public year_based_generator<date_type>
176   {
177   public:
178     typedef typename date_type::calendar_type calendar_type;
179     typedef typename calendar_type::day_of_week_type  day_of_week_type;
180     typedef typename calendar_type::month_type        month_type;
181     typedef typename calendar_type::year_type         year_type;
182     typedef typename date_type::duration_type        duration_type;
183     enum week_num {first=1, second, third, fourth, fifth};
184     nth_kday_of_month(week_num week_no,
185                       day_of_week_type dow,
186                       month_type m) :
187       month_(m),
188       wn_(week_no),
189       dow_(dow)
190     {}
191     //! Return a concrete date when provided with a year specific year.
192     date_type get_date(year_type y) const
193     {
194       date_type d(y, month_, 1); //first day of month
195       duration_type one_day(1);
196       duration_type one_week(7);
197       while (dow_ != d.day_of_week()) {
198         d = d + one_day;
199       }
200       int week = 1;
201       while (week < wn_) {
202         d = d + one_week;
203         week++;
204       }
205       // remove wrapping to next month behavior
206       if(d.month() != month_) {
207         d = d - one_week;
208       }
209       return d;
210     }
211     // added for streaming
212     month_type month() const
213     {
214       return month_;
215     }
216     week_num nth_week() const
217     {
218       return wn_;
219     }
220     day_of_week_type day_of_week() const
221     {
222       return dow_;
223     }
224     const char* nth_week_as_str() const
225     {
226       return nth_as_str(wn_);
227     }
228     //! Returns string suitable for use in POSIX time zone string
229     /*! Returns a string formatted as "M4.3.0" ==> 3rd Sunday in April. */
230     virtual std::string to_string() const
231     {
232      std::stringstream ss;
233      ss << 'M' 
234        << static_cast<int>(month_) << '.'
235        << static_cast<int>(wn_) << '.'
236        << static_cast<int>(dow_);
237      return ss.str();
238     }
239   private:
240     month_type month_;
241     week_num wn_;
242     day_of_week_type dow_;
243   };
244   
245   //! Returns nth arg as string. 1 -> "first", 2 -> "second", max is 5.
246   BOOST_DATE_TIME_DECL const char* nth_as_str(int n);
247
248   //! Useful generator functor for finding holidays and daylight savings
249   /*! Similar to nth_kday_of_month, but requires less paramters
250    *  \ingroup date_alg
251    */
252   template<class date_type>
253   class first_kday_of_month : public year_based_generator<date_type>
254   {
255   public:
256     typedef typename date_type::calendar_type calendar_type;
257     typedef typename calendar_type::day_of_week_type  day_of_week_type;
258     typedef typename calendar_type::month_type        month_type;
259     typedef typename calendar_type::year_type         year_type;
260     typedef typename date_type::duration_type        duration_type;
261     //!Specify the first 'Sunday' in 'April' spec
262     /*!@param dow The day of week, eg: Sunday, Monday, etc
263      * @param m The month of the year, eg: Jan, Feb, Mar, etc
264      */
265     first_kday_of_month(day_of_week_type dow, month_type m) :
266       month_(m),
267       dow_(dow)
268     {}
269     //! Return a concrete date when provided with a year specific year.
270     date_type get_date(year_type year) const
271     {
272       date_type d(year, month_,1);
273       duration_type one_day(1);
274       while (dow_ != d.day_of_week()) {
275         d = d + one_day;
276       }
277       return d;
278         }
279     // added for streaming
280     month_type month() const
281     {
282       return month_;
283     }
284     day_of_week_type day_of_week() const
285     {
286       return dow_;
287     }
288     //! Returns string suitable for use in POSIX time zone string
289     /*! Returns a string formatted as "M4.1.0" ==> 1st Sunday in April. */
290     virtual std::string to_string() const
291     {
292      std::stringstream ss;
293      ss << 'M' 
294        << static_cast<int>(month_) << '.'
295        << 1 << '.'
296        << static_cast<int>(dow_);
297      return ss.str();
298     }
299   private:
300     month_type month_;
301     day_of_week_type dow_;
302   };
303   
304   
305   
306   //! Calculate something like Last Sunday of January
307   /*! Useful generator functor for finding holidays and daylight savings
308    *  Get the last day of the month and then calculate the difference
309    *  to the last previous day.
310    *  @param date_type A date class that exports day_of_week, month_type, etc.
311    *  \ingroup date_alg
312    */
313   template<class date_type>
314   class last_kday_of_month : public year_based_generator<date_type>
315   {
316   public:
317     typedef typename date_type::calendar_type calendar_type;
318     typedef typename calendar_type::day_of_week_type  day_of_week_type;
319     typedef typename calendar_type::month_type        month_type;
320     typedef typename calendar_type::year_type         year_type;
321     typedef typename date_type::duration_type        duration_type;
322     //!Specify the date spec like last 'Sunday' in 'April' spec
323     /*!@param dow The day of week, eg: Sunday, Monday, etc
324      * @param m The month of the year, eg: Jan, Feb, Mar, etc
325      */
326     last_kday_of_month(day_of_week_type dow, month_type m) :
327       month_(m),
328       dow_(dow)
329     {}
330     //! Return a concrete date when provided with a year specific year.
331     date_type get_date(year_type year) const
332     {
333       date_type d(year, month_, calendar_type::end_of_month_day(year,month_));
334       duration_type one_day(1);
335       while (dow_ != d.day_of_week()) {
336         d = d - one_day;
337       }
338       return d;
339     }
340     // added for streaming
341     month_type month() const
342     {
343       return month_;
344     }
345     day_of_week_type day_of_week() const
346     {
347       return dow_;
348     }
349     //! Returns string suitable for use in POSIX time zone string
350     /*! Returns a string formatted as "M4.5.0" ==> last Sunday in April. */
351     virtual std::string to_string() const
352     {
353       std::stringstream ss;
354       ss << 'M' 
355          << static_cast<int>(month_) << '.'
356          << 5 << '.'
357          << static_cast<int>(dow_);
358       return ss.str();
359     }
360   private:
361     month_type month_;
362     day_of_week_type dow_;
363    };
364   
365   
366   //! Calculate something like "First Sunday after Jan 1,2002
367   /*! Date generator that takes a date and finds kday after
368    *@code
369      typedef boost::date_time::first_kday_after<date> firstkdayafter;
370      firstkdayafter fkaf(Monday);
371      fkaf.get_date(date(2002,Feb,1));
372    @endcode
373    *  \ingroup date_alg
374    */
375   template<class date_type>
376   class first_kday_after
377   {
378   public:
379     typedef typename date_type::calendar_type calendar_type;
380     typedef typename calendar_type::day_of_week_type day_of_week_type;
381     typedef typename date_type::duration_type        duration_type;
382     first_kday_after(day_of_week_type dow) :
383       dow_(dow)
384     {}
385     //! Return next kday given.
386     date_type get_date(date_type start_day) const
387     {
388       duration_type one_day(1);
389       date_type d = start_day + one_day;
390       while (dow_ != d.day_of_week()) {
391         d = d + one_day;
392       }
393       return d;
394     }
395     // added for streaming
396     day_of_week_type day_of_week() const
397     {
398       return dow_;
399     }
400   private:
401     day_of_week_type dow_;
402   };
403   
404   //! Calculate something like "First Sunday before Jan 1,2002
405   /*! Date generator that takes a date and finds kday after
406    *@code
407      typedef boost::date_time::first_kday_before<date> firstkdaybefore;
408      firstkdaybefore fkbf(Monday);
409      fkbf.get_date(date(2002,Feb,1));
410    @endcode
411    *  \ingroup date_alg
412    */
413   template<class date_type>
414   class first_kday_before
415   {
416   public:
417     typedef typename date_type::calendar_type calendar_type;
418     typedef typename calendar_type::day_of_week_type day_of_week_type;
419     typedef typename date_type::duration_type        duration_type;
420     first_kday_before(day_of_week_type dow) :
421       dow_(dow)
422     {}
423     //! Return next kday given.
424     date_type get_date(date_type start_day) const
425     {
426       duration_type one_day(1);
427       date_type d = start_day - one_day;
428       while (dow_ != d.day_of_week()) {
429         d = d - one_day;
430       }
431       return d;
432     }
433     // added for streaming
434     day_of_week_type day_of_week() const
435     {
436       return dow_;
437     }
438   private:
439     day_of_week_type dow_;
440   };
441   
442   //! Calculates the number of days until the next weekday
443   /*! Calculates the number of days until the next weekday.
444    * If the date given falls on a Sunday and the given weekday 
445    * is Tuesday the result will be 2 days */
446   template<typename date_type, class weekday_type>
447   inline
448   typename date_type::duration_type days_until_weekday(const date_type& d, const weekday_type& wd)
449   {
450     typedef typename date_type::duration_type duration_type;
451     duration_type wks(0);
452     duration_type dd(wd.as_number() - d.day_of_week().as_number());
453     if(dd.is_negative()){
454       wks = duration_type(7);
455     }
456     return dd + wks;
457   }
458
459   //! Calculates the number of days since the previous weekday
460   /*! Calculates the number of days since the previous weekday
461    * If the date given falls on a Sunday and the given weekday 
462    * is Tuesday the result will be 5 days. The answer will be a positive 
463    * number because Tuesday is 5 days before Sunday, not -5 days before. */
464   template<typename date_type, class weekday_type>
465   inline
466   typename date_type::duration_type days_before_weekday(const date_type& d, const weekday_type& wd)
467   {
468     typedef typename date_type::duration_type duration_type;
469     duration_type wks(0);
470     duration_type dd(wd.as_number() - d.day_of_week().as_number());
471     if(dd.days() > 0){
472       wks = duration_type(7);
473     }
474     // we want a number of days, not an offset. The value returned must
475     // be zero or larger.
476     return (-dd + wks);
477   }
478
479   //! Generates a date object representing the date of the following weekday from the given date
480   /*! Generates a date object representing the date of the following 
481    * weekday from the given date. If the date given is 2004-May-9 
482    * (a Sunday) and the given weekday is Tuesday then the resulting date 
483    * will be 2004-May-11. */
484   template<class date_type, class weekday_type>
485   inline
486   date_type next_weekday(const date_type& d, const weekday_type& wd)
487   {
488     return d + days_until_weekday(d, wd);
489   }
490
491   //! Generates a date object representing the date of the previous weekday from the given date
492   /*! Generates a date object representing the date of the previous 
493    * weekday from the given date. If the date given is 2004-May-9 
494    * (a Sunday) and the given weekday is Tuesday then the resulting date 
495    * will be 2004-May-4. */
496   template<class date_type, class weekday_type>
497   inline
498   date_type previous_weekday(const date_type& d, const weekday_type& wd)
499   {
500     return d - days_before_weekday(d, wd);
501   }
502
503 } } //namespace date_time
504
505
506
507
508 #endif
509