]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #ifndef DATE_TIME_PERIOD_HPP___ |
2 | #define DATE_TIME_PERIOD_HPP___ | |
3 | ||
4 | /* Copyright (c) 2002,2003 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.txt or http://www.boost.org/LICENSE_1_0.txt) | |
8 | * Author: Jeff Garland, Bart Garst | |
9 | * $Date$ | |
10 | */ | |
11 | ||
12 | /*! \file period.hpp | |
13 | This file contain the implementation of the period abstraction. This is | |
14 | basically the same idea as a range. Although this class is intended for | |
15 | use in the time library, it is pretty close to general enough for other | |
16 | numeric uses. | |
17 | ||
18 | */ | |
19 | ||
b32b8144 FG |
20 | #include <boost/operators.hpp> |
21 | #include <boost/date_time/compiler_config.hpp> | |
7c673cae FG |
22 | |
23 | ||
24 | namespace boost { | |
25 | namespace date_time { | |
26 | //!Provides generalized period type useful in date-time systems | |
27 | /*!This template uses a class to represent a time point within the period | |
28 | and another class to represent a duration. As a result, this class is | |
29 | not appropriate for use when the number and duration representation | |
30 | are the same (eg: in the regular number domain). | |
31 | ||
32 | A period can be specified by providing either the begining point and | |
33 | a duration or the begining point and the end point( end is NOT part | |
34 | of the period but 1 unit past it. A period will be "invalid" if either | |
35 | end_point <= begin_point or the given duration is <= 0. Any valid period | |
36 | will return false for is_null(). | |
37 | ||
38 | Zero length periods are also considered invalid. Zero length periods are | |
39 | periods where the begining and end points are the same, or, the given | |
40 | duration is zero. For a zero length period, the last point will be one | |
41 | unit less than the begining point. | |
42 | ||
43 | In the case that the begin and last are the same, the period has a | |
44 | length of one unit. | |
45 | ||
46 | The best way to handle periods is usually to provide a begining point and | |
47 | a duration. So, day1 + 7 days is a week period which includes all of the | |
48 | first day and 6 more days (eg: Sun to Sat). | |
49 | ||
50 | */ | |
51 | template<class point_rep, class duration_rep> | |
b32b8144 | 52 | class BOOST_SYMBOL_VISIBLE period : private |
7c673cae FG |
53 | boost::less_than_comparable<period<point_rep, duration_rep> |
54 | , boost::equality_comparable< period<point_rep, duration_rep> | |
55 | > > | |
56 | { | |
57 | public: | |
58 | typedef point_rep point_type; | |
59 | typedef duration_rep duration_type; | |
60 | ||
61 | period(point_rep first_point, point_rep end_point); | |
62 | period(point_rep first_point, duration_rep len); | |
63 | point_rep begin() const; | |
64 | point_rep end() const; | |
65 | point_rep last() const; | |
66 | duration_rep length() const; | |
67 | bool is_null() const; | |
68 | bool operator==(const period& rhs) const; | |
69 | bool operator<(const period& rhs) const; | |
70 | void shift(const duration_rep& d); | |
71 | void expand(const duration_rep& d); | |
72 | bool contains(const point_rep& point) const; | |
73 | bool contains(const period& other) const; | |
74 | bool intersects(const period& other) const; | |
75 | bool is_adjacent(const period& other) const; | |
76 | bool is_before(const point_rep& point) const; | |
77 | bool is_after(const point_rep& point) const; | |
78 | period intersection(const period& other) const; | |
79 | period merge(const period& other) const; | |
80 | period span(const period& other) const; | |
81 | private: | |
82 | point_rep begin_; | |
83 | point_rep last_; | |
84 | }; | |
85 | ||
86 | //! create a period from begin to last eg: [begin,end) | |
87 | /*! If end <= begin then the period will be invalid | |
88 | */ | |
89 | template<class point_rep, class duration_rep> | |
90 | inline | |
91 | period<point_rep,duration_rep>::period(point_rep first_point, | |
92 | point_rep end_point) : | |
93 | begin_(first_point), | |
94 | last_(end_point - duration_rep::unit()) | |
95 | {} | |
96 | ||
97 | //! create a period as [begin, begin+len) | |
98 | /*! If len is <= 0 then the period will be invalid | |
99 | */ | |
100 | template<class point_rep, class duration_rep> | |
101 | inline | |
102 | period<point_rep,duration_rep>::period(point_rep first_point, duration_rep len) : | |
103 | begin_(first_point), | |
104 | last_(first_point + len-duration_rep::unit()) | |
105 | { } | |
106 | ||
107 | ||
108 | //! Return the first element in the period | |
109 | template<class point_rep, class duration_rep> | |
110 | inline | |
111 | point_rep period<point_rep,duration_rep>::begin() const | |
112 | { | |
113 | return begin_; | |
114 | } | |
115 | ||
116 | //! Return one past the last element | |
117 | template<class point_rep, class duration_rep> | |
118 | inline | |
119 | point_rep period<point_rep,duration_rep>::end() const | |
120 | { | |
121 | return last_ + duration_rep::unit(); | |
122 | } | |
123 | ||
124 | //! Return the last item in the period | |
125 | template<class point_rep, class duration_rep> | |
126 | inline | |
127 | point_rep period<point_rep,duration_rep>::last() const | |
128 | { | |
129 | return last_; | |
130 | } | |
131 | ||
132 | //! True if period is ill formed (length is zero or less) | |
133 | template<class point_rep, class duration_rep> | |
134 | inline | |
135 | bool period<point_rep,duration_rep>::is_null() const | |
136 | { | |
137 | return end() <= begin_; | |
138 | } | |
139 | ||
140 | //! Return the length of the period | |
141 | template<class point_rep, class duration_rep> | |
142 | inline | |
143 | duration_rep period<point_rep,duration_rep>::length() const | |
144 | { | |
145 | if(last_ < begin_){ // invalid period | |
146 | return last_+duration_rep::unit() - begin_; | |
147 | } | |
148 | else{ | |
149 | return end() - begin_; // normal case | |
150 | } | |
151 | } | |
152 | ||
153 | //! Equality operator | |
154 | template<class point_rep, class duration_rep> | |
155 | inline | |
156 | bool period<point_rep,duration_rep>::operator==(const period& rhs) const | |
157 | { | |
158 | return ((begin_ == rhs.begin_) && | |
159 | (last_ == rhs.last_)); | |
160 | } | |
161 | ||
162 | //! Strict as defined by rhs.last <= lhs.last | |
163 | template<class point_rep, class duration_rep> | |
164 | inline | |
165 | bool period<point_rep,duration_rep>::operator<(const period& rhs) const | |
166 | { | |
167 | return (last_ < rhs.begin_); | |
168 | } | |
169 | ||
170 | ||
171 | //! Shift the start and end by the specified amount | |
172 | template<class point_rep, class duration_rep> | |
173 | inline | |
174 | void period<point_rep,duration_rep>::shift(const duration_rep& d) | |
175 | { | |
176 | begin_ = begin_ + d; | |
177 | last_ = last_ + d; | |
178 | } | |
179 | ||
180 | /** Expands the size of the period by the duration on both ends. | |
181 | * | |
182 | *So before expand | |
183 | *@code | |
184 | * | |
185 | * [-------] | |
186 | * ^ ^ ^ ^ ^ ^ ^ | |
187 | * 1 2 3 4 5 6 7 | |
188 | * | |
189 | *@endcode | |
190 | * After expand(2) | |
191 | *@code | |
192 | * | |
193 | * [----------------------] | |
194 | * ^ ^ ^ ^ ^ ^ ^ | |
195 | * 1 2 3 4 5 6 7 | |
196 | * | |
197 | *@endcode | |
198 | */ | |
199 | template<class point_rep, class duration_rep> | |
200 | inline | |
201 | void period<point_rep,duration_rep>::expand(const duration_rep& d) | |
202 | { | |
203 | begin_ = begin_ - d; | |
204 | last_ = last_ + d; | |
205 | } | |
206 | ||
207 | //! True if the point is inside the period, zero length periods contain no points | |
208 | template<class point_rep, class duration_rep> | |
209 | inline | |
210 | bool period<point_rep,duration_rep>::contains(const point_rep& point) const | |
211 | { | |
212 | return ((point >= begin_) && | |
213 | (point <= last_)); | |
214 | } | |
215 | ||
216 | ||
217 | //! True if this period fully contains (or equals) the other period | |
218 | template<class point_rep, class duration_rep> | |
219 | inline | |
220 | bool period<point_rep,duration_rep>::contains(const period<point_rep,duration_rep>& other) const | |
221 | { | |
222 | return ((begin_ <= other.begin_) && (last_ >= other.last_)); | |
223 | } | |
224 | ||
225 | ||
226 | //! True if periods are next to each other without a gap. | |
227 | /* In the example below, p1 and p2 are adjacent, but p3 is not adjacent | |
228 | * with either of p1 or p2. | |
229 | *@code | |
230 | * [-p1-) | |
231 | * [-p2-) | |
232 | * [-p3-) | |
233 | *@endcode | |
234 | */ | |
235 | template<class point_rep, class duration_rep> | |
236 | inline | |
237 | bool | |
238 | period<point_rep,duration_rep>::is_adjacent(const period<point_rep,duration_rep>& other) const | |
239 | { | |
240 | return (other.begin() == end() || | |
241 | begin_ == other.end()); | |
242 | } | |
243 | ||
244 | ||
245 | //! True if all of the period is prior or t < start | |
246 | /* In the example below only point 1 would evaluate to true. | |
247 | *@code | |
248 | * [---------]) | |
249 | * ^ ^ ^ ^ ^ | |
250 | * 1 2 3 4 5 | |
251 | * | |
252 | *@endcode | |
253 | */ | |
254 | template<class point_rep, class duration_rep> | |
255 | inline | |
256 | bool | |
257 | period<point_rep,duration_rep>::is_after(const point_rep& t) const | |
258 | { | |
259 | if (is_null()) | |
260 | { | |
261 | return false; //null period isn't after | |
262 | } | |
263 | ||
264 | return t < begin_; | |
265 | } | |
266 | ||
267 | //! True if all of the period is prior to the passed point or end <= t | |
268 | /* In the example below points 4 and 5 return true. | |
269 | *@code | |
270 | * [---------]) | |
271 | * ^ ^ ^ ^ ^ | |
272 | * 1 2 3 4 5 | |
273 | * | |
274 | *@endcode | |
275 | */ | |
276 | template<class point_rep, class duration_rep> | |
277 | inline | |
278 | bool | |
279 | period<point_rep,duration_rep>::is_before(const point_rep& t) const | |
280 | { | |
281 | if (is_null()) | |
282 | { | |
283 | return false; //null period isn't before anything | |
284 | } | |
285 | ||
286 | return last_ < t; | |
287 | } | |
288 | ||
289 | ||
290 | //! True if the periods overlap in any way | |
291 | /* In the example below p1 intersects with p2, p4, and p6. | |
292 | *@code | |
293 | * [---p1---) | |
294 | * [---p2---) | |
295 | * [---p3---) | |
296 | * [---p4---) | |
297 | * [-p5-) | |
298 | * [-p6-) | |
299 | *@endcode | |
300 | */ | |
301 | template<class point_rep, class duration_rep> | |
302 | inline | |
303 | bool period<point_rep,duration_rep>::intersects(const period<point_rep,duration_rep>& other) const | |
304 | { | |
305 | return ( contains(other.begin_) || | |
306 | other.contains(begin_) || | |
307 | ((other.begin_ < begin_) && (other.last_ >= begin_))); | |
308 | } | |
309 | ||
310 | //! Returns the period of intersection or invalid range no intersection | |
311 | template<class point_rep, class duration_rep> | |
312 | inline | |
313 | period<point_rep,duration_rep> | |
314 | period<point_rep,duration_rep>::intersection(const period<point_rep,duration_rep>& other) const | |
315 | { | |
316 | if (begin_ > other.begin_) { | |
317 | if (last_ <= other.last_) { //case2 | |
318 | return *this; | |
319 | } | |
320 | //case 1 | |
321 | return period<point_rep,duration_rep>(begin_, other.end()); | |
322 | } | |
323 | else { | |
324 | if (last_ <= other.last_) { //case3 | |
325 | return period<point_rep,duration_rep>(other.begin_, this->end()); | |
326 | } | |
327 | //case4 | |
328 | return other; | |
329 | } | |
330 | //unreachable | |
331 | } | |
332 | ||
333 | //! Returns the union of intersecting periods -- or null period | |
334 | /*! | |
335 | */ | |
336 | template<class point_rep, class duration_rep> | |
337 | inline | |
338 | period<point_rep,duration_rep> | |
339 | period<point_rep,duration_rep>::merge(const period<point_rep,duration_rep>& other) const | |
340 | { | |
341 | if (this->intersects(other)) { | |
342 | if (begin_ < other.begin_) { | |
343 | return period<point_rep,duration_rep>(begin_, last_ > other.last_ ? this->end() : other.end()); | |
344 | } | |
345 | ||
346 | return period<point_rep,duration_rep>(other.begin_, last_ > other.last_ ? this->end() : other.end()); | |
347 | ||
348 | } | |
349 | return period<point_rep,duration_rep>(begin_,begin_); // no intersect return null | |
350 | } | |
351 | ||
352 | //! Combine two periods with earliest start and latest end. | |
353 | /*! Combines two periods and any gap between them such that | |
354 | * start = min(p1.start, p2.start) | |
355 | * end = max(p1.end , p2.end) | |
356 | *@code | |
357 | * [---p1---) | |
358 | * [---p2---) | |
359 | * result: | |
360 | * [-----------p3----------) | |
361 | *@endcode | |
362 | */ | |
363 | template<class point_rep, class duration_rep> | |
364 | inline | |
365 | period<point_rep,duration_rep> | |
366 | period<point_rep,duration_rep>::span(const period<point_rep,duration_rep>& other) const | |
367 | { | |
368 | point_rep start((begin_ < other.begin_) ? begin() : other.begin()); | |
369 | point_rep newend((last_ < other.last_) ? other.end() : this->end()); | |
370 | return period<point_rep,duration_rep>(start, newend); | |
371 | } | |
372 | ||
373 | ||
374 | } } //namespace date_time | |
375 | ||
376 | ||
377 | ||
378 | #endif |