Coverage Report

Created: 2026-06-23 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/time/calendars/unitedstates.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2004, 2005 Ferdinando Ametrano
5
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
6
 Copyright (C) 2003, 2004, 2005, 2006 StatPro Italia srl
7
 Copyright (C) 2017 Peter Caspers
8
 Copyright (C) 2017 Oleg Kulkov
9
 Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ)
10
 Copyright (C) 2024 Dirk Eddelbuettel
11
12
 This file is part of QuantLib, a free-software/open-source library
13
 for financial quantitative analysts and developers - http://quantlib.org/
14
15
 QuantLib is free software: you can redistribute it and/or modify it
16
 under the terms of the QuantLib license.  You should have received a
17
 copy of the license along with this program; if not, please email
18
 <quantlib-dev@lists.sf.net>. The license is also available online at
19
 <https://www.quantlib.org/license.shtml>.
20
21
 This program is distributed in the hope that it will be useful, but WITHOUT
22
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23
 FOR A PARTICULAR PURPOSE.  See the license for more details.
24
*/
25
26
#include <ql/time/calendars/unitedstates.hpp>
27
#include <ql/errors.hpp>
28
29
namespace QuantLib {
30
31
    namespace {
32
33
        // a few rules used by multiple calendars
34
35
20.3M
        bool isWashingtonBirthday(Day d, Month m, Year y, Weekday w) {
36
20.3M
            if (y >= 1971) {
37
                // third Monday in February
38
17.6M
                return (d >= 15 && d <= 21) && w == Monday && m == February;
39
17.6M
            } else {
40
                // February 22nd, possibly adjusted
41
2.67M
                return (d == 22 || (d == 23 && w == Monday)
42
2.56M
                        || (d == 21 && w == Friday)) && m == February;
43
2.67M
            }
44
20.3M
        }
45
46
20.2M
        bool isMemorialDay(Day d, Month m, Year y, Weekday w) {
47
20.2M
            if (y >= 1971) {
48
                // last Monday in May
49
17.5M
                return d >= 25 && w == Monday && m == May;
50
17.5M
            } else {
51
                // May 30th, possibly adjusted
52
2.65M
                return (d == 30 || (d == 31 && w == Monday)
53
2.56M
                        || (d == 29 && w == Friday)) && m == May;
54
2.65M
            }
55
20.2M
        }
56
57
20.0M
        bool isLaborDay(Day d, Month m, Year y, Weekday w) {
58
            // first Monday in September
59
20.0M
            return d <= 7 && w == Monday && m == September;
60
20.0M
        }
61
62
10.1M
        bool isColumbusDay(Day d, Month m, Year y, Weekday w) {
63
            // second Monday in October
64
10.1M
            return (d >= 8 && d <= 14) && w == Monday && m == October
65
39.8k
                && y >= 1971;
66
10.1M
        }
67
68
0
        bool isVeteransDay(Day d, Month m, Year y, Weekday w) {
69
0
            if (y <= 1970 || y >= 1978) {
70
                // November 11th, adjusted
71
0
                return (d == 11 || (d == 12 && w == Monday) ||
72
0
                        (d == 10 && w == Friday)) && m == November;
73
0
            } else {
74
                // fourth Monday in October
75
0
                return (d >= 22 && d <= 28) && w == Monday && m == October;
76
0
            }
77
0
        }
78
79
10.1M
        bool isVeteransDayNoSaturday(Day d, Month m, Year y, Weekday w) {
80
10.1M
            if (y <= 1970 || y >= 1978) {
81
                // November 11th, adjusted, but no Saturday to Friday
82
9.85M
                return (d == 11 || (d == 12 && w == Monday)) && m == November;
83
9.85M
            } else {
84
                // fourth Monday in October
85
301k
                return (d >= 22 && d <= 28) && w == Monday && m == October;
86
301k
            }
87
10.1M
        }
88
89
20.1M
        bool isJuneteenth(Day d, Month m, Year y, Weekday w, bool moveToFriday = true) {
90
            // declared in 2021, but only observed by exchanges since 2022
91
20.1M
            return (d == 19 || (d == 20 && w == Monday) || ((d == 18 && w == Friday) && moveToFriday))
92
881k
                && m == June && y >= 2022;
93
20.1M
        }
94
    }
95
96
18.5k
    UnitedStates::UnitedStates(UnitedStates::Market market) {
97
        // all calendar instances on the same market share the same implementation instance
98
18.5k
        static auto settlementImpl = ext::make_shared<UnitedStates::SettlementImpl>();
99
18.5k
        static auto liborImpactImpl = ext::make_shared<UnitedStates::LiborImpactImpl>();
100
18.5k
        static auto nyseImpl = ext::make_shared<UnitedStates::NyseImpl>();
101
18.5k
        static auto governmentImpl = ext::make_shared<UnitedStates::GovernmentBondImpl>();
102
18.5k
        static auto nercImpl = ext::make_shared<UnitedStates::NercImpl>();
103
18.5k
        static auto federalReserveImpl = ext::make_shared<UnitedStates::FederalReserveImpl>();
104
18.5k
        static auto sofrImpl = ext::make_shared<UnitedStates::SofrImpl>();
105
106
18.5k
        switch (market) {
107
0
          case Settlement:
108
0
            impl_ = settlementImpl;
109
0
            break;
110
0
          case LiborImpact:
111
0
            impl_ = liborImpactImpl;
112
0
            break;
113
6.18k
          case NYSE:
114
6.18k
            impl_ = nyseImpl;
115
6.18k
            break;
116
6.18k
          case GovernmentBond:
117
6.18k
            impl_ = governmentImpl;
118
6.18k
            break;
119
0
          case SOFR:
120
0
            impl_ = sofrImpl;
121
0
            break;
122
0
          case NERC:
123
0
            impl_ = nercImpl;
124
0
            break;
125
6.18k
          case FederalReserve:
126
6.18k
            impl_ = federalReserveImpl;
127
6.18k
            break;
128
0
          default:
129
0
            QL_FAIL("unknown market");
130
18.5k
        }
131
18.5k
    }
132
133
134
0
    bool UnitedStates::SettlementImpl::isBusinessDay(const Date& date) const {
135
0
        Weekday w = date.weekday();
136
0
        Day d = date.dayOfMonth();
137
0
        Month m = date.month();
138
0
        Year y = date.year();
139
0
        if (isWeekend(w)
140
            // New Year's Day (possibly moved to Monday if on Sunday)
141
0
            || ((d == 1 || (d == 2 && w == Monday)) && m == January)
142
            // (or to Friday if on Saturday)
143
0
            || (d == 31 && w == Friday && m == December)
144
            // Martin Luther King's birthday (third Monday in January)
145
0
            || ((d >= 15 && d <= 21) && w == Monday && m == January
146
0
                && y >= 1983)
147
            // Washington's birthday (third Monday in February)
148
0
            || isWashingtonBirthday(d, m, y, w)
149
            // Memorial Day (last Monday in May)
150
0
            || isMemorialDay(d, m, y, w)
151
            // Juneteenth (Monday if Sunday or Friday if Saturday)
152
0
            || isJuneteenth(d, m, y, w)
153
            // Independence Day (Monday if Sunday or Friday if Saturday)
154
0
            || ((d == 4 || (d == 5 && w == Monday) ||
155
0
                 (d == 3 && w == Friday)) && m == July)
156
            // Labor Day (first Monday in September)
157
0
            || isLaborDay(d, m, y, w)
158
            // Columbus Day (second Monday in October)
159
0
            || isColumbusDay(d, m, y, w)
160
            // Veteran's Day (Monday if Sunday or Friday if Saturday)
161
0
            || isVeteransDay(d, m, y, w)
162
            // Thanksgiving Day (fourth Thursday in November)
163
0
            || ((d >= 22 && d <= 28) && w == Thursday && m == November)
164
            // Christmas (Monday if Sunday or Friday if Saturday)
165
0
            || ((d == 25 || (d == 26 && w == Monday) ||
166
0
                 (d == 24 && w == Friday)) && m == December))
167
0
            return false; // NOLINT(readability-simplify-boolean-expr)
168
0
        return true;
169
0
    }
170
171
0
    bool UnitedStates::LiborImpactImpl::isBusinessDay(const Date& date) const {
172
        // Since 2015 Independence Day only impacts Libor if it falls
173
        // on a weekday
174
0
        Weekday w = date.weekday();
175
0
        Day d = date.dayOfMonth();
176
0
        Month m = date.month();
177
0
        Year y = date.year();
178
0
        if (((d == 5 && w == Monday) ||
179
0
            (d == 3 && w == Friday)) && m == July && y >= 2015)
180
0
            return true;
181
0
        return SettlementImpl::isBusinessDay(date);
182
0
    }
183
184
13.9M
    bool UnitedStates::NyseImpl::isBusinessDay(const Date& date) const {
185
13.9M
        Weekday w = date.weekday();
186
13.9M
        Day d = date.dayOfMonth(), dd = date.dayOfYear();
187
13.9M
        Month m = date.month();
188
13.9M
        Year y = date.year();
189
13.9M
        Day em = easterMonday(y);
190
13.9M
        if (isWeekend(w)
191
            // New Year's Day (possibly moved to Monday if on Sunday)
192
10.0M
            || ((d == 1 || (d == 2 && w == Monday)) && m == January)
193
            // Washington's birthday (third Monday in February)
194
9.97M
            || isWashingtonBirthday(d, m, y, w)
195
            // Good Friday
196
9.94M
            || (dd == em-3)
197
            // Memorial Day (last Monday in May)
198
9.90M
            || isMemorialDay(d, m, y, w)
199
            // Juneteenth (Monday if Sunday or Friday if Saturday)
200
9.86M
            || isJuneteenth(d, m, y, w)
201
            // Independence Day (Monday if Sunday or Friday if Saturday)
202
9.84M
            || ((d == 4 || (d == 5 && w == Monday) ||
203
9.44M
                 (d == 3 && w == Friday)) && m == July)
204
            // Labor Day (first Monday in September)
205
9.80M
            || isLaborDay(d, m, y, w)
206
            // Thanksgiving Day (fourth Thursday in November)
207
9.76M
            || ((d >= 22 && d <= 28) && w == Thursday && m == November)
208
            // Christmas (Monday if Sunday or Friday if Saturday)
209
9.72M
            || ((d == 25 || (d == 26 && w == Monday) ||
210
9.34M
                 (d == 24 && w == Friday)) && m == December)
211
13.9M
            ) return false;
212
213
9.68M
        if (y >= 1998 && (d >= 15 && d <= 21) && w == Monday && m == January)
214
            // Martin Luther King's birthday (third Monday in January)
215
28.2k
            return false;
216
217
9.65M
        if ((y <= 1968 || (y <= 1980 && y % 4 == 0)) && m == November
218
96.9k
            && d <= 7 && w == Tuesday)
219
            // Presidential election days
220
4.62k
            return false;
221
222
        // Special closings
223
9.65M
        if (// President Carter's Funeral
224
9.65M
            (y == 2025 && m == January && d == 9)
225
            // President Bush's Funeral
226
9.65M
            || (y == 2018 && m == December && d == 5)
227
            // Hurricane Sandy
228
9.65M
            || (y == 2012 && m == October && (d == 29 || d == 30))
229
            // President Ford's funeral
230
9.65M
            || (y == 2007 && m == January && d == 2)
231
            // President Reagan's funeral
232
9.65M
            || (y == 2004 && m == June && d == 11)
233
            // September 11-14, 2001
234
9.65M
            || (y == 2001 && m == September && (11 <= d && d <= 14))
235
            // President Nixon's funeral
236
9.65M
            || (y == 1994 && m == April && d == 27)
237
            // Hurricane Gloria
238
9.65M
            || (y == 1985 && m == September && d == 27)
239
            // 1977 Blackout
240
9.65M
            || (y == 1977 && m == July && d == 14)
241
            // Funeral of former President Lyndon B. Johnson.
242
9.65M
            || (y == 1973 && m == January && d == 25)
243
            // Funeral of former President Harry S. Truman
244
9.65M
            || (y == 1972 && m == December && d == 28)
245
            // National Day of Participation for the lunar exploration.
246
9.65M
            || (y == 1969 && m == July && d == 21)
247
            // Funeral of former President Eisenhower.
248
9.65M
            || (y == 1969 && m == March && d == 31)
249
            // Closed all day - heavy snow.
250
9.65M
            || (y == 1969 && m == February && d == 10)
251
            // Day after Independence Day.
252
9.65M
            || (y == 1968 && m == July && d == 5)
253
            // June 12-Dec. 31, 1968
254
            // Four day week (closed on Wednesdays) - Paperwork Crisis
255
9.64M
            || (y == 1968 && dd >= 163 && w == Wednesday)
256
            // Day of mourning for Martin Luther King Jr.
257
9.64M
            || (y == 1968 && m == April && d == 9)
258
            // Funeral of President Kennedy
259
9.64M
            || (y == 1963 && m == November && d == 25)
260
            // Day before Decoration Day
261
9.64M
            || (y == 1961 && m == May && d == 29)
262
            // Day after Christmas
263
9.64M
            || (y == 1958 && m == December && d == 26)
264
            // Christmas Eve
265
9.64M
            || ((y == 1954 || y == 1956 || y == 1965)
266
82.1k
                && m == December && d == 24)
267
9.65M
            ) return false;
268
269
9.64M
        return true;
270
9.65M
    }
271
272
273
8.37M
    bool UnitedStates::GovernmentBondImpl::isBusinessDay(const Date& date) const {
274
8.37M
        Weekday w = date.weekday();
275
8.37M
        Day d = date.dayOfMonth(), dd = date.dayOfYear();
276
8.37M
        Month m = date.month();
277
8.37M
        Year y = date.year();
278
8.37M
        Day em = easterMonday(y);
279
8.37M
        if (isWeekend(w)
280
            // New Year's Day (possibly moved to Monday if on Sunday)
281
5.98M
            || ((d == 1 || (d == 2 && w == Monday)) && m == January)
282
            // Martin Luther King's birthday (third Monday in January)
283
5.95M
            || ((d >= 15 && d <= 21) && w == Monday && m == January
284
22.5k
                && y >= 1983)
285
            // Washington's birthday (third Monday in February)
286
5.94M
            || isWashingtonBirthday(d, m, y, w)
287
            // Good Friday. Since 1996 it's an early close and not a full market
288
            // close when it coincides with the NFP release date, which is the
289
            // first Friday of the month(*).
290
            // See <https://www.sifma.org/resources/general/holiday-schedule/>
291
            //
292
            // (*) The full rule is "the third Friday after the conclusion of the
293
            // week which includes the 12th of the month". This is usually the
294
            // first Friday of the next month, but can be the second Friday if the
295
            // month has fewer than 31 days. Since Good Friday is always between
296
            // March 20th and April 23rd, it can only coincide with the April NFP,
297
            // which is always on the first Friday, because March has 31 days.
298
5.91M
            || (dd == em-3 && (y < 1996 || d > 7))
299
            // Memorial Day (last Monday in May)
300
5.89M
            || isMemorialDay(d, m, y, w)
301
            // Juneteenth (Monday if Sunday or Friday if Saturday)
302
5.87M
            || isJuneteenth(d, m, y, w)
303
            // Independence Day (Monday if Sunday or Friday if Saturday)
304
5.86M
            || ((d == 4 || (d == 5 && w == Monday) ||
305
5.62M
                 (d == 3 && w == Friday)) && m == July)
306
            // Labor Day (first Monday in September)
307
5.83M
            || isLaborDay(d, m, y, w)
308
            // Columbus Day (second Monday in October)
309
5.81M
            || isColumbusDay(d, m, y, w)
310
            // Veteran's Day (Monday if Sunday)
311
5.79M
            || isVeteransDayNoSaturday(d, m, y, w)
312
            // Thanksgiving Day (fourth Thursday in November)
313
5.77M
            || ((d >= 22 && d <= 28) && w == Thursday && m == November)
314
            // Christmas (Monday if Sunday or Friday if Saturday)
315
5.75M
            || ((d == 25 || (d == 26 && w == Monday) ||
316
5.52M
                 (d == 24 && w == Friday)) && m == December))
317
2.64M
            return false;
318
319
        // Special closings
320
5.73M
        if (// President Bush's Funeral
321
5.73M
            (y == 2018 && m == December && d == 5)
322
            // Hurricane Sandy
323
5.73M
            || (y == 2012 && m == October && d == 30)
324
            // President Reagan's funeral
325
5.73M
            || (y == 2004 && m == June && d == 11)
326
5.73M
            ) return false;
327
328
5.73M
        return true;
329
5.73M
    }
330
331
332
0
    bool UnitedStates::SofrImpl::isBusinessDay(const Date& date) const {
333
        // so far (that is, up to 2023 at the time of this change) SOFR never fixed
334
        // on Good Friday.  We're extrapolating that pattern.  This might change if
335
        // a fixing on Good Friday occurs in future years.
336
0
        const Day dY = date.dayOfYear();
337
0
        const Year y = date.year();
338
339
        // Good Friday
340
0
        if (dY == (easterMonday(y) - 3))
341
0
            return false;
342
343
0
        return GovernmentBondImpl::isBusinessDay(date);
344
0
    }
345
346
347
0
    bool UnitedStates::NercImpl::isBusinessDay(const Date& date) const {
348
0
        Weekday w = date.weekday();
349
0
        Day d = date.dayOfMonth();
350
0
        Month m = date.month();
351
0
        Year y = date.year();
352
0
        if (isWeekend(w)
353
            // New Year's Day (possibly moved to Monday if on Sunday)
354
0
            || ((d == 1 || (d == 2 && w == Monday)) && m == January)
355
            // Memorial Day (last Monday in May)
356
0
            || isMemorialDay(d, m, y, w)
357
            // Independence Day (Monday if Sunday)
358
0
            || ((d == 4 || (d == 5 && w == Monday)) && m == July)
359
            // Labor Day (first Monday in September)
360
0
            || isLaborDay(d, m, y, w)
361
            // Thanksgiving Day (fourth Thursday in November)
362
0
            || ((d >= 22 && d <= 28) && w == Thursday && m == November)
363
            // Christmas (Monday if Sunday)
364
0
            || ((d == 25 || (d == 26 && w == Monday)) && m == December))
365
0
            return false; // NOLINT(readability-simplify-boolean-expr)
366
0
        return true;
367
0
    }
368
369
370
6.25M
    bool UnitedStates::FederalReserveImpl::isBusinessDay(const Date& date) const {
371
        // see https://www.frbservices.org/about/holiday-schedules for details
372
6.25M
        Weekday w = date.weekday();
373
6.25M
        Day d = date.dayOfMonth();
374
6.25M
        Month m = date.month();
375
6.25M
        Year y = date.year();
376
6.25M
        if (isWeekend(w)
377
            // New Year's Day (possibly moved to Monday if on Sunday)
378
4.47M
            || ((d == 1 || (d == 2 && w == Monday)) && m == January)
379
            // Martin Luther King's birthday (third Monday in January)
380
4.45M
            || ((d >= 15 && d <= 21) && w == Monday && m == January
381
17.2k
                && y >= 1983)
382
            // Washington's birthday (third Monday in February)
383
4.44M
            || isWashingtonBirthday(d, m, y, w)
384
            // Memorial Day (last Monday in May)
385
4.42M
            || isMemorialDay(d, m, y, w)
386
            // Juneteenth (Monday if Sunday)
387
4.41M
            || isJuneteenth(d, m, y, w, false)
388
            // Independence Day (Monday if Sunday)
389
4.40M
            || ((d == 4 || (d == 5 && w == Monday)) && m == July)
390
            // Labor Day (first Monday in September)
391
4.38M
            || isLaborDay(d, m, y, w)
392
            // Columbus Day (second Monday in October)
393
4.37M
            || isColumbusDay(d, m, y, w)
394
            // Veteran's Day (Monday if Sunday)
395
4.35M
            || isVeteransDayNoSaturday(d, m, y, w)
396
            // Thanksgiving Day (fourth Thursday in November)
397
4.34M
            || ((d >= 22 && d <= 28) && w == Thursday && m == November)
398
            // Christmas (Monday if Sunday)
399
4.32M
            || ((d == 25 || (d == 26 && w == Monday)) && m == December))
400
1.94M
            return false; // NOLINT(readability-simplify-boolean-expr)
401
4.30M
        return true;
402
6.25M
    }
403
404
}