/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 | | } |