Skip to content

Commit bbe464a

Browse files
mgwebgroupArt Kurbakov
authored andcommitted
feat: Add New York Stock Exchange (NYSE) provider (#384)
Added a holiday provider to track holidays and closures for the New York Stock Exchange. In addition to observed holidays, the closures include emergency, weather and presidential proclamation events since year 2000. Other US exchanges like NASDAQ and CBOE normally follow suit for holidays and closures. This PR supersedes PR #382. --------- Signed-off-by: Art Kurbakov <admin@mgwebgroup.com> Co-authored-by: Art Kurbakov <admin@mgwebgroup.com>
1 parent 5cd2c11 commit bbe464a

10 files changed

Lines changed: 897 additions & 0 deletions

phpunit.xml.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,5 +199,9 @@
199199
<testsuite name="USA">
200200
<directory>./tests/USA</directory>
201201
</testsuite>
202+
<!-- Test Suite for holidays and closures of the New York Stock Exchange (NYSE) in United States -->
203+
<testsuite name="NYSE">
204+
<directory>./tests/USA/NYSE</directory>
205+
</testsuite>
202206
</testsuites>
203207
</phpunit>

src/Yasumi/Provider/USA/NYSE.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* This file is part of the 'Yasumi' package.
7+
*
8+
* The easy PHP Library for calculating holidays.
9+
*
10+
* Copyright (c) 2015 - 2025 AzuyaLabs
11+
*
12+
* For the full copyright and license information, please view the LICENSE
13+
* file that was distributed with this source code.
14+
*
15+
* @author Sacha Telgenhof <me at sachatelgenhof dot com>
16+
*/
17+
18+
namespace Yasumi\Provider\USA;
19+
20+
use Yasumi\Holiday;
21+
use Yasumi\Provider\USA;
22+
23+
/**
24+
* Class NYSE in addition to regular holidays observed by the New York Stock
25+
* Exchange (NYSE), includes full day special closure events due to:
26+
* weather, national emergencies and presidential proclamations.
27+
* All trading closure events are included from year 2000.
28+
*
29+
* @author Art Kurbakov <admin at mgwebgroup dot com>
30+
*/
31+
class NYSE extends USA
32+
{
33+
/**
34+
* Code to identify this Holiday Provider. Typically, this is the ISO3166 code corresponding to the respective
35+
* country or sub-region.
36+
*/
37+
public const ID = 'US-NYSE';
38+
39+
/**
40+
* Initialize holidays for the NYSE.
41+
*
42+
* @throws \Exception
43+
*/
44+
public function initialize(): void
45+
{
46+
$this->timezone = 'America/New_York';
47+
48+
// Add exhange-specific holidays
49+
$this->addHoliday($this->newYearsDay($this->year, $this->timezone, $this->locale));
50+
$this->calculateMartinLutherKingday();
51+
$this->calculateWashingtonsBirthday();
52+
$this->addHoliday($this->goodFriday($this->year, $this->timezone, $this->locale));
53+
$this->calculateMemorialDay();
54+
if (2021 < $this->year) {
55+
$this->calculateJuneteenth();
56+
}
57+
$this->calculateIndependenceDay();
58+
$this->calculateLabourDay();
59+
$this->calculateThanksgivingDay();
60+
$this->addHoliday($this->christmasDay($this->year, $this->timezone, $this->locale));
61+
62+
$this->calculateSubstituteHolidays();
63+
64+
// Add other full-day closure events
65+
$this->addWeatherEvents();
66+
$this->addEmergencies();
67+
$this->addProclamations();
68+
}
69+
70+
public function getSources(): array
71+
{
72+
return [
73+
'https://www.nyse.com/trader-update/history#11507',
74+
'https://s3.amazonaws.com/armstrongeconomics-wp/2013/07/NYSE-Closings.pdf',
75+
'https://ir.theice.com/press/news-details/2018/New-York-Stock-Exchange-to-Honor-President-George-H-W-Bush/default.aspx',
76+
'https://ir.theice.com/press/news-details/2024/The-New-York-Stock-Exchange-Will-Close-Markets-on-January-9-to-Honor-the-Passing-of-Former-President-Jimmy-Carter-on-National-Day-of-Mourning/default.aspx',
77+
'https://www.thecorporatecounsel.net/blog/2021/10/nyse-makes-juneteenth-a-new-market-holiday.html',
78+
];
79+
}
80+
81+
private function addWeatherEvents(): void
82+
{
83+
if (2012 == $this->year) {
84+
$this->addHoliday(new Holiday('hurricaneSandy1', [], new \DateTime('2012-10-29', new \DateTimeZone($this->timezone))));
85+
$this->addHoliday(new Holiday('hurricaneSandy2', [], new \DateTime('2012-10-30', new \DateTimeZone($this->timezone))));
86+
}
87+
}
88+
89+
private function addEmergencies(): void
90+
{
91+
if (2001 == $this->year) {
92+
$this->addHoliday(new Holiday('groundZero1', [], new \DateTime('2001-09-11', new \DateTimeZone($this->timezone))));
93+
$this->addHoliday(new Holiday('groundZero2', [], new \DateTime('2001-09-12', new \DateTimeZone($this->timezone))));
94+
$this->addHoliday(new Holiday('groundZero3', [], new \DateTime('2001-09-13', new \DateTimeZone($this->timezone))));
95+
$this->addHoliday(new Holiday('groundZero4', [], new \DateTime('2001-09-14', new \DateTimeZone($this->timezone))));
96+
}
97+
}
98+
99+
private function addProclamations(): void
100+
{
101+
if (2004 == $this->year) {
102+
$this->addHoliday(new Holiday('ReaganMourning', [], new \DateTime('2004-06-11', new \DateTimeZone($this->timezone))));
103+
}
104+
if (2007 == $this->year) {
105+
$this->addHoliday(new Holiday('GRFordMourning', [], new \DateTime('2007-01-02', new \DateTimeZone($this->timezone))));
106+
}
107+
if (2018 == $this->year) {
108+
$this->addHoliday(new Holiday('HWBushMourning', [], new \DateTime('2018-12-05', new \DateTimeZone($this->timezone))));
109+
}
110+
if (2025 == $this->year) {
111+
$this->addHoliday(new Holiday('CarterMourning', [], new \DateTime('2025-01-09', new \DateTimeZone($this->timezone))));
112+
}
113+
}
114+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* This file is part of the 'Yasumi' package.
7+
*
8+
* The easy PHP Library for calculating holidays.
9+
*
10+
* Copyright (c) 2015 - 2025 AzuyaLabs
11+
*
12+
* For the full copyright and license information, please view the LICENSE
13+
* file that was distributed with this source code.
14+
*
15+
* @author Sacha Telgenhof <me at sachatelgenhof dot com>
16+
*/
17+
18+
namespace Yasumi\tests\USA\NYSE;
19+
20+
use Yasumi\tests\USA\USABaseTestCase;
21+
22+
/**
23+
* Class to test day of closure of NYSE due to Jimmy Carter mourning proclamation
24+
*
25+
* @author Art Kurbakov <admin at mgwebgroup dot com>
26+
*/
27+
class CarterMourningTest extends USABaseTestCase
28+
{
29+
/**
30+
* Name of provider to be tested.
31+
*/
32+
public const REGION = 'USA/NYSE';
33+
34+
/**
35+
* The year when the closure was observed.
36+
*/
37+
public const OBSERVED_YEAR = 2025;
38+
39+
/**
40+
* Tests day of closure on January 9th 2025
41+
*
42+
* @throws \Exception
43+
*/
44+
public function testCarterMourning(): void
45+
{
46+
$this->assertHoliday(
47+
self::REGION,
48+
'CarterMourning',
49+
self::OBSERVED_YEAR,
50+
new \DateTime('2025-01-09', new \DateTimeZone(self::TIMEZONE))
51+
);
52+
}
53+
54+
/**
55+
* Tests years before 2025
56+
*
57+
* @throws \Exception
58+
*/
59+
public function testCarterMourningBefore2025(): void
60+
{
61+
$this->assertNotHoliday(
62+
self::REGION,
63+
'CarterMourning',
64+
self::OBSERVED_YEAR - 1
65+
);
66+
}
67+
68+
/**
69+
* Tests years after 2025
70+
*
71+
* @throws \Exception
72+
*/
73+
public function testCarterMourningAfter2025(): void
74+
{
75+
$this->assertNotHoliday(
76+
self::REGION,
77+
'CarterMourning',
78+
self::OBSERVED_YEAR + 1
79+
);
80+
}
81+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* This file is part of the 'Yasumi' package.
7+
*
8+
* The easy PHP Library for calculating holidays.
9+
*
10+
* Copyright (c) 2015 - 2025 AzuyaLabs
11+
*
12+
* For the full copyright and license information, please view the LICENSE
13+
* file that was distributed with this source code.
14+
*
15+
* @author Sacha Telgenhof <me at sachatelgenhof dot com>
16+
*/
17+
18+
namespace Yasumi\tests\USA\NYSE;
19+
20+
use Yasumi\tests\USA\USABaseTestCase;
21+
22+
/**
23+
* Class to test day of closure of NYSE due to G.R. Ford Mourning proclamation
24+
*
25+
* @author Art Kurbakov <admin at mgwebgroup dot com>
26+
*/
27+
class GRFordMourningTest extends USABaseTestCase
28+
{
29+
/**
30+
* Name of provider to be tested.
31+
*/
32+
public const REGION = 'USA/NYSE';
33+
34+
/**
35+
* The year when the closure was observed.
36+
*/
37+
public const OBSERVED_YEAR = 2007;
38+
39+
/**
40+
* Tests day of closure on January 2nd 2007
41+
*
42+
* @throws \Exception
43+
*/
44+
public function testGRFordMourning(): void
45+
{
46+
$this->assertHoliday(
47+
self::REGION,
48+
'GRFordMourning',
49+
self::OBSERVED_YEAR,
50+
new \DateTime('2007-01-02', new \DateTimeZone(self::TIMEZONE))
51+
);
52+
}
53+
54+
/**
55+
* Tests years before 2007
56+
*
57+
* @throws \Exception
58+
*/
59+
public function testGRFordMourningBefore2007(): void
60+
{
61+
$this->assertNotHoliday(
62+
self::REGION,
63+
'GRFordMourning',
64+
self::OBSERVED_YEAR - 1
65+
);
66+
}
67+
68+
/**
69+
* Tests years after 2007
70+
*
71+
* @throws \Exception
72+
*/
73+
public function testGRFordMourningAfter2007(): void
74+
{
75+
$this->assertNotHoliday(
76+
self::REGION,
77+
'GRFordMourning',
78+
self::OBSERVED_YEAR + 1
79+
);
80+
}
81+
}

0 commit comments

Comments
 (0)