3 Utility to let you figure out if we should've daylight now.
4 Usage: ./suntime.lua [debug]
5 Exit code is either 0, we have daylight - or 1, no daylight.
6 Adjust lon, lat, tolerance for your location and the required
11 lon,lat = "3.1234","51.1234"
13 dateTable = os.date("*t")
14 curEpoch = os.time(dateTable)
15 year = dateTable["year"]
16 month = dateTable["month"]
17 day = dateTable["day"]
19 function force_range(v, max)
22 elseif( v >= max) then
28 function daysInMonth(year, month)
29 month2days = {"31", "28", "31", "30", "31", "30", "31", "31", "30", "31", "30", "31"}
31 -- special case February http://www.henk-reints.nl/cal/gregcal.htm
33 -- no century change and can be devided by 4
34 if(((year % 100) ~= 0) and ((year % 4) == 0)) then
36 -- change of a century and can be devided by 400
37 elseif(((year % 100) == 0) and ((year % 400) == 0)) then
41 return month2days[month]
44 --[[ Ported from https://pypi.org/project/suntime/
45 which is based on https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql
46 which took the algorithm from https://web.archive.org/web/20161022214335/https://williams.best.vwh.net/sunrise_sunset_algorithm.htm
51 lon: string, longitude of your location
52 lat: string, latitude of your location
53 getRiseTime: bool, true -> sunrise time, false -> sunset time
55 return value: time in Unix epoch
57 function calc_sun_time(year, month, day, lon, lat, getRiseTime)
59 TO_RAD = (math.pi/180)
62 -- 1. first calculate the day of the year
63 N1 = math.floor(275 * month / 9)
64 N2 = math.floor((month + 9 ) / 12)
65 N3 = ( 1 + math.floor((year - 4 * math.floor(year / 4) + 2) / 3))
66 N = N1 - (N2 * N3) + day - 30
68 -- 2. convert the longitude to hour value and calculate an approximate time
72 t = N + ((6 - lonHour) / 24)
74 t = N + ((18 - lonHour) / 24)
77 -- 3. calculate the Sun's mean anomaly
78 M = (0.9856 * t) - 3.289
80 -- 4. calculate the Sun's true longitude
81 L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
82 L = force_range(L, 360 ) -- NOTE: L adjusted into the range [0,360)
84 -- 5a. calculate the Sun's right ascension
85 RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
86 RA = force_range(RA, 360 ) -- NOTE: RA adjusted into the range [0,360)
88 -- 5b. right ascension value needs to be in the same quadrant as L
89 Lquadrant = (math.floor( L/90)) * 90
90 RAquadrant = (math.floor(RA/90)) * 90
91 RA = RA + (Lquadrant - RAquadrant)
93 -- 5c. right ascension value needs to be converted into hours
96 -- 6. calculate the Sun's declination
97 sinDec = 0.39782 * math.sin(TO_RAD * L)
98 cosDec = math.cos(math.asin(sinDec))
100 -- 7a. calculate the Sun's local hour angle
101 cosH = (math.cos(TO_RAD * zenith) - (sinDec * math.sin(TO_RAD * lat))) / (cosDec * math.cos(TO_RAD * lat))
103 -- return None -- The sun never rises on this location (on the specified date)
104 print("Sun will never rise here and now.")
106 elseif(cosH < -1) then
107 -- return None -- The sun never sets on this location (on the specified date)
108 print("Sun will never rise here and now.")
112 -- 7b. finish calculating H and convert into hours
114 H = 360 - (1/TO_RAD) * math.acos(cosH)
116 H = (1/TO_RAD) * math.acos(cosH)
120 -- 8. calculate local mean time of rising/setting
121 T = H + RA - (0.06571 * t) - 6.622
123 -- 9. adjust back to UTC
125 UT = force_range(UT, 24) -- UTC time in decimal format (e.g. 23.23)
128 hr = force_range(math.floor(UT), 24)
129 min = tonumber(string.format("%.0f", (UT - math.floor(UT))*60))
135 -- 10. check corner case https://github.com/SatAgro/suntime/issues/1 and issue 8
140 if(day > daysInMonth(year, month)) then
151 return os.time{year=year, month=month, day=day, hour=hr, min=math.floor(min)}
154 -- calculate sunrise and sunset time adjusted by tolerance seconds
155 sunrise = (calc_sun_time(year, month, day, lon, lat, true) + tolerance)
156 sunset = (calc_sun_time(year, month, day, lon, lat, false) - tolerance)
159 if(arg[1] == "debug") then
160 print("Expected sunrise for today in UTC seconds adjusted +", tolerance)
161 print(os.date("%c", sunrise))
162 print("Expected sunset for today in UTC seconds adjusted -", tolerance)
163 print(os.date("%c", sunset))
166 -- return 0 in case we should've daylight, otherwise return 1
167 if((curEpoch >= sunrise) and (curEpoch <= sunset)) then