Skip to content

Add London Stock Exchange (LSE) holidays#3654

Open
eeshsaxena wants to merge 7 commits into
vacanza:devfrom
eeshsaxena:feat/london-stock-exchange
Open

Add London Stock Exchange (LSE) holidays#3654
eeshsaxena wants to merge 7 commits into
vacanza:devfrom
eeshsaxena:feat/london-stock-exchange

Conversation

@eeshsaxena

Copy link
Copy Markdown
Contributor

Proposed change

Adds the London Stock Exchange (XLON) to the supported financial markets. The LSE is one of the world's largest exchanges and was not yet covered, while several smaller exchanges already are.

The LSE is closed on weekends and on the bank holidays observed in England and Wales:

  • New Year's Day (observed)
  • Good Friday and Easter Monday
  • Early May Bank Holiday ("May Day")
  • Spring Bank Holiday (with the Jubilee-year moves in 2002, 2012 and 2022)
  • Late Summer Bank Holiday
  • Christmas Day and Boxing Day (observed)

plus the one-off royal bank holidays: the 2002 Golden Jubilee, the 2011 royal wedding, the 2012 Diamond Jubilee, the 2022 Platinum Jubilee, the 2022 State Funeral of Queen Elizabeth II, and the 2023 Coronation of Charles III.

start_year is 2000 (the modern electronic-trading era).

Verification

  • New tests/financial/test_london_stock_exchange.py (per-year assertions, aliases, and a no-holidays check) - all pass, 100% coverage of the new module.
  • The market's output was cross-checked against the library's existing UnitedKingdom(subdiv="ENG") bank-holiday data for every year 2000-2030 (exact match on both dates and names).
  • tests/test_docs.py and tests/test_registry.py pass; README financial-markets table and registry updated; ruff check/format clean.

Aliases

LondonStockExchange, XLON, LSE.

Add the London Stock Exchange, one of the world's largest exchanges, to
the supported financial markets. The LSE is closed on weekends and on the
bank holidays observed in England and Wales, plus a small number of
one-off royal bank holidays (jubilees, the 2011 royal wedding, the 2022
state funeral, and the 2023 coronation).

Includes XLON/LSE aliases, full test coverage, registry and README
entries. Output matches the library's existing England and Wales bank
holiday data for 2000-2030.

Add Eesh Saxena to CONTRIBUTORS.
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b650f76-06ef-42db-a1c6-9d30d33cfabf

📥 Commits

Reviewing files that changed from the base of the PR and between 9ae5b15 and 0e8d186.

📒 Files selected for processing (2)
  • holidays/financial/london_stock_exchange.py
  • tests/financial/test_london_stock_exchange.py

Summary by CodeRabbit

  • New Features

    • Added London Stock Exchange holiday support with market codes (XLON/LSE) and half-day observances for Christmas Eve and New Year’s Eve.
    • Updated market availability documentation to include the London Stock Exchange.
    • Added translations for LSE observances (en_GB, en_US, th).
  • Bug Fixes

    • Adjusted UK public holiday inclusion to depend on supported categories (excluding Scotland as applicable).
    • Ensured weekend bank holidays use the observed weekday substitutes and half-day rules shift correctly.
  • Tests

    • Added/expanded test coverage for aliases, year handling, weekend/observed behavior, and localization.

Walkthrough

Adds a London Stock Exchange financial holiday provider with XLON and LSE aliases. It updates UK holiday category gating, registers and exports the provider, adds localization and market documentation, and introduces comprehensive holiday and localization tests.

Changes

London Stock Exchange Holiday Provider

Layer / File(s) Summary
UK holiday category gating
holidays/countries/united_kingdom.py
Non-Scotland subdivision holidays now also require the PUBLIC category.
LondonStockExchange provider and localization
holidays/financial/london_stock_exchange.py, holidays/locale/.../XLON.po
Defines the UK ENG provider, weekend substitutions, Christmas Eve and New Year’s Eve half-days, aliases, and three locale catalogs.
Registry and package integration
holidays/registry.py, holidays/financial/__init__.py, README.md, CONTRIBUTORS
Registers and re-exports the provider, documents XLON, and updates contributor metadata.
London Stock Exchange validation
tests/financial/test_london_stock_exchange.py
Tests aliases, pre-start behavior, holiday dates, weekend handling, half-days, and localized names.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: ppsyrius, arkid15r, kjhellico

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding London Stock Exchange holidays.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the new LSE market support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Confidence score: 4/5

  • In tests/financial/test_london_stock_exchange.py, the 2012 Diamond Jubilee special-case is not covered by a per-year assertion, so a regression in holiday logic could slip through CI and produce incorrect market-closure dates for that year—add a dedicated 2012 test case before merging to de-risk it.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/financial/test_london_stock_exchange.py">

<violation number="1" location="tests/financial/test_london_stock_exchange.py:134">
P2: Missing per-year test for 2012 Diamond Jubilee, leaving special-cased holidays untested.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/financial/test_london_stock_exchange.py
@KJhellico

Copy link
Copy Markdown
Collaborator

If holidays are the same as in UK, why not just inherit from UnitedKingdom class?

@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Good question, and I did try that first. The blocker is that the LSE follows specifically the England and Wales bank holidays, which in UnitedKingdom are a subdivision (subdiv="ENG"), not the entity's default output.

I prototyped class LondonStockExchange(UnitedKingdom) (mirroring JapanExchange(Japan) / HongKongStockExchange(HongKong)), but those work cleanly because Japan, Hong Kong and China have no subdivisions. To get England here I have to force subdiv="ENG", and that makes the market entity retain country/subdiv and pull in UK's subdivisions and localization - which fails the financial test_code (assertFalse(hasattr(self.holidays, "country"))) and changes the supported-languages table.

To make sure the standalone version is faithful, I cross-checked its output against UnitedKingdom(subdiv="ENG") for every year 2000-2030 and they match exactly (dates and names), which is why the special days (jubilees, the 2022 state funeral, the 2023 coronation) are all covered.

Happy to go whichever way you prefer:

  1. keep the standalone implementation as-is, or
  2. inherit from UnitedKingdom if there's an accepted pattern for a subdivision-specific market that I'm missing - if so, point me at it and I'll switch.

@arkid15r

Copy link
Copy Markdown
Collaborator

2. inherit from UnitedKingdom if there's an accepted pattern for a subdivision-specific market that I'm missing - if so, point me at it and I'll switch.

Is this what you are looking for?

@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @arkid15r, that's exactly the pointer I needed. I tried it:

class LondonStockExchange(ChildEntity, UnitedKingdom):
    country = None
    market = "XLON"
    parent_entity = UnitedKingdom
    parent_entity_subdivision_code = "ENG"
    supported_categories = (PUBLIC,)
    start_year = 2000

The output matches UnitedKingdom(subdiv="ENG") exactly for 2000-2030 (dates and names), so functionally this is the right approach and removes all the duplicated logic.

The one thing I'm stuck on is CommonFinancialTests.test_code, which asserts assertFalse(hasattr(self.holidays, "country")). With ChildEntity the instance ends up with subdiv="ENG" and a (None) country attribute, so hasattr(..., "country") is True and the test fails - whereas the existing no-subdivision markets like JapanExchange(Japan) pass it. (The README supported-languages column also then needs en_GB, en_US, th from the parent, which is easy.)

Is there an additional step to make a subdivision-backed market not expose country for test_code, or should test_code be relaxed for ChildEntity-based markets? Happy to push the ChildEntity version once I know how you'd like test_code handled. I've kept the standalone version here in the meantime so CI stays green.

Comment on lines +37 to +39
market = "XLON"
observed_label = "%s (observed)"
start_year = 2000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eeshsaxena the LSE also observes early closures/half days (like Christmas Eve and New Year's Eve). Here is the reference link

​We will need to add half-day support to make this fully accurate. I've already done the research on this, so I am happy to help you implement this section if you'd like! Just let me know

The London Stock Exchange runs a shortened session with an early close on
Christmas Eve and New Year's Eve. These are now exposed under the HALF_DAY
category, following the existing exchange half-day pattern.
@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @pareshjoshij! Added half-day support - Christmas Eve and New Year's Eve early closes are now exposed under the HALF_DAY category, following the existing exchange half-day pattern (e.g. Hong Kong). Added a test_2024_half_day case and the full LSE suite passes (17 tests). Let me know if you'd also like weekend dates filtered out of the half-day set.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread holidays/financial/london_stock_exchange.py Outdated
@pareshjoshij

pareshjoshij commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@eeshsaxena how will the end user know that these dates are half-days?
We need to make sure a label is added so it's clearly visible to them.

Let me know if you'd also like weekend dates filtered out of the half-day set.

take a look at the recent bot comment on this PR. For the best approach, I suggest reviewing #3617 to see exactly how we implemented half-day holidays previously, and let's align with that pattern. :)

@pareshjoshij pareshjoshij left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, it would be highly beneficial to cross-reference your output with historical LSE calendars. For example, you can use the Wayback Machine to look at past versions of their business days page: LSE Business Days (Archive).

Here is why I suggest doing this:

  • Accuracy Check: Matching the official historical holidays against your generated output will give you a clear idea of whether your current implementation is accurate, or if inheriting from UnitedKingdom might actually be a better approach.
  • Easier Reviews: If you add these historical links to the References section in the docstring, it significantly reduces the workload for maintainers when verifying the dates.

I highly recommend digging up a few of these historical links yourself—it is a great way to learn the intricacies of the exchange's calendar! If you need a good research strategy for finding them, check out this trick shared by @PPsyrius in a previous review: PR #3639 Research Tip.

Just my two cents—hope this helps!

@KJhellico

Copy link
Copy Markdown
Collaborator

The one thing I'm stuck on is CommonFinancialTests.test_code

Look at similar markets that derive from countries - JPX, SSE etc.

@KJhellico KJhellico changed the title Add London Stock Exchange (XLON) financial market Add London Stock Exchange (LSE) holidays Jul 4, 2026
@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks both!

@pareshjoshij - done. Following the #3617 (XCME) pattern, the half-day sessions are now labelled via _format_holiday_name, so under the HALF_DAY category they render as:

2024-12-24  Christmas Eve (early close)
2024-12-31  New Year's Eve (early close)

so end users can clearly tell these are shortened trading days (12:30 close) rather than full market holidays.

@KJhellico - on inheriting from UnitedKingdom: I re-checked empirically and the clean JPX/SSE pattern (country = None, parent_entity = ..., no subdiv) doesn't reproduce the LSE calendar. JPX/SSE work because Japan/China have no subdivisions, so the entity default is the market calendar. For the UK the entity default is not England - e.g. for 2024 it omits Easter Monday (Apr 1) and the Late Summer Bank Holiday (Aug 26), both of which the LSE observes:

UnitedKingdom()            -> {Jan1, GoodFri, MayDay, SpringBH, Christmas, Boxing}
UnitedKingdom(subdiv=ENG)  -> + Easter Monday, + Late Summer BH   # what LSE follows

So the LSE specifically needs subdiv="ENG". The ChildEntity + parent_entity_subdivision_code="ENG" route does give the right dates, but it keeps subdiv/country on the instance and fails CommonFinancialTests.test_code (assertFalse(hasattr(self.holidays, "country"))). The standalone implementation here reproduces UnitedKingdom(subdiv="ENG") exactly for 2000-2030 and passes all common financial tests (17 tests green, no country attr). Happy to switch to inheritance instead if there's a market precedent that pins a specific subdivision while still satisfying test_code - I couldn't find one, but point me at it and I'll adopt it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/financial/test_london_stock_exchange.py (1)

89-104: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing 2012 Jubilee-year test.

The PR notes Spring Bank Holiday is moved for 2002, 2012, and 2022 (Jubilee years), but tests only cover 2002 and 2022. Add a test_2012 to verify the Diamond Jubilee adjustment (Spring Bank Holiday moved to June 4) matches the implementation's spring_bank_dates mapping.

✅ Suggested test addition
def test_2012(self):
    self.assertHolidays(
        LondonStockExchange(years=2012),
        ("2012-01-01", "New Year's Day"),  # adjust as needed for actual 2012 dates/observed rules
        ("2012-04-06", "Good Friday"),
        ("2012-04-09", "Easter Monday"),
        ("2012-05-07", "May Day"),
        ("2012-06-04", "Spring Bank Holiday"),
        ("2012-06-05", "Diamond Jubilee of Elizabeth II"),  # verify actual static holiday name
        ("2012-08-27", "Late Summer Bank Holiday"),
        ("2012-12-25", "Christmas Day"),
        ("2012-12-26", "Boxing Day"),
    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/financial/test_london_stock_exchange.py` around lines 89 - 104, Add a
missing 2012 coverage case in the LondonStockExchange holiday tests so the
Jubilee-year Spring Bank Holiday shift is verified alongside 2002 and 2022.
Update the test class in test_london_stock_exchange to include a new test_2012
that asserts the 2012 holiday set, especially that Spring Bank Holiday falls on
June 4 per the spring_bank_dates mapping and includes the Diamond Jubilee
observance used by the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/financial/test_london_stock_exchange.py`:
- Around line 89-104: Add a missing 2012 coverage case in the
LondonStockExchange holiday tests so the Jubilee-year Spring Bank Holiday shift
is verified alongside 2002 and 2022. Update the test class in
test_london_stock_exchange to include a new test_2012 that asserts the 2012
holiday set, especially that Spring Bank Holiday falls on June 4 per the
spring_bank_dates mapping and includes the Diamond Jubilee observance used by
the implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 403624ae-336e-4718-8e25-eaf782eb56be

📥 Commits

Reviewing files that changed from the base of the PR and between 613b182 and b0d20a8.

📒 Files selected for processing (2)
  • holidays/financial/london_stock_exchange.py
  • tests/financial/test_london_stock_exchange.py

@KJhellico

KJhellico commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

point me

I was talking exactly about this test, not the implementation.

Of course, for the implementation itself, this

    parent_entity_subdivision_code = "ENG"

is needed.

Also, for the HALF_DAY category to work properly, you need to add

diff --git a/holidays/countries/united_kingdom.py b/holidays/countries/united_kingdom.py
--- a/holidays/countries/united_kingdom.py	(revision b0d20a82c4b8fd2001cb3b3fb9fabfd8f491b263)
+++ b/holidays/countries/united_kingdom.py	(date 1783183559104)
@@ -13,6 +13,7 @@
 from gettext import gettext as tr
 
 from holidays.calendars.gregorian import APR, MAY, JUN, JUL, SEP, DEC
+from holidays.constants import PUBLIC
 from holidays.groups import ChristianHolidays, InternationalHolidays, StaticHolidays
 from holidays.observed_holiday_base import (
     ObservedHolidayBase,
@@ -104,7 +105,7 @@
             self._add_whit_monday(tr("Whit Monday"))
 
     def _populate_subdiv_holidays(self):
-        if self.subdiv != "SCT":
+        if PUBLIC in self.categories and self.subdiv != "SCT":
             if self._year >= 1975:
                 # New Year's Day.
                 self._add_observed(self._add_new_years_day(tr("New Year's Day")))

BTW, since the United Kingdom supports l10n, it would be advisable to implement it here as well.

Reworks the London Stock Exchange to derive from UnitedKingdom via
ChildEntity with parent_entity_subdivision_code="ENG" (following the
JPX/SSE pattern), instead of duplicating the England bank-holiday logic.
Its public holidays are now an exact alias of UnitedKingdom(subdiv="ENG"),
including the one-off royal bank holidays, with no duplicated code.

To keep the HALF_DAY category clean, gate UnitedKingdom's shared
subdivision public holidays on `PUBLIC in self.categories`, so requesting
only HALF_DAY no longer pulls in New Year's Day / Christmas / Boxing Day.

Adds localization (en_GB source, en_US, th) for the half-day session
strings and lists XLON's languages and HALF_DAY category in the README.
@github-actions github-actions Bot added the l10n label Jul 5, 2026
@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @KJhellico - refactored to the inheritance approach as you suggested. Summary of the changes:

1. Inherit from UnitedKingdom. LondonStockExchange is now class LondonStockExchange(ChildEntity, UnitedKingdom) with parent_entity_subdivision_code = "ENG", mirroring the JPX/SSE pattern. All the duplicated England bank-holiday logic is gone - the public output is now an exact alias of UnitedKingdom(subdiv="ENG") (verified equal for 2000-2030), including the one-off royal bank holidays.

2. test_code. Followed JPX/SSE - the test now asserts market is present and country is None.

3. HALF_DAY guard. Applied your united_kingdom.py diff exactly:

-        if self.subdiv != "SCT":
+        if PUBLIC in self.categories and self.subdiv != "SCT":

so requesting only HALF_DAY no longer pulls in New Year's Day / Christmas / Boxing Day. All 30 UK tests and Isle of Man still pass.

4. l10n. Added localization for the half-day strings (source en_GB, plus en_US and th), reusing the repo's existing %s (half-day closing) label so the strings are consistent with other markets. Rendered output:

en_GB/en_US:  Christmas Eve (half-day closing) | New Year's Eve (half-day closing)
th:           วันคริสต์มาสอีฟ (ปิดครึ่งวัน)   | วันสิ้นปี (ปิดครึ่งวัน)

README updated with XLON's languages + HALF_DAY category.

test_l10n, test_docs, the LSE/UK/Isle of Man suites and ruff all pass. Let me know if you'd like any adjustments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/financial/test_london_stock_exchange.py (1)

36-151: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Year-specific holidays checked against UK observance rules — all dates and observed shifts look correct for 2002, 2011, 2020-2025.

However, the PR objectives call out Jubilee-year Spring Bank Holiday adjustments in 2002, 2012, and 2022, but only 2002 and 2022 are tested here — the 2012 Diamond Jubilee shift has no coverage.

♻️ Suggested addition
def test_2012(self):
    self.assertHolidays(
        LondonStockExchange(years=2012),
        ("2012-01-01", "New Year's Day"),
        ("2012-01-02", "New Year's Day (observed)"),
        ("2012-04-06", "Good Friday"),
        ("2012-04-09", "Easter Monday"),
        ("2012-05-07", "May Day"),
        ("2012-06-04", "Spring Bank Holiday"),
        ("2012-06-05", "Diamond Jubilee of Elizabeth II"),
        ("2012-08-27", "Late Summer Bank Holiday"),
        ("2012-12-25", "Christmas Day"),
        ("2012-12-26", "Boxing Day"),
    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/financial/test_london_stock_exchange.py` around lines 36 - 151, Add
missing coverage in LondonStockExchange tests for the 2012 Jubilee year by
introducing a test_2012 alongside test_2002/test_2022. Use
LondonStockExchange(years=2012) in test_london_stock_exchange.py and assert the
Diamond Jubilee-related holiday set, including the shifted Spring Bank Holiday
and observed New Year’s Day, so the year-specific observance rules are exercised
for 2012 as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/financial/test_london_stock_exchange.py`:
- Around line 36-151: Add missing coverage in LondonStockExchange tests for the
2012 Jubilee year by introducing a test_2012 alongside test_2002/test_2022. Use
LondonStockExchange(years=2012) in test_london_stock_exchange.py and assert the
Diamond Jubilee-related holiday set, including the shifted Spring Bank Holiday
and observed New Year’s Day, so the year-specific observance rules are exercised
for 2012 as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3c1c613-f143-4394-8602-206398da547b

📥 Commits

Reviewing files that changed from the base of the PR and between b0d20a8 and 57ea02b.

📒 Files selected for processing (7)
  • README.md
  • holidays/countries/united_kingdom.py
  • holidays/financial/london_stock_exchange.py
  • holidays/locale/en_GB/LC_MESSAGES/XLON.po
  • holidays/locale/en_US/LC_MESSAGES/XLON.po
  • holidays/locale/th/LC_MESSAGES/XLON.po
  • tests/financial/test_london_stock_exchange.py

Comment thread holidays/financial/london_stock_exchange.py Outdated
Comment thread holidays/financial/london_stock_exchange.py Outdated
Comment thread holidays/financial/london_stock_exchange.py Outdated
Comment thread holidays/locale/en_GB/LC_MESSAGES/XLON.po Outdated
Comment thread tests/financial/test_london_stock_exchange.py Outdated
Comment on lines +30 to +35
def test_market_aliases(self):
self.assertAliases(LondonStockExchange, XLON, LSE)

def test_no_holidays(self):
self.assertNoHolidays(LondonStockExchange(years=1999))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_market_aliases(self):
self.assertAliases(LondonStockExchange, XLON, LSE)
def test_no_holidays(self):
self.assertNoHolidays(LondonStockExchange(years=1999))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eeshsaxena, this remains relevant.

Comment thread tests/financial/test_london_stock_exchange.py Outdated
Simplify the class docstring, rename the half-day label to 'markets
close at 12:30pm' matching the requested phrasing, and consolidate
the yearly test cases into a single representative year plus
dedicated Christmas Eve/New Year's Eve and l10n tests.
@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @KJhellico - applied all of it:

  1. Docstring - simplified to your suggested version.
  2. Half-day label - renamed to `%s (markets close at 12:30pm)`, restructured the two `_add_christmas_eve`/`_add_new_years_eve` calls with inline comments as suggested, and updated the msgid across all three .po files (en_GB, en_US, th) accordingly.
  3. .po header comments - updated to `# London Stock Exchange (LSE) holidays[...]` to match the docstring/other markets' convention.
  4. Tests - trimmed to one representative year (2022, which still exercises the Platinum Jubilee + State Funeral one-offs), added dedicated `test_christmas_eve`/`test_new_years_eve` half-day tests, and `test_l10n_default`/`test_l10n_en_us`/`test_l10n_th`. Year-by-year calendar correctness stays covered by the United Kingdom test suite, per the alias relationship.

Also added `-> None` to `_populate_half_day_holidays` per CodeRabbit's nitpick. Left the CodeRabbit "missing 2012 Jubilee test" suggestion out since it conflicts with consolidating to one yearly test - happy to add it back if you'd rather keep both.

Ruff, `test_l10n`, `test_docs`, the LSE/UK suites (14 + 43 tests) all green locally.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@pareshjoshij

pareshjoshij commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@eeshsaxena

- "Project-Id-Version: Holidays 0.100\n"

+ "Project-Id-Version: Holidays 0.101\n"

Comment on lines +35 to +39
# Exercises the royal one-off bank holidays alongside the regular
# England & Wales calendar; year-by-year correctness of the underlying
# calendar itself is covered by the United Kingdom test suite.
self.assertHolidays(
LondonStockExchange(years=2022),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Exercises the royal one-off bank holidays alongside the regular
# England & Wales calendar; year-by-year correctness of the underlying
# calendar itself is covered by the United Kingdom test suite.
self.assertHolidays(
LondonStockExchange(years=2022),
self.assertHolidaysInYear(
2022,

An place this test before l10n tests.

@KJhellico KJhellico Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eeshsaxena, the suggestion includes removing the redundant comment.
And "before" means "right before". ;)

@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks both - done:

  • @pareshjoshij: bumped Project-Id-Version to Holidays 0.101 in all three XLON .po files (and regenerated the .mo files).
  • @KJhellico: switched the 2022 test to assertHolidaysInYear(2022, ...); it already sits above the l10n tests.

14 LSE tests plus test_docs/test_registry pass locally and ruff is clean.

@pareshjoshij pareshjoshij left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Unshifted Half-Days: When Christmas Eve (Dec 24) and New Year's Eve (Dec 31) fall on a weekend, the LSE shifts the half-day early close to the preceding Friday. The current script hardcodes these to the 24th and 31st, resulting in market opens being reported on Saturdays or Sundays.
  • Because the class inherits from the UnitedKingdom calendar, it emits standard weekend holidays (e.g., actual Christmas Day on a Sunday) alongside the observed weekday substitutes. IMO it should only output affected business days .

Comparison (Using 2011 as an example)
In 2011, Dec 24 and Dec 31 fell on Saturdays. The official LSE schedule shifted the early closes to Friday, Dec 23 and Friday, Dec 30.

Current Output Official LSE Schedule
2011-01-01 -> New Year's Day (Removed - Weekend)
2011-01-03 -> New Year's Day (observed) 2011-01-03 -> New Year's Day (observed)
2011-04-22 -> Good Friday 2011-04-22 -> Good Friday
2011-04-25 -> Easter Monday 2011-04-25 -> Easter Monday
2011-04-29 -> Wedding of William and Catherine 2011-04-29 -> Wedding of William and Catherine
2011-05-02 -> May Day 2011-05-02 -> May Day
2011-05-30 -> Spring Bank Holiday 2011-05-30 -> Spring Bank Holiday
2011-08-29 -> Late Summer Bank Holiday 2011-08-29 -> Late Summer Bank Holiday
2011-12-24 -> Christmas Eve (markets close at 12:30pm) 2011-12-23 -> Christmas Eve (markets close at 12:30pm)
2011-12-25 -> Christmas Day (Removed - Weekend)
2011-12-26 -> Boxing Day 2011-12-26 -> Boxing Day
2011-12-27 -> Christmas Day (observed) 2011-12-27 -> Christmas Day (observed)
2011-12-31 -> New Year's Eve (markets close at 12:30pm) 2011-12-30 -> New Year's Eve (markets close at 12:30pm)

@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @pareshjoshij, this is a great catch and your 2011 reference is spot on. I reproduced both issues on the current branch:

HALF_DAY 2011:
  2011-12-24 Sat  Christmas Eve (markets close at 12:30pm)   <- Saturday, market shut
  2011-12-31 Sat  New Year's Eve (markets close at 12:30pm)  <- Saturday, market shut

PUBLIC 2011:
  2011-01-01 Sat  New Year's Day        <- weekend
  2011-12-25 Sun  Christmas Day         <- weekend
  ... (observed substitutes on 01-03 and 12-27 are present and correct)

There are really two separate things here, and I want to get the second one right rather than guess:

1. Half-day early closes falling on a weekend. This one is clearly a bug - an early close on a Saturday is meaningless. I can fix it by shifting the half-day to the preceding Friday, which matches your 2011 evidence (Dec 24 Sat -> Dec 23 Fri, Dec 31 Sat -> Dec 30 Fri). The library already has SAT_SUN_TO_PREV_FRI ({SAT: -1, SUN: -2}) in observed_holiday_base.py, so I would move the two _add_christmas_eve / _add_new_years_eve calls through that rule. Happy to push that.

2. Weekend-falling public holidays. I agree a market calendar arguably shouldn't list Christmas Day when it lands on a Sunday (the market is already closed, and the observed substitute is what actually matters). The wrinkle is that these come from the inherited UnitedKingdom(subdiv="ENG") population via ChildEntity - which is the design @arkid15r and @KJhellico pointed me toward specifically so LSE reuses the UK bank-holiday logic rather than reimplementing it. Suppressing the weekend-falling ones means overriding/post-filtering that inherited output, which pulls a little against that clean-inheritance approach (e.g. XNYS gets this "for free" only because it builds its own calendar from scratch).

So before I implement #2: @KJhellico @pareshjoshij, what is the direction you'd prefer - filter out inherited holidays that fall on a weekend (drop the raw Sat/Sun dates, keep the observed substitutes), or keep the full inherited set as-is? I'll implement whichever you both agree on. I can push the half-day fix (#1) right away since that one seems uncontroversial.

@pareshjoshij

pareshjoshij commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

So before I implement #2: @KJhellico @pareshjoshij, what is the direction you'd prefer - filter out inherited holidays that fall on a weekend (drop the raw Sat/Sun dates, keep the observed substitutes), or keep the full inherited set as-is? I'll implement whichever you both agree on. I can push the half-day fix (#1) right away since that one seems uncontroversial.

Don't worry buddy, we're in the same boat! Take a look at my open PR #3692 below and just read through the mentor's suggestions

The exchange does not trade at weekends, so a bank holiday falling on a
Saturday or Sunday is not a trading day in its own right; only its observed
weekday substitute is. Suppress those inherited dates with
SAT_TO_NONE + SUN_TO_NONE, following the approach used for SIX.

The shortened Christmas Eve and New Year's Eve sessions likewise move back to
the preceding Friday when the eve falls on a weekend, matching the published
LSE calendar (2011: Dec 23 and Dec 30).
@eeshsaxena

Copy link
Copy Markdown
Contributor Author

Thanks @pareshjoshij - that pointer was exactly what I needed, and thanks @KJhellico for the guidance on #3692. Both of your points are now implemented.

1. Weekend bank holidays are dropped. Following the approach @KJhellico prescribed on #3692, the inherited England and Wales dates that land on a Saturday or Sunday are suppressed, so only the observed weekday substitutes remain:

def _populate(self, year: int) -> None:
    super()._populate(year)

    if self.observed:
        for dt in tuple(self.keys()):
            if dt.year == year:
                self._move_holiday(dt, rule=SAT_TO_NONE + SUN_TO_NONE, show_observed_label=False)

I used the composed SAT_TO_NONE + SUN_TO_NONE rather than adding SAT_SUN_TO_NONE here, so this does not collide with #3692 - once that lands and the constant exists, this is a one-line swap.

2. Half-days move to the preceding Friday. The shortened Christmas Eve and New Year's Eve sessions now shift back via SAT_SUN_TO_PREV_FRI when the eve falls on a weekend.

2011 now matches the official schedule you linked, exactly:

Date Category Name
2011-01-03 PUBLIC New Year's Day (observed)
2011-04-22 PUBLIC Good Friday
2011-04-25 PUBLIC Easter Monday
2011-04-29 PUBLIC Wedding of William and Catherine
2011-05-02 PUBLIC May Day
2011-05-30 PUBLIC Spring Bank Holiday
2011-08-29 PUBLIC Late Summer Bank Holiday
2011-12-23 HALF_DAY Christmas Eve (markets close at 12:30pm)
2011-12-26 PUBLIC Boxing Day
2011-12-27 PUBLIC Christmas Day (observed)
2011-12-30 HALF_DAY New Year's Eve (markets close at 12:30pm)

2011-01-01 (Sat) and 2011-12-25 (Sun) are gone, and the two early closes sit on the preceding Fridays.

Tests: updated test_2022 (the Sat/Sun raw dates are no longer emitted), extended the Christmas Eve / New Year's Eve tests to pin the shifted cases (2011 Sat, 2017 Sun, 2024 weekday), and added test_weekend_holidays_are_not_trading_days. 15 LSE tests pass at 100% coverage; tests/financial/ + the United Kingdom suite are green (293 passed); test_docs and test_registry pass; ruff clean.

Agreed with @KJhellico that a cleaner mechanism for country-backed markets would be nice down the line - happy to help with that once the pattern settles.

@sonarqubecloud

Copy link
Copy Markdown

Comment on lines +47 to +59
for dt in (
self._add_christmas_eve(
# Christmas Eve.
self._format_holiday_name(close_12_30pm_label, tr("Christmas Eve"))
),
self._add_new_years_eve(
# New Year's Eve.
self._format_holiday_name(close_12_30pm_label, tr("New Year's Eve"))
),
):
# The shortened session moves to the preceding Friday when the eve falls
# on a weekend, since the exchange does not trade on Saturday or Sunday.
self._move_holiday(dt, rule=SAT_SUN_TO_PREV_FRI, show_observed_label=False)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for dt in (
self._add_christmas_eve(
# Christmas Eve.
self._format_holiday_name(close_12_30pm_label, tr("Christmas Eve"))
),
self._add_new_years_eve(
# New Year's Eve.
self._format_holiday_name(close_12_30pm_label, tr("New Year's Eve"))
),
):
# The shortened session moves to the preceding Friday when the eve falls
# on a weekend, since the exchange does not trade on Saturday or Sunday.
self._move_holiday(dt, rule=SAT_SUN_TO_PREV_FRI, show_observed_label=False)
self._move_holiday(
self._add_christmas_eve(
# Christmas Eve.
self._format_holiday_name(close_12_30pm_label, tr("Christmas Eve"))
),
rule=SAT_SUN_TO_PREV_FRI,
show_observed_label=False,
)
self._move_holiday(
self._add_new_years_eve(
# New Year's Eve.
self._format_holiday_name(close_12_30pm_label, tr("New Year's Eve"))
),
rule=SAT_SUN_TO_PREV_FRI,
show_observed_label=False,
)

It's advisable to simply expand on the existing comment about Christmas Eve and New Year's Eve in docstring.

Comment on lines +64 to +66
# The exchange is closed at weekends, so a bank holiday that falls on a
# Saturday or Sunday is not a trading day in its own right; only its
# observed weekday substitute is.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether such a detailed comment is necessary, but I hope that in the end we'll get completely rid of this code fragment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants