any idea how I figure out if it's currently night/day or sunrise/dawn based on time and location of the user?
I haven't found anything useful that I could use within either the client or backend.
What makes it tricky is the hour doesn't necessarily define if it is night and day, this depends pretty much on the year, month, day, hour and geo coordinates.
For clarification... to emulate something like this.
A way to approximate this would be very useful as well.
Hope that someone can help!
Note to Python 3 users: I just tried the code below using Python 3.8.6 and it works in that version, too. I had to convert the print
statements into print()
function calls, but that was all.
I noticed that the example implementation of tzinfo
classes is different from what's in the Python 2 documentation, but using the example tzinfo
classes in the tzinfo_example.py file referenced in the latest documentation worked just fine (but so did the older 2.x versions).
You can download a Python 3 version of the sunriseset.py
file shown below from here.
You can do as I did and use this public domain Sun.py module to compute the position of the sun relative to positions on the Earth. (Warning: it contains tab characters and assumes tabs are every 8 characters.) It's pretty old, but has continued to work well for me for many years. I made a few superficial modifications to it to be more up-to-date with Python 2.7, such as making the few classes in it new-style, but for the most part it's unchanged.
Here's one module I created, called sunriseset.py
, which shows how to use it to calculate the sunrise and sunset times for a specific location given its geographic coordinates and timezone. The referenced timezone
module is an implementation of the tzinfo
abstract base class described in the datetime
module's documentation on tzinfo
objects.
# -*- coding: iso-8859-1 -*-
import datetime
import timezone # concrete tzinfo subclass based on the Python docs
import math
from Sun import Sun
__all__ = ['getsuninfo', 'Place']
class Place(object):
def __init__(self, name, coords, tz=timezone.Pacific):
self.name = name # string
self.coords = coords # tuple (E/W long, N/S lat)
self.tz = tz # tzinfo constant
def _hoursmins(hours):
"""Convert floating point decimal time in hours to integer hrs,mins"""
frac,h = math.modf(hours)
m = round(frac*60, 0)
if m == 60: # rounded up to next hour
h += 1; m = 0
return int(h),int(m)
def _ymd(date):
"""Return y,m,d from datetime object as tuple"""
return date.timetuple()[:3]
def getsuninfo(location, date=None):
"""Return local datetime of sunrise, sunset, and length of day in hrs,mins)"""
if date == None:
querydate = datetime.date.today()
else: # date given should be datetime instance
querydate = date
args = _ymd(querydate) + location.coords
utcrise, utcset = Sun().sunRiseSet(*args)
daylength = Sun().dayLength(*args)
hrs,mins = _hoursmins(daylength)
risehour, risemin = _hoursmins(utcrise)
sethour, setmin = _hoursmins(utcset)
# convert times to timedelta values (ie from midnight utc of the date)
midnight = datetime.datetime(tzinfo=timezone.utc, *_ymd(querydate))
deltarise = datetime.timedelta(hours=risehour, minutes=risemin)
utcdatetimerise = midnight+deltarise
deltaset = datetime.timedelta(hours=sethour, minutes=setmin)
utcdatetimeset = midnight+deltaset
# convert results from UTC time to local time of location
localrise = utcdatetimerise.astimezone(location.tz)
localset = utcdatetimeset.astimezone(location.tz)
return localrise, localset, hrs, mins
if __name__ == "__main__":
import datetime, timezone
def unittest(location, testdate):
risetime, settime, hrs, mins = getsuninfo(location, testdate)
print "Location:", location.name
print "Date:", testdate.strftime("%a %x")
print risetime.strftime("Sunrise %I:%M %p"), settime.strftime("- Sunset %I:%M %p (%Z)")
print "daylight: %d:%02d" % (hrs,mins)
print
place = Place("My House", (-121.990278, 47.204444), timezone.Pacific)
# test dates just before and after DST transitions
print "pre 2007"
print "========="
unittest(place, datetime.date(2006, 4, 1))
unittest(place, datetime.date(2006, 4, 2))
unittest(place, datetime.date(2006, 10, 28))
unittest(place, datetime.date(2006, 10, 29))
print "2007"
print "========="
unittest(place, datetime.date(2007, 3, 10))
unittest(place, datetime.date(2007, 3, 11))
unittest(place, datetime.date(2007, 11, 3))
unittest(place, datetime.date(2007, 11, 4))
A concise description of an algorithm to calculate the sunrise and sunset is provided by the United States Naval Observatory, available here:
http://edwilliams.org/sunrise_sunset_algorithm.htm
In addition to providing the date and location, you also need to select a Zenith angle (at which the sun will be considered to have "risen" or "set") - the page linked has several options.
Update
Because the linked page is no longer available, I am quoting its text below. Note that the formulae included are in a pseudo-code-like form, rather than JavaScript.
Source:
Almanac for Computers, 1990
published by Nautical Almanac Office
United States Naval Observatory
Washington, DC 20392
Inputs:
day, month, year: date of sunrise/sunset
latitude, longitude: location for sunrise/sunset
zenith: Sun's zenith for sunrise/sunset
offical = 90 degrees 50'
civil = 96 degrees
nautical = 102 degrees
astronomical = 108 degrees
NOTE: longitude is positive for East and negative for West
NOTE: the algorithm assumes the use of a calculator with the
trig functions in "degree" (rather than "radian") mode. Most
programming languages assume radian arguments, requiring back
and forth convertions. The factor is 180/pi. So, for instance,
the equation RA = atan(0.91764 * tan(L)) would be coded as RA
= (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree
answer with a degree input for L.
1. first calculate the day of the year
N1 = floor(275 * month / 9)
N2 = floor((month + 9) / 12)
N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3))
N = N1 - (N2 * N3) + day - 30
2. convert the longitude to hour value and calculate an approximate time
lngHour = longitude / 15
if rising time is desired:
t = N + ((6 - lngHour) / 24)
if setting time is desired:
t = N + ((18 - lngHour) / 24)
3. calculate the Sun's mean anomaly
M = (0.9856 * t) - 3.289
4. calculate the Sun's true longitude
L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634
NOTE: L potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
5a. calculate the Sun's right ascension
RA = atan(0.91764 * tan(L))
NOTE: RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
5b. right ascension value needs to be in the same quadrant as L
Lquadrant = (floor( L/90)) * 90
RAquadrant = (floor(RA/90)) * 90
RA = RA + (Lquadrant - RAquadrant)
5c. right ascension value needs to be converted into hours
RA = RA / 15
6. calculate the Sun's declination
sinDec = 0.39782 * sin(L)
cosDec = cos(asin(sinDec))
7a. calculate the Sun's local hour angle
cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
if (cosH > 1)
the sun never rises on this location (on the specified date)
if (cosH < -1)
the sun never sets on this location (on the specified date)
7b. finish calculating H and convert into hours
if if rising time is desired:
H = 360 - acos(cosH)
if setting time is desired:
H = acos(cosH)
H = H / 15
8. calculate local mean time of rising/setting
T = H + RA - (0.06571 * t) - 6.622
9. adjust back to UTC
UT = T - lngHour
NOTE: UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24
10. convert UT value to local time zone of latitude/longitude
localT = UT + localOffset
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With