为薪资核算列出各国各年的法定节假日

对您发放薪资的每个国家和年份,各调用一次节假日端点。每个条目都带有 scope 和 subdivision_codes 列表,因此薪资系统可以把全国性假日、地区性假日和集体休假日分开,再对每一组套用自己的规则。

获取一个国家的全国性假日清单

GET /v1/calendars/holidays/{country}/{year} 返回某个国家某一年的节假日。可选的 scope 参数只保留一种类型的假日。下面的请求查询印度尼西亚 2026 年,只保留 national_public。

curl,印度尼西亚的全国法定节假日
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/calendars/holidays/IDN/2026?scope=national_public"
响应
{
  "country": "IDN",
  "coverage": [
    {
      "scope": "national_public",
      "subdivision_codes": []
    }
  ],
  "holidays": [
    {
      "date": "2026-01-01",
      "name": "New Year’s Day",
      "holiday_key": "new_years_day",
      "local_name": "Tahun Baru 2026 Masehi",
      "scope": "national_public",
      "subdivision_codes": []
    },
    {
      "date": "2026-01-16",
      "name": "Isra and Mi’raj",
      "holiday_key": "isra_and_miraj",
      "local_name": "Isra Mikraj Nabi Muhammad S.A.W.",
      "scope": "national_public",
      "subdivision_codes": []
    }
  ]
}

coverage 列表说明了日历包含的内容:一个 scope,即 national_public,其 subdivision_codes 列表为空。条目上的空列表表示该假日适用于全国。已知时,local_name 会给出当地语言的名称。完整响应包含 17 个日期。

获取另一个国家的地区性清单

并非所有假日都是全国性的。下面的请求不带 scope,查询澳大利亚 2026 年。

curl,澳大利亚全年的节假日
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/calendars/holidays/AUS/2026"
响应
{
  "country": "AUS",
  "coverage": [
    {
      "scope": "bank",
      "subdivision_codes": [
        "AU-NSW"
      ]
    },
    {
      "scope": "regional_public",
      "subdivision_codes": [
        "AU-NSW"
      ]
    }
  ],
  "holidays": [
    {
      "date": "2026-01-01",
      "name": "New Year’s Day",
      "holiday_key": "new_years_day",
      "scope": "regional_public",
      "subdivision_codes": [
        "AU-NSW"
      ]
    },
    {
      "date": "2026-01-26",
      "name": "Australia Day",
      "holiday_key": "australia_day",
      "scope": "regional_public",
      "subdivision_codes": [
        "AU-NSW"
      ]
    }
  ]
}

这里每个条目的 subdivision_codes 都是 AU-NSW,coverage 列出的 scope 是 bank 和 regional_public,两者都只针对 AU-NSW。这份日历覆盖新南威尔士州,其他澳大利亚州没有任何数据,所以在维多利亚州有员工的薪资核算需要另找来源。条目还带有 observed:12 月 28 日的 Additional Day for Boxing Day 是为落在周六的 12 月 26 日补休的工作日。

统计适用于每位员工的假日

  1. 获取 API 密钥 在注册页获取 API 密钥,并保存在 NARWHAL_API_KEY 中。
  2. 把每个国家与员工所在的行政区划配对 脚本把 IDN 配成无行政区划,把 AUS 配成 AU-NSW。请使用员工记录中的行政区划。
  3. 保留适用的条目并按 scope 分组 当条目的 subdivision_codes 列表为空,或包含员工所在的行政区划时,该条目适用。脚本按 scope 对日期分组,并统计其中落在工作日的天数,因为周末的假日不会减少工作日。
    Python,两个国家各 scope 的假日
    import os
    from collections import defaultdict
    from datetime import date
    import requests
    
    HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}
    
    
    def year_list(country, year):
        r = requests.get(
            f"https://api.narwhalapi.com/v1/calendars/holidays/{country}/{year}",
            headers=HEADERS,
            timeout=10,
        )
        r.raise_for_status()
        return r.json()
    
    
    for country, region in (("IDN", None), ("AUS", "AU-NSW")):
        data = year_list(country, 2026)
        print(country, [(c["scope"], c["subdivision_codes"]) for c in data["coverage"]])
        dates = defaultdict(set)  # scope -> dates that apply to this employee
        for h in data["holidays"]:
            if not h["subdivision_codes"] or region in h["subdivision_codes"]:
                dates[h["scope"]].add(h["date"])
        for scope, days in sorted(dates.items()):
            weekdays = [d for d in days if date.fromisoformat(d).weekday() < 5]
            print("  ", scope, len(days), "dates,", len(weekdays), "on weekdays")
脚本输出
IDN [('collective_leave', []), ('national_public', [])]
   collective_leave 8 dates, 8 on weekdays
   national_public 17 dates, 13 on weekdays
AUS [('bank', ['AU-NSW']), ('regional_public', ['AU-NSW'])]
   bank 1 dates, 1 on weekdays
   regional_public 13 dates, 9 on weekdays
国家范围日期落在工作日的天数
印度尼西亚national_public1713
印度尼西亚collective_leave88
澳大利亚,新南威尔士州regional_public139
澳大利亚,新南威尔士州bank11

工作日天数就是用来和薪资日历对照的数字。印度尼西亚两个 scope 合计有 21 个工作日日期,新南威尔士州有 10 个。collective_leave 日或 bank 假日在您的合同中是否算带薪休假,由您的政策和当地规定决定。API 只给每一天打标签,不决定薪资如何发放。

实例:印度尼西亚开斋节所在的一周

日期星期namescope
2026-03-20星期五开斋节collective_leave
2026-03-21星期六开斋节national_public
2026-03-22星期日开斋节national_public
2026-03-23星期一开斋节collective_leave
2026-03-24星期二开斋节collective_leave

两个 national_public 日期都落在周末,所以用 scope=national_public 请求时,那一周看起来没有损失任何工作日。三个工作日,即 3 月 20 日(周五)、23 日(周一)和 24 日(周二),只出现在 collective_leave 之下。只按全国性假日筛选的薪资核算会漏掉它们。这就是脚本读取整个列表并按 scope 分组的原因。

各 scope 对薪资核算意味着什么?

national_public 是全国性的假日。collective_leave 收录印度尼西亚的 cuti bersama(集体休假),日历把这些额外的休息日单独列在一个 scope 下。regional_public 和 bank 条目带有行政区划代码,说明它们适用的地区。

像脚本那样,请求全年时不带 scope,在代码中按它分组,这样就能看到每一种日子。只有当某条薪资规则只需要其中一类时,才在请求中加上 scope。

当规则指向某一个特定的假日时,请以 holiday_key 而不是 name 作为键。在同一个国家内,键唯一对应一个假日,因此针对 christmas_day 的规则会在返回的列表中选出那一个假日,而名称只是展示文字,可能因国家而异。其他国家的同类假日使用相同的键,这在一套政策覆盖多个国家时很有帮助。

如何只获取某个州或地区的假日?

年度端点没有行政区划参数,所以要在代码中筛选:保留 subdivision_codes 为空或包含该代码(例如 AU-NSW)的条目。请先看 coverage,因为它会显示这份日历究竟覆盖哪些行政区划。

如果一个国家在某个 scope 下没有假日会怎样?

API 会返回 404 problem 响应,其代码为 calendar_not_published,detail 中会指出国家、年份和所请求的 scope。对英国使用 scope=regional_public 发起请求时就返回了它,因为英国的条目使用的是 bank scope。不带 scope 请求即可看到存在哪些 scope。

发布于。

后续步骤