- 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>
108 lines
4.3 KiB
Python
108 lines
4.3 KiB
Python
# converter.py
|
|
import os
|
|
import json
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
class Converter:
|
|
"""
|
|
提供西曆與農曆互轉功能,並能判斷特定年份的閏月。
|
|
"""
|
|
LUNAR_JSON_DIR = 'lunar_json'
|
|
|
|
def __init__(self):
|
|
"""初始化時,建立一個空的快取來存放已查詢過的年份閏月資訊。"""
|
|
self._leap_month_cache = {}
|
|
|
|
@classmethod
|
|
def load_solar_to_lunar(cls, year: int) -> dict:
|
|
"""這是一個類別方法,用來載入指定年份的資料檔。"""
|
|
path = os.path.join(cls.LUNAR_JSON_DIR, f"{year}.json")
|
|
if not os.path.exists(path):
|
|
raise FileNotFoundError(f"找不到農曆資料檔: {path}")
|
|
with open(path, encoding='utf-8') as f:
|
|
return json.load(f)
|
|
|
|
def solar_to_lunar(self, solar_date_str: str) -> dict:
|
|
try:
|
|
dt = datetime.strptime(solar_date_str, '%Y-%m-%d').date()
|
|
except ValueError:
|
|
raise ValueError("西曆日期格式應為 YYYY-MM-DD")
|
|
# 【修正點】從 self. 呼叫改為 Converter. 呼叫
|
|
data = Converter.load_solar_to_lunar(dt.year)
|
|
if solar_date_str not in data:
|
|
raise KeyError(f"沒有找到對應 {solar_date_str} 的農曆資料")
|
|
return data[solar_date_str]
|
|
|
|
def lunar_to_solar(self, lunar_year: int, lunar_month: int, lunar_day: int, leap: bool=False) -> str:
|
|
years_to_check = {lunar_year, lunar_year + 1}
|
|
for year in sorted(list(years_to_check)):
|
|
try:
|
|
# 【修正點】從 self. 呼叫改為 Converter. 呼叫
|
|
data = Converter.load_solar_to_lunar(year)
|
|
for solar_date, info in data.items():
|
|
lstr = info.get('農曆', '')
|
|
is_leap = lstr.startswith('閏')
|
|
core = lstr[1:] if is_leap else lstr
|
|
if not all(c in core for c in ['月', '日']): continue
|
|
m_str, d_str = core.replace('月','-').replace('日','').split('-')
|
|
if int(m_str) == lunar_month and int(d_str) == lunar_day and is_leap == leap:
|
|
return info.get("西曆")
|
|
except FileNotFoundError:
|
|
continue
|
|
raise KeyError(f"找不到對應農曆 {lunar_year}{'閏' if leap else ''}{lunar_month}月{lunar_day}日 的西曆")
|
|
|
|
def lunar_to_solar_all(self, lunar_year: int, lunar_month: int, lunar_day: int) -> List[str]:
|
|
results = []
|
|
try:
|
|
solar_date = self.lunar_to_solar(lunar_year, lunar_month, lunar_day, leap=False)
|
|
results.append(solar_date)
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
solar_date = self.lunar_to_solar(lunar_year, lunar_month, lunar_day, leap=True)
|
|
results.append(solar_date)
|
|
except KeyError:
|
|
pass
|
|
return results
|
|
|
|
def get_leap_month(self, year: int) -> Optional[int]:
|
|
if year in self._leap_month_cache:
|
|
return self._leap_month_cache[year]
|
|
try:
|
|
# 【修正點】從 self. 呼叫改為 Converter. 呼叫
|
|
data = Converter.load_solar_to_lunar(year)
|
|
for info in data.values():
|
|
lstr = info.get('農曆', '')
|
|
if lstr.startswith('閏'):
|
|
core = lstr[1:]
|
|
if '月' in core:
|
|
month_str = core.split('月')[0]
|
|
leap_month = int(month_str)
|
|
self._leap_month_cache[year] = leap_month
|
|
return leap_month
|
|
self._leap_month_cache[year] = None
|
|
return None
|
|
except FileNotFoundError:
|
|
self._leap_month_cache[year] = None
|
|
return None
|
|
|
|
# ... (main 函式不變) ...
|
|
def main():
|
|
"""展示 Converter 的新功能:查詢閏月"""
|
|
print("=== Converter 閏月判斷功能展示 ===")
|
|
try:
|
|
converter = Converter()
|
|
years_to_test = [1976, 1977, 2023, 2024, 2025]
|
|
for year in years_to_test:
|
|
leap_month = converter.get_leap_month(year)
|
|
if leap_month:
|
|
print(f"西曆 {year} 年,有閏月,是閏 {leap_month} 月。")
|
|
else:
|
|
print(f"西曆 {year} 年,沒有閏月。")
|
|
except Exception as e:
|
|
print(f"\n發生錯誤: {e}")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|