source: NonGTP/Boost/boost/date_time/date_generators.hpp @ 857

Revision 857, 16.4 KB checked in by igarcia, 18 years ago (diff)
Line 
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
20namespace boost {
21namespace 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
Note: See TracBrowser for help on using the repository browser.