Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/time/period.cpp
Line
Count
Source (jump to first uncovered line)
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
 <https://www.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
27
namespace QuantLib {
28
29
170k
    Period::Period(Frequency f) {
30
170k
        switch (f) {
31
0
          case NoFrequency:
32
            // same as Period()
33
0
            units_ = Days;
34
0
            length_ = 0;
35
0
            break;
36
0
          case Once:
37
0
            units_ = Years;
38
0
            length_ = 0;
39
0
            break;
40
0
          case Annual:
41
0
            units_ = Years;
42
0
            length_ = 1;
43
0
            break;
44
0
          case Semiannual:
45
0
          case EveryFourthMonth:
46
0
          case Quarterly:
47
0
          case Bimonthly:
48
170k
          case Monthly:
49
170k
            units_ = Months;
50
170k
            length_ = 12/f;
51
170k
            break;
52
0
          case EveryFourthWeek:
53
0
          case Biweekly:
54
0
          case Weekly:
55
0
            units_ = Weeks;
56
0
            length_ = 52/f;
57
0
            break;
58
0
          case Daily:
59
0
            units_ = Days;
60
0
            length_ = 1;
61
0
            break;
62
0
          case OtherFrequency:
63
0
            QL_FAIL("unknown frequency");  // no point in showing 999...
64
0
          default:
65
0
            QL_FAIL("unknown frequency (" << Integer(f) << ")");
66
170k
        }
67
170k
    }
68
69
85.0k
    Frequency Period::frequency() const {
70
        // unsigned version
71
85.0k
        Size length = std::abs(length_);
72
73
85.0k
        if (length==0) {
74
0
            if (units_==Years) return Once;
75
0
            return NoFrequency;
76
0
        }
77
78
85.0k
        switch (units_) {
79
0
          case Years:
80
0
            if (length == 1)
81
0
                return Annual;
82
0
            else
83
0
                return OtherFrequency;
84
85.0k
          case Months:
85
85.0k
            if (12%length == 0 && length <= 12)
86
85.0k
                return Frequency(12/length);
87
0
            else
88
0
                return OtherFrequency;
89
0
          case Weeks:
90
0
            if (length==1)
91
0
                return Weekly;
92
0
            else if (length==2)
93
0
                return Biweekly;
94
0
            else if (length==4)
95
0
                return EveryFourthWeek;
96
0
            else
97
0
                return OtherFrequency;
98
0
          case Days:
99
0
            if (length==1)
100
0
                return Daily;
101
0
            else
102
0
                return OtherFrequency;
103
0
          default:
104
0
            QL_FAIL("unknown time unit (" << Integer(units_) << ")");
105
85.0k
        }
106
85.0k
    }
107
108
0
    void Period::normalize() {
109
0
        if (length_ == 0) {
110
0
            units_ = Days;
111
0
        } else {
112
0
            switch (units_) {
113
0
              case Months:
114
0
                if ((length_ % 12) == 0) {
115
0
                    length_ /= 12;
116
0
                    units_ = Years;
117
0
                }
118
0
                break;
119
0
              case Days:
120
0
                if ((length_ % 7) == 0) {
121
0
                    length_ /= 7;
122
0
                    units_ = Weeks;
123
0
                }
124
0
                break;
125
0
              case Weeks:
126
0
              case Years:
127
0
                break;
128
0
              default:
129
0
                QL_FAIL("unknown time unit (" << Integer(units_) << ")");
130
0
            }
131
0
        }
132
0
    }
133
134
0
    Period& Period::operator+=(const Period& p) {
135
136
0
        if (length_==0) {
137
0
            length_ = p.length();
138
0
            units_ = p.units();
139
0
        } else if (units_==p.units()) {
140
            // no conversion needed
141
0
            length_ += p.length();
142
0
        } else {
143
0
            switch (units_) {
144
145
0
              case Years:
146
0
                switch (p.units()) {
147
0
                  case Months:
148
0
                    units_ = Months;
149
0
                    length_ = length_*12 + p.length();
150
0
                    break;
151
0
                  case Weeks:
152
0
                  case Days:
153
0
                    QL_REQUIRE(p.length()==0,
154
0
                               "impossible addition between " << *this <<
155
0
                               " and " << p);
156
0
                    break;
157
0
                  default:
158
0
                    QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
159
0
                }
160
0
                break;
161
162
0
              case Months:
163
0
                switch (p.units()) {
164
0
                  case Years:
165
0
                    length_ += p.length()*12;
166
0
                    break;
167
0
                  case Weeks:
168
0
                  case Days:
169
0
                    QL_REQUIRE(p.length()==0,
170
0
                               "impossible addition between " << *this <<
171
0
                               " and " << p);
172
0
                    break;
173
0
                  default:
174
0
                    QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
175
0
                }
176
0
                break;
177
178
0
              case Weeks:
179
0
                switch (p.units()) {
180
0
                  case Days:
181
0
                    units_ = Days;
182
0
                    length_ = length_*7 + p.length();
183
0
                    break;
184
0
                  case Years:
185
0
                  case Months:
186
0
                    QL_REQUIRE(p.length()==0,
187
0
                               "impossible addition between " << *this <<
188
0
                               " and " << p);
189
0
                    break;
190
0
                  default:
191
0
                    QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
192
0
                }
193
0
                break;
194
195
0
              case Days:
196
0
                switch (p.units()) {
197
0
                  case Weeks:
198
0
                    length_ += p.length()*7;
199
0
                    break;
200
0
                  case Years:
201
0
                  case Months:
202
0
                    QL_REQUIRE(p.length()==0,
203
0
                               "impossible addition between " << *this <<
204
0
                               " and " << p);
205
0
                    break;
206
0
                  default:
207
0
                    QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
208
0
                }
209
0
                break;
210
211
0
              default:
212
0
                QL_FAIL("unknown time unit (" << Integer(units_) << ")");
213
0
            }
214
0
        }
215
216
0
        return *this;
217
0
    }
218
219
0
    Period& Period::operator-=(const Period& p) {
220
0
        return operator+=(-p);
221
0
    }
222
223
0
    Period& Period::operator*=(Integer n) {
224
0
        length_ *= n;
225
0
        return *this;
226
0
    }
227
228
0
    Period& Period::operator/=(Integer n) {
229
0
        QL_REQUIRE(n != 0, "cannot be divided by zero");
230
0
        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
0
            length_ /= n;
235
0
        } else {
236
            // try
237
0
            TimeUnit units = units_;
238
0
            Integer length = length_;
239
0
            switch (units) {
240
0
              case Years:
241
0
                length *= 12;
242
0
                units = Months;
243
0
                break;
244
0
              case Weeks:
245
0
                length *= 7;
246
0
                units = Days;
247
0
                break;
248
0
              default:
249
0
                ;
250
0
            }
251
0
            QL_REQUIRE(length % n == 0,
252
0
                       *this << " cannot be divided by " << n);
253
0
            length_ = length/n;
254
0
            units_ = units;
255
0
        }
256
0
        return *this;
257
0
    }
258
259
260
    namespace {
261
262
0
        std::pair<Integer,Integer> daysMinMax(const Period& p) {
263
0
            switch (p.units()) {
264
0
              case Days:
265
0
                return std::make_pair(p.length(), p.length());
266
0
              case Weeks:
267
0
                return std::make_pair(7*p.length(), 7*p.length());
268
0
              case Months:
269
0
                return std::make_pair(28*p.length(), 31*p.length());
270
0
              case Years:
271
0
                return std::make_pair(365*p.length(), 366*p.length());
272
0
              default:
273
0
                QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
274
0
            }
275
0
        }
276
277
    }
278
279
0
    Real years(const Period& p) {
280
0
        if (p.length()==0) return 0.0;
281
282
0
        switch (p.units()) {
283
0
          case Days:
284
0
            QL_FAIL("cannot convert Days into Years");
285
0
          case Weeks:
286
0
            QL_FAIL("cannot convert Weeks into Years");
287
0
          case Months:
288
0
              return p.length()/12.0;
289
0
          case Years:
290
0
              return p.length();
291
0
          default:
292
0
            QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
293
0
        }
294
0
    }
295
296
0
    Real months(const Period& p) {
297
0
        if (p.length()==0) return 0.0;
298
299
0
        switch (p.units()) {
300
0
          case Days:
301
0
            QL_FAIL("cannot convert Days into Months");
302
0
          case Weeks:
303
0
            QL_FAIL("cannot convert Weeks into Months");
304
0
          case Months:
305
0
              return p.length();
306
0
          case Years:
307
0
              return p.length()*12.0;
308
0
          default:
309
0
            QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
310
0
        }
311
0
    }
312
313
0
    Real weeks(const Period& p) {
314
0
        if (p.length()==0) return 0.0;
315
316
0
        switch (p.units()) {
317
0
          case Days:
318
0
              return p.length()/7.0;
319
0
          case Weeks:
320
0
              return p.length();
321
0
          case Months:
322
0
            QL_FAIL("cannot convert Months into Weeks");
323
0
          case Years:
324
0
            QL_FAIL("cannot convert Years into Weeks");
325
0
          default:
326
0
            QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
327
0
        }
328
0
    }
329
330
0
    Real days(const Period& p) {
331
0
        if (p.length()==0) return 0.0;
332
333
0
        switch (p.units()) {
334
0
          case Days:
335
0
              return p.length();
336
0
          case Weeks:
337
0
              return p.length()*7.0;
338
0
          case Months:
339
0
            QL_FAIL("cannot convert Months into Days");
340
0
          case Years:
341
0
            QL_FAIL("cannot convert Years into Days");
342
0
          default:
343
0
            QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
344
0
        }
345
0
    }
346
347
62.1M
    bool operator<(const Period& p1, const Period& p2) {
348
349
        // special cases
350
62.1M
        if (p1.length() == 0)
351
61.2M
            return p2.length() > 0;
352
850k
        if (p2.length() == 0)
353
0
            return p1.length() < 0;
354
355
        // exact comparisons
356
850k
        if (p1.units() == p2.units())
357
85.0k
            return p1.length() < p2.length();
358
765k
        if (p1.units() == Months && p2.units() == Years)
359
680k
            return p1.length() < 12*p2.length();
360
85.0k
        if (p1.units() == Years && p2.units() == Months)
361
85.0k
            return 12*p1.length() < p2.length();
362
0
        if (p1.units() == Days && p2.units() == Weeks)
363
0
            return p1.length() < 7*p2.length();
364
0
        if (p1.units() == Weeks && p2.units() == Days)
365
0
            return 7*p1.length() < p2.length();
366
367
        // inexact comparisons (handled by converting to days and using limits)
368
0
        std::pair<Integer, Integer> p1lim = daysMinMax(p1);
369
0
        std::pair<Integer, Integer> p2lim = daysMinMax(p2);
370
371
0
        if (p1lim.second < p2lim.first)
372
0
            return true;
373
0
        else if (p1lim.first > p2lim.second)
374
0
            return false;
375
0
        else
376
0
            QL_FAIL("undecidable comparison between " << p1 << " and " << p2);
377
0
    }
378
379
380
0
    Period operator+(const Period& p1, const Period& p2) {
381
0
        Period result = p1;
382
0
        result += p2;
383
0
        return result;
384
0
    }
385
386
0
    Period operator-(const Period& p1, const Period& p2) {
387
0
        return p1+(-p2);
388
0
    }
389
390
0
    Period operator/(const Period& p, Integer n) {
391
0
        Period result = p;
392
0
        result /= n;
393
0
        return result;
394
0
    }
395
396
    // period formatting
397
398
0
    std::ostream& operator<<(std::ostream& out, const Period& p) {
399
0
        return out << io::short_period(p);
400
0
    }
401
402
    namespace detail {
403
404
        std::ostream& operator<<(std::ostream& out,
405
0
                                 const long_period_holder& holder) {
406
0
            Integer n = holder.p.length();
407
0
            switch (holder.p.units()) {
408
0
              case Days:
409
0
                return out << n << (n == 1 ? " day" : " days");
410
0
              case Weeks:
411
0
                return out << n << (n == 1 ? " week" : " weeks");
412
0
              case Months:
413
0
                return out << n << (n == 1 ? " month" : " months");
414
0
              case Years:
415
0
                return out << n << (n == 1 ? " year" : " years");
416
0
              default:
417
0
                QL_FAIL("unknown time unit (" << Integer(holder.p.units()) << ")");
418
0
            }
419
0
        }
420
421
        std::ostream& operator<<(std::ostream& out,
422
0
                                 const short_period_holder& holder) {
423
0
            Integer n = holder.p.length();
424
0
            switch (holder.p.units()) {
425
0
              case Days:
426
0
                return out << n << "D";
427
0
              case Weeks:
428
0
                return out << n << "W";
429
0
              case Months:
430
0
                return out << n << "M";
431
0
              case Years:
432
0
                return out << n << "Y";
433
0
              default:
434
0
                QL_FAIL("unknown time unit (" << Integer(holder.p.units()) << ")");
435
0
            }
436
0
        }
437
438
    }
439
440
    namespace io {
441
442
0
        detail::long_period_holder long_period(const Period& p) {
443
0
            return detail::long_period_holder(p);
444
0
        }
445
446
0
        detail::short_period_holder short_period(const Period& p) {
447
0
            return detail::short_period_holder(p);
448
0
        }
449
450
    }
451
452
}