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.

The game_event.start_time column in the database serves as an override for calculated dates. If start_time is set to a date in the current year or later, it will be used instead of the dynamically calculated date. This allows custom servers to override specific holiday dates without modifying the code.

This PR proposes changes to:

  • Core (units, players, creatures, game systems).
  • Scripts (bosses, spell scripts, creature scripts).
  • Database (removes holiday_dates table - no longer needed).

New Files

  • HolidayDateCalculator.h/.cpp - Utility class that calculates holiday dates at runtime:
    • Lunar New Year calculation (Meeus Astronomical Algorithms)
    • Computus algorithm for Easter-based holidays (Noblegarden)
    • Nth weekday calculation for floating holidays (Pilgrim's Bounty)
    • Autumn equinox / Winter solstice calculations (Harvest Festival, Winter Veil)
    • Darkmoon Faire 3-location monthly rotation
    • 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 Chinese New Year - 1 day (astronomical)
Love is in the Air First Monday on or after Feb 3
Noblegarden Easter Sunday + 1 day (Computus algorithm)
Children's Week First Monday on or after Apr 25
Midsummer Fire Festival Fixed Jun 21
Fireworks Spectacular Fixed Jul 4
Pirates' Day Fixed Sep 19
Brewfest First Saturday on or after Sept 15, minus 7 days
Harvest Festival Autumn equinox - 2 days (astronomical)
Hallow's End Fixed Oct 18
Day of the Dead Fixed Nov 1
Pilgrim's Bounty 4th Thursday of November - 4 days
Winter Veil Winter solstice - 6 days (astronomical)
Darkmoon Faire (Elwynn) First Sunday of Mar/Jun/Sep/Dec - 2 days
Darkmoon Faire (Mulgore) First Sunday of Jan/Apr/Jul/Oct - 2 days
Darkmoon Faire (Terokkar) First Sunday of Feb/May/Aug/Nov - 2 days

Client Limitation

While the algorithm calculates dates for any year (validated 1900-2200), the WoW 3.3.5a client uses a 5-bit year offset field, limiting displayable dates to years 2000-2031.

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. Lunar New Year validated against astronomical sources. Equinox/solstice calculations use Jean Meeus algorithms.

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
  • Lunar New Year algorithm validated
  • Darkmoon Faire 3-location rotation 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
  5. Verify Darkmoon Faire shows all 3 locations rotating monthly
  6. Test override by setting custom start_time in game_event

Known Issues and TODO List:

  • Holiday dates verified against official Blizzard schedule
  • Darkmoon Faire 3-location rotation implemented
  • holiday_dates table removed (dates calculated dynamically)
  • In-game calendar tested

@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
@blinkysc
Copy link
Contributor Author

blinkysc commented Dec 20, 2025

I think maybe what I should do to tidy this up is likely just generate 3 years vs 10 (client limitation anyways) as well potentially have game_event.start_time be the override since it's human readable to begin with? Big question I have is should we rename to something like start_time_override to be more specific? And it should be that it's blank for the most part unless admins want to change those dates?

EDIT: Refactored for these changes other than renaming sql

@github-actions github-actions bot added the DB related to the SQL database label Jan 2, 2026
sudlud and others added 7 commits January 2, 2026 09:48
- otherwise events would disappear right on their ending date after restart
- also you can look very far back in the calendar
- Add all three Darkmoon Faire locations (374, 375, 376) to HolidayRules
- Special handling in LoadHolidayDates() for multi-date holidays (4 per year)
- Set Duration[0] = 168 hours (7 days) if not set in DBC
- Use -2 day offset (Friday start for building phase before Sunday faire)
- Add GetDarkmoonFaireDates dayOffset parameter for configurable offset
- Add integration test verifying Darkmoon entries in HolidayRules
- Change from month % 3 (3 locations) to month % 2 (2 locations)
- Odd months (Jan, Mar, May...) = Alliance Elwynn (374)
- Even months (Feb, Apr, Jun...) = Horde Mulgore (375)
- Remove Terokkar (376) as it's not used in WotLK
Updated CalculateHolidayDate() to use month % 2 instead of month % 3
for proper 2-location alternation (Elwynn odd months, Mulgore even months).
Also updated unit tests to match the WotLK 2-location pattern.
Rotation: Mulgore (Jan/Apr/Jul/Oct) -> Terokkar (Feb/May/Aug/Nov) -> Elwynn (Mar/Jun/Sep/Dec)
Uses month % 3 pattern for location offset calculation.
@blinkysc
Copy link
Contributor Author

blinkysc commented Jan 2, 2026

Wiki PR azerothcore/wiki#1153

…me_event

- these also get calculated automatically now
@sudlud
Copy link
Member

sudlud commented Jan 2, 2026

tested again on the latest revision. dates in the in-game calendar look good to me for the last year and the upcoming year visible in the client

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 12 comments.


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

- Clamp years before 2000 to offset 0 to prevent integer underflow
- Change per-date holiday logging from LOG_INFO to LOG_DEBUG
- Fix Metonic cycle test comment to explain 30-day tolerance
Tests Apr 25, Apr 30, and Dec 31 edge cases where the calculated
weekday may roll into the next month (or next year for Dec 31).
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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.


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

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@sudlud sudlud merged commit c1d753f into azerothcore:master Jan 2, 2026
12 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CORE Related to the core DB related to the SQL database file-cpp Used to trigger the matrix build Requires WIKI Update Wiki sources will need to be updated after merging this PR. run-build Used to trigger the windows/mac/docker and matrix builds Tested This PR has been tested and is working. To Be Merged UnitTests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lunar Festival Active and Many Holidays Missing from Calendar [World Events] Mismatched Holiday event dates between in-game/aowow/wowhead calendars

5 participants