Files
lunar-converter/api.py
Timmy ae21a0ae66 Initial commit: Lunar calendar converter project
- Add lunar calendar astronomical calculation engine (lunar_calendar.py)
- Add converter utilities for solar/lunar date conversion
- Add historical era name conversions (chrono_converter.py, converter_with_eras.py)
- Add FastAPI REST API for date conversion services
- Add age calculator using lunar calendar
- Add batch generation script for lunar calendar JSON data
- Add project documentation (CLAUDE.md, pyproject.toml)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:51:45 +08:00

111 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# 提供農曆-西曆轉換的 API 服務FastAPI 版本)
# 依賴: fastapi, uvicorn, converter.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "fastapi",
# "uvicorn",
# ]
# ///
import socket
from contextlib import closing
from typing import Any, Dict, List
from fastapi import FastAPI, Query # type: ignore[reportMissingImports]
from fastapi.responses import JSONResponse # type: ignore[reportMissingImports]
import uvicorn # type: ignore[reportMissingImports]
from converter import Converter
def find_free_port() -> int:
"""使用臨時 socket 綁定到 0 取得可用埠號"""
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(("", 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s.getsockname()[1]
app = FastAPI(title="Lunar Calendar Converter API")
conv = Converter()
@app.get("/solar2lunar")
def solar2lunar_endpoint(date: str = Query(None, description="YYYY-MM-DD")):
"""西曆轉農曆"""
if not date:
return JSONResponse({"error": "缺少參數 date格式 YYYY-MM-DD"}, status_code=400)
try:
info_raw = conv.solar_to_lunar(date)
info = {
"gregorian": info_raw.get("西曆"),
"lunar": info_raw.get("農曆"),
"ganzhi": info_raw.get("干支"),
"zodiac": info_raw.get("生肖"),
"solar_term": info_raw.get("節氣"),
}
return info
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=400)
@app.get("/lunar2solar")
def lunar2solar_endpoint(date: str = Query(None, description="YYYY-MM-DD")):
"""農曆轉西曆(自動處理閏月,回傳所有可能性)"""
if not date:
return JSONResponse({"error": "缺少參數 date格式 YYYY-MM-DD"}, status_code=400)
try:
y_str, m_str, d_str = date.split("-", 2)
y, m, d = int(y_str), int(m_str), int(d_str)
except Exception:
return JSONResponse({"error": "農曆日期格式應為 YYYY-MM-DD"}, status_code=400)
possible_solar_dates: List[str] = conv.lunar_to_solar_all(y, m, d)
if not possible_solar_dates:
error_msg = f"找不到任何對應農曆 {y}{m}{d}日 的西曆日期"
return JSONResponse({"error": error_msg}, status_code=404)
results_list: List[Dict[str, Any]] = []
for solar_date in possible_solar_dates:
try:
info_raw = conv.solar_to_lunar(solar_date)
results_list.append({
"gregorian": info_raw.get("西曆"),
"lunar": info_raw.get("農曆"),
"ganzhi": info_raw.get("干支"),
"zodiac": info_raw.get("生肖"),
"solar_term": info_raw.get("節氣"),
})
except Exception:
continue
return results_list
@app.get("/leap-info")
def leap_info_endpoint(year: str = Query(None, description="西曆年份,數字")):
"""查詢指定西曆年份是否有農曆閏月,以及是閏幾月。"""
if not year:
return JSONResponse({"error": "缺少參數 year"}, status_code=400)
try:
y = int(year)
leap_month = conv.get_leap_month(y)
return {"year": y, "leap_month": leap_month}
except ValueError:
return JSONResponse({"error": "參數 year 必須是有效的整數"}, status_code=400)
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=500)
if __name__ == "__main__":
try:
free_port = find_free_port()
print(f"找到一個可用的 port: {free_port}")
uvicorn.run(app, host="0.0.0.0", port=free_port, reload=False)
except Exception as e:
print(f"啟動服務失敗: {e}")