在產品中提供財經日曆的團隊需要依使用者篩選國家、依每位檢視者的時區顯示發布時間,以及顯示被調整的發布。Narwhal 回傳的發布帶有 UTC 時間、發布機構的時區、所屬期間、前值和修訂計數,篩選、時鐘與快取則由應用程式自行加上。
應用程式向日曆 API 請求什麼
在自己的應用程式裡提供財經日曆的產品團隊,有三件事資料來源無法代勞:讓每位使用者選擇國家、把所有時間顯示為檢視者的時區,以及在發布被調整時讓畫面內容保持正確。API 提供發布資料,應用程式負責篩選、時鐘和快取。GET /v1/economics/calendar 接受 countries(每個國家重複一次,使用 USA 或 CAN 這樣的三字母代碼)、start_date、end_date、limit 和 cursor。這個請求取得兩個國家兩天的資料。
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/economics/calendar?countries=USA&countries=CAN&start_date=2026-11-05&end_date=2026-11-06&limit=100"{
"releases": [
{
"release_id": "1b503ce9-69ee-4ec4-8dd9-deba7f1a6554",
"revision": 2,
"country": "USA",
"series_id": "usa-employment-report",
"name": "Employment report",
"topic": "employment",
"family": "labour",
"period": "2026-10",
"status": "scheduled",
"at": "2026-11-06T13:30:00Z",
"timezone": "America/New_York",
"values": [
{
"measure": "nonfarm_payrolls_change",
"concept": "payrolls",
"unit": "thousands",
"transformation": "change",
"previous": "29"
},
{
"measure": "unemployment_rate",
"concept": "unemployment_rate",
"unit": "percent",
"previous": "4.2"
}
]
},
{
"release_id": "20c30c26-9a9a-4218-a8fb-6233ac8d291a",
"revision": 1,
"country": "USA",
"series_id": "usa-productivity-and-costs",
"name": "Productivity and costs",
"topic": "other",
"family": "labour",
"period": "2026-Q3",
"stage": "preliminary",
"status": "scheduled",
"at": "2026-11-05T13:30:00Z",
"timezone": "America/New_York",
"values": []
}
],
"next_cursor": null
}完整回應包含五筆發布。at 是 UTC 發布時間,timezone 是發布機構自己的時區,所以應用程式可以為每位檢視者轉換 at,並把發布機構的時區作為第二個標籤顯示。period 是參考期間,values[].previous 是之前的數值。release_id 在不同請求之間識別同一筆發布,其內容變動時 revision 會增加。values 一律存在,而且可能是空清單。
依國家快取,並依檢視者時區呈現
使用者選擇的國家組合各不相同,所以依國家分別快取,再依使用者合併清單。兩位都關注美國的使用者就共用同一份快取清單。
- 一次取得一個國家
country_releases請求一個國家,並跟隨next_cursor直到它為null,因為一頁最多包含 100 筆發布。 - 讓資料保持短時效
TTL是快取的國家資料保持新鮮的秒數。請依使用者需要多快看到被調整的時間來選擇。 - 為使用者合併並轉換
user_calendar彙整使用者的國家,用使用者的時區轉換每個at,並依當地時間排序。ZoneInfo會套用檢視者所在地的時鐘變化,所以應用程式不必儲存偏移量。import os import time from datetime import datetime from zoneinfo import ZoneInfo import requests BASE = "https://api.narwhalapi.com" HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"} TTL = 300 # seconds a cached country stays fresh cache = {} # (country, start, end) -> (fetched_at, releases) def country_releases(country, start, end): key = (country, start, end) hit = cache.get(key) if hit and time.time() - hit[0] < TTL: return hit[1] params = {"countries": country, "start_date": start, "end_date": end, "limit": 100} releases = [] while True: r = requests.get(f"{BASE}/v1/economics/calendar", params=params, headers=HEADERS, timeout=10) r.raise_for_status() body = r.json() releases += body["releases"] if not body["next_cursor"]: break params["cursor"] = body["next_cursor"] cache[key] = (time.time(), releases) return releases def user_calendar(user, start, end): zone = ZoneInfo(user["tz"]) rows = [] for country in user["countries"]: for x in country_releases(country, start, end): if "at" in x: local = datetime.fromisoformat(x["at"].replace("Z", "+00:00")).astimezone(zone) rows.append((local, x)) return sorted(rows, key=lambda t: t[0]) user = {"countries": ["USA", "CAN"], "tz": "Asia/Jakarta"} day = None for local, x in user_calendar(user, "2026-11-05", "2026-11-06"): if local.date() != day: day = local.date() print(day.isoformat()) print(f" {local:%H:%M} {x['country']} {x['name']} {x.get('period', '-')}")
下面的輸出針對一位關注美國和加拿大的雅加達使用者。Credit statistics 的發布時間是 11 月 6 日 20:00 UTC,在雅加達是 11 月 7 日 03:00,所以在該使用者的檢視中歸入另一個日期。請一律依檢視者的當地日期分組,而不是依 UTC 日期。
2026-11-05
20:30 USA Productivity and costs 2026-Q3
20:30 USA Jobless claims 2026-10-31
2026-11-06
20:30 USA Employment report 2026-10
20:30 CAN Labour force survey 2026-10
2026-11-07
03:00 USA Credit statistics 2026-09發布被調整時讓畫面保持正確
隨每一列顯示的內容一起儲存 release_id 和 revision。重新整理後,revision 高於已儲存值的資料列已經變化,需要重繪並標出新時間。狀態為 cancelled 的發布應加刪除線或隱藏;在該時間範圍中不再回傳的資料列可能已移到別的日期,所以請把它從該檢視中移除。
有些發布沒有確切時間,帶有 date、between 或 by 而不是 at。上面的腳本會跳過它們。在實際應用程式中,請把它們作為全天項目列在當天的最前面。若要推播,webhook 會回報 release.scheduled、release.rescheduled 和 release.cancelled,請參閱接收 webhook。
產品團隊只需決定一次的事項
| 決策 | 選擇 | 欄位或參數 |
|---|---|---|
| 依使用者篩選國家 | 傳送使用者的國家,每個國家一份快取清單 | countries |
| 顯示的時間 | 把 UTC 時間轉換為檢視者的時區,並把發布機構的時區作為標籤顯示 | at, timezone |
| 日期分組 | 依檢視者的當地日期分組 | at |
| 變更偵測 | 比較已儲存的 revision 與新的 revision | release_id, revision |
| 主題或系列標籤 | 在應用程式中篩選,因為請求沒有主題參數 | family, topic |
| 速率限制與重試 | 使用快取,並在收到 429 回應時退避,請參閱快取回應並處理 429 錯誤 | 回應標頭 |
使用者可以依主題或系列篩選嗎?
請求接受國家、日期、limit 和 cursor,沒有主題篩選。每筆發布都有 family,大多數有 topic,所以請在你自己的程式碼中篩選。請使用 x.get("topic"),因為有些發布沒有主題,直接取值會引發鍵錯誤。
日期範圍依據 UTC 還是發布機構的當地日期?
依據發布機構的當地日期。日本在 UTC 23:30 或 23:50 的發布,在東京已是隔天早晨,所以落在後一天的範圍中。當應用程式依檢視者的時區建構一天時,請在兩側各多請求一天,並依 at 篩選。
一個畫面需要多少次請求?
快取中每個國家一次,next_cursor 有值時每多一頁再加一次。已快取的國家在其有效期結束前不產生請求。
發布於 。