顯示送達日期的結帳頁面,必須依倉庫所在國計算出貨日,依目的國計算承運天數。Narwhal 提供兩國的國定假日,截止時間與承運天數仍是商店自己的設定。
購物車裡的日期是依兩個日曆承諾的
客戶只看到一個日期,但由兩個國家共同決定。倉庫只在本國工作日出貨,承運商只在目的國工作日投遞。對德國倉庫來說是平常日子的平安夜,對波蘭買家卻是國定假日。這個請求從範例訂單當天起,查詢波蘭的下一個休息日。
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
"https://api.narwhalapi.com/v1/calendars/holidays/POL/next?from_date=2026-12-21"{
"country": "POL",
"coverage": [
{
"scope": "national_public",
"subdivision_codes": []
}
],
"holiday": {
"date": "2026-12-24",
"name": "Christmas Eve",
"holiday_key": "christmas_eve",
"local_name": "Wigilia Bożego Narodzenia",
"scope": "national_public",
"subdivision_codes": []
}
}holiday 物件給出 date、英文 name、local_name 和 scope。對德國發出同樣的請求會回傳 12 月 25 日的聖誕節,所以波蘭的第一個休息日比德國早一天。coverage 顯示兩個日曆都只包含全國性假日。
依截止時間、出貨日和承運天數得出承諾日期
各項輸入的來源不同。下表顯示哪些來自商店,哪些來自日曆。
| 輸入 | 擁有者 | 範例 |
|---|---|---|
| 下單截止時間 | 商店設定 | 倉庫當地時間 16:00 |
| 出貨日 | 倉庫日曆 | 依截止規則之後的第一個營業工作日 |
| 承運天數 | 承運合約 | 3 天 |
| 送達日 | 目的國日曆 | 只計算營業的工作日 |
| 國定假日 | Narwhal 年度清單 | 每個國家每年一次請求 |
下面的函式在訂單於截止時間之後或休息日送達時,把訂單順延到倉庫的下一個工作日。然後依目的國的營業工作日計算承運天數。它對每個國家和年份只載入一次,當計數跨過 1 月 1 日時再載入下一年。
import json, os, urllib.request
from datetime import datetime, time, timedelta
from functools import cache
def get(path):
req = urllib.request.Request("https://api.narwhalapi.com" + path,
headers={"Authorization": "Bearer " + os.environ["NARWHAL_API_KEY"]})
with urllib.request.urlopen(req, timeout=10) as r:
return json.load(r)
@cache
def closed_days(country, year):
return {h["date"] for h in get(f"/v1/calendars/holidays/{country}/{year}")["holidays"]}
def is_open(day, country):
return day.weekday() < 5 and day.isoformat() not in closed_days(country, day.year)
def promise(placed, cutoff, origin, dest, transit_days):
dispatch = placed.date()
if placed.time() >= cutoff or not is_open(dispatch, origin):
dispatch += timedelta(1)
while not is_open(dispatch, origin):
dispatch += timedelta(1)
day, left = dispatch, transit_days
while left:
day += timedelta(1)
left -= is_open(day, dest)
return dispatch, day
CUTOFF = time(16, 0)
for placed, dest in [
(datetime(2026, 12, 21, 14, 0), "POL"),
(datetime(2026, 12, 21, 17, 30), "POL"),
(datetime(2026, 12, 21, 17, 30), "DEU"),
(datetime(2026, 12, 31, 10, 0), "POL"),
]:
dispatch, arrive = promise(placed, CUTOFF, "DEU", dest, 3)
print(f"{placed:%a %d %b %H:%M} to {dest}: dispatch {dispatch:%a %d %b}, delivery {arrive:%a %d %b %Y}")
Mon 21 Dec 14:00 to POL: dispatch Mon 21 Dec, delivery Mon 28 Dec 2026
Mon 21 Dec 17:30 to POL: dispatch Tue 22 Dec, delivery Tue 29 Dec 2026
Mon 21 Dec 17:30 to DEU: dispatch Tue 22 Dec, delivery Mon 28 Dec 2026
Thu 31 Dec 10:00 to POL: dispatch Thu 31 Dec, delivery Thu 07 Jan 202712 月 21 日星期一 14:00 的訂單當天出貨,12 月 28 日星期一送達。同一訂單如果在 17:30 下單,則於 12 月 22 日星期二出貨,12 月 29 日星期二送達,比第一種晚一天,因為平安夜在波蘭是休息日。如果改寄德國,17:30 的訂單會在 12 月 28 日星期一送達。12 月 31 日星期四的訂單會跨年,跳過 1 月 1 日和波蘭的 1 月 6 日主顯節,在 2027 年 1 月 7 日星期四送達。
顯示一個範圍,並把規則放在同一處
承運商通常報一個範圍,例如 2 到 3 天。對兩個端點各執行一次函式。對上面 17:30 的訂單,承運 2 天是 12 月 28 日星期一,3 天是 12 月 29 日星期二,所以購物車可以顯示在這兩個日期之間送達。截止時間、承運天數和週末規則應放在同一個函式裡,讓購物車、確認信和訂單頁面都呼叫它,這樣三處不會顯示不同的日期。請先把下單時間轉換為倉庫的時鐘,因為買家的晚上在倉庫可能已經是隔天早上,而截止判斷讀取的是倉庫時間。
日曆列出的是國定假日。它不知道倉庫停工、承運商的旺季附加費日期或地區性休息。德國的回應只包含全國範圍,所以邦假日不在其中。請把這些日期加入函式所讀取的集合。
每一步該用哪個國家的日曆?
出貨國決定出貨日。目的國決定承運天數,因為最後一段配送發生在那裡。如果包裹經過第三國,只有在承運商計算該國天數時,才把它的天數加入計數。
在休息日下的訂單會怎樣?
它從倉庫的下一個工作日開始處理。函式在套用截止時間之前,先用 is_open 檢查訂單日期,所以週六或假日的訂單會在下一個營業日出貨。截止時間是商店的規則,依倉庫當地時間比較。
結帳頁面每次檢視購物車都要呼叫 API 嗎?
不需要。同一國家同一年的年度清單對所有訂單都一樣,所以腳本把它存在快取中,一次請求就能服務所有購物車。請依適合你的頻率更新快取。加上工作日指南展示了在單一國家內同樣的計算,國定假日檢查指南展示了用於確認步驟的單一日期端點。
週六可以算作送達日嗎?
可以,前提是承運商週六投遞。把 is_open 中的星期判斷從 day.weekday() < 5 改為 day.weekday() < 6。假日檢查仍然保留,所以在目的國是國定假日的週六依然會被跳過。如果倉庫週六休息而承運商不休息,請為出貨和送達分別保留一個判斷。
發布於 。