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 | : 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 | |