- 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>
100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
# age_calculator.py
|
||
import datetime
|
||
from collections import namedtuple
|
||
from loguru import logger # type: ignore[reportMissingImports]
|
||
|
||
# 從 converter.py 這個檔案,匯入 Converter 這個 class
|
||
from converter import Converter
|
||
|
||
# --- 類型定義 ---
|
||
AgeResult = namedtuple("AgeResult", ["real_age", "east_asian_age"])
|
||
|
||
# ======================================================================
|
||
# 主要類別:AgeCalculator
|
||
# ======================================================================
|
||
class AgeCalculator:
|
||
"""
|
||
提供精準的年齡計算功能 (實歲與虛歲)。
|
||
初始化時需要傳入一個 Converter 物件。
|
||
"""
|
||
|
||
def __init__(self, converter: Converter):
|
||
"""
|
||
初始化年齡計算機。
|
||
這個設計模式稱為「依賴注入 (Dependency Injection)」,
|
||
AgeCalculator 本身不建立 Converter,而是由外部提供,讓架構更靈活。
|
||
|
||
:param converter: 一個 Converter 的實體物件。
|
||
"""
|
||
if not isinstance(converter, Converter):
|
||
raise TypeError("初始化 AgeCalculator 時,必須提供一個有效的 Converter 物件")
|
||
self.converter = converter
|
||
|
||
def calculate(
|
||
self,
|
||
solar_birth_date: datetime.date,
|
||
lunar_birth_info: dict,
|
||
today: datetime.date
|
||
) -> AgeResult:
|
||
"""
|
||
根據生日資訊,使用農曆轉換器精準計算實歲和虛歲。
|
||
"""
|
||
# 虛歲計算 (邏輯不變)
|
||
east_asian_age = today.year - solar_birth_date.year + 1
|
||
|
||
# 實歲計算 (使用 self.converter)
|
||
try:
|
||
# 用 converter 找出「今年的農曆生日」對應的西曆日期
|
||
this_year_lunar_bday_solar_str = self.converter.lunar_to_solar(
|
||
lunar_year=today.year,
|
||
lunar_month=lunar_birth_info['month'],
|
||
lunar_day=lunar_birth_info['day'],
|
||
leap=lunar_birth_info['leap']
|
||
)
|
||
this_year_lunar_bday_date = datetime.datetime.strptime(this_year_lunar_bday_solar_str, '%Y-%m-%d').date()
|
||
except KeyError:
|
||
# Edge case: 若今年沒有該農曆日期(如閏月),則認定生日已過,讓邏輯更強健
|
||
this_year_lunar_bday_date = today - datetime.timedelta(days=1)
|
||
|
||
real_age = today.year - solar_birth_date.year
|
||
# 核心判斷:如果今天還沒到「今年的農曆生日(的西曆日期)」,那實歲就 -1
|
||
if today < this_year_lunar_bday_date:
|
||
real_age -= 1
|
||
|
||
return AgeResult(real_age=real_age, east_asian_age=east_asian_age)
|
||
|
||
|
||
# ======================================================================
|
||
# 主程式區塊:作為使用範例
|
||
# ======================================================================
|
||
if __name__ == "__main__":
|
||
# --- 作為範例的資料 ---
|
||
TODAY_OVERRIDE = datetime.date(2025, 7, 30)
|
||
SOLAR_BIRTH_DATE_STR = "1977-10-12"
|
||
LUNAR_BIRTH_INFO = {"month": 8, "day": 30, "leap": False}
|
||
|
||
try:
|
||
# --- 步驟 1: 建立依賴物件 (工具人) ---
|
||
converter = Converter()
|
||
|
||
# --- 步驟 2: 建立主物件,並注入依賴 (主角登場,並把工具人交給他) ---
|
||
age_calculator = AgeCalculator(converter=converter)
|
||
|
||
# --- 步驟 3: 執行計算 (叫主角開始幹活) ---
|
||
solar_birth_date = datetime.datetime.strptime(SOLAR_BIRTH_DATE_STR, "%Y-%m-%d").date()
|
||
age_result = age_calculator.calculate(
|
||
solar_birth_date=solar_birth_date,
|
||
lunar_birth_info=LUNAR_BIRTH_INFO,
|
||
today=TODAY_OVERRIDE,
|
||
)
|
||
|
||
# --- 步驟 4: 使用 loguru 輸出結果 ---
|
||
logger.remove()
|
||
logger.add(lambda msg: print(msg, end=""), format="{message}")
|
||
|
||
logger.info(f"實歲 (足歲):{age_result.real_age} 歲。")
|
||
logger.info(f"虛歲:{age_result.east_asian_age} 歲。")
|
||
|
||
except Exception as e:
|
||
logger.error(f"執行時發生錯誤: {e}")
|