Skip to content

Conversation

@blinkysc
Copy link
Contributor

@blinkysc blinkysc commented Dec 4, 2025

Changes Proposed:

Replaces static SQL-based holiday dates with dynamic C++ calculation, eliminating the need for recurring SQL maintenance as years progress.

This PR proposes changes to:

  • Core (units, players, creatures, game systems).
  • Scripts (bosses, spell scripts, creature scripts).
  • Database (SAI, creatures, etc).

New Files

  • HolidayDateCalculator.h/.cpp - Utility class that calculates holiday dates at runtime:
    • Computus algorithm for Easter-based holidays (Noblegarden)
    • Nth weekday calculation for floating holidays (Pilgrim's Bounty = 4th Thursday of November)
    • Fixed date handling for standard holidays
  • HolidayDateCalculatorTest.cpp - Unit tests covering years 1900-2200

Modified Files

  • GameEventMgr.cpp - Generates dynamic dates on load, DB overrides preserved for custom servers

Holidays Covered

Holiday Calculation Type
Lunar Festival Fixed: Jan 22
Love is in the Air Fixed: Feb 6
Noblegarden Easter + 7 days
Children's Week Fixed: Apr 30
Harvest Festival Fixed: Sep 28
Pilgrim's Bounty 4th Thursday of November

Issues Addressed:

SOURCE:

The changes have been validated through:

  • Live research (checked on live servers, e.g Classic WotLK, Retail, etc.)
  • Sniffs (remember to share them with the open source community!)
  • Video evidence, knowledge databases or other public sources (e.g forums, Wowhead, etc.)
  • The changes promoted by this pull request come partially or entirely from another project (cherry-pick).

Easter dates validated against known astronomical Easter tables. Thanksgiving dates validated against US calendar.

Tests Performed:

This PR has been:

  • Tested in-game by the author.
  • Tested in-game by other community members/someone else other than the author/has been live on production servers.
  • This pull request requires further testing and may have edge cases to be tested.

Unit tests pass:

  • All holidays calculated correctly for 301 years (1900-2200)
  • Leap year edge cases verified (1900, 2000, 2100, 2200)
  • Easter Computus algorithm validated
  • Pack/unpack roundtrip verified

How to Test the Changes:

  • This pull request can be tested by following the reproduction steps provided in the linked issue
  • This pull request requires further testing. Provide steps to test your changes.
  1. Build the server with unit tests enabled
  2. Run HolidayDateCalculatorTest to verify calculations
  3. Start server and check calendar in-game
  4. Verify holiday dates display correctly for current and future years

Known Issues and TODO List:

  • May need additional holidays added to the rules table
  • In-game calendar testing needed

@github-actions github-actions bot added CORE Related to the core UnitTests file-cpp Used to trigger the matrix build labels Dec 4, 2025
Replaces static SQL-based holiday dates with dynamic C++ calculation.
Holidays are now computed at runtime using:
- Computus algorithm for Easter-based holidays (Noblegarden)
- Nth weekday calculation for floating holidays (Pilgrim's Bounty)
- Fixed dates for standard holidays

Includes unit tests covering years 1900-2200.
DB overrides still supported for custom servers.
@blinkysc blinkysc force-pushed the feature/dynamic-holiday-date-calculator branch from 5d19425 to 61eee6f Compare December 4, 2025 20:43
@blinkysc blinkysc requested a review from sudlud December 4, 2025 20:52
@Nyeriah Nyeriah added the run-build Used to trigger the windows/mac/docker and matrix builds label Dec 5, 2025
@sudlud
Copy link
Member

sudlud commented Dec 5, 2025

does this align with the entries in game_event table?

or even better could those entries be taken into account maybe

@sudlud
Copy link
Member

sudlud commented Dec 5, 2025

it would be great if the calendar could actually reflect what game_event is configured for these events, right now the actual game_event handling and in game calendar are completely independent unfortunately. this pr now introduces another place where these get hardcoded.

@sudlud
Copy link
Member

sudlud commented Dec 5, 2025

also is this one of your AI generated fixes?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces dynamic holiday date calculation to replace static SQL-based holiday dates. It adds a new HolidayDateCalculator utility class that computes holiday dates at runtime using mathematical algorithms (Computus for Easter, nth-weekday calculation for Thanksgiving-like holidays, and fixed dates for regular holidays). The system generates dates for the current year plus 10 future years at server startup, while preserving database override capabilities for custom servers.

Key Changes:

  • New holiday date calculation system supporting three types: fixed dates, nth weekday of month, and Easter-relative dates
  • Comprehensive unit test coverage spanning 301 years (1900-2200) with edge case validation
  • Integration with existing GameEventMgr that generates dynamic dates while maintaining DB override compatibility

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.

File Description
HolidayDateCalculator.h Declares calculator class with Easter (Computus), nth-weekday, and fixed-date calculation methods; defines holiday rule structures and enums
HolidayDateCalculator.cpp Implements Gregorian Computus algorithm for Easter, nth-weekday finder, date packing/unpacking for WoW's 2000-2031 format, and holiday rule configuration
HolidayDateCalculatorTest.cpp Comprehensive test suite with 485 lines covering known Easter dates, weekday calculations, pack/unpack roundtrips, leap years, and century boundaries across 1900-2200
GameEventMgr.cpp Modified LoadHolidayDates() to generate 11 years of dynamic dates per holiday, then load DB overrides; preserves backward compatibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

blinkysc added 2 commits December 5, 2025 07:30
- Use std::chrono with thread-safe localtime_r/localtime_s
- Add const to applicable variables
- Change Weekday to enum class
- Add source reference for Computus algorithm
- Add unit test for packed date year limitations
@blinkysc
Copy link
Contributor Author

blinkysc commented Dec 5, 2025

does this align with the entries in game_event table?
it would be great if the calendar could actually reflect what game_event is configured for these events, right now the actual game_event handling and in game calendar are completely independent unfortunately. this pr now introduces another place where these get hardcoded.

No currently it doesn't... I'll see what I can do today

also is this one of your AI generated fixes?

I use AI to help me quite a bit yeah, but its more like don't trust and verify/read through exactly what I'm doing. It's why also I wanted the unit tests.

@sudlud
Copy link
Member

sudlud commented Dec 6, 2025

darkmoon faires dates are already beeing calculated in the core via the dbc configuration, the dbc entry is linked in game_event see e.g.

void GameEventMgr::SetHolidayEventTime(GameEventData& event)

@blinkysc
Copy link
Contributor Author

blinkysc commented Dec 6, 2025

After digging deeper into this, I want to clarify how the integration actually works:

The dynamic dates DO feed into game_event timing - they're not independent systems.

The loading order in World.cpp is:

  1. LoadHolidayDates() - calculates dynamic dates → writes to sHolidaysStore->Date[]
  2. LoadFromDB() → LoadEvents() → SetHolidayEventTime()

SetHolidayEventTime() (line 1880) reads from holiday->Date[] to set gameEvent.Start. So when we populate Date[] dynamically, both the calendar AND the game event timing use the same calculated dates.

HolidayDateCalculator


sHolidaysStore->Date[] ←── Single source of truth

├──► Calendar display

└──► SetHolidayEventTime() → gameEvent.Start

The only thing "hardcoded" in C++ is the calculation rules (Easter algorithm, "4th Thursday of November" logic), not the dates themselves. The holiday_dates table still works as an override layer for custom servers or cultural differences.

@blinkysc
Copy link
Contributor Author

blinkysc commented Dec 6, 2025

darkmoon faires dates are already beeing calculated in the core via the dbc configuration, the dbc entry is linked in game_event see e.g.

void GameEventMgr::SetHolidayEventTime(GameEventData& event)

this PR doesn't change that. Darkmoon uses Looping=1 with a fixed occurence interval, which works because it recurs at regular intervals

Implement Jean Meeus algorithm to calculate Chinese New Year dates
dynamically using astronomical new moon calculation instead of a
static lookup table. CNY falls on the new moon between Jan 21-Feb 20.

- Add CalculateLunarNewYear() with full Meeus algorithm
- Add DateToJulianDay/JulianDayToDate conversion functions
- Add CalculateNewMoon() with 15 correction terms from Table 49.A
- Update Lunar Festival rule to use LUNAR_NEW_YEAR calculation type
- Add 5 unit tests covering years 1900-2200 with 100% accuracy
Add 7 additional fixed-date holidays to the dynamic system:
- Midsummer Fire Festival (Jun 21)
- Fireworks Spectacular (Jul 4)
- Pirates' Day (Sep 19)
- Brewfest (Sep 20)
- Hallow's End (Oct 18)
- Day of the Dead (Nov 1)
- Winter Veil (Dec 15)

Total holidays now managed by dynamic system: 13
- Alphabetize HolidayDateCalculator.h include
- Use explicit time_point type instead of auto
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CORE Related to the core file-cpp Used to trigger the matrix build Ready to be Reviewed run-build Used to trigger the windows/mac/docker and matrix builds UnitTests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants