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
50using boost::posix_time::ptime;
51using boost::posix_time::time_duration;
52#endif
53
54
55namespace QuantLib {
56#ifndef QL_HIGH_RESOLUTION_DATE
57 // constructors
58 Date::Date()
59 : serialNumber_(Date::serial_type(0)) {}
60
61 Date::Date(Date::serial_type serialNumber)
62 : serialNumber_(serialNumber) {
63 checkSerialNumber(serialNumber);
64 }
65
66 Date::Date(Day d, Month m, Year y) {
67 QL_REQUIRE(y > 1900 && y < 2200,
68 "year " << y << " out of bound. It must be in [1901,2199]");
69 QL_REQUIRE(Integer(m) > 0 && Integer(m) < 13,
70 "month " << Integer(m)
71 << " outside January-December range [1,12]");
72
73 bool leap = isLeap(y);
74 Day len = monthLength(m,leapYear: leap), offset = monthOffset(m,leapYear: leap);
75 QL_REQUIRE(d <= len && d > 0,
76 "day outside month (" << Integer(m) << ") day-range "
77 << "[1," << len << "]");
78
79 serialNumber_ = d + offset + yearOffset(y);
80 }
81
82 Month Date::month() const {
83 Day d = dayOfYear(); // dayOfYear is 1 based
84 Integer m = d/30 + 1;
85 bool leap = isLeap(y: year());
86 while (d <= monthOffset(m: Month(m),leapYear: leap))
87 --m;
88 while (d > monthOffset(m: Month(m+1),leapYear: leap)) // NOLINT(misc-misplaced-widening-cast)
89 ++m;
90 return Month(m);
91 }
92
93 Year Date::year() const {
94 Year y = (serialNumber_ / 365)+1900;
95 // yearOffset(y) is December 31st of the preceding year
96 if (serialNumber_ <= yearOffset(y))
97 --y;
98 return y;
99 }
100
101 Date& Date::operator+=(Date::serial_type days) {
102 Date::serial_type serial = serialNumber_ + days;
103 checkSerialNumber(serialNumber: serial);
104 serialNumber_ = serial;
105 return *this;
106 }
107
108 Date& Date::operator+=(const Period& p) {
109 serialNumber_ = advance(d: *this,units: p.length(),p.units()).serialNumber();
110 return *this;
111 }
112
113 Date& Date::operator-=(Date::serial_type days) {
114 Date::serial_type serial = serialNumber_ - days;
115 checkSerialNumber(serialNumber: serial);
116 serialNumber_ = serial;
117 return *this;
118 }
119
120 Date& Date::operator-=(const Period& p) {
121 serialNumber_ = advance(d: *this,units: -p.length(),p.units()).serialNumber();
122 return *this;
123 }
124
125 Date& Date::operator++() {
126 Date::serial_type serial = serialNumber_ + 1;
127 checkSerialNumber(serialNumber: serial);
128 serialNumber_ = serial;
129 return *this;
130 }
131
132 Date& Date::operator--() {
133 Date::serial_type serial = serialNumber_ - 1;
134 checkSerialNumber(serialNumber: serial);
135 serialNumber_ = serial;
136 return *this;
137 }
138
139 Date Date::advance(const Date& date, Integer n, TimeUnit units) {
140 switch (units) {
141 case Days:
142 return date + n;
143 case Weeks:
144 return date + 7*n;
145 case Months: {
146 Day d = date.dayOfMonth();
147 Integer m = Integer(date.month())+n;
148 Year y = date.year();
149 while (m > 12) {
150 m -= 12;
151 y += 1;
152 }
153 while (m < 1) {
154 m += 12;
155 y -= 1;
156 }
157
158 QL_ENSURE(y >= 1900 && y <= 2199,
159 "year " << y << " out of bounds. "
160 << "It must be in [1901,2199]");
161
162 Integer length = monthLength(m: Month(m), leapYear: isLeap(y));
163 if (d > length)
164 d = length;
165
166 return {d, Month(m), y};
167 }
168 case Years: {
169 Day d = date.dayOfMonth();
170 Month m = date.month();
171 Year y = date.year()+n;
172
173 QL_ENSURE(y >= 1900 && y <= 2199,
174 "year " << y << " out of bounds. "
175 << "It must be in [1901,2199]");
176
177 if (d == 29 && m == February && !isLeap(y))
178 d = 28;
179
180 return {d, m, y};
181 }
182 default:
183 QL_FAIL("undefined time units");
184 }
185 }
186
187 bool Date::isLeap(Year y) {
188 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 true,false,false,false, true,false,false,false, true,false,
193 // 1910-1919
194 false,false, true,false,false,false, true,false,false,false,
195 // 1920-1929
196 true,false,false,false, true,false,false,false, true,false,
197 // 1930-1939
198 false,false, true,false,false,false, true,false,false,false,
199 // 1940-1949
200 true,false,false,false, true,false,false,false, true,false,
201 // 1950-1959
202 false,false, true,false,false,false, true,false,false,false,
203 // 1960-1969
204 true,false,false,false, true,false,false,false, true,false,
205 // 1970-1979
206 false,false, true,false,false,false, true,false,false,false,
207 // 1980-1989
208 true,false,false,false, true,false,false,false, true,false,
209 // 1990-1999
210 false,false, true,false,false,false, true,false,false,false,
211 // 2000-2009
212 true,false,false,false, true,false,false,false, true,false,
213 // 2010-2019
214 false,false, true,false,false,false, true,false,false,false,
215 // 2020-2029
216 true,false,false,false, true,false,false,false, true,false,
217 // 2030-2039
218 false,false, true,false,false,false, true,false,false,false,
219 // 2040-2049
220 true,false,false,false, true,false,false,false, true,false,
221 // 2050-2059
222 false,false, true,false,false,false, true,false,false,false,
223 // 2060-2069
224 true,false,false,false, true,false,false,false, true,false,
225 // 2070-2079
226 false,false, true,false,false,false, true,false,false,false,
227 // 2080-2089
228 true,false,false,false, true,false,false,false, true,false,
229 // 2090-2099
230 false,false, true,false,false,false, true,false,false,false,
231 // 2100-2109
232 false,false,false,false, true,false,false,false, true,false,
233 // 2110-2119
234 false,false, true,false,false,false, true,false,false,false,
235 // 2120-2129
236 true,false,false,false, true,false,false,false, true,false,
237 // 2130-2139
238 false,false, true,false,false,false, true,false,false,false,
239 // 2140-2149
240 true,false,false,false, true,false,false,false, true,false,
241 // 2150-2159
242 false,false, true,false,false,false, true,false,false,false,
243 // 2160-2169
244 true,false,false,false, true,false,false,false, true,false,
245 // 2170-2179
246 false,false, true,false,false,false, true,false,false,false,
247 // 2180-2189
248 true,false,false,false, true,false,false,false, true,false,
249 // 2190-2199
250 false,false, true,false,false,false, true,false,false,false,
251 // 2200
252 false
253 };
254 QL_REQUIRE(y>=1900 && y<=2200, "year outside valid range");
255 return YearIsLeap[y-1900];
256 }
257
258
259 Integer Date::monthLength(Month m, bool leapYear) {
260 static const Integer MonthLength[] = {
261 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
262 };
263 static const Integer MonthLeapLength[] = {
264 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
265 };
266 return (leapYear? MonthLeapLength[m-1] : MonthLength[m-1]);
267 }
268
269 Integer Date::monthOffset(Month m, bool leapYear) {
270 static const Integer MonthOffset[] = {
271 0, 31, 59, 90, 120, 151, // Jan - Jun
272 181, 212, 243, 273, 304, 334, // Jun - Dec
273 365 // used in dayOfMonth to bracket day
274 };
275 static const Integer MonthLeapOffset[] = {
276 0, 31, 60, 91, 121, 152, // Jan - Jun
277 182, 213, 244, 274, 305, 335, // Jun - Dec
278 366 // used in dayOfMonth to bracket day
279 };
280 return (leapYear? MonthLeapOffset[m-1] : MonthOffset[m-1]);
281 }
282
283 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 static const Date::serial_type YearOffset[] = {
287 // 1900-1909
288 0, 366, 731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
289 // 1910-1919
290 3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
291 // 1920-1929
292 7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862,10227,10593,
293 // 1930-1939
294 10958,11323,11688,12054,12419,12784,13149,13515,13880,14245,
295 // 1940-1949
296 14610,14976,15341,15706,16071,16437,16802,17167,17532,17898,
297 // 1950-1959
298 18263,18628,18993,19359,19724,20089,20454,20820,21185,21550,
299 // 1960-1969
300 21915,22281,22646,23011,23376,23742,24107,24472,24837,25203,
301 // 1970-1979
302 25568,25933,26298,26664,27029,27394,27759,28125,28490,28855,
303 // 1980-1989
304 29220,29586,29951,30316,30681,31047,31412,31777,32142,32508,
305 // 1990-1999
306 32873,33238,33603,33969,34334,34699,35064,35430,35795,36160,
307 // 2000-2009
308 36525,36891,37256,37621,37986,38352,38717,39082,39447,39813,
309 // 2010-2019
310 40178,40543,40908,41274,41639,42004,42369,42735,43100,43465,
311 // 2020-2029
312 43830,44196,44561,44926,45291,45657,46022,46387,46752,47118,
313 // 2030-2039
314 47483,47848,48213,48579,48944,49309,49674,50040,50405,50770,
315 // 2040-2049
316 51135,51501,51866,52231,52596,52962,53327,53692,54057,54423,
317 // 2050-2059
318 54788,55153,55518,55884,56249,56614,56979,57345,57710,58075,
319 // 2060-2069
320 58440,58806,59171,59536,59901,60267,60632,60997,61362,61728,
321 // 2070-2079
322 62093,62458,62823,63189,63554,63919,64284,64650,65015,65380,
323 // 2080-2089
324 65745,66111,66476,66841,67206,67572,67937,68302,68667,69033,
325 // 2090-2099
326 69398,69763,70128,70494,70859,71224,71589,71955,72320,72685,
327 // 2100-2109
328 73050,73415,73780,74145,74510,74876,75241,75606,75971,76337,
329 // 2110-2119
330 76702,77067,77432,77798,78163,78528,78893,79259,79624,79989,
331 // 2120-2129
332 80354,80720,81085,81450,81815,82181,82546,82911,83276,83642,
333 // 2130-2139
334 84007,84372,84737,85103,85468,85833,86198,86564,86929,87294,
335 // 2140-2149
336 87659,88025,88390,88755,89120,89486,89851,90216,90581,90947,
337 // 2150-2159
338 91312,91677,92042,92408,92773,93138,93503,93869,94234,94599,
339 // 2160-2169
340 94964,95330,95695,96060,96425,96791,97156,97521,97886,98252,
341 // 2170-2179
342 98617,98982,99347,99713,100078,100443,100808,101174,101539,101904,
343 // 2180-2189
344 102269,102635,103000,103365,103730,104096,104461,104826,105191,105557,
345 // 2190-2199
346 105922,106287,106652,107018,107383,107748,108113,108479,108844,109209,
347 // 2200
348 109574
349 };
350 return YearOffset[y-1900];
351 }
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 = (dateTime_.date()
622 - serialNumberDateReference()).days();
623 checkSerialNumber(n);
624
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 Date::serial_type Date::minimumSerialNumber() {
750 return 367; // Jan 1st, 1901
751 }
752
753 Date::serial_type Date::maximumSerialNumber() {
754 return 109574; // Dec 31st, 2199
755 }
756
757 void Date::checkSerialNumber(Date::serial_type serialNumber) {
758 QL_REQUIRE(serialNumber >= minimumSerialNumber() &&
759 serialNumber <= maximumSerialNumber(),
760 "Date's serial number (" << serialNumber << ") outside "
761 "allowed range [" << minimumSerialNumber() <<
762 "-" << maximumSerialNumber() << "], i.e. [" <<
763 minDate() << "-" << maxDate() << "]");
764 }
765
766 Date Date::minDate() {
767 static const Date minimumDate(minimumSerialNumber());
768 return minimumDate;
769 }
770
771 Date Date::maxDate() {
772 static const Date maximumDate(maximumSerialNumber());
773 return maximumDate;
774 }
775
776 Date Date::operator++(int ) {
777 Date old(*this);
778 ++*this; // use the pre-increment
779 return old;
780 }
781
782 Date Date::operator--(int ) {
783 Date old(*this);
784 --*this; // use the pre-decrement
785 return old;
786 }
787
788 Date Date::todaysDate() {
789 std::time_t t;
790
791 if (std::time(timer: &t) == std::time_t(-1)) // -1 means time() didn't work
792 return {};
793 std::tm *lt = std::localtime(timer: &t);
794 return {Day(lt->tm_mday), Month(lt->tm_mon + 1), Year(lt->tm_year + 1900)};
795 }
796
797 Date Date::nextWeekday(const Date& d, Weekday dayOfWeek) {
798 Weekday wd = d.weekday();
799 return d + ((wd>dayOfWeek ? 7 : 0) - wd + dayOfWeek);
800 }
801
802 Date Date::nthWeekday(Size nth, Weekday dayOfWeek,
803 Month m, Year y) {
804 QL_REQUIRE(nth>0,
805 "zeroth day of week in a given (month, year) is undefined");
806 QL_REQUIRE(nth<6,
807 "no more than 5 weekday in a given (month, year)");
808 Weekday first = Date(1, m, y).weekday();
809 Size skip = nth - (dayOfWeek>=first ? 1 : 0);
810 return {Day((1 + dayOfWeek + skip * 7) - first), m, y};
811 }
812
813 // month formatting
814
815 std::ostream& operator<<(std::ostream& out, Month m) {
816 switch (m) {
817 case January:
818 return out << "January";
819 case February:
820 return out << "February";
821 case March:
822 return out << "March";
823 case April:
824 return out << "April";
825 case May:
826 return out << "May";
827 case June:
828 return out << "June";
829 case July:
830 return out << "July";
831 case August:
832 return out << "August";
833 case September:
834 return out << "September";
835 case October:
836 return out << "October";
837 case November:
838 return out << "November";
839 case December:
840 return out << "December";
841 default:
842 QL_FAIL("unknown month (" << Integer(m) << ")");
843 }
844 }
845
846 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 return std::hash<Date::serial_type>()(d.serialNumber());
854#endif
855 }
856
857 // date formatting
858
859 std::ostream& operator<<(std::ostream& out, const Date& d) {
860 return out << io::long_date(d);
861 }
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 std::string do_grouping() const override { return ""; }
871 };
872 explicit FormatResetter(std::ostream &out)
873 : out_(&out), flags_(out.flags()), filler_(out.fill()),
874 loc_(out.getloc()) {
875 std::locale loc (out.getloc(),new nopunct);
876 out.imbue(loc: loc);
877 out << std::resetiosflags(
878 mask: std::ios_base::adjustfield | std::ios_base::basefield |
879 std::ios_base::floatfield | std::ios_base::showbase |
880 std::ios_base::showpos | std::ios_base::uppercase);
881 out << std::right;
882 }
883 ~FormatResetter() {
884 out_->flags(fmtfl: flags_);
885 out_->fill(ch: filler_);
886 out_->imbue(loc: loc_);
887 }
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 const short_date_holder& holder) {
896 const Date& d = holder.d;
897 if (d == Date()) {
898 out << "null date";
899 } else {
900 FormatResetter resetter(out);
901 Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
902 yyyy = d.year();
903 char filler = out.fill();
904 out << std::setw(2) << std::setfill('0') << mm << "/";
905 out << std::setw(2) << std::setfill('0') << dd << "/";
906 out << yyyy;
907 out.fill(ch: filler);
908 }
909 return out;
910 }
911
912 std::ostream& operator<<(std::ostream& out,
913 const long_date_holder& holder) {
914 const Date& d = holder.d;
915 if (d == Date()) {
916 out << "null date";
917 } else {
918 FormatResetter resetter(out);
919 out << d.month() << " ";
920 out << io::ordinal(n: d.dayOfMonth()) << ", ";
921 out << d.year();
922 }
923 return out;
924 }
925
926 std::ostream& operator<<(std::ostream& out,
927 const iso_date_holder& holder) {
928 const Date& d = holder.d;
929 if (d == Date()) {
930 out << "null date";
931 } else {
932 FormatResetter resetter(out);
933 Integer dd = d.dayOfMonth(), mm = Integer(d.month()),
934 yyyy = d.year();
935 out << yyyy << "-";
936 out << std::setw(2) << std::setfill('0') << mm << "-";
937 out << std::setw(2) << std::setfill('0') << dd;
938 }
939 return out;
940 }
941
942 std::ostream& operator<<(std::ostream& out,
943 const formatted_date_holder& holder) {
944 using namespace boost::gregorian;
945 const Date& d = holder.d;
946 if (d == Date()) {
947 out << "null date";
948 } else {
949 FormatResetter resetter(out);
950 date boostDate(d.year(), d.month(), d.dayOfMonth());
951 out.imbue(loc: std::locale(std::locale(),
952 new date_facet(holder.f.c_str())));
953 out << boostDate;
954 }
955 return out;
956 }
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 detail::short_date_holder short_date(const Date& d) {
984 return detail::short_date_holder(d);
985 }
986
987 detail::long_date_holder long_date(const Date& d) {
988 return detail::long_date_holder(d);
989 }
990
991 detail::iso_date_holder iso_date(const Date& d) {
992 return detail::iso_date_holder(d);
993 }
994
995 detail::formatted_date_holder formatted_date(const Date& d,
996 const std::string& f) {
997 return detail::formatted_date_holder(d, f);
998 }
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}
1007

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