- Sunrise and sunset times vary with date, geographic location (lat/long,) and timezone. This formula returns all results as UT (also known as GMT or Time-Zone 0). A simpe method for convering the GMT results to local time (adjusting for timezone and/or summer time according to host computers regional settings) is included at the end of this page.
-
It is not practical for a short function to predict sunrise/sunset times with absolute precision.
- There are too many "periodic" variables due to planetary interactions etc. for a short program
- Most sunrise/set tables assume a low altidude (often near sea level) and an unobstructed view to the appropriate horizon.
- The calculation results are expected to be within a couple of minutes for normal latitudes but less accurate for polar regions.
- Under certain circumstances, sunrise can occur before midnight and/or sunset could occur after midnight
- In some locations, the sun does not rise (or set) for days, months or seasons. The absence of sunrise/sunset is flagged by setting SunError to true in thr returned result
- Changing from UT to local time could change the date of some sunrise/sunset predictions. This is correct behaviour, occurring when the time is before midnight in one timezone and after midnight in the other.
At the time of writing this page, the NOAA spreadsheetwas available via https://gml.noaa.gov/grad/solcalc/NOAA_Solar_Calculations_day.xls
' Notes
' Times are approximate, usually within a couple of minutes but larger errors possible in polar regions
' Some timezone/latitude/longitude/sunrise-type combinations can result in sunrises before midnight
' or sunsets after midnight. You may need to check sunrise/sunset dates as well as the times.
' You must check the SunError flag on the returned value.
' SunError = True means that the sun will not rise/set on requested date/lat/long
'
' Returned times are always GMT.
' They can be converted into local time using the VB.Net "built in" function .ToLocalTime()
'
' RiseType.Normal is the most frequently used value for RiseType1
'
Public Function SunRise(Dat As Date, Lat As Double, lon As Double, RiseType1 As RiseType) As SunTimes
Dim B3, B4, E2, F2, G2, I2, J2, K2, L2, M2, N2, O2, P2 As Double
Dim Q2, R_I2, R_J2, R2, S2, T2, U2, V2, W2, W2_a, X2, Y2, Z2, sunAngle As Double
Dim tempDate As Date
Dim st As SunTimes
Select Case RiseType1
Case RiseType.Normal : sunAngle = 90 + 50 / 60
Case RiseType.Civil : sunAngle = 96
Case RiseType.Nautical : sunAngle = 102
Case RiseType.Astrological : sunAngle = 108
End Select
B3 = Lat
B4 = lon
E2 = Dat.Hour / 24 + (Dat.Minute / (24 * 60))
F2 = Gregorian_To_JulianDay(Dat)
G2 = (F2 - 2451545) / 36525
I2 = 280.46646
I2 += G2 * (36000.76983 + G2 * 0.0003032) Mod 360 '
I2 = Zero_360(I2)
J2 = 357.52911
J2 += G2 * (35999.05029 - 0.0001537 * G2) '
K2 = 0.016708634
K2 -= G2 * (0.000042037 + 0.0000001267 * G2)
L2 = Math.Sin(Radians(J2)) * (1.914602 - G2 * (0.004817 + 0.000014 * G2))
L2 += Math.Sin(Radians(2 * J2)) * (0.019993 - 0.000101 * G2)
L2 += Math.Sin(Radians(3 * J2)) * 0.000289
M2 = I2 + L2
N2 = J2 + L2
O2 = (1.000001018 * (1 - K2 * K2)) / (1 + K2 * Math.Cos(Radians(N2)))
P2 = M2 - 0.00569
P2 -= 0.00478 * Math.Sin(Radians(125.04 - 1934.136 * G2))
Q2 = 23 + (26 + ((21.448 - G2 * (46.815 + G2 * (0.00059 - G2 * 0.001813)))) / 60) / 60
R2 = Q2 + 0.00256 * Math.Cos(Radians(125.04 - 1934.136 * G2))
S2 = Degrees(Math.Atan2(Math.Cos(Radians(R2)) * Math.Sin(Radians(P2)), Math.Cos(Radians(P2))))
T2 = Degrees(Math.Asin(Math.Sin(Radians(R2)) * Math.Sin(Radians(P2)))) '
U2 = Math.Tan(Radians(R2 / 2)) * Math.Tan(Radians(R2 / 2))
' changes made to shorten lines, avoid excessively long lines on (minor browser issues)
' the changes do not affect the results of V2 or W2 calculations
R_I2 = Radians(I2)
R_J2 = Radians(J2)
V2 = U2 * Math.Sin(2 * R_I2)
V2 -= 2 * K2 * Math.Sin(R_J2)
V2 += 4 * K2 * U2 * Math.Sin(R_J2) * Math.Cos(2 * R_I2)
V2 -= 0.5 * U2 * U2 * Math.Sin(4 * R_I2)
V2 -= 1.25 * K2 * K2 * Math.Sin(2 * R_J2)
V2 = 4 * Degrees(V2)
W2_a = Math.Cos(Radians(sunAngle)) / (Math.Cos(Radians(B3)) * Math.Cos(Radians(T2)))
W2_a -= Math.Tan(Radians(B3)) * Math.Tan(Radians(T2))
If W2_a <= -1 OrElse W2_a >= 1 Then ' the sun does not rise or set
Return FlagError()
End If
W2 = Degrees(Math.Acos(W2_a))
X2 = (720 - 4 * B4 - V2) / 1440
Y2 = X2 - W2 * 4 / 1440
Z2 = X2 + W2 * 4 / 1440
st.RiseTime = ExcelDate_To_Gregorian(Y2)
tempDate = New Date(Dat.Year, Dat.Month, Dat.Day, 0, 0, 0, 0, 0)
tempDate = tempDate.AddHours(st.RiseTime.Hour)
tempDate = tempDate.AddMinutes(st.RiseTime.Minute)
tempDate = tempDate.AddSeconds(st.RiseTime.Second)
st.RiseTime = tempDate
st.SetTime = ExcelDate_To_Gregorian(Z2)
tempDate = New Date(Dat.Year, Dat.Month, Dat.Day, 0, 0, 0, 0, 0)
tempDate = tempDate.AddHours(st.SetTime.Hour)
tempDate = tempDate.AddMinutes(st.SetTime.Minute)
tempDate = tempDate.AddSeconds(st.SetTime.Second)
st.SetTime = tempDate
st.SunError = False
Return st
End Function
'
Private Function JulianDay_To_Gregorian(JulianDay As Double) As Date
' The JulianDay epoch date is in in a year over 4000 years BC.
' VB.Net functions do not expect dates from ancient pre-historic times
' The number 2400000.5, combined with a "modified" epoch in 1858 avoids various problems
Return New Date(1858, 11, 17, 0, 0, 0, 0, 0).AddDays(JulianDay - 2400000.5)
End Function
'
Private Function Gregorian_To_JulianDay(dat_Greg As Date) As Double
' Calculate Julian Day via Modified Julian Day because dotNet compiler dislikes the year -4712 used in Julian Day epoch
Dim mjd As Double = DateDiff(DateInterval.Second, New Date(1858, 11, 17, 0, 0, 0, 0, 0), dat_Greg)
Return (mjd / (24 * 3600)) + 2400000.5
End Function
'
Private Function Radians(a As Double) As Double
Return a * Math.PI / 180
End Function
'
Private Function Degrees(a As Double) As Double
Return a * 180 / Math.PI
End Function
'
Private Function ExcelDate_To_Gregorian(ExcelDate As Double) As Date
' Excel Time Stamp should not be used for dates prior to 1901
Return New Date(1899, 12, 30, 0, 0, 0, 0, 0).AddDays(ExcelDate)
End Function
'
Private Function Zero_360(a As Double) As Double
If a >= 0 Then Return a Mod 360
Dim b As Integer = CInt(Math.Floor(Math.Abs(a) / 360)) + 1
Return (a + (b * 360)) Mod 360
End Function
'
Private Function FlagError() As SunTimes
Dim ret As SunTimes
ret.RiseTime = New Date(1900, 1, 1, 0, 0, 0, 0, 0)
ret.SetTime = New Date(1900, 1, 1, 0, 0, 0, 0, 0)
ret.SunError = True
Return ret
End Function
'
Public Structure SunTimes
Dim RiseTime As Date
Dim SetTime As Date
Dim SunError As Boolean
End Structure
'
Public Enum RiseType
Normal
Civil
Nautical
Astrological
End Enum
How to Obtain SunRise/Sunset in UT (GMT or Time Zone 0)
Get Sunrise and Sunset for Normal Sunrise in Universal-Time for 24 January 2026 at latitude 51.7143 and Longitude -5.0427
Dim dat As New Date(2026, 1, 24, 12, 0, 0, 0, 0, 0)
Dim st as SunTimes = SunRise(dat, 51.7143, -5.0427, RiseType.Normal)
Dim result As String
If st.SunError Then
result = "Sun does not rise/set"
Else
result = "Sunrise "& st.RiseTime.ToString("ddd dd MMM yyyy - HH:mm")
result &= " SunSet "& st.SetTime.ToString("ddd dd MMM yyyy - HH:mm")
End If
How to Obtain SunRise/Sunset in host computers Local Time
Get Sunrise and Sunset for Astrological Sunrise in Host-Computers_Local-Time for 24 January 2026 at latitude 51.7143 and Longitude -5.0427
Dim dat As New Date(2026, 1, 24, 12, 0, 0, 0, 0, 0)
Dim st as SunTimes = SunRise(dat, 51.7143, -5.0427, RiseType.Astological)
Dim result As String
If st.SunError Then
result = "Sun does not rise/set"
Else
result = "Sunrise "& st.RiseTime.ToLocalTime().ToString("ddd dd MMM yyyy - HH:mm")
result &= " SunSet "& st.SetTime.ToLocalTime().ToString("ddd dd MMM yyyy - HH:mm")
End If
DigitalDan.co.uk