天気予報アプリやCLIツールで、晴れマークを「☀️(太陽)」にするか「🌙(月)」にするか。固定の時間(例:18時以降は夜)で判定すると、夏至や冬至などの季節による日照時間の変化に対応できず、ユーザー体験に違和感が生じます。
本記事では、Open-Meteo APIのsunrise(日の出)とsunset(日の入り)パラメータを活用し、緯度・経度に基づいた正確な昼夜判定を実装する方法を解説します。
1. 従来の課題と改善アプローチ
多くの簡易的な実装では、「06:00〜18:00=昼」といった固定時間を採用しています。しかし、例えば兵庫県西宮市では、夏は4時台に日が昇り、冬は17時過ぎには暗くなります。
今回の改善策
- Daily Endpointの活用: current(現在の天気)とは別に、daily(日次データ)からその日の正確な日の出・日の入り時刻を取得します。
- 動的な時刻比較: 取得した時刻と現在時刻を比較し、その瞬間の「明るさ」を判定します。
- フォールバック処理: 万が一API取得に失敗した場合は、従来の固定時間判定に切り替える堅牢な設計にします。
2. Pythonによる実装コード
A. APIエンドポイントの設定とヘルパー関数
まず、日次データを取得するためのURLを設定し、ISO8601形式の文字列をPythonのdatetimeオブジェクトに変換する処理を準備します。
import datetime
import sys
# 西宮市の座標を指定したDaily API(日の出・日の入りを取得)
DAILY_API = (
"https://api.open-meteo.com/v1/forecast"
"?latitude=34.73&longitude=135.34"
"&daily=sunrise,sunset"
"&timezone=Asia/Tokyo"
"&forecast_days=1"
)
def parse_iso_time(iso_str: str) -> datetime.datetime:
"""Open-MeteoのISO時刻文字列をdatetimeオブジェクトに変換"""
return datetime.datetime.fromisoformat(iso_str)
def get_sun_times():
"""本日の日の出・日の入り時刻を取得"""
try:
data = fetch_json(DAILY_API)
daily = data["daily"]
# APIはリスト形式で返るため、インデックス0を取得
today_sunrise = parse_iso_time(daily["sunrise"][0])
today_sunset = parse_iso_time(daily["sunset"][0])
return today_sunrise, today_sunset
except Exception as e:
print(f"Warning: 日照時間の取得に失敗しました ({e})。固定時間判定を使用します。", file=sys.stderr)
return None, NoneB. 高精度な昼夜判定ロジック
取得したデータを用いて、現在の天候コードに適用する絵文字セット(昼用・夜用)を切り替えます。
def is_night(current_iso: str, sunrise: datetime.datetime = None, sunset: datetime.datetime = None) -> bool:
"""現在時刻が「夜」であるかを判定"""
try:
current_dt = parse_iso_time(current_iso)
if sunrise and sunset:
# 【高精度】実際の日の出・日の入り時刻と比較
# 日の出時刻 <= 現在時刻 <= 日の入り時刻 であれば「昼」
return not (sunrise <= current_dt <= sunset)
else:
# 【フォールバック】API失敗時は18:00〜06:00を夜と見なす
hour = current_dt.hour
return hour >= 18 or hour < 6
except ValueError:
return False
