1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2014 Ferdinando Ametrano
5 Copyright (C) 2006 Katiuscia Manzoni
6 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
7 Copyright (C) 2003, 2004, 2005, 2006, 2008 StatPro Italia srl
8 Copyright (C) 2014 Paolo Mazzocchi
9
10 This file is part of QuantLib, a free-software/open-source library
11 for financial quantitative analysts and developers - http://quantlib.org/
12
13 QuantLib is free software: you can redistribute it and/or modify it
14 under the terms of the QuantLib license. You should have received a
15 copy of the license along with this program; if not, please email
16 <quantlib-dev@lists.sf.net>. The license is also available online at
17 <http://quantlib.org/license.shtml>.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 FOR A PARTICULAR PURPOSE. See the license for more details.
22*/
23
24#include <ql/time/period.hpp>
25#include <ql/errors.hpp>
26
27namespace QuantLib {
28
29 Period::Period(Frequency f) {
30 switch (f) {
31 case NoFrequency:
32 // same as Period()
33 units_ = Days;
34 length_ = 0;
35 break;
36 case Once:
37 units_ = Years;
38 length_ = 0;
39 break;
40 case Annual:
41 units_ = Years;
42 length_ = 1;
43 break;
44 case Semiannual:
45 case EveryFourthMonth:
46 case Quarterly:
47 case Bimonthly:
48 case Monthly:
49 units_ = Months;
50 length_ = 12/f;
51 break;
52 case EveryFourthWeek:
53 case Biweekly:
54 case Weekly:
55 units_ = Weeks;
56 length_ = 52/f;
57 break;
58 case Daily:
59 units_ = Days;
60 length_ = 1;
61 break;
62 case OtherFrequency:
63 QL_FAIL("unknown frequency"); // no point in showing 999...
64 default:
65 QL_FAIL("unknown frequency (" << Integer(f) << ")");
66 }
67 }
68
69 Frequency Period::frequency() const {
70 // unsigned version
71 Size length = std::abs(x: length_);
72
73 if (length==0) {
74 if (units_==Years) return Once;
75 return NoFrequency;
76 }
77
78 switch (units_) {
79 case Years:
80 if (length == 1)
81 return Annual;
82 else
83 return OtherFrequency;
84 case Months:
85 if (12%length == 0 && length <= 12)
86 return Frequency(12/length);
87 else
88 return OtherFrequency;
89 case Weeks:
90 if (length==1)
91 return Weekly;
92 else if (length==2)
93 return Biweekly;
94 else if (length==4)
95 return EveryFourthWeek;
96 else
97 return OtherFrequency;
98 case Days:
99 if (length==1)
100 return Daily;
101 else
102 return OtherFrequency;
103 default:
104 QL_FAIL("unknown time unit (" << Integer(units_) << ")");
105 }
106 }
107
108 void Period::normalize() {
109 if (length_ == 0) {
110 units_ = Days;
111 } else {
112 switch (units_) {
113 case Months:
114 if ((length_ % 12) == 0) {
115 length_ /= 12;
116 units_ = Years;
117 }
118 break;
119 case Days:
120 if ((length_ % 7) == 0) {
121 length_ /= 7;
122 units_ = Weeks;
123 }
124 break;
125 case Weeks:
126 case Years:
127 break;
128 default:
129 QL_FAIL("unknown time unit (" << Integer(units_) << ")");
130 }
131 }
132 }
133
134 Period& Period::operator+=(const Period& p) {
135
136 if (length_==0) {
137 length_ = p.length();
138 units_ = p.units();
139 } else if (units_==p.units()) {
140 // no conversion needed
141 length_ += p.length();
142 } else {
143 switch (units_) {
144
145 case Years:
146 switch (p.units()) {
147 case Months:
148 units_ = Months;
149 length_ = length_*12 + p.length();
150 break;
151 case Weeks:
152 case Days:
153 QL_REQUIRE(p.length()==0,
154 "impossible addition between " << *this <<
155 " and " << p);
156 break;
157 default:
158 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
159 }
160 break;
161
162 case Months:
163 switch (p.units()) {
164 case Years:
165 length_ += p.length()*12;
166 break;
167 case Weeks:
168 case Days:
169 QL_REQUIRE(p.length()==0,
170 "impossible addition between " << *this <<
171 " and " << p);
172 break;
173 default:
174 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
175 }
176 break;
177
178 case Weeks:
179 switch (p.units()) {
180 case Days:
181 units_ = Days;
182 length_ = length_*7 + p.length();
183 break;
184 case Years:
185 case Months:
186 QL_REQUIRE(p.length()==0,
187 "impossible addition between " << *this <<
188 " and " << p);
189 break;
190 default:
191 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
192 }
193 break;
194
195 case Days:
196 switch (p.units()) {
197 case Weeks:
198 length_ += p.length()*7;
199 break;
200 case Years:
201 case Months:
202 QL_REQUIRE(p.length()==0,
203 "impossible addition between " << *this <<
204 " and " << p);
205 break;
206 default:
207 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
208 }
209 break;
210
211 default:
212 QL_FAIL("unknown time unit (" << Integer(units_) << ")");
213 }
214 }
215
216 return *this;
217 }
218
219 Period& Period::operator-=(const Period& p) {
220 return operator+=(p: -p);
221 }
222
223 Period& Period::operator*=(Integer n) {
224 length_ *= n;
225 return *this;
226 }
227
228 Period& Period::operator/=(Integer n) {
229 QL_REQUIRE(n != 0, "cannot be divided by zero");
230 if (length_ % n == 0) {
231 // keep the original units. If the user created a
232 // 24-months period, he'll probably want a 12-months one
233 // when he halves it.
234 length_ /= n;
235 } else {
236 // try
237 TimeUnit units = units_;
238 Integer length = length_;
239 switch (units) {
240 case Years:
241 length *= 12;
242 units = Months;
243 break;
244 case Weeks:
245 length *= 7;
246 units = Days;
247 break;
248 default:
249 ;
250 }
251 QL_REQUIRE(length % n == 0,
252 *this << " cannot be divided by " << n);
253 length_ = length/n;
254 units_ = units;
255 }
256 return *this;
257 }
258
259
260 namespace {
261
262 std::pair<Integer,Integer> daysMinMax(const Period& p) {
263 switch (p.units()) {
264 case Days:
265 return std::make_pair(x: p.length(), y: p.length());
266 case Weeks:
267 return std::make_pair(x: 7*p.length(), y: 7*p.length());
268 case Months:
269 return std::make_pair(x: 28*p.length(), y: 31*p.length());
270 case Years:
271 return std::make_pair(x: 365*p.length(), y: 366*p.length());
272 default:
273 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
274 }
275 }
276
277 }
278
279 Real years(const Period& p) {
280 if (p.length()==0) return 0.0;
281
282 switch (p.units()) {
283 case Days:
284 QL_FAIL("cannot convert Days into Years");
285 case Weeks:
286 QL_FAIL("cannot convert Weeks into Years");
287 case Months:
288 return p.length()/12.0;
289 case Years:
290 return p.length();
291 default:
292 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
293 }
294 }
295
296 Real months(const Period& p) {
297 if (p.length()==0) return 0.0;
298
299 switch (p.units()) {
300 case Days:
301 QL_FAIL("cannot convert Days into Months");
302 case Weeks:
303 QL_FAIL("cannot convert Weeks into Months");
304 case Months:
305 return p.length();
306 case Years:
307 return p.length()*12.0;
308 default:
309 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
310 }
311 }
312
313 Real weeks(const Period& p) {
314 if (p.length()==0) return 0.0;
315
316 switch (p.units()) {
317 case Days:
318 return p.length()/7.0;
319 case Weeks:
320 return p.length();
321 case Months:
322 QL_FAIL("cannot convert Months into Weeks");
323 case Years:
324 QL_FAIL("cannot convert Years into Weeks");
325 default:
326 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
327 }
328 }
329
330 Real days(const Period& p) {
331 if (p.length()==0) return 0.0;
332
333 switch (p.units()) {
334 case Days:
335 return p.length();
336 case Weeks:
337 return p.length()*7.0;
338 case Months:
339 QL_FAIL("cannot convert Months into Days");
340 case Years:
341 QL_FAIL("cannot convert Years into Days");
342 default:
343 QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
344 }
345 }
346
347 bool operator<(const Period& p1, const Period& p2) {
348
349 // special cases
350 if (p1.length() == 0)
351 return p2.length() > 0;
352 if (p2.length() == 0)
353 return p1.length() < 0;
354
355 // exact comparisons
356 if (p1.units() == p2.units())
357 return p1.length() < p2.length();
358 if (p1.units() == Months && p2.units() == Years)
359 return p1.length() < 12*p2.length();
360 if (p1.units() == Years && p2.units() == Months)
361 return 12*p1.length() < p2.length();
362 if (p1.units() == Days && p2.units() == Weeks)
363 return p1.length() < 7*p2.length();
364 if (p1.units() == Weeks && p2.units() == Days)
365 return 7*p1.length() < p2.length();
366
367 // inexact comparisons (handled by converting to days and using limits)
368 std::pair<Integer, Integer> p1lim = daysMinMax(p: p1);
369 std::pair<Integer, Integer> p2lim = daysMinMax(p: p2);
370
371 if (p1lim.second < p2lim.first)
372 return true;
373 else if (p1lim.first > p2lim.second)
374 return false;
375 else
376 QL_FAIL("undecidable comparison between " << p1 << " and " << p2);
377 }
378
379
380 Period operator+(const Period& p1, const Period& p2) {
381 Period result = p1;
382 result += p2;
383 return result;
384 }
385
386 Period operator-(const Period& p1, const Period& p2) {
387 return p1+(-p2);
388 }
389
390 Period operator/(const Period& p, Integer n) {
391 Period result = p;
392 result /= n;
393 return result;
394 }
395
396 // period formatting
397
398 std::ostream& operator<<(std::ostream& out, const Period& p) {
399 return out << io::short_period(p);
400 }
401
402 namespace detail {
403
404 std::ostream& operator<<(std::ostream& out,
405 const long_period_holder& holder) {
406 Integer n = holder.p.length();
407 switch (holder.p.units()) {
408 case Days:
409 return out << n << (n == 1 ? " day" : " days");
410 case Weeks:
411 return out << n << (n == 1 ? " week" : " weeks");
412 case Months:
413 return out << n << (n == 1 ? " month" : " months");
414 case Years:
415 return out << n << (n == 1 ? " year" : " years");
416 default:
417 QL_FAIL("unknown time unit (" << Integer(holder.p.units()) << ")");
418 }
419 }
420
421 std::ostream& operator<<(std::ostream& out,
422 const short_period_holder& holder) {
423 Integer n = holder.p.length();
424 switch (holder.p.units()) {
425 case Days:
426 return out << n << "D";
427 case Weeks:
428 return out << n << "W";
429 case Months:
430 return out << n << "M";
431 case Years:
432 return out << n << "Y";
433 default:
434 QL_FAIL("unknown time unit (" << Integer(holder.p.units()) << ")");
435 }
436 }
437
438 }
439
440 namespace io {
441
442 detail::long_period_holder long_period(const Period& p) {
443 return detail::long_period_holder(p);
444 }
445
446 detail::short_period_holder short_period(const Period& p) {
447 return detail::short_period_holder(p);
448 }
449
450 }
451
452}
453

source code of quantlib/ql/time/period.cpp