Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement other kind of dusk/dawns #24

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ dist/
example.py
suntime.egg-info/
suntime/__pycache__/
venv
*.pyc
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ try:
format(abd, abd_sr.strftime('%H:%M'), abd_ss.strftime('%H:%M')))
except SunTimeException as e:
print("Error: {0}.".format(e))

# if you need any different twilight (https://www.timeanddate.com/astronomy/different-types-twilight.html)
# you can use their respective methods

print("---------- DAWN ----------")

print(sun.get_civil_dawn_time())
print(sun.get_nautical_dawn_time()))
print(sun.get_astronomical_dawn_time())


print("---------- DUSK ----------")

print(sun.get_civil_dusk_time())
print(sun.get_nautical_dusk_time())
print(sun.get_astronomical_dusk_time())
```

## License
Expand Down
186 changes: 143 additions & 43 deletions suntime/suntime.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,180 @@ class Sun:
Approximated calculation of sunrise and sunset datetimes. Adapted from:
https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql
"""
def __init__(self, lat, lon):
def __init__(self, lat, lon, zenith=90.8):
"""
:param lat(float): Latitude
:param lon(float): Longitude
:param zenith(float): zenith default position (degrees)
"""
self._lat = lat
self._lon = lon
self._ZENITH = zenith

def get_sunrise_time(self, date=None):
"""
Calculate the sunrise time for given date.
:param lat: Latitude
:param lon: Longitude
:param date: Reference date. Today if not provided.
:return: UTC sunrise datetime
:raises: SunTimeException when there is no sunrise and sunset on given location and date
:return: UTC sunrise datetime
"""
date = datetime.date.today() if date is None else date
sr = self._calc_sun_time(date, True)
if sr is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return sr
return self._calc_sun_time(date, True)

def get_local_sunrise_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get sunrise time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
:return: Local time zone sunrise datetime
"""
date = datetime.date.today() if date is None else date
sr = self._calc_sun_time(date, True)
if sr is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return sr.astimezone(local_time_zone)
return self._calc_sun_time(date, True, timezone=local_time_zone)

def get_sunset_time(self, date=None):
"""
Calculate the sunset time for given date.
:param lat: Latitude
:param lon: Longitude
:param date: Reference date. Today if not provided.
:return: UTC sunset datetime
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
"""
date = datetime.date.today() if date is None else date
ss = self._calc_sun_time(date, False)
if ss is None:
raise SunTimeException('The sun never sets on this location (on the specified date)')
else:
return ss
return self._calc_sun_time(date, False)

def get_local_sunset_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get sunset time for local or custom time zone.
:param date: Reference date
Get local sunset time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:return: Local time zone sunset datetime
:raises: SunTimeException when there is no unrise and sunset on given location and date.
:return: Local time zone sunrise datetime
"""
date = datetime.date.today() if date is None else date
ss = self._calc_sun_time(date, False)
if ss is None:
raise SunTimeException('The sun never sets on this location (on the specified date)')
else:
return ss.astimezone(local_time_zone)
return self._calc_sun_time(date, False, timezone=local_time_zone)

def get_civil_dawn_time(self, date=None):
"""
Get civil dawn time for local or custom time zone.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is civil dawn on given location and date.
:return: UTC sunset datetime
"""
return self._calc_sun_time(date, True, offset=6)

def get_local_civil_dawn_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local civil dawn time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no civil danw on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, True, offset=6, timezone=local_time_zone)

def get_civil_dusk_time(self, date=None):
"""
Calculate the civil dusk time for given date.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is no civil dusk on given location and date
:return: UTC civil dusk datetime
"""
return self._calc_sun_time(date, False, offset=6)

def get_local_civil_dusk_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local civil dusk time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no civil dusk on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, False, offset=6, timezone=local_time_zone)

def get_nautical_dawn_time(self, date=None):
"""
Calculate the nautical dawn time for given date.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is no nautical dawn on given location and date
:return: UTC nautical dawn datetime
"""
return self._calc_sun_time(date, True, offset=12)

def get_local_nautical_dawn_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local nautical dawn time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no nautical dawn on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, True, offset=12, timezone=local_time_zone)

def get_nautical_dusk_time(self, date=None):
"""
Calculate the nautical dusk time for given date.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is no nautical dusk on given location and date
:return: UTC nautical dusk datetime
"""
return self._calc_sun_time(date, False, offset=12)

def get_local_nautical_dusk_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local nautical dusk time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no nautical dusk on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, False, offset=12, timezone=local_time_zone)

def get_astronomical_dawn_time(self, date=None):
"""
Calculate the astronomical dawn time for given date.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is no astronomical dawn on given location and date
:return: UTC astronomical dawn datetime
"""
return self._calc_sun_time(date, True, offset=18)

def get_local_astronomical_dawn_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local astronomical dawn time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no astronomical dawn on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, True, offset=18, timezone=local_time_zone)

def get_astronomical_dusk_time(self, date=None):
"""
Calculate the astronomical dusk time for given date.
:param date: Reference date. Today if not provided.
:raises: SunTimeException when there is no astronomical dusk on given location and date
:return: UTC astronomical dusk datetime
"""
return self._calc_sun_time(date, False, offset=18)

def get_local_astronomical_dusk_time(self, date=None, local_time_zone=tz.tzlocal()):
"""
Get local astronomical dusk time for local or custom time zone.
:param date: Reference date. Today if not provided.
:param local_time_zone: Local or custom time zone.
:raises: SunTimeException when there is no astronomical dusk on given location and date.
:return: Local time zone sunrise datetime
"""
return self._calc_sun_time(date, False, offset=18, timezone=local_time_zone)

def _calc_sun_time(self, date, isRiseTime=True, zenith=90.8):
def _calc_sun_time(self, date, isRiseTime=True, offset=None, timezone=None):
"""
Calculate sunrise or sunset date.
:param date: Reference date
:param isRiseTime: True if you want to calculate sunrise time.
:param zenith: Sun reference zenith
:return: UTC sunset or sunrise datetime
:raises: SunTimeException when there is no sunrise and sunset on given location and date
:param offset: Dawn/dusk offset relative to the Sun reference zenith
:param local_time_zone: Local or custom time zone.
:return: UTC sunset or sunrise datetime if no local_timezone specified.
:raises: SunTimeException when there is no dawn/dusk on given location and date
"""
date = datetime.date.today() if date is None else date
zenith = self._ZENITH if offset is None else (self._ZENITH + (+1 * offset))
# isRiseTime == False, returns sunsetTime
day = date.day
month = date.month
Expand Down Expand Up @@ -136,10 +237,9 @@ def _calc_sun_time(self, date, isRiseTime=True, zenith=90.8):
# 7a. calculate the Sun's local hour angle
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*self._lat))) / (cosDec * math.cos(TO_RAD*self._lat))

if cosH > 1:
return None # The sun never rises on this location (on the specified date)
if cosH < -1:
return None # The sun never sets on this location (on the specified date)
if cosH > 1 or cosH < -1:
# The sun never rises on this location (on the specified date)
raise SunTimeException('The sun never sets on this location (on the specified date)')

# 7b. finish calculating H and convert into hours

Expand Down Expand Up @@ -176,8 +276,8 @@ def _calc_sun_time(self, date, isRiseTime=True, zenith=90.8):
if month > 12:
month = 1
year += 1

return datetime.datetime(year, month, day, hr, int(min), tzinfo=tz.tzutc())
result = datetime.datetime(year, month, day, hr, int(min), tzinfo=tz.tzutc())
return result.astimezone(timezone) if timezone else result

@staticmethod
def _force_range(v, max):
Expand Down