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