You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No Hong Kong holiday calendar exists in this library today. Hong Kong's statutory holidays are gazetted under Cap. 149 (General Holidays Ordinance) and include a mix of Western (colonial-era) and Chinese lunar-calendar holidays, plus an unusual asymmetric weekend in-lieu rule not currently modeled by any existing DateRoll in this codebase.
Proposed Solution
Create HolidayCalendarServiceHK (code HK) as the national/general statutory holiday calendar, following the ISO 3166-1 alpha-2 convention. This is the base calendar that XHKG (#258) builds on top of.
Holiday list
New Year's Day (Jan 1, fixed)
Lunar New Year (3-day cluster: eve + Day 1 + Day 2) — reuses org.holiday.calendar.observance.lunar.ChineseNewYearDay, widening its dayNumber guard to allow 0 for "eve"; see Open Questions below for an alternative
Ching Ming Festival (solar term ~Apr 4-5)
Good Friday and Easter Monday — needs independent verification against Cap. 149 / HK Labour Department gazette before merging; do not copy-paste from Singapore's precedent (SG only has Good Friday, no Easter Monday) or assume UK-style Easter Monday-only. HK's actual ordinance is understood to include Good Friday, the day following Good Friday, and Easter Monday as three separate entries — confirm exact count and names against the primary source.
Labour Day (May 1, fixed)
Buddha's Birthday (4th lunar month, 8th day) — needs a new algorithmic observance class, BuddhasBirthday in observance.lunar (Time4J-backed, EastAsianMonth.valueOf(4), day 8) — distinct from the existing Singapore VesakDay (different day-of-month, different data source: CSV/gazette table vs. algorithmic)
Tuen Ng Festival / Dragon Boat Festival (5th lunar month, 5th day)
HKSAR Establishment Day (Jul 1, fixed)
Day following Mid-Autumn Festival (8th lunar month, 15th day, +1 day) — needs a day-offset variant of MidAutumnFestival
National Day (Oct 1, fixed)
Chung Yeung Festival / Double Ninth Festival (9th lunar month, 9th day)
Christmas Day (Dec 25, fixed) and the first weekday after Christmas Day
Weekend roll — asymmetric in-lieu rule
Hong Kong's Employment Ordinance in-lieu rule is asymmetric, unlike anything currently in DateRolls:
Falls on Sunday → substitute on the next day that is not itself a weekend day or another statutory/substitute holiday (iteratively skip forward)
Falls on Saturday → no substitute at all (statute treats Saturday as a working day; some employers grant a courtesy Monday, but this is not the statutory rule and should not be modeled as such)
Falls on a weekday → observed as-is
New class: org.holiday.calendar.observance.hk.HKDateRolls.sundayToNextNonHolidayWeekday(List<Holiday> allHolidays) — a holiday-set-aware DateRoll closure. Note: this is genuinely new mechanics, not a straightforward reuse of UKDateRolls.fixedHolidayRoll — that method is a hardcoded 3-holiday switch with flat day offsets, not a generic iterating list-scan. Budget this as new development.
Cross-year boundary question — RESOLVED (structural proof, not just empirical absence). A researcher checked 15 years of actual HK Government gazetted "General Holidays" schedules (2012–2027) plus the definitional bounds of Lunar New Year, and confirmed the skip-forward walk can never cross the Dec 31/Jan 1 boundary:
HK's only late-December holidays are Dec 25 (Christmas Day) and Dec 26 (day following Christmas Day) — nothing else falls between Dec 27–31.
The worst-case chain is Dec 25 landing on Sunday with Dec 26 (Monday, itself a holiday) forcing the walk to continue — and it always terminates at Dec 27, since Dec 27 is never itself a holiday. Confirmed in every weekend-collision year found (2016, 2021, 2022, 2027 — all resolved to Dec 27, the latest possible substitute date observed or derivable).
The related "Lunar New Year's Eve could itself fall in the previous year" concern (if LNY Day 1 landed on Jan 1/2) is also structurally impossible: Chinese New Year is defined to fall between Jan 21 and Feb 20 every year (second new moon after the winter solstice), so it can never land on Jan 1 or 2.
Conclusion: HKDateRolls.sundayToNextNonHolidayWeekday is correctly scoped to same-calendar-year-only. Document this structural proof in the class Javadoc (not just as an implementation limitation, but as a proven invariant of HK's actual holiday set) so a future reader doesn't mistake the same-year scoping for an oversight.
Scope
Verify Good Friday/Easter Monday/day-after-Good-Friday holiday list against Cap. 149 primary source
Create org.holiday.calendar.observance.lunar.BuddhasBirthday (new, East Asian 4th-month/8th-day convention)
Add day-offset variant for Mid-Autumn day-after (either widen MidAutumnFestival or add a sibling class)
Widen ChineseNewYearDay's dayNumber guard to allow 0, update Javadoc to document the new "eve" semantics, OR add a sibling ChineseNewYearEve class instead (recommended — avoids changing the documented contract of a class two already-shipped calendars, CN/CNY, depend on)
Create org.holiday.calendar.observance.hk.HKDateRolls with the asymmetric Sunday/Saturday roll, scoped to same-calendar-year-only per the resolved structural proof above (document the proof in the class Javadoc, don't just silently assume it)
Create package-private HKHolidays shared holiday-list factory in org.holiday.calendar.impl (mirrors IsraelHolidays)
Create HolidayCalendarServiceHK
Register in holiday-calendar-apac/src/main/java/module-info.java (provides entry; export new observance.hk package)
Testing
HolidayCalendarServiceHKTest — metadata, count, golden-master floating-holiday dates for 2024-2026 (Lunar New Year, Ching Ming, Buddha's Birthday, Tuen Ng, Mid-Autumn day-after, Chung Yeung), factory discovery, chronological ordering, name definitions
testChristmasCluster2026() — Dec 25, 2026 (Fri, no roll) + day-after Dec 26 (Sat) — assert no phantom in-lieu entry
testChristmasSubstituteNeverExceedsDec27(int year) — parametrized over the confirmed historical/derivable weekend-collision years (2016, 2021, 2022, 2027) — assert the substitute always lands on Dec 27 and never later, as the concrete regression guard for the resolved structural proof
HKDateRollsTest — direct unit tests of the roll function in isolation: single-hop skip, multi-hop skip (immediately-following Monday also a holiday), Saturday-never-rolls. Include testCrossYearBoundaryInputIsUnreachableForHK() (or similar) as a defensive unit test documenting that a cross-year-spanning holiday list, while mechanically constructible as an input to the isolated function, never actually arises from HK's real holiday set per the resolved structural proof — keep this test's docstring explicit that it's defensive, not a live production scenario
BuddhasBirthdayTest — golden-master dates (2024 May 15, 2025 May 5, 2026 May 24 raw/pre-roll), null-safety, and an explicit testDiffersFromVesakDay() sanity check proving this isn't an accidental alias of the existing Singapore class
Cross-check test for the Mid-Autumn day-after variant: assert it always equals MidAutumnFestival().apply(year).plusDays(1), not just independently-hardcoded dates
Add HK to HolidayCalendar30YearIT's allCalendarCodes() for the 4 generic checks (range completeness, min holiday count, no nulls, chronological order)
Add a bespoke 30-year IT section re-deriving expected Saturday-no-roll/Sunday-rolls behavior for all floating holidays across 2026-2055, including an assertion that no Christmas-cluster substitute ever falls after Dec 27 or crosses into the following year — this section is no longer gated as a TODO, since the cross-year-boundary question is resolved
Verification
mvn -pl holiday-calendar-apac clean install
mvn -pl tests clean verify
factory.create("HK").dataValidThrough() returns OptionalInt.empty() (fully algorithmic, no year ceiling)
Considered modeling the Saturday-no-roll case by simply omitting rollable(true) on Saturday-adjacent holidays — rejected because rollability must be evaluated per-instance-year (a holiday can fall on Saturday in one year and Sunday in another), not statically per-holiday-definition.
Additional Context
Research package flagged Ching Ming's exact date and the Saturday-in-lieu "courtesy Monday" government/employer practice (as distinct from statute) as open verification items — see parent issue notes.
Cross-year boundary resolution (2026 follow-up research): verified against 15 years (2012–2027) of HK Government gazetted "General Holidays" schedules. Every weekend-collision year for the Dec 25/26 cluster (2016, 2021, 2022, 2027) resolved to a Dec 27 substitute, never later — and no other HK holiday falls between Dec 27–31 to extend the chain further, so a cross-year walk is structurally impossible, not merely unobserved. Lunar New Year's Eve crossing into the previous year is similarly impossible since Chinese New Year is defined to fall between Jan 21–Feb 20 every year.
Module(s) Affected
holiday-calendar-apac
Problem / Motivation
No Hong Kong holiday calendar exists in this library today. Hong Kong's statutory holidays are gazetted under Cap. 149 (General Holidays Ordinance) and include a mix of Western (colonial-era) and Chinese lunar-calendar holidays, plus an unusual asymmetric weekend in-lieu rule not currently modeled by any existing
DateRollin this codebase.Proposed Solution
Create
HolidayCalendarServiceHK(codeHK) as the national/general statutory holiday calendar, following the ISO 3166-1 alpha-2 convention. This is the base calendar thatXHKG(#258) builds on top of.Holiday list
org.holiday.calendar.observance.lunar.ChineseNewYearDay, widening itsdayNumberguard to allow0for "eve"; see Open Questions below for an alternativeBuddhasBirthdayinobservance.lunar(Time4J-backed,EastAsianMonth.valueOf(4), day 8) — distinct from the existing SingaporeVesakDay(different day-of-month, different data source: CSV/gazette table vs. algorithmic)MidAutumnFestivalWeekend roll — asymmetric in-lieu rule
Hong Kong's Employment Ordinance in-lieu rule is asymmetric, unlike anything currently in
DateRolls:New class:
org.holiday.calendar.observance.hk.HKDateRolls.sundayToNextNonHolidayWeekday(List<Holiday> allHolidays)— a holiday-set-awareDateRollclosure. Note: this is genuinely new mechanics, not a straightforward reuse ofUKDateRolls.fixedHolidayRoll— that method is a hardcoded 3-holiday switch with flat day offsets, not a generic iterating list-scan. Budget this as new development.Cross-year boundary question — RESOLVED (structural proof, not just empirical absence). A researcher checked 15 years of actual HK Government gazetted "General Holidays" schedules (2012–2027) plus the definitional bounds of Lunar New Year, and confirmed the skip-forward walk can never cross the Dec 31/Jan 1 boundary:
Conclusion:
HKDateRolls.sundayToNextNonHolidayWeekdayis correctly scoped to same-calendar-year-only. Document this structural proof in the class Javadoc (not just as an implementation limitation, but as a proven invariant of HK's actual holiday set) so a future reader doesn't mistake the same-year scoping for an oversight.Scope
org.holiday.calendar.observance.lunar.BuddhasBirthday(new, East Asian 4th-month/8th-day convention)MidAutumnFestivalor add a sibling class)ChineseNewYearDay'sdayNumberguard to allow0, update Javadoc to document the new "eve" semantics, OR add a siblingChineseNewYearEveclass instead (recommended — avoids changing the documented contract of a class two already-shipped calendars,CN/CNY, depend on)org.holiday.calendar.observance.hk.HKDateRollswith the asymmetric Sunday/Saturday roll, scoped to same-calendar-year-only per the resolved structural proof above (document the proof in the class Javadoc, don't just silently assume it)HKHolidaysshared holiday-list factory inorg.holiday.calendar.impl(mirrorsIsraelHolidays)HolidayCalendarServiceHKholiday-calendar-apac/src/main/java/module-info.java(providesentry; export newobservance.hkpackage)Testing
HolidayCalendarServiceHKTest— metadata, count, golden-master floating-holiday dates for 2024-2026 (Lunar New Year, Ching Ming, Buddha's Birthday, Tuen Ng, Mid-Autumn day-after, Chung Yeung), factory discovery, chronological ordering, name definitionstestTuenNg2025SaturdayNoRoll()(May 31, 2025 stays put, no in-lieu),testMidAutumnDayAfter2026SaturdayNoRoll()(Sep 26, 2026 stays put),testChingMing2026SundayRollsToMonday()(positive contrast: Apr 5, 2026 Sunday → Apr 6 Monday)testChristmasCluster2026()— Dec 25, 2026 (Fri, no roll) + day-after Dec 26 (Sat) — assert no phantom in-lieu entrytestChristmasSubstituteNeverExceedsDec27(int year)— parametrized over the confirmed historical/derivable weekend-collision years (2016, 2021, 2022, 2027) — assert the substitute always lands on Dec 27 and never later, as the concrete regression guard for the resolved structural proofHKDateRollsTest— direct unit tests of the roll function in isolation: single-hop skip, multi-hop skip (immediately-following Monday also a holiday), Saturday-never-rolls. IncludetestCrossYearBoundaryInputIsUnreachableForHK()(or similar) as a defensive unit test documenting that a cross-year-spanning holiday list, while mechanically constructible as an input to the isolated function, never actually arises from HK's real holiday set per the resolved structural proof — keep this test's docstring explicit that it's defensive, not a live production scenarioBuddhasBirthdayTest— golden-master dates (2024 May 15, 2025 May 5, 2026 May 24 raw/pre-roll), null-safety, and an explicittestDiffersFromVesakDay()sanity check proving this isn't an accidental alias of the existing Singapore classMidAutumnFestival().apply(year).plusDays(1), not just independently-hardcoded datesHKtoHolidayCalendar30YearIT'sallCalendarCodes()for the 4 generic checks (range completeness, min holiday count, no nulls, chronological order)Verification
mvn -pl holiday-calendar-apac clean installmvn -pl tests clean verifyfactory.create("HK").dataValidThrough()returnsOptionalInt.empty()(fully algorithmic, no year ceiling)Related
HKHolidays)Alternatives Considered
Considered modeling the Saturday-no-roll case by simply omitting
rollable(true)on Saturday-adjacent holidays — rejected because rollability must be evaluated per-instance-year (a holiday can fall on Saturday in one year and Sunday in another), not statically per-holiday-definition.Additional Context
Research package flagged Ching Ming's exact date and the Saturday-in-lieu "courtesy Monday" government/employer practice (as distinct from statute) as open verification items — see parent issue notes.
Cross-year boundary resolution (2026 follow-up research): verified against 15 years (2012–2027) of HK Government gazetted "General Holidays" schedules. Every weekend-collision year for the Dec 25/26 cluster (2016, 2021, 2022, 2027) resolved to a Dec 27 substitute, never later — and no other HK holiday falls between Dec 27–31 to extend the chain further, so a cross-year walk is structurally impossible, not merely unobserved. Lunar New Year's Eve crossing into the previous year is similarly impossible since Chinese New Year is defined to fall between Jan 21–Feb 20 every year.