py-seasonal-hours-clock/seasonal_clock/times.py

140 lines
3.8 KiB
Python

from datetime import datetime, timedelta
from typing import List, Optional, Tuple
from astral import LocationInfo, SunDirection, sun
def collect_day_parts(
observer: LocationInfo, date: datetime
) -> List[Tuple[str, datetime, str, str]]:
"""Collect timestamp for all parts of the day on date"""
day_parts = []
midnight = sun.midnight(observer, date=date)
if midnight.date() < date.date():
midnight = sun.midnight(observer, date=date + timedelta(days=1))
elif midnight.date() > date.date():
midnight = sun.midnight(observer, date=date - timedelta(days=1))
day_parts.append(('midnight', midnight, 'Night', 'Midnight'))
try:
morning_blue_start = sun.blue_hour(
observer, date=date, direction=SunDirection.RISING
)[0]
except ValueError:
# At certain times and latitudes there might be no blue hour
pass
else:
day_parts.append(
(
'morning_blue_start',
morning_blue_start,
'Morning blue hour',
'Morning golden hour',
)
)
try:
golden_start, golden_end = sun.golden_hour(
observer, date=date, direction=SunDirection.RISING
)
except ValueError:
# At certain times and latitudes there might be no golden hour
pass
else:
day_parts.append(
(
'morning_golden_start',
golden_start,
'Morning golden hour',
'Full Daytime',
)
)
day_parts.append(('morning_golden_end', golden_end, 'Morning', 'Noon'))
day_parts.append(('noon', sun.noon(observer, date=date), 'Afternoon', 'Noon'))
try:
evening_golden_start = sun.golden_hour(
observer, date=date, direction=SunDirection.SETTING
)[0]
except ValueError:
# At certain times and latitudes there might be no golden hour
pass
else:
day_parts.append(
(
'evening_golden_start',
evening_golden_start,
'Evening golden hour',
'Evening blue hour',
)
)
try:
blue_start, blue_end = sun.blue_hour(
observer, date=date, direction=SunDirection.SETTING
)
except ValueError:
# At certain times and latitudes there might be no blue hour
pass
else:
day_parts.append(
('evening_blue_start', blue_start, 'Evening blue hour', 'Night')
)
day_parts.append(('evening_blue_end', blue_end, 'Night', 'Midnight'))
day_parts.sort(key=lambda elem: elem[1])
return day_parts
def get_rahukaalam(
observer: LocationInfo, date: datetime
) -> Tuple[Optional[bool], datetime]:
"""Get the time of the next Rāhukāla or, if we are in the middle of one, the time of its end"""
yesterday = date - timedelta(days=1)
tomorrow = date + timedelta(days=1)
times: List[Tuple[datetime, datetime]] = []
for day in (yesterday, date, tomorrow):
try:
daytime_rahukaalam = sun.rahukaalam(observer, date=day, daytime=False)
except ValueError:
pass
else:
times.append(daytime_rahukaalam)
try:
night_rahukaalam = sun.rahukaalam(observer, date=day, daytime=True)
except ValueError:
pass
else:
times.append(night_rahukaalam)
times.sort(key=lambda items: items[0])
active: bool = False
next_time: Optional[datetime] = None
for start, end in times:
if date < start:
active = False
next_time = start
break
if start < date < end:
active = True
next_time = end
break
if next_time is None:
return None, None
return active, next_time