# Holiday API Integration Guide

**Last Updated:** 2026-01-07

## Overview

All calculator tools that require holiday data now use the shared `HolidayService` which integrates with the OpenHolidays API. This provides accurate, up-to-date holiday data for DACH countries (Germany, Austria, Switzerland) with automatic fallback to local data.

## Recent Fixes (2026-01-07)

Fixed critical issues with holiday detection:

- **Date Comparison:** Fixed timezone issues in date filtering by normalizing dates to YYYY-MM-DD format
- **Region Code Mapping:** Verified correct transformation from UI dropdown values (e.g., 'be') to API subdivision codes (e.g., 'DE-BE')
- **Fallback Data:** Expanded fallback data to include all 16 German states for 2025 and 2026
- **Error Handling:** Improved error handling and fallback mechanism
- **Service Initialization:** Added proper initialization check for HolidayService availability
- **Basic Mode Support:** Added default region support for basic mode (DE → 'be', AT → 'wien', CH → 'zurich')
- **Mode Detection:** Removed `isAdvanced` requirement from holiday detection condition - holidays now work in both modes
- **Date Loop:** Fixed date iteration loop to use reliable while loop instead of for loop with date mutation
- **Comprehensive Logging:** Added detailed console logging throughout holiday detection flow for debugging

## Architecture

### Shared Holiday Service

**Location:** `v2/js/shared/holiday-service.js`

The `HolidayService` class provides a unified interface for fetching holiday data:

- **API Integration:** Uses OpenHolidays API (`https://openholidaysapi.org`)
- **Caching:** 24-hour cache to reduce API calls
- **Fallback:** Automatic fallback to local JSON data if API fails
- **Multi-country:** Supports DE, AT, CH with regional subdivisions

### Fallback Data

**Location:** `v2/data/holidays-fallback.json`

Contains minimal fallback data for offline functionality. Structure:

```json
{
  "DE": {
    "by": {
      "2024": [...],
      "2025": [...],
      "2026": [...]
    },
    "be": {
      "2024": [...],
      "2025": [...],
      "2026": [...]
    }
  }
}
```

## Tools Using Holiday API

### 1. Arbeitstage-Rechner

**Status:** ✅ Using shared service (Fixed 2026-01-07)

- Refactored to use `HolidayService`
- Supports DACH countries with regional selection
- Fetches holidays for date ranges spanning multiple years
- **Holiday Support:** Works in both basic and advanced modes
- **Basic Mode:** Uses default region (DE → Berlin, AT → Vienna, CH → Zurich) for holiday detection
- **Advanced Mode:** Uses selected region from dropdown
- **Fixed Issues:**
  - Date comparison now uses normalized YYYY-MM-DD format to avoid timezone issues
  - Proper initialization check for HolidayService availability
  - Improved error handling with fallback to local data
  - Removed `isAdvanced` requirement - holidays work in both modes
  - Fixed date iteration loop for reliable date range processing
  - Added default region support for basic mode

### 2. Arbeitszeitrechner

**Status:** ✅ Using shared service (Fixed 2026-01-07)

- Replaced static `holidays2025` data with API integration
- **Multi-Year Support:** Automatically loads holidays for all years in date range
- Year automatically derived from period dates
- Holidays cached per bundesland-year combination
- Async loading with loading state
- **Fixed Issues:**
  - Multi-year period handling: Now loads holidays for all years in range (not just start year)
  - Date iteration: Fixed timezone issues by using reliable while loop instead of for loop
  - Holiday loading timing: Ensures holidays are loaded before checking in `generateDailyEntries()`
  - Error handling: Improved error visibility and fallback mechanisms
  - Comprehensive logging: Added detailed console logs for debugging

### 3. Urlaubsanspruch-Rechner

**Status:** ✅ Calculation-based (no API needed)

- Uses mathematically accurate Easter calculation algorithm
- No API integration needed (calculation is always correct)
- Can be enhanced with API validation in future if needed

### 4. ShiftOps (Backend)

**Status:** ✅ Using Nager.Date API (kept separate)

- Backend PHP uses Nager.Date API
- Different use case than frontend tools
- Works well, no migration needed

## Usage Examples

### Basic Usage

```javascript
// Initialize service
const holidayService = new HolidayService();

// Get holidays for a region and year
const holidays = await holidayService.getHolidays("DE", "be", 2026);

// Check if a date is a holiday
const isHoliday = await holidayService.isHoliday(
  "DE",
  "be",
  2026,
  "2026-01-01"
);
```

### In Alpine.js Component

```javascript
Alpine.data("myCalculator", () => ({
  holidayService: null,
  holidaysCache: {},

  async init() {
    if (typeof HolidayService !== "undefined") {
      this.holidayService = new HolidayService();
      await this.loadHolidays();
    }
  },

  async loadHolidays() {
    const regionCode = this.getRegionCode(this.bundesland);
    const holidays = await this.holidayService.getHolidays(
      "DE",
      regionCode,
      this.year
    );
    this.holidaysCache[`${this.bundesland}-${this.year}`] = holidays.map(
      (h) => h.date
    );
  },

  isHoliday(dateStr) {
    const cacheKey = `${this.bundesland}-${this.year}`;
    return this.holidaysCache[cacheKey]?.includes(dateStr) || false;
  },
}));
```

## API Details

### OpenHolidays API

**Endpoint:** `https://openholidaysapi.org/PublicHolidays`

**Parameters:**

- `countryIsoCode`: DE, AT, or CH
- `validFrom`: Start date (YYYY-MM-DD)
- `validTo`: End date (YYYY-MM-DD)
- `subdivisionCode`: Optional regional code (e.g., DE-BE for Berlin)

**Response Format:**

```json
[
  {
    "startDate": "2026-01-01",
    "endDate": "2026-01-01",
    "name": [
      {
        "language": "DE",
        "text": "Neujahr"
      }
    ]
  }
]
```

### Region Code Mapping

**Germany (DE):**

- `bw` → DE-BW (Baden-Württemberg)
- `by` → DE-BY (Bayern)
- `be` → DE-BE (Berlin)
- `bb` → DE-BB (Brandenburg)
- `hb` → DE-HB (Bremen)
- `hh` → DE-HH (Hamburg)
- `he` → DE-HE (Hessen)
- `mv` → DE-MV (Mecklenburg-Vorpommern)
- `ni` → DE-NI (Niedersachsen)
- `nw` → DE-NW (Nordrhein-Westfalen)
- `rp` → DE-RP (Rheinland-Pfalz)
- `sl` → DE-SL (Saarland)
- `sn` → DE-SN (Sachsen)
- `st` → DE-ST (Sachsen-Anhalt)
- `sh` → DE-SH (Schleswig-Holstein)
- `th` → DE-TH (Thüringen)

**Austria (AT):**

- `wien` → AT-9
- `niederösterreich` → AT-3
- `oberösterreich` → AT-4
- `salzburg` → AT-5
- `tirol` → AT-7
- `vorarlberg` → AT-8
- `kärnten` → AT-2
- `steiermark` → AT-6
- `burgenland` → AT-1

**Switzerland (CH):**

- All 26 cantons supported (see `holiday-service.js` for full list)

## Error Handling

The service implements robust error handling:

1. **API Failure:** Falls back to local JSON data
2. **Network Timeout:** 10-second timeout, then fallback
3. **Invalid Region:** Returns empty array
4. **Cache Miss:** Fetches from API or fallback

## Performance Considerations

- **Caching:** 24-hour cache reduces API calls
- **Lazy Loading:** Holidays loaded only when needed
- **Batch Loading:** Multiple years loaded in parallel if needed
- **Fallback:** Instant fallback if API unavailable

## Maintenance

### Annual Updates

1. **Fallback Data:** Update `v2/data/holidays-fallback.json` with new year data
2. **Testing:** Verify API returns correct data for new year
3. **Documentation:** Update year references in documentation

### Monitoring

- Monitor API availability and response times
- Check cache hit rates
- Verify fallback data accuracy

## Troubleshooting

### Holidays Not Detected (Fixed 2026-01-07)

If holidays are not being detected:

1. **Check Browser Console:** Look for errors related to HolidayService initialization
2. **Verify Region Selection:** Ensure a region is selected in advanced mode
3. **Check API Status:** Verify OpenHolidays API is accessible (check Network tab)
4. **Fallback Data:** Verify fallback data exists for selected region/year in `v2/data/holidays-fallback.json`
5. **Date Format:** Ensure dates are in YYYY-MM-DD format (timezone-normalized)

### Common Issues

**Issue:** Holidays show as 0 even when holidays exist in date range

- **Solution:** Fixed in 2026-01-07 update. Ensure using latest code with normalized date comparison.
- **Check:** Verify region is selected (advanced mode) or country is selected (basic mode uses default region)
- **Check:** Verify "no holidays" checkbox is NOT checked
- **Check:** Open browser console and look for `[Arbeitstage-Rechner]` logs to see what's happening

**Issue:** API errors but fallback not working

- **Solution:** Verify fallback JSON structure matches lookup path: `data[country][region][year]` (year as string)
- **Check:** Verify fallback data exists for selected region/year in `v2/data/holidays-fallback.json`

**Issue:** Wrong holidays for region

- **Solution:** Verify region code mapping in `getSubdivisionCode()` matches UI dropdown values
- **Check:** Basic mode uses default regions (DE → Berlin, AT → Vienna, CH → Zurich)

**Issue:** Holidays not detected in basic mode

- **Solution:** Fixed in 2026-01-07 update. Basic mode now uses default region automatically.
- **Check:** Ensure country is selected (DE, AT, or CH)
- **Check:** Basic mode will use default region: DE → 'be' (Berlin), AT → 'wien' (Vienna), CH → 'zurich' (Zurich)

### Holidays Not Loading

1. Check browser console for errors
2. Verify `HolidayService` is loaded before use
3. Check network tab for API requests
4. Verify region code mapping is correct

### Incorrect Holidays

1. Verify region code matches selected region
2. Check year is correct
3. Compare with official sources
4. Clear cache: `holidayService.clearCache()`

### API Rate Limits

- OpenHolidays API has generous rate limits
- Caching reduces API calls significantly
- If limits hit, fallback data ensures functionality

## Future Enhancements

- [ ] Add support for more countries
- [ ] Implement multi-year holiday loading optimization
- [ ] Add holiday name translations
- [ ] Create admin interface for fallback data updates
- [ ] Add API usage analytics
