#ifndef DATE_TIME_PERIOD_HPP___ #define DATE_TIME_PERIOD_HPP___ /* Copyright (c) 2002,2003 CrystalClear Software, Inc. * Use, modification and distribution is subject to the * Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) * Author: Jeff Garland, Bart Garst * $Date: 2008-02-27 12:00:24 -0800 (Wed, 27 Feb 2008) $ */ /*! \file period.hpp This file contain the implementation of the period abstraction. This is basically the same idea as a range. Although this class is intended for use in the time library, it is pretty close to general enough for other numeric uses. */ #include "boost/operators.hpp" namespace boost { namespace date_time { //!Provides generalized period type useful in date-time systems /*!This template uses a class to represent a time point within the period and another class to represent a duration. As a result, this class is not appropriate for use when the number and duration representation are the same (eg: in the regular number domain). A period can be specified by providing either the begining point and a duration or the begining point and the end point( end is NOT part of the period but 1 unit past it. A period will be "invalid" if either end_point <= begin_point or the given duration is <= 0. Any valid period will return false for is_null(). Zero length periods are also considered invalid. Zero length periods are periods where the begining and end points are the same, or, the given duration is zero. For a zero length period, the last point will be one unit less than the begining point. In the case that the begin and last are the same, the period has a length of one unit. The best way to handle periods is usually to provide a begining point and a duration. So, day1 + 7 days is a week period which includes all of the first day and 6 more days (eg: Sun to Sat). */ template<class point_rep, class duration_rep> class period : private boost::less_than_comparable<period<point_rep, duration_rep> , boost::equality_comparable< period<point_rep, duration_rep> > > { public: typedef point_rep point_type; typedef duration_rep duration_type; period(point_rep first_point, point_rep end_point); period(point_rep first_point, duration_rep len); point_rep begin() const; point_rep end() const; point_rep last() const; duration_rep length() const; bool is_null() const; bool operator==(const period& rhs) const; bool operator<(const period& rhs) const; void shift(const duration_rep& d); void expand(const duration_rep& d); bool contains(const point_rep& point) const; bool contains(const period& other) const; bool intersects(const period& other) const; bool is_adjacent(const period& other) const; bool is_before(const point_rep& point) const; bool is_after(const point_rep& point) const; period intersection(const period& other) const; period merge(const period& other) const; period span(const period& other) const; private: point_rep begin_; point_rep last_; }; //! create a period from begin to last eg: [begin,end) /*! If end <= begin then the period will be invalid */ template<class point_rep, class duration_rep> inline period<point_rep,duration_rep>::period(point_rep first_point, point_rep end_point) : begin_(first_point), last_(end_point - duration_rep::unit()) {} //! create a period as [begin, begin+len) /*! If len is <= 0 then the period will be invalid */ template<class point_rep, class duration_rep> inline period<point_rep,duration_rep>::period(point_rep first_point, duration_rep len) : begin_(first_point), last_(first_point + len-duration_rep::unit()) { } //! Return the first element in the period template<class point_rep, class duration_rep> inline point_rep period<point_rep,duration_rep>::begin() const { return begin_; } //! Return one past the last element template<class point_rep, class duration_rep> inline point_rep period<point_rep,duration_rep>::end() const { return last_ + duration_rep::unit(); } //! Return the last item in the period template<class point_rep, class duration_rep> inline point_rep period<point_rep,duration_rep>::last() const { return last_; } //! True if period is ill formed (length is zero or less) template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::is_null() const { return end() <= begin_; } //! Return the length of the period template<class point_rep, class duration_rep> inline duration_rep period<point_rep,duration_rep>::length() const { if(last_ < begin_){ // invalid period return last_+duration_rep::unit() - begin_; } else{ return end() - begin_; // normal case } } //! Equality operator template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::operator==(const period& rhs) const { return ((begin_ == rhs.begin_) && (last_ == rhs.last_)); } //! Strict as defined by rhs.last <= lhs.last template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::operator<(const period& rhs) const { return (last_ < rhs.begin_); } //! Shift the start and end by the specified amount template<class point_rep, class duration_rep> inline void period<point_rep,duration_rep>::shift(const duration_rep& d) { begin_ = begin_ + d; last_ = last_ + d; } /** Expands the size of the period by the duration on both ends. * *So before expand *@code * * [-------] * ^ ^ ^ ^ ^ ^ ^ * 1 2 3 4 5 6 7 * *@endcode * After expand(2) *@code * * [----------------------] * ^ ^ ^ ^ ^ ^ ^ * 1 2 3 4 5 6 7 * *@endcode */ template<class point_rep, class duration_rep> inline void period<point_rep,duration_rep>::expand(const duration_rep& d) { begin_ = begin_ - d; last_ = last_ + d; } //! True if the point is inside the period, zero length periods contain no points template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::contains(const point_rep& point) const { return ((point >= begin_) && (point <= last_)); } //! True if this period fully contains (or equals) the other period template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::contains(const period<point_rep,duration_rep>& other) const { return ((begin_ <= other.begin_) && (last_ >= other.last_)); } //! True if periods are next to each other without a gap. /* In the example below, p1 and p2 are adjacent, but p3 is not adjacent * with either of p1 or p2. *@code * [-p1-) * [-p2-) * [-p3-) *@endcode */ template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::is_adjacent(const period<point_rep,duration_rep>& other) const { return (other.begin() == end() || begin_ == other.end()); } //! True if all of the period is prior or t < start /* In the example below only point 1 would evaluate to true. *@code * [---------]) * ^ ^ ^ ^ ^ * 1 2 3 4 5 * *@endcode */ template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::is_after(const point_rep& t) const { if (is_null()) { return false; //null period isn't after } return t < begin_; } //! True if all of the period is prior to the passed point or end <= t /* In the example below points 4 and 5 return true. *@code * [---------]) * ^ ^ ^ ^ ^ * 1 2 3 4 5 * *@endcode */ template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::is_before(const point_rep& t) const { if (is_null()) { return false; //null period isn't before anything } return last_ < t; } //! True if the periods overlap in any way /* In the example below p1 intersects with p2, p4, and p6. *@code * [---p1---) * [---p2---) * [---p3---) * [---p4---) * [-p5-) * [-p6-) *@endcode */ template<class point_rep, class duration_rep> inline bool period<point_rep,duration_rep>::intersects(const period<point_rep,duration_rep>& other) const { return ( contains(other.begin_) || other.contains(begin_) || ((other.begin_ < begin_) && (other.last_ >= begin_))); } //! Returns the period of intersection or invalid range no intersection template<class point_rep, class duration_rep> inline period<point_rep,duration_rep> period<point_rep,duration_rep>::intersection(const period<point_rep,duration_rep>& other) const { if (begin_ > other.begin_) { if (last_ <= other.last_) { //case2 return *this; } //case 1 return period<point_rep,duration_rep>(begin_, other.end()); } else { if (last_ <= other.last_) { //case3 return period<point_rep,duration_rep>(other.begin_, this->end()); } //case4 return other; } //unreachable } //! Returns the union of intersecting periods -- or null period /*! */ template<class point_rep, class duration_rep> inline period<point_rep,duration_rep> period<point_rep,duration_rep>::merge(const period<point_rep,duration_rep>& other) const { if (this->intersects(other)) { if (begin_ < other.begin_) { return period<point_rep,duration_rep>(begin_, last_ > other.last_ ? this->end() : other.end()); } return period<point_rep,duration_rep>(other.begin_, last_ > other.last_ ? this->end() : other.end()); } return period<point_rep,duration_rep>(begin_,begin_); // no intersect return null } //! Combine two periods with earliest start and latest end. /*! Combines two periods and any gap between them such that * start = min(p1.start, p2.start) * end = max(p1.end , p2.end) *@code * [---p1---) * [---p2---) * result: * [-----------p3----------) *@endcode */ template<class point_rep, class duration_rep> inline period<point_rep,duration_rep> period<point_rep,duration_rep>::span(const period<point_rep,duration_rep>& other) const { point_rep start((begin_ < other.begin_) ? begin() : other.begin()); point_rep newend((last_ < other.last_) ? other.end() : this->end()); return period<point_rep,duration_rep>(start, newend); } } } //namespace date_time #endif