배송일 계산 API로 두 나라에 걸친 체크아웃 배송일 약속하기

배송일을 보여주는 체크아웃은 출고일을 창고가 있는 나라 기준으로, 택배 소요일을 목적지 나라 기준으로 세어야 합니다. Narwhal은 두 나라의 공휴일을 제공하고, 마감 시각과 택배 소요일은 쇼핑몰의 설정으로 남습니다.

장바구니의 날짜는 두 개의 달력에 맞춰 약속됩니다

고객에게 보이는 날짜는 하나지만 두 나라가 이를 정합니다. 창고는 자기 나라의 영업일에만 출고하고, 택배는 목적지의 영업일에만 배달합니다. 독일 창고에는 평범한 날인 크리스마스 이브가 폴란드 구매자에게는 공휴일입니다. 이 요청은 예시 주문 날짜부터 폴란드의 다음 휴일을 묻습니다.

curl, 목적지 국가의 다음 휴일
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일을 넘으면 다음 해 목록을 불러옵니다.

python, 출고일과 배송일
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 2027

12월 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일 화요일이므로, 장바구니는 이 날짜 사이에 도착한다고 표시할 수 있습니다. 마감 시각, 택배 소요일, 주말 규칙은 장바구니, 확인 이메일, 주문 페이지가 모두 호출하는 함수 한 곳에 두어 세 곳이 서로 다른 날짜를 보여주지 않게 하세요. 먼저 주문 시각을 창고의 시계로 바꾸세요. 구매자의 저녁이 창고에서는 이미 다음 날 아침일 수 있고, 마감 판정은 창고의 시각으로 하기 때문입니다.

달력에는 공휴일이 담겨 있습니다. 물류센터의 가동 중단, 택배사의 성수기 할증일, 지역 휴무는 알지 못합니다. 독일의 응답은 전국 범위만 담고 있어 주 공휴일은 없습니다. 그런 날짜는 함수가 읽는 집합에 직접 추가하세요.

어느 단계에 어느 나라의 달력이 쓰이나요?

출고일은 창고가 있는 나라가 정합니다. 택배 소요일은 마지막 구간이 이루어지는 목적지 나라가 정합니다. 소포가 제3국을 거친다면, 택배사가 그 나라의 일수를 세는 경우에만 그 일수를 더하세요.

휴일에 들어온 주문은 어떻게 되나요?

창고의 다음 영업일부터 시작합니다. 함수는 마감을 적용하기 전에 is_open으로 주문 날짜를 확인하므로, 토요일이나 공휴일 주문은 다음 영업일에 출고됩니다. 마감 시각은 쇼핑몰의 규칙이며 창고의 현지 시각으로 비교합니다.

장바구니를 볼 때마다 체크아웃이 API를 호출해야 하나요?

그럴 필요가 없습니다. 연간 목록은 같은 국가와 연도의 모든 주문에 같으므로, 스크립트는 이를 캐시에 보관하고 요청 한 번으로 모든 장바구니를 처리합니다. 캐시는 편한 주기로 갱신하세요. 영업일 더하기 가이드는 같은 계산을 한 나라에서 보여주고, 공휴일 확인 가이드는 확인 단계에서 쓸 단일 날짜 엔드포인트를 보여줍니다.

토요일을 배송일로 셀 수 있나요?

예. 택배사가 토요일에 배달한다면 is_open의 요일 조건을 day.weekday() < 5에서 day.weekday() < 6으로 바꾸세요. 공휴일 확인은 그대로 남으므로 목적지에서 공휴일인 토요일은 여전히 건너뜁니다. 창고는 토요일에 쉬고 택배사는 쉬지 않는다면 출고용과 배송용 조건을 따로 두세요.

에 게시되었습니다.

다음 단계