Coverage Report

Created: 2025-08-28 06:30

/src/quantlib/ql/time/date.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) 2000, 2001, 2002, 2003 RiskMap srl
5
 Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl
6
 Copyright (C) 2004, 2005, 2006 Ferdinando Ametrano
7
 Copyright (C) 2006 Katiuscia Manzoni
8
 Copyright (C) 2006 Toyin Akin
9
 Copyright (C) 2015 Klaus Spanderen
10
 Copyright (C) 2020 Leonardo Arcari
11
 Copyright (C) 2020 Kline s.r.l.
12
13
 This file is part of QuantLib, a free-software/open-source library
14
 for financial quantitative analysts and developers - http://quantlib.org/
15
16
 QuantLib is free software: you can redistribute it and/or modify it
17
 under the terms of the QuantLib license.  You should have received a
18
 copy of the license along with this program; if not, please email
19
 <quantlib-dev@lists.sf.net>. The license is also available online at
20
 <http://quantlib.org/license.shtml>.
21
22
 This program is distributed in the hope that it will be useful, but WITHOUT
23
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24
 FOR A PARTICULAR PURPOSE.  See the license for more details.
25
*/
26
27
#include <ql/time/date.hpp>
28
#include <ql/utilities/dataformatters.hpp>
29
#include <ql/errors.hpp>
30
#include <boost/date_time/gregorian/gregorian.hpp>
31
#include <boost/date_time/posix_time/posix_time_types.hpp>
32
#include <functional>
33
#include <iomanip>
34
#include <ctime>
35
36
#ifdef QL_HIGH_RESOLUTION_DATE
37
#if BOOST_VERSION < 106700
38
#include <boost/functional/hash.hpp>
39
#else
40
#include <boost/container_hash/hash.hpp>
41
#endif
42
#endif
43
44
#if defined(BOOST_NO_STDC_NAMESPACE)
45
    namespace std { using ::time; using ::time_t; using ::tm;
46
                    using ::gmtime; using ::localtime; }
47
#endif
48
49
#ifdef QL_HIGH_RESOLUTION_DATE
50
using boost::posix_time::ptime;
51
using boost::posix_time::time_duration;
52
#endif
53
54
55
namespace QuantLib {
56
#ifndef QL_HIGH_RESOLUTION_DATE
57
    // constructors
58
    Date::Date()
59
386M
    : serialNumber_(Date::serial_type(0)) {}
60
61
    Date::Date(Date::serial_type serialNumber)
62
509k
    : serialNumber_(serialNumber) {
63
509k
        checkSerialNumber(serialNumber);
64
509k
    }
65
66
32.4M
    Date::Date(Day d, Month m, Year y) {
67
32.4M
        QL_REQUIRE(y > 1900 && y < 2200,
68
32.4M
                   "year " << y << " out of bound. It must be in [1901,2199]");
69
32.4M
        QL_REQUIRE(Integer(m) > 0 && Integer(m) < 13,
70
32.4M
                   "month " << Integer(m)
71
32.4M
                   << " outside January-December range [1,12]");
72
73
32.4M
        bool leap = isLeap(y);
74
32.4M
        Day len = monthLength(m,leap), offset = monthOffset(m,leap);
75
32.4M
        QL_REQUIRE(d <= len && d > 0,
76
32.4M
                   "day outside month (" << Integer(m) << ") day-range "
77
32.4M
                   << "[1," << len << "]");
78
79
32.4M
        serialNumber_ = d + offset + yearOffset(y);
80
32.4M
    }
81
82
63.8M
    Month Date::month() const {
83
63.8M
        Day d = dayOfYear(); // dayOfYear is 1 based
84
63.8M
        Integer m = d/30 + 1;
85
63.8M
        bool leap = isLeap(year());
86
127M
        while (d <= monthOffset(Month(m),leap))
87
63.8M
            --m;
88
63.8M
        while (d > monthOffset(Month(m+1),leap)) // NOLINT(misc-misplaced-widening-cast)
89
0
            ++m;
90
63.8M
        return Month(m);
91
63.8M
    }
92
93
223M
    Year Date::year() const {
94
223M
        Year y = (serialNumber_ / 365)+1900;
95
        // yearOffset(y) is December 31st of the preceding year
96
223M
        if (serialNumber_ <= yearOffset(y))
97
2.33k
            --y;
98
223M
        return y;
99
223M
    }
100
101
0
    Date& Date::operator+=(Date::serial_type days) {
102
0
        Date::serial_type serial = serialNumber_ + days;
103
0
        checkSerialNumber(serial);
104
0
        serialNumber_ = serial;
105
0
        return *this;
106
0
    }
107
108
0
    Date& Date::operator+=(const Period& p) {
109
0
        serialNumber_ = advance(*this,p.length(),p.units()).serialNumber();
110
0
        return *this;
111
0
    }
112
113
0
    Date& Date::operator-=(Date::serial_type days) {
114
0
        Date::serial_type serial = serialNumber_ - days;
115
0
        checkSerialNumber(serial);
116
0
        serialNumber_ = serial;
117
0
        return *this;
118
0
    }
119
120
0
    Date& Date::operator-=(const Period& p) {
121
0
        serialNumber_ = advance(*this,-p.length(),p.units()).serialNumber();
122
0
        return *this;
123
0
    }
124
125
0
    Date& Date::operator++() {
126
0
        Date::serial_type serial = serialNumber_ + 1;
127
0
        checkSerialNumber(serial);
128
0
        serialNumber_ = serial;
129
0
        return *this;
130
0
    }
131
132
0
    Date& Date::operator--() {
133
0
        Date::serial_type serial = serialNumber_ - 1;
134
0
        checkSerialNumber(serial);
135
0
        serialNumber_ = serial;
136
0
        return *this;
137
0
    }
138
139
31.9M
    Date Date::advance(const Date& date, Integer n, TimeUnit units) {
140
31.9M
        switch (units) {
141
0
          case Days:
142
0
            return date + n;
143
0
          case Weeks:
144
0
            return date + 7*n;
145
31.8M
          case Months: {
146
31.8M
            Day d = date.dayOfMonth();
147
31.8M
            Integer m = Integer(date.month())+n;
148
31.8M
            Year y = date.year();
149
31.8M
            while (m > 12) {
150
0
                m -= 12;
151
0
                y += 1;
152
0
            }
153
508M
            while (m < 1) {
154
476M
                m += 12;
155
476M
                y -= 1;
156
476M
            }
157
158
31.8M
            QL_ENSURE(y >= 1900 && y <= 2199,
159
31.8M
                      "year " << y << " out of bounds. "
160
31.8M
                      << "It must be in [1901,2199]");
161
162
31.8M
            Integer length = monthLength(Month(m), isLeap(y));
163
31.8M
            if (d > length)
164
0
                d = length;
165
166
31.8M
            return {d, Month(m), y};
167
31.8M
          }
168
88.1k
          case Years: {
169
88.1k
              Day d = date.dayOfMonth();
170
88.1k
              Month m = date.month();
171
88.1k
              Year y = date.year()+n;
172
173
88.1k
              QL_ENSURE(y >= 1900 && y <= 2199,
174
88.1k
                        "year " << y << " out of bounds. "
175
88.1k
                        << "It must be in [1901,2199]");
176
177
88.1k
              if (d == 29 && m == February && !isLeap(y))
178
0
                  d = 28;
179
180
88.1k
              return {d, m, y};
181
88.1k
          }
182
0
          default:
183
0
            QL_FAIL("undefined time units");
184
31.9M
        }
185
31.9M
    }
186
187
160M
    bool Date::isLeap(Year y) {
188
160M
        static const bool YearIsLeap[] = {
189
            // 1900 is leap in agreement with Excel's bug
190
            // 1900 is out of valid date range anyway
191
            // 1900-1909
192
160M
             true,false,false,false, true,false,false,false, true,false,
193
            // 1910-1919
194
160M
            false,false, true,false,false,false, true,false,false,false,
195
            // 1920-1929
196
160M
             true,false,false,false, true,false,false,false, true,false,
197
            // 1930-1939
198
160M
            false,false, true,false,false,false, true,false,false,false,
199
            // 1940-1949
200
160M
             true,false,false,false, true,false,false,false, true,false,
201
            // 1950-1959
202
160M
            false,false, true,false,false,false, true,false,false,false,
203
            // 1960-1969
204
160M
             true,false,false,false, true,false,false,false, true,false,
205
            // 1970-1979
206
160M
            false,false, true,false,false,false, true,false,false,false,
207
            // 1980-1989
208
160M
             true,false,false,false, true,false,false,false, true,false,
209
            // 1990-1999
210
160M
            false,false, true,false,false,false, true,false,false,false,
211
            // 2000-2009
212
160M
             true,false,false,false, true,false,false,false, true,false,
213
            // 2010-2019
214
160M
            false,false, true,false,false,false, true,false,false,false,
215
            // 2020-2029
216
160M
             true,false,false,false, true,false,false,false, true,false,
217
            // 2030-2039
218
160M
            false,false, true,false,false,false, true,false,false,false,
219
            // 2040-2049
220
160M
             true,false,false,false, true,false,false,false, true,false,
221
            // 2050-2059
222
160M
            false,false, true,false,false,false, true,false,false,false,
223
            // 2060-2069
224
160M
             true,false,false,false, true,false,false,false, true,false,
225
            // 2070-2079
226
160M
            false,false, true,false,false,false, true,false,false,false,
227
            // 2080-2089
228
160M
             true,false,false,false, true,false,false,false, true,false,
229
            // 2090-2099
230
160M
            false,false, true,false,false,false, true,false,false,false,
231
            // 2100-2109
232
160M
            false,false,false,false, true,false,false,false, true,false,
233
            // 2110-2119
234
160M
            false,false, true,false,false,false, true,false,false,false,
235
            // 2120-2129
236
160M
             true,false,false,false, true,false,false,false, true,false,
237
            // 2130-2139
238
160M
            false,false, true,false,false,false, true,false,false,false,
239
            // 2140-2149
240
160M
             true,false,false,false, true,false,false,false, true,false,
241
            // 2150-2159
242
160M
            false,false, true,false,false,false, true,false,false,false,
243
            // 2160-2169
244
160M
             true,false,false,false, true,false,false,false, true,false,
245
            // 2170-2179
246
160M
            false,false, true,false,false,false, true,false,false,false,
247
            // 2180-2189
248
160M
             true,false,false,false, true,false,false,false, true,false,
249
            // 2190-2199
250
160M
            false,false, true,false,false,false, true,false,false,false,
251
            // 2200
252
160M
            false
253
160M
        };
254
160M
        QL_REQUIRE(y>=1900 && y<=2200, "year outside valid range");
255
160M
        return YearIsLeap[y-1900];
256
160M
    }
257
258
259
64.2M
    Integer Date::monthLength(Month m, bool leapYear) {
260
64.2M
        static const Integer MonthLength[] = {
261
64.2M
            31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
262
64.2M
        };
263
64.2M
        static const Integer MonthLeapLength[] = {
264
64.2M
            31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
265
64.2M
        };
266
64.2M
        return (leapYear? MonthLeapLength[m-1] : MonthLength[m-1]);
267
64.2M
    }
268
269
255M
    Integer Date::monthOffset(Month m, bool leapYear) {
270
255M
        static const Integer MonthOffset[] = {
271
255M
              0,  31,  59,  90, 120, 151,   // Jan - Jun
272
255M
            181, 212, 243, 273, 304, 334,   // Jun - Dec
273
255M
            365     // used in dayOfMonth to bracket day
274
255M
        };
275
255M
        static const Integer MonthLeapOffset[] = {
276
255M
              0,  31,  60,  91, 121, 152,   // Jan - Jun
277
255M
            182, 213, 244, 274, 305, 335,   // Jun - Dec
278
255M
            366     // used in dayOfMonth to bracket day
279
255M
        };
280
255M
        return (leapYear? MonthLeapOffset[m-1] : MonthOffset[m-1]);
281
255M
    }
282
283
351M
    Date::serial_type Date::yearOffset(Year y) {
284
        // the list of all December 31st in the preceding year
285
        // e.g. for 1901 yearOffset[1] is 366, that is, December 31 1900
286
351M
        static const Date::serial_type YearOffset[] = {
287
            // 1900-1909
288
351M
                0,  366,  731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
289
            // 1910-1919
290
351M
             3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
291
            // 1920-1929
292
351M
             7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862,10227,10593,
293
            // 1930-1939
294
351M
            10958,11323,11688,12054,12419,12784,13149,13515,13880,14245,
295
            // 1940-1949
296
351M
            14610,14976,15341,15706,16071,16437,16802,17167,17532,17898,
297
            // 1950-1959
298
351M
            18263,18628,18993,19359,19724,20089,20454,20820,21185,21550,
299
            // 1960-1969
300
351M
            21915,22281,22646,23011,23376,23742,24107,24472,24837,25203,
301
            // 1970-1979
302
351M
            25568,25933,26298,26664,27029,27394,27759,28125,28490,28855,
303
            // 1980-1989
304
351M
            29220,29586,29951,30316,30681,31047,31412,31777,32142,32508,
305
            // 1990-1999
306
351M
            32873,33238,33603,33969,34334,34699,35064,35430,35795,36160,
307
            // 2000-2009
308
351M
            36525,36891,37256,37621,37986,38352,38717,39082,39447,39813,
309
            // 2010-2019
310
351M
            40178,40543,40908,41274,41639,42004,42369,42735,43100,43465,
311
            // 2020-2029
312
351M
            43830,44196,44561,44926,45291,45657,46022,46387,46752,47118,
313
            // 2030-2039
314
351M
            47483,47848,48213,48579,48944,49309,49674,50040,50405,50770,
315
            // 2040-2049
316
351M
            51135,51501,51866,52231,52596,52962,53327,53692,54057,54423,
317
            // 2050-2059
318
351M
            54788,55153,55518,55884,56249,56614,56979,57345,57710,58075,
319
            // 2060-2069
320
351M
            58440,58806,59171,59536,59901,60267,60632,60997,61362,61728,
321
            // 2070-2079
322
351M
            62093,62458,62823,63189,63554,63919,64284,64650,65015,65380,
323
            // 2080-2089
324
351M
            65745,66111,66476,66841,67206,67572,67937,68302,68667,69033,
325
            // 2090-2099
326
351M
            69398,69763,70128,70494,70859,71224,71589,71955,72320,72685,
327
            // 2100-2109
328
351M
            73050,73415,73780,74145,74510,74876,75241,75606,75971,76337,
329
            // 2110-2119
330
351M
            76702,77067,77432,77798,78163,78528,78893,79259,79624,79989,
331
            // 2120-2129
332
351M
            80354,80720,81085,81450,81815,82181,82546,82911,83276,83642,
333
            // 2130-2139
334
351M
            84007,84372,84737,85103,85468,85833,86198,86564,86929,87294,
335
            // 2140-2149
336
351M
            87659,88025,88390,88755,89120,89486,89851,90216,90581,90947,
337
            // 2150-2159
338
351M
            91312,91677,92042,92408,92773,93138,93503,93869,94234,94599,
339
            // 2160-2169
340
351M
            94964,95330,95695,96060,96425,96791,97156,97521,97886,98252,
341
            // 2170-2179
342
351M
            98617,98982,99347,99713,100078,100443,100808,101174,101539,101904,
343
            // 2180-2189
344
351M
            102269,102635,103000,103365,103730,104096,104461,104826,105191,105557,
345
            // 2190-2199
346
351M
            105922,106287,106652,107018,107383,107748,108113,108479,108844,109209,
347
            // 2200
348
351M
            109574
349
351M
        };
350
351M
        return YearOffset[y-1900];
351
351M
    }
352
353
#else
354
355
    namespace {
356
        const boost::gregorian::date& serialNumberDateReference() {
357
            static const boost::gregorian::date dateReference(
358
                1899, boost::gregorian::Dec, 30);
359
            return dateReference;
360
        }
361
362
363
#define compatibleEnums (   int(boost::date_time::Monday)   +1 == Monday    \
364
                         && int(boost::date_time::Tuesday)  +1 == Tuesday   \
365
                         && int(boost::date_time::Wednesday)+1 == Wednesday \
366
                         && int(boost::date_time::Thursday) +1 == Thursday  \
367
                         && int(boost::date_time::Friday)   +1 == Friday    \
368
                         && int(boost::date_time::Saturday) +1 == Saturday  \
369
                         && int(boost::date_time::Sunday)   +1 == Sunday    \
370
                         && int(boost::date_time::Jan) == January           \
371
                         && int(boost::date_time::Feb) == February          \
372
                         && int(boost::date_time::Mar) == March             \
373
                         && int(boost::date_time::Apr) == April             \
374
                         && int(boost::date_time::May) == May               \
375
                         && int(boost::date_time::Jun) == June              \
376
                         && int(boost::date_time::Jul) == July              \
377
                         && int(boost::date_time::Aug) == August            \
378
                         && int(boost::date_time::Sep) == September         \
379
                         && int(boost::date_time::Oct) == October           \
380
                         && int(boost::date_time::Nov) == November          \
381
                         && int(boost::date_time::Dec) == December           )
382
383
        template <bool compatible>
384
        Weekday mapBoostDateType2QL(boost::gregorian::greg_weekday d) {
385
            if (compatible) {
386
                return Weekday(d.as_number() + 1);
387
            }
388
            else {
389
                switch (d) {
390
                  case boost::date_time::Monday   : return Monday;
391
                  case boost::date_time::Tuesday  : return Tuesday;
392
                  case boost::date_time::Wednesday: return Wednesday;
393
                  case boost::date_time::Thursday : return Thursday;
394
                  case boost::date_time::Friday   : return Friday;
395
                  case boost::date_time::Saturday : return Saturday;
396
                  case boost::date_time::Sunday   : return Sunday;
397
                  default:
398
                    QL_FAIL("unknown boost date_time day of week given");
399
                }
400
            }
401
        }
402
403
        template <bool compatible>
404
        Month mapBoostDateType2QL(boost::gregorian::greg_month m) {
405
            if (compatible) {
406
                return Month(m.as_number());
407
            }
408
            else {
409
                switch (m) {
410
                  case boost::date_time::Jan : return January;
411
                  case boost::date_time::Feb : return February;
412
                  case boost::date_time::Mar : return March;
413
                  case boost::date_time::Apr : return April;
414
                  case boost::date_time::May : return May;
415
                  case boost::date_time::Jun : return June;
416
                  case boost::date_time::Jul : return July;
417
                  case boost::date_time::Aug : return August;
418
                  case boost::date_time::Sep : return September;
419
                  case boost::date_time::Oct : return October;
420
                  case boost::date_time::Nov : return November;
421
                  case boost::date_time::Dec : return December;
422
                  default:
423
                    QL_FAIL("unknown boost date_time month of week given");
424
                }
425
            }
426
        }
427
428
429
        template <bool compatible>
430
        boost::gregorian::greg_month mapQLDateType2Boost(Month m) {
431
            if (compatible) {
432
                return boost::gregorian::greg_month(m);
433
            }
434
            else {
435
                switch (m) {
436
                  case January  : return boost::date_time::Jan;
437
                  case February : return boost::date_time::Feb;
438
                  case March    : return boost::date_time::Mar;
439
                  case April    : return boost::date_time::Apr;
440
                  case May      : return boost::date_time::May;
441
                  case June     : return boost::date_time::Jun;
442
                  case July     : return boost::date_time::Jul;
443
                  case August   : return boost::date_time::Aug;
444
                  case September: return boost::date_time::Sep;
445
                  case October  : return boost::date_time::Oct;
446
                  case November : return boost::date_time::Nov;
447
                  case December : return boost::date_time::Dec;
448
                  default:
449
                    QL_FAIL("unknown boost date_time month of week given");
450
                }
451
            }
452
        }
453
454
        void advance(ptime& dt, Integer n, TimeUnit units) {
455
            using boost::gregorian::gregorian_calendar;
456
457
            switch (units) {
458
              case Days:
459
                dt += boost::gregorian::days(n);
460
                break;
461
              case Weeks:
462
                dt += boost::gregorian::weeks(n);
463
                break;
464
              case Months:
465
              case Years : {
466
                  const boost::gregorian::date date = dt.date();
467
                  const Day eoM = gregorian_calendar::end_of_month_day(
468
                      date.year(), date.month());
469
470
                  if (units == Months) {
471
                      dt += boost::gregorian::months(n);
472
                  }
473
                  else {
474
                      dt += boost::gregorian::years(n);
475
                  }
476
477
                  if (date.day() == eoM) {
478
                      // avoid snap-to-end-of-month
479
                      // behavior of boost::date_time
480
                      const Day newEoM
481
                            = gregorian_calendar::end_of_month_day(
482
                                  dt.date().year(), dt.date().month());
483
484
                      if (newEoM > eoM) {
485
                          dt -= boost::gregorian::days(newEoM - eoM);
486
                      }
487
                  }
488
                 }
489
                break;
490
              case Hours:
491
                dt += boost::posix_time::hours(n);
492
                break;
493
              case Minutes:
494
                  dt += boost::posix_time::minutes(n);
495
                  break;
496
              case Seconds:
497
                dt += boost::posix_time::seconds(n);
498
                break;
499
              case Milliseconds:
500
                dt += boost::posix_time::milliseconds(n);
501
                break;
502
              case Microseconds:
503
                dt += boost::posix_time::microseconds(n);
504
                break;
505
              default:
506
                QL_FAIL("undefined time units");
507
           }
508
        }
509
510
        boost::gregorian::date gregorianDate(Year y, Month m, Day d) {
511
            QL_REQUIRE(y > 1900 && y < 2200,
512
                       "year " << y << " out of bound. It must be in [1901,2199]");
513
            QL_REQUIRE(Integer(m) > 0 && Integer(m) < 13,
514
                       "month " << Integer(m)
515
                       << " outside January-December range [1,12]");
516
517
            const boost::gregorian::greg_month bM
518
                = mapQLDateType2Boost<compatibleEnums>(m);
519
520
            const Day len =
521
                boost::gregorian::gregorian_calendar::end_of_month_day(y, bM);
522
            QL_REQUIRE(d <= len && d > 0,
523
                       "day outside month (" << Integer(m) << ") day-range "
524
                       << "[1," << len << "]");
525
526
            return boost::gregorian::date(y, bM, d);
527
        }
528
    }
529
530
531
    Date::Date()
532
    : dateTime_(serialNumberDateReference()) {}
533
534
    Date::Date(const ptime& dateTime)
535
    : dateTime_(dateTime) {}
536
537
    Date::Date(Day d, Month m, Year y)
538
    : dateTime_(gregorianDate(y, m, d)) {}
539
540
    Date::Date(Day d, Month m, Year y,
541
               Hour hours, Minute minutes, Second seconds,
542
               Millisecond millisec, Microsecond microsec)
543
    : dateTime_(
544
         gregorianDate(y, m, d),
545
         boost::posix_time::time_duration(
546
             hours, minutes, seconds,
547
               millisec*(time_duration::ticks_per_second()/1000)
548
             + microsec*(time_duration::ticks_per_second()/1000000))) {}
549
550
    Date::Date(Date::serial_type serialNumber)
551
    : dateTime_(
552
         serialNumberDateReference() +
553
         boost::gregorian::days(serialNumber)) {
554
        checkSerialNumber(serialNumber);
555
    }
556
557
    Weekday Date::weekday() const {
558
        return mapBoostDateType2QL<compatibleEnums>(
559
            dateTime_.date().day_of_week());
560
    }
561
562
    Day Date::dayOfMonth() const {
563
        return dateTime_.date().day();
564
    }
565
566
    Day Date::dayOfYear() const {
567
        return dateTime_.date().day_of_year();
568
    }
569
570
    Month Date::month() const {
571
        return mapBoostDateType2QL<compatibleEnums>(dateTime_.date().month());
572
    }
573
574
    Year Date::year() const {
575
        return dateTime_.date().year();
576
    }
577
578
    Hour Date::hours() const {
579
        return dateTime_.time_of_day().hours();
580
    }
581
582
    Minute Date::minutes() const {
583
        return dateTime_.time_of_day().minutes();
584
    }
585
586
    Second Date::seconds() const {
587
        return dateTime_.time_of_day().seconds();
588
    }
589
590
    Time Date::fractionOfDay() const {
591
        const time_duration t = dateTime().time_of_day();
592
593
        const Time seconds
594
            = (t.hours()*60.0 + t.minutes())*60.0 + t.seconds()
595
                + Real(t.fractional_seconds())/ticksPerSecond();
596
597
        return seconds/86400.0; // ignore any DST hocus-pocus
598
    }
599
600
    Time Date::fractionOfSecond() const {
601
        return dateTime_.time_of_day().fractional_seconds()
602
            /Real(ticksPerSecond());
603
    }
604
605
    Millisecond Date::milliseconds() const {
606
        return dateTime_.time_of_day().fractional_seconds()
607
               /(ticksPerSecond()/1000);
608
    }
609
610
    Microsecond Date::microseconds() const {
611
        return (dateTime_.time_of_day().fractional_seconds()
612
                - milliseconds()*(time_duration::ticks_per_second()/1000))
613
            /(ticksPerSecond()/1000000);
614
    }
615
616
    time_duration::tick_type Date::ticksPerSecond() {
617
        return time_duration::ticks_per_second();
618
    }
619
620
    Date::serial_type Date::serialNumber() const {
621
        const Date::serial_type n =
622
            (dateTime_.date() - serialNumberDateReference()).days();
623
        if (n != 0)
624
            checkSerialNumber(n);
625
        return n;
626
    }
627
628
    const ptime& Date::dateTime() const { return dateTime_; }
629
630
    Date& Date::operator+=(Date::serial_type d) {
631
        dateTime_ += boost::gregorian::days(d);
632
        return *this;
633
    }
634
635
    Date& Date::operator+=(const Period& p) {
636
        advance(dateTime_, p.length(), p.units());
637
        return *this;
638
    }
639
640
    Date& Date::operator-=(Date::serial_type d) {
641
        dateTime_ -= boost::gregorian::days(d);
642
        return *this;
643
    }
644
    Date& Date::operator-=(const Period& p) {
645
        advance(dateTime_, -p.length(), p.units());
646
        return *this;
647
    }
648
649
    Date& Date::operator++() {
650
        dateTime_ +=boost::gregorian::days(1);
651
        return *this;
652
    }
653
654
    Date& Date::operator--() {
655
        dateTime_ -=boost::gregorian::days(1);
656
        return *this;
657
    }
658
659
    Date Date::operator+(Date::serial_type days) const {
660
        Date retVal(*this);
661
        retVal+=days;
662
663
        return retVal;
664
    }
665
666
    Date Date::operator-(Date::serial_type days) const {
667
        Date retVal(*this);
668
        retVal-=days;
669
670
        return retVal;
671
    }
672
673
    Date Date::operator+(const Period& p) const {
674
        Date retVal(*this);
675
        retVal+=p;
676
677
        return retVal;
678
    }
679
680
    Date Date::operator-(const Period& p) const {
681
        Date retVal(*this);
682
        retVal-=p;
683
684
        return retVal;
685
    }
686
687
    Date Date::localDateTime() {
688
        return Date(boost::posix_time::microsec_clock::local_time());
689
    }
690
691
    Date Date::universalDateTime() {
692
        return Date(boost::posix_time::microsec_clock::universal_time());
693
    }
694
695
    bool Date::isLeap(Year y) {
696
        return boost::gregorian::gregorian_calendar::is_leap_year(y);
697
    }
698
699
    Date Date::endOfMonth(const Date& d) {
700
        const Month m = d.month();
701
        const Year y = d.year();
702
        const Day eoM = boost::gregorian::gregorian_calendar::end_of_month_day(
703
            d.year(), mapQLDateType2Boost<compatibleEnums>(d.month()));
704
705
        return Date(eoM, m, y);
706
    }
707
708
    bool Date::isEndOfMonth(const Date& d) {
709
        return d.dayOfMonth() ==
710
            boost::gregorian::gregorian_calendar::end_of_month_day(
711
                d.year(), mapQLDateType2Boost<compatibleEnums>(d.month()));
712
    }
713
714
715
    Date::serial_type operator-(const Date& d1, const Date& d2) {
716
        return (d1.dateTime().date() - d2.dateTime().date()).days();
717
    }
718
719
    Time daysBetween(const Date& d1, const Date& d2) {
720
        const Date::serial_type days = d2 - d1;
721
        return days + d2.fractionOfDay() - d1.fractionOfDay();
722
    }
723
724
    bool operator<(const Date& d1, const Date& d2) {
725
        return (d1.dateTime() < d2.dateTime());
726
    }
727
728
    bool operator<=(const Date& d1, const Date& d2) {
729
        return (d1.dateTime() <= d2.dateTime());
730
    }
731
732
    bool operator>(const Date& d1, const Date& d2) {
733
        return (d1.dateTime() > d2.dateTime());
734
    }
735
736
    bool operator>=(const Date& d1, const Date& d2) {
737
        return (d1.dateTime() >= d2.dateTime());
738
    }
739
740
    bool operator==(const Date& d1, const Date& d2) {
741
        return (d1.dateTime() == d2.dateTime());
742
    }
743
744
    bool operator!=(const Date& d1, const Date& d2) {
745
        return (d1.dateTime() != d2.dateTime());
746
    }
747
#endif
748
749
510k
    Date::serial_type Date::minimumSerialNumber() {
750
510k
        return 367;       // Jan 1st, 1901
751
510k
    }
752
753
510k
    Date::serial_type Date::maximumSerialNumber() {
754
510k
        return 109574;    // Dec 31st, 2199
755
510k
    }
756
757
509k
    void Date::checkSerialNumber(Date::serial_type serialNumber) {
758
509k
        QL_REQUIRE(serialNumber >= minimumSerialNumber() &&
759
509k
                   serialNumber <= maximumSerialNumber(),
760
509k
                   "Date's serial number (" << serialNumber << ") outside "
761
509k
                   "allowed range [" << minimumSerialNumber() <<
762
509k
                   "-" << maximumSerialNumber() << "], i.e. [" <<
763
509k
                   minDate() << "-" << maxDate() << "]");
764
509k
    }
765
766
334
    Date Date::minDate() {
767
334
        static const Date minimumDate(minimumSerialNumber());
768
334
        return minimumDate;
769
334
    }
770
771
334
    Date Date::maxDate() {
772
334
        static const Date maximumDate(maximumSerialNumber());
773
334
        return maximumDate;
774
334
    }
775
776
0
    Date Date::operator++(int ) {
777
0
        Date old(*this);
778
0
        ++*this; // use the pre-increment
779
0
        return old;
780
0
    }
781
782
0
    Date Date::operator--(int ) {
783
0
        Date old(*this);
784
0
        --*this; // use the pre-decrement
785
0
        return old;
786
0
    }
787
788
511k
    Date Date::todaysDate() {
789
511k
        std::time_t t;
790
791
511k
        if (std::time(&t) == std::time_t(-1)) // -1 means time() didn't work
792
0
            return {};
793
511k
        std::tm *lt = std::localtime(&t);
794
511k
        return {Day(lt->tm_mday), Month(lt->tm_mon + 1), Year(lt->tm_year + 1900)};
795
511k
    }
796
797
0
    Date Date::nextWeekday(const Date& d, Weekday dayOfWeek) {
798
0
        Weekday wd = d.weekday();
799
0
        return d + ((wd>dayOfWeek ? 7 : 0) - wd + dayOfWeek);
800
0
    }
801
802
    Date Date::nthWeekday(Size nth, Weekday dayOfWeek,
803
0
                          Month m, Year y) {
804
0
        QL_REQUIRE(nth>0,
805
0
                   "zeroth day of week in a given (month, year) is undefined");
806
0
        QL_REQUIRE(nth<6,
807
0
                   "no more than 5 weekday in a given (month, year)");
808
0
        Weekday first = Date(1, m, y).weekday();
809
0
        Size skip = nth - (dayOfWeek>=first ? 1 : 0);
810
0
        return {Day((1 + dayOfWeek + skip * 7) - first), m, y};
811
0
    }
812
813
    // month formatting
814
815
668
    std::ostream& operator<<(std::ostream& out, Month m) {
816
668
        switch (m) {
817
334
          case January:
818
334
            return out << "January";
819
0
          case February:
820
0
            return out << "February";
821
0
          case March:
822
0
            return out << "March";
823
0
          case April:
824
0
            return out << "April";
825
0
          case May:
826
0
            return out << "May";
827
0
          case June:
828
0
            return out << "June";
829
0
          case July:
830
0
            return out << "July";
831
0
          case August:
832
0
            return out << "August";
833
0
          case September:
834
0
            return out << "September";
835
0
          case October:
836
0
            return out << "October";
837
0
          case November:
838
0
            return out << "November";
839
334
          case December:
840
334
            return out << "December";
841
0
          default:
842
0
            QL_FAIL("unknown month (" << Integer(m) << ")");
843
668
        }
844
668
    }
845
846
0
    std::size_t hash_value(const Date& d) {
847
#ifdef QL_HIGH_RESOLUTION_DATE
848
        std::size_t seed = 0;
849
        boost::hash_combine(seed, d.serialNumber());
850
        boost::hash_combine(seed, d.dateTime().time_of_day().total_nanoseconds());
851
        return seed;
852
#else
853
0
        return std::hash<Date::serial_type>()(d.serialNumber());
854
0
#endif
855
0
    }
856
857
    // date formatting
858
859
668
    std::ostream& operator<<(std::ostream& out, const Date& d) {
860
668
        return out << io::long_date(d);
861
668
    }
862
863
    namespace detail {
864
865
        struct FormatResetter { // NOLINT(cppcoreguidelines-special-member-functions)
866
            // An instance of this object will have undefined behaviour
867
            // if the object out passed in the constructor is destroyed
868
            // before this instance
869
            struct nopunct : std::numpunct<char> {
870
1.33k
                std::string do_grouping() const override { return ""; }
871
            };
872
            explicit FormatResetter(std::ostream &out)
873
668
                : out_(&out), flags_(out.flags()), filler_(out.fill()),
874
668
                  loc_(out.getloc()) {
875
668
                std::locale loc (out.getloc(),new nopunct);
876
668
                out.imbue(loc);
877
668
                out << std::resetiosflags(
878
668
                    std::ios_base::adjustfield | std::ios_base::basefield |
879
668
                    std::ios_base::floatfield | std::ios_base::showbase |
880
668
                    std::ios_base::showpos | std::ios_base::uppercase);
881
668
                out << std::right;
882
668
            }
883
668
            ~FormatResetter() {
884
668
                out_->flags(flags_);
885
668
                out_->fill(filler_);
886
668
                out_->imbue(loc_);
887
668
            }
888
            std::ostream *out_;
889
            std::ios_base::fmtflags flags_;
890
            char filler_;
891
            std::locale loc_;
892
        };
893
894
        std::ostream& operator<<(std::ostream& out,
895
0
                                 const short_date_holder& holder) {
896
0
            const Date& d = holder.d;
897
0
            if (d == Date()) {
898
0
                out << "null date";
899
0
            } else {
900
0
                FormatResetter resetter(out);
901
0
                Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
902
0
                        yyyy = d.year();
903
0
                char filler = out.fill();
904
0
                out << std::setw(2) << std::setfill('0') << mm << "/";
905
0
                out << std::setw(2) << std::setfill('0') << dd << "/";
906
0
                out << yyyy;
907
0
                out.fill(filler);
908
0
            }
909
0
            return out;
910
0
        }
911
912
        std::ostream& operator<<(std::ostream& out,
913
668
                                 const long_date_holder& holder) {
914
668
            const Date& d = holder.d;
915
668
            if (d == Date()) {
916
0
                out << "null date";
917
668
            } else {
918
668
                FormatResetter resetter(out);
919
668
                out << d.month() << " ";
920
668
                out << io::ordinal(d.dayOfMonth()) << ", ";
921
668
                out << d.year();
922
668
            }
923
668
            return out;
924
668
        }
925
926
        std::ostream& operator<<(std::ostream& out,
927
0
                                 const iso_date_holder& holder) {
928
0
            const Date& d = holder.d;
929
0
            if (d == Date()) {
930
0
                out << "null date";
931
0
            } else {
932
0
                FormatResetter resetter(out);
933
0
                Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
934
0
                        yyyy = d.year();
935
0
                out << yyyy << "-";
936
0
                out << std::setw(2) << std::setfill('0') << mm << "-";
937
0
                out << std::setw(2) << std::setfill('0') << dd;
938
0
            }
939
0
            return out;
940
0
        }
941
942
        std::ostream& operator<<(std::ostream& out,
943
0
                                 const formatted_date_holder& holder) {
944
0
            using namespace boost::gregorian;
945
0
            const Date& d = holder.d;
946
0
            if (d == Date()) {
947
0
                out << "null date";
948
0
            } else {
949
0
                FormatResetter resetter(out);
950
0
                date boostDate(d.year(), d.month(), d.dayOfMonth());
951
0
                out.imbue(std::locale(std::locale(),
952
0
                                      new date_facet(holder.f.c_str())));
953
0
                out << boostDate;
954
0
            }
955
0
            return out;
956
0
        }
957
958
#ifdef QL_HIGH_RESOLUTION_DATE
959
        std::ostream& operator<<(std::ostream& out,
960
                                 const iso_datetime_holder& holder) {
961
            const Date& d = holder.d;
962
963
            out << io::iso_date(d) << "T";
964
            FormatResetter resetter(out);
965
            const Hour hh= d.hours();
966
            const Minute mm = d.minutes();
967
            const Second s = d.seconds();
968
            const Millisecond millis = d.milliseconds();
969
            const Microsecond micros = d.microseconds();
970
971
            out << std::setw(2) << std::setfill('0') << hh << ":"
972
                << std::setw(2) << std::setfill('0') << mm << ":"
973
                << std::setw(2) << std::setfill('0') << s << ","
974
                << std::setw(3) << std::setfill('0') << millis
975
                << std::setw(3) << std::setfill('0') << micros;
976
977
            return out;
978
        }
979
#endif
980
    }
981
982
    namespace io {
983
0
        detail::short_date_holder short_date(const Date& d) {
984
0
            return detail::short_date_holder(d);
985
0
        }
986
987
668
        detail::long_date_holder long_date(const Date& d) {
988
668
            return detail::long_date_holder(d);
989
668
        }
990
991
0
        detail::iso_date_holder iso_date(const Date& d) {
992
0
            return detail::iso_date_holder(d);
993
0
        }
994
995
        detail::formatted_date_holder formatted_date(const Date& d,
996
0
                                                     const std::string& f) {
997
0
            return detail::formatted_date_holder(d, f);
998
0
        }
999
1000
#ifdef QL_HIGH_RESOLUTION_DATE
1001
        detail::iso_datetime_holder iso_datetime(const Date& d) {
1002
            return detail::iso_datetime_holder(d);
1003
        }
1004
#endif
1005
    }
1006
}