SunRiseSet


Description:
This is a class I wrote based off a Java applet on a NOAA web page (see the source code for the URL). It calculates the sunrise, sunset, and solar noon given longitude and latitude coordinates. Additional, it knows the coordinates for about 40 cities.

7/29/2000: Fixed the number of leap days, thanks to Ken Walthew for pointing out the problem.

6/4/2001: Updated the source code off of NOAA's updates, including fixes for the extreme north and south regions.

11/18/2003: Updated the coordinates of the cities in the class based off changes on NOAA's webpage.
 
Code:
' ------ Class clsSunRiseSet
' Version 2.01

Option Explicit

' -- The following properties are exposed:
'Sunrise (r) - Sunrise time
'Sunset (r) - Sunset time
'SolarNoon (r) - Solar noon
'
'CityCount (r) - Number of cities
'CityName (r) - Name of city, by index
'City (w) - Sets the longitude/latitude/timezone based off a city
'           name or city index
'
'TimeZone (r/w) - Current Timezone
'DaySavings (r/w) - Daylight savings time in effect
'Longitude (r/w) - Longitude to calculate for
'Latitude (r/w) - Latitude to calculate for
'
'DateDay (r/w) - Date to calculate for
'
'
' -- The following method is exposed
'CalculateSun - Calculate sunrise, sunset and solar noon
'
'
' Scott Seligman
'   http://www.scottandmichelle.net/scott/
' Based off of
'   http://www.srrb.noaa.gov/highlights/sunrise/gen.html

Private Type typeMonth
    Name As String
    NumDays As Long
End Type

Private Type typeCity
    Name As String
    longitude As Double
    latitude As Double
    TimeZone As Long
End Type

Private m_cNumberCities As Long
Private m_Cities() As typeCity
    
Private m_monthList(0 To 11) As typeMonth
Private m_monthLeap(0 To 11) As typeMonth

Private m_nTimeZone As Long
Private m_bDaySavings As Boolean
Private m_nLongitude As Double
Private m_nLatitude As Double
Private m_dateSel As Date

Private m_dateSunrise As Date
Private m_dateSunset As Date
Private m_dateNoon As Date

Public Property Get Sunrise() As Date
    Sunrise = m_dateSunrise
End Property

Public Property Get Sunset() As Date
    Sunset = m_dateSunset
End Property

Public Property Get SolarNoon() As Date
    SolarNoon = m_dateNoon
End Property

Public Property Get CityCount() As Long
    CityCount = m_cNumberCities + 1
End Property

Public Property Get CityName(nCity As Long) As String
    If nCity < 0 Or nCity > m_cNumberCities Then
        CityName = "(Error)"
    Else
        CityName = m_Cities(nCity).Name
    End If
End Property

Public Property Let City(City)
    Dim nCity As Long
    Dim bFound As Boolean
    
    If VarType(City) = vbString Then
        For nCity = 0 To m_cNumberCities
            If Trim(LCase(City)) = _
                Trim(LCase(m_Cities(nCity).Name)) Then
                bFound = True
                Exit For
            End If
        Next
        If Not bFound Then
            nCity = -1
        End If
    Else
        If IsNumeric(City) Then
            nCity = City
        Else
            nCity = -1
        End If
    End If
    
    If nCity < 0 Or nCity > m_cNumberCities Then
        m_nTimeZone = 0
        m_bDaySavings = False
        m_nLongitude = 0
        m_nLatitude = 0
    Else
        m_nTimeZone = m_Cities(nCity).TimeZone
        m_bDaySavings = False
        m_nLongitude = m_Cities(nCity).longitude
        m_nLatitude = m_Cities(nCity).latitude
    End If
    
End Property

Public Property Let TimeZone(nNew As Long)
    m_nTimeZone = nNew
End Property

Public Property Get TimeZone() As Long
    TimeZone = m_nTimeZone
End Property

Public Property Let DaySavings(bNew As Boolean)
    m_bDaySavings = bNew
End Property

Public Property Get DaySavings() As Boolean
    DaySavings = m_bDaySavings
End Property

Public Property Let longitude(nNew As Double)
    m_nLongitude = nNew
End Property

Public Property Get longitude() As Double
    longitude = m_nLongitude
End Property

Public Property Let latitude(nNew As Double)
    m_nLatitude = nNew
End Property

Public Property Get latitude() As Double
    latitude = m_nLatitude
End Property
    
Public Property Let DateDay(dateNew As Date)
    m_dateSel = dateNew
End Property
    
Public Property Get DateDay() As Date
    DateDay = m_dateSel
End Property


Private Function IsLeapYear(nYear As Long) As Boolean
    If (nYear Mod 4 = 0 And nYear Mod _
        100 <> 0) Or nYear Mod 400 = 0 Then
        IsLeapYear = True
    Else
        IsLeapYear = False
    End If
End Function

Private Function RadToDeg(angleRad As Double) As Double
    RadToDeg = 180 * angleRad / 3.1415926535
End Function

Private Function DegToRad(angleDeg As Double) As Double
    DegToRad = 3.1415926535 * angleDeg / 180
End Function

Private Function acos(X As Double) As Double
    On Error Resume Next
    acos = Atn(-X / Sqr(-X * X + 1)) + 2 * Atn(1)
End Function

Private Sub InitMonths()

    m_monthList(0).Name = "January": m_monthList(0).NumDays = 31
    m_monthList(1).Name = "February": m_monthList(1).NumDays = 28
    m_monthList(2).Name = "March": m_monthList(2).NumDays = 31
    m_monthList(3).Name = "April": m_monthList(3).NumDays = 30
    m_monthList(4).Name = "May": m_monthList(4).NumDays = 31
    m_monthList(5).Name = "June": m_monthList(5).NumDays = 30
    m_monthList(6).Name = "July": m_monthList(6).NumDays = 31
    m_monthList(7).Name = "August": m_monthList(7).NumDays = 31
    m_monthList(8).Name = "September": m_monthList(8).NumDays = 30
    m_monthList(9).Name = "October": m_monthList(9).NumDays = 31
    m_monthList(10).Name = "November": m_monthList(10).NumDays = 30
    m_monthList(11).Name = "DEcember": m_monthList(11).NumDays = 31
    
    m_monthLeap(0).Name = "January": m_monthLeap(0).NumDays = 31
    m_monthLeap(1).Name = "February": m_monthLeap(1).NumDays = 29
    m_monthLeap(2).Name = "March": m_monthLeap(2).NumDays = 31
    m_monthLeap(3).Name = "April": m_monthLeap(3).NumDays = 30
    m_monthLeap(4).Name = "May": m_monthLeap(4).NumDays = 31
    m_monthLeap(5).Name = "June": m_monthLeap(5).NumDays = 30
    m_monthLeap(6).Name = "July": m_monthLeap(6).NumDays = 31
    m_monthLeap(7).Name = "August": m_monthLeap(7).NumDays = 31
    m_monthLeap(8).Name = "September": m_monthLeap(8).NumDays = 30
    m_monthLeap(9).Name = "October": m_monthLeap(9).NumDays = 31
    m_monthLeap(10).Name = "November": m_monthLeap(10).NumDays = 30
    m_monthLeap(11).Name = "DEcember": m_monthLeap(11).NumDays = 31

End Sub

Private Sub AddCity(sCity As String, nLatitude As Double, _
    nLongitude As Double, nZone As Long)


    m_cNumberCities = m_cNumberCities + 1
    If m_cNumberCities > UBound(m_Cities) Then
        ReDim Preserve m_Cities(UBound(m_Cities) + 10)
    End If

    m_Cities(m_cNumberCities).Name = sCity
    m_Cities(m_cNumberCities).latitude = nLatitude
    m_Cities(m_cNumberCities).longitude = nLongitude
    m_Cities(m_cNumberCities).TimeZone = nZone

End Sub

Private Sub InitCities()

    m_cNumberCities = -1
    ReDim m_Cities(0)

    AddCity "Albuquerque, NM", 35.0833, 106.65, 7
    AddCity "Anchorage, AK", 61.217, 149.90, 9
    AddCity "Atlanta, GA", 33.733, 84.383, 5
    AddCity "Austin, TX", 30.283, 97.733, 6
    AddCity "Birmingham, AL", 33.521, 86.8025, 6
    AddCity "Bismarck, ND", 46.817, 100.783, 6
    AddCity "Boston, MA", 42.35, 71.05, 5
    AddCity "Boulder, CO", 40.125, 105.237, 7
    AddCity "Chicago, IL", 41.85, 87.65, 6
    AddCity "Dallas, TX", 32.46, 96.47, 6
    AddCity "Denver, CO", 39.733, 104.983, 7
    AddCity "Detroit, MI", 42.333, 83.05, 5
    AddCity "Honolulu, HI", 21.30, 157.85, 10
    AddCity "Houston, TX", 29.75, 95.35, 6
    AddCity "Indianapolis, IN", 39.767, 86.15, 5
    AddCity "Jackson, MS", 32.283, 90.183, 6
    AddCity "Kansas City, MO", 39.083, 94.567, 6
    AddCity "Los Angeles, CA", 34.05, 118.233, 8
    AddCity "Menomonee Falls, WI", 43.11, 88.10, 6
    AddCity "Miami, FL", 25.767, 80.183, 5
    AddCity "Minneapolis, MN", 44.967, 93.25, 6
    AddCity "New Orleans, LA", 29.95, 90.067, 6
    AddCity "New York City, NY", 40.7167, 74.0167, 5
    AddCity "Oklahoma City, OK", 35.483, 97.533, 6
    AddCity "Philadelphia, PA", 39.95, 75.15, 5
    AddCity "Phoenix, AZ", 33.433, 112.067, 7
    AddCity "Pittsburgh, PA", 40.433, 79.9833, 5
    AddCity "Portland, ME", 43.666, 70.283, 5
    AddCity "Portland, OR", 45.517, 122.65, 8
    AddCity "Raleigh, NC", 35.783, 78.65, 5
    AddCity "Richmond, VA", 37.5667, 77.450, 5
    AddCity "Saint Louis, MO", 38.6167, 90.1833, 6
    AddCity "San Diego, CA", 32.7667, 117.2167, 8
    AddCity "San Francisco, CA", 37.7667, 122.4167, 8
    AddCity "Seattle, WA", 47.60, 122.3167, 8
    AddCity "Washington DC", 38.8833, 77.0333, 5
    AddCity "Beijing, China", 39.9167, -116.4167, -8
    AddCity "Berlin, Germany", 52.33, -13.30, -1
    AddCity "Bombay, India", 18.9333, -72.8333, -5.5
    AddCity "Buenos Aires, Argentina", -34.60, 58.45, 3
    AddCity "Cairo, Egypt", 30.10, -31.3667, -2
    AddCity "Cape Town, South Africa", -33.9167, -18.3667, -2
    AddCity "Caracas, Venezuela", 10.50, 66.9333, 4
    AddCity "Helsinki, Finland", 60.1667, -24.9667, -2
    AddCity "Hong Kong, China", 22.25, -114.1667, -8
    AddCity "Jerusalem, Israel", 31.7833, -35.2333, -2
    AddCity "London, England", 51.50, 0.1667, 0
    AddCity "Mexico City, Mexico", 19.4, 99.15, 6
    AddCity "Moscow, Russia", 55.75, -37.5833, -3
    AddCity "New Delhi, India", 28.6, -77.2, -5.5
    AddCity "Ottawa, Canada", 45.41667, 75.7, 5
    AddCity "Paris, France", 48.8667, -2.667, -1
    AddCity "Rio de Janeiro, Brazil", -22.90, 43.2333, 3
    AddCity "Riyadh, Saudi Arabia", 24.633, -46.71667, -3
    AddCity "Rome, Italy", 41.90, -12.4833, -1
    AddCity "Sydney, Australia", -33.8667, -151.2167, -10
    AddCity "Tokyo, Japan", 35.70, -139.7667, -9
    AddCity "Zurich, Switzerland", 47.3833, -8.5333, -1

End Sub

Private Sub Class_Initialize()

    InitMonths
    InitCities
    m_dateSel = Now

End Sub

Private Function calcDayOfYear(ByVal nmn As Long, ByVal ndy As Long, _
    bLeapYear As Boolean) As Long

    calcDayOfYear = Int((275 * nmn) / 9) - IIf(bLeapYear, 1, 2) _
        * Int((nmn + 9) / 12) + ndy - 30

End Function

Private Function calcJD(ByVal nYear As Long, ByVal nMonth As Long, _
    ByVal nDay As Long) As Double

    If nMonth <= 2 Then
        nYear = nYear - 1
        nMonth = nMonth + 12
    End If
    Dim A As Long
    Dim B As Long
    
    A = Int(nYear / 100)
    B = 2 - A + Int(A / 4)
    
    calcJD = Int(365.25 * (nYear + 4716)) + Int(30.6001 * _
        (nMonth + 1)) + nDay + B - 1524.5

End Function

Private Function calcTimeJulianCent(ByVal njd As Double) As Double

    calcTimeJulianCent = (njd - 2451545#) / 36525#
    
End Function

Private Function calcJDFromJulianCent(ByVal nt As Double) As Double

    calcJDFromJulianCent = nt * 36525# + 2451545#

End Function

Private Function calcGeomMeanLongSun(ByVal nt As Double) As Double

    Dim nLO As Double
    nLO = 280.46646 + nt * (36000.76983 + 0.0003032 * nt)
    Do While nLO > 360#
        nLO = nLO - 360#
    Loop
    Do While nLO < 0
        nLO = nLO + 360#
    Loop
    calcGeomMeanLongSun = nLO

End Function

Private Function calcGeomMeanAnomalySun(ByVal nt As Double) As Double

    calcGeomMeanAnomalySun = 357.52911 + nt * (35999.05029 - _
        0.0001537 * nt)

End Function

Private Function calcEccentricityEarthOrbit(ByVal nt As Double) _
    As Double

    calcEccentricityEarthOrbit = 0.016708634 - nt * _
        (0.000042037 + 0.0000001267 * nt)

End Function

Private Function calcSunEqOfCenter(ByVal nt As Double) As Double

    Dim nm As Double
    Dim nmrad As Double
    Dim nsinm As Double
    Dim nsin2m As Double
    Dim nsin3m As Double

    nm = calcGeomMeanAnomalySun(nt)
    
    nmrad = DegToRad(nm)
    nsinm = Sin(nmrad)
    nsin2m = Sin(nmrad + nmrad)
    nsin3m = Sin(nmrad + nmrad + nmrad)
    
    calcSunEqOfCenter = nsinm * (1.914602 - nt * _
        (0.004817 + 0.000014 * nt)) + nsin2m * _
        (0.019993 - 0.000101 * nt) + nsin3m * 0.000289

End Function

Private Function calcSunTrueLong(ByVal nt As Double) As Double

    Dim n10 As Double
    Dim nc As Double
    
    n10 = calcGeomMeanLongSun(nt)
    nc = calcSunEqOfCenter(nt)
    
    calcSunTrueLong = n10 + nc

End Function

Private Function calcSunApparentLong(ByVal nt As Double) As Double
    Dim no As Double
    Dim nomega As Double
    
    no = calcSunTrueLong(nt)
    nomega = 125.04 - 1934.136 * nt
    calcSunApparentLong = no - 0.00569 - 0.00478 * _
        Sin(DegToRad(nomega))
    
End Function

Private Function calcMeanObliquityOfEcliptic(ByVal nt As Double) _
    As Double

    Dim nseconds As Double
    
    nseconds = 21.448 - nt * (46.815 + nt * _
        (0.00059 - nt * (0.001813)))
    calcMeanObliquityOfEcliptic = 23# + (26# + _
        (nseconds / 60#)) / 60#

End Function

Private Function calcObliquityCorrection(ByVal nt As Double) As Double

    Dim ne0 As Double
    ne0 = calcMeanObliquityOfEcliptic(nt)
    
    Dim nomega As Double
    nomega = 125.04 - 1934.136 * nt
    calcObliquityCorrection = ne0 + 0.00256 * Cos(DegToRad(nomega))

End Function

Private Function calcSunRtAscension(nt As Double) As Double

    Dim ne As Double
    Dim nlambda As Double
    Dim ntananum As Double
    Dim ntanadenom As Double
    
    ne = calcObliquityCorrection(nt)
    nlambda = calcSunApparentLong(nt)

    ntananum = (Cos(DegToRad(ne)) * Sin(DegToRad(nlambda)))
    ntanadenom = (Cos(DegToRad(nlambda)))
    
    calcSunRtAscension = RadToDeg(atan2(ntananum, ntanadenom))
    
End Function

Private Function atan2(ByVal Y As Double, ByVal X As Double) As Double

    If X > 0 Then
        atan2 = Atn(Y / X)
    ElseIf X < 0 Then
        atan2 = Atn(Y / X) + 3.1415926535
    Else
        atan2 = 3.1415926535 / 2 * Sgn(Y)
    End If

End Function

Private Function asin(ByVal X As Double) As Double
    asin = Atn(X / Sqr(-X * X + 1))
End Function

Private Function calcSunDeclination(ByVal nt As Double) As Double

    Dim ne As Double
    Dim nlambda As Double
    Dim nsint As Double
    
    ne = calcObliquityCorrection(nt)
    nlambda = calcSunApparentLong(nt)
    
    nsint = Sin(DegToRad(ne)) * Sin(DegToRad(nlambda))
    calcSunDeclination = RadToDeg(asin(nsint))
    
End Function

Private Function calcEquationOfTime(ByVal nt As Double) As Double

    Dim nepsilon As Double
    Dim nl0 As Double
    Dim ne As Double
    Dim nm As Double
    Dim ny As Double
    Dim nsin2l0 As Double
    Dim nsinm As Double
    Dim ncos2l0 As Double
    Dim nsin4l0 As Double
    Dim nsin2m As Double
    Dim nEtime As Double
    
    nepsilon = calcObliquityCorrection(nt)
    nl0 = calcGeomMeanLongSun(nt)
    ne = calcEccentricityEarthOrbit(nt)
    nm = calcGeomMeanAnomalySun(nt)
    
    ny = Math.Tan(DegToRad(nepsilon) / 2#)
    ny = ny * ny
    
    nsin2l0 = Sin(2# * DegToRad(nl0))
    nsinm = Sin(DegToRad(nm))
    ncos2l0 = Cos(2# * DegToRad(nl0))
    nsin4l0 = Sin(4# * DegToRad(nl0))
    nsin2m = Sin(2# * DegToRad(nm))
    
    nEtime = ny * nsin2l0 - 2# * ne * nsinm + 4# * ne * _
        ny * nsinm * ncos2l0 - 0.5 * ny * ny * nsin4l0 - _
        1.25 * ne * ne * nsin2m
    
    calcEquationOfTime = RadToDeg(nEtime) * 4#
End Function

Private Function calcHourAngleSunrise(ByVal nlat As Double, _
    ByVal nsolarDec As Double) As Double

    Dim nlatRad As Double
    Dim nsdRad As Double
    Dim nHAarg As Double
    Dim nHA As Double

    nlatRad = DegToRad(nlat)
    nsdRad = DegToRad(nsolarDec)

    nHAarg = (Cos(DegToRad(90.833)) / (Cos(nlatRad) * _
        Cos(nsdRad)) - Tan(nlatRad) * Tan(nsdRad))

    Dim nTemp As Double
    nTemp = Cos(DegToRad(90.833)) / (Cos(nlatRad) * _
        Cos(nsdRad)) - Tan(nlatRad) * Tan(nsdRad)
    If Abs(nTemp) > 1 Then
        nHA = -999
    Else
        nHA = (acos(nTemp))
    End If

    calcHourAngleSunrise = nHA

End Function

Private Function calcHourAngleSunset(ByVal nlat As Double, _
    ByVal nsolarDec As Double) As Double

    Dim nlatRad As Double
    Dim nsdRad As Double
    Dim nHAarg As Double
    Dim nHA As Double

    nlatRad = DegToRad(nlat)
    nsdRad = DegToRad(nsolarDec)
    
    nHAarg = (Cos(DegToRad(90.833)) / (Cos(nlatRad) * _
        Cos(nsdRad)) - Tan(nlatRad) * Tan(nsdRad))
    
    Dim nTemp As Double
    nTemp = Cos(DegToRad(90.833)) / (Cos(nlatRad) * _
        Cos(nsdRad)) - Tan(nlatRad) * Tan(nsdRad)
    If Abs(nTemp) > 1 Then
        nHA = 999
    Else
        nHA = (acos(nTemp))
    End If
    
    calcHourAngleSunset = -nHA

End Function

Private Function calcSunriseUTC(ByVal njd As Double, _
    ByVal nLatitude As Double, ByVal nLongitude As Double) As Double

    Dim nt As Double
    Dim neqTime As Double
    Dim nsolarDec As Double
    Dim nhourAngle As Double
    
    Dim ndelta As Double
    Dim ntimeDiff As Double
    Dim ntimeUTC As Double
    
    nt = calcTimeJulianCent(njd)

    neqTime = calcEquationOfTime(nt)
    nsolarDec = calcSunDeclination(nt)
    nhourAngle = calcHourAngleSunrise(nLatitude, nsolarDec)
    If nhourAngle = -999 Then
        calcSunriseUTC = -999
        Exit Function
    End If

    ndelta = nLongitude - RadToDeg(nhourAngle)
    ntimeDiff = 4 * ndelta
    ntimeUTC = 720 + ntimeDiff - neqTime

    Dim nnewt As Double
    nnewt = calcTimeJulianCent(calcJDFromJulianCent(nt) + _
        ntimeUTC / 1440#)
    neqTime = calcEquationOfTime(nnewt)
    nsolarDec = calcSunDeclination(nnewt)
    nhourAngle = calcHourAngleSunrise(nLatitude, nsolarDec)
    If nhourAngle = -999 Then
        calcSunriseUTC = -999
        Exit Function
    End If
    ndelta = nLongitude - RadToDeg(nhourAngle)
    ntimeDiff = 4 * ndelta
    ntimeUTC = 720 + ntimeDiff - neqTime

    calcSunriseUTC = ntimeUTC
    
End Function

Private Function calcSolNoonUTC(ByVal nt As Double, _
    ByVal nLongitude As Double) As Double

    Dim nnewt As Double
    Dim neqTime As Double
    Dim nsolarNoonDec As Double
    Dim nsolNoonUTC As Double
    
    nnewt = calcTimeJulianCent(calcJDFromJulianCent(nt) + _
        0.5 + nLongitude / 360#)
    neqTime = calcEquationOfTime(nt)
    nsolarNoonDec = calcSunDeclination(nt)
    nsolNoonUTC = 720 + (nLongitude * 4) - neqTime
    
    calcSolNoonUTC = nsolNoonUTC

End Function

Private Function calcSunsetUTC(ByVal njd As Double, _
    ByVal nLatitude As Double, ByVal nLongitude As _
    Double) As Double

    Dim neqTime As Double
    Dim nsolarDec As Double
    Dim nhourAngle As Double
    
    Dim ndelta As Double
    Dim ntimeDiff As Double
    Dim ntimeUTC As Double
    Dim nnewt As Double
    Dim nt As Double

    nt = calcTimeJulianCent(njd)

    neqTime = calcEquationOfTime(nt)
    nsolarDec = calcSunDeclination(nt)
    nhourAngle = calcHourAngleSunset(nLatitude, nsolarDec)
    If nhourAngle = -999 Then
        calcSunsetUTC = -999
        Exit Function
    End If

    ndelta = nLongitude - RadToDeg(nhourAngle)
    ntimeDiff = 4 * ndelta
    ntimeUTC = 720 + ntimeDiff - neqTime
    
    nnewt = calcTimeJulianCent(calcJDFromJulianCent(nt) + _
        ntimeUTC / 1440#)
    neqTime = calcEquationOfTime(nnewt)
    nsolarDec = calcSunDeclination(nnewt)
    nhourAngle = calcHourAngleSunset(nLatitude, nsolarDec)
    If nhourAngle = -999 Then
        calcSunsetUTC = -999
        Exit Function
    End If
    
    ndelta = nLongitude - RadToDeg(nhourAngle)
    ntimeDiff = 4 * ndelta
    ntimeUTC = 720 + ntimeDiff - neqTime
    
    calcSunsetUTC = ntimeUTC

End Function

Private Function findRecentSunrise(ByVal njd As Double, _
    ByVal nLatitude As Double, ByVal nLongitude _
    As Double) As Double

    Dim njulianday As Double
    njulianday = njd
    Dim nBail As Long
    
    Dim ntime As Double
    ntime = calcSunriseUTC(njulianday, nLatitude, nLongitude)
    Do While ntime = -999 And nBail < 367
        nBail = nBail + 1
        njulianday = njulianday - 1
        ntime = calcSunriseUTC(njulianday, nLatitude, nLongitude)
    Loop
    
    findRecentSunrise = njulianday

End Function

Private Function findRecentSunset(ByVal njd As Double, _
    ByVal nLatitude As Double, ByVal nLongitude _
    As Double) As Double

    Dim njulianday As Double
    Dim ntime As Double
    Dim nBail As Long
    
    njulianday = njd

    ntime = calcSunsetUTC(njulianday, nLatitude, nLongitude)
    Do While ntime = -999 And nBail < 367
        nBail = nBail + 1
        njulianday = njulianday - 1
        ntime = calcSunsetUTC(njulianday, nLatitude, nLongitude)
    Loop
    
    findRecentSunset = njulianday

End Function

Private Function findNextSunrise(ByVal njd As Double, ByVal _
    nLatitude As Double, ByVal nLongitude As Double) As Double
    Dim njulianday As Double
    Dim ntime As Double
    Dim nBail As Long

    njulianday = njd

    ntime = calcSunriseUTC(njulianday, nLatitude, nLongitude)
    Do While ntime = -999 And nBail < 367
        nBail = nBail + 1
        njulianday = njulianday + 1
        ntime = calcSunriseUTC(njulianday, nLatitude, nLongitude)
    Loop
    
    findNextSunrise = njulianday

End Function

Private Function findNextSunset(ByVal njd As Double, ByVal _
    nLatitude As Double, ByVal nLongitude As Double) As Double

    Dim njulianday As Double
    Dim ntime As Double
    Dim nBail As Long
    njulianday = njd
    ntime = calcSunsetUTC(njulianday, nLatitude, nLongitude)
    Do While ntime = -999 And nBail < 367
        nBail = nBail + 1
        njulianday = njulianday + 1
        ntime = calcSunsetUTC(njulianday, nLatitude, nLongitude)
    Loop
    
    findNextSunset = njulianday

End Function

Public Function CalculateSun()
    Dim nLatitude As Double
    Dim nLongitude As Double
    nLatitude = m_nLatitude
    nLongitude = m_nLongitude

    If nLatitude >= -90 And nLatitude < -89.5 Then
        nLatitude = -89.5
    End If
    If nLatitude <= 90 And nLatitude > 89.8 Then
        nLatitude = 89.8
    End If

    Dim njd As Double
    Dim ndoy As Double
    Dim nt As Double
    
    njd = calcJD(Year(m_dateSel), Month(m_dateSel), Day(m_dateSel))
    ndoy = calcDayOfYear(Month(m_dateSel), Day(m_dateSel), _
        IsLeapYear(Year(m_dateSel)))
    nt = calcTimeJulianCent(njd)
    
    Dim nAlpha As Double
    Dim nTheta As Double
    Dim nEtime As Double
    
    nAlpha = calcSunRtAscension(nt)
    nTheta = calcSunDeclination(nt)
    nEtime = calcEquationOfTime(nt)
    
    Dim neqTime As Double
    Dim nsolarDec As Double
    
    neqTime = nEtime
    nsolarDec = nTheta
    
    'Calculate sunrise
    Dim bNoSunrise As Boolean
    bNoSunrise = False
    
    Dim nRiseTimeGMT As Double
    nRiseTimeGMT = calcSunriseUTC(njd, nLatitude, nLongitude)
    
    If nRiseTimeGMT = -999 Then
        bNoSunrise = True
    End If

    'Calculate sunset
    Dim bNoSunset As Boolean
    bNoSunset = False
    Dim nSetTimeGMT As Double
    nSetTimeGMT = calcSunsetUTC(njd, nLatitude, nLongitude)
    If nSetTimeGMT = -999 Then
        bNoSunset = True
    End If
    
    Dim ndaySavings As Double
    Dim nZone As Double
    
    ndaySavings = IIf(m_bDaySavings, 60, 0)
    nZone = m_nTimeZone
    If nZone > 12 Or nZone < -12.5 Then
        nZone = 0
    End If
    
    If Not bNoSunrise Then
        Dim nriseTimeLST As Double
        nriseTimeLST = nRiseTimeGMT - (60 * nZone) + ndaySavings
        
        m_dateSunrise = DateAdd("s", nriseTimeLST * 60, _
            Int(m_dateSel))
    End If

    If Not bNoSunset Then
        Dim nsetTimeLST As Double
        nsetTimeLST = nSetTimeGMT - (60 * nZone) + ndaySavings
        
        m_dateSunset = DateAdd("s", nsetTimeLST * 60, Int(m_dateSel))
    End If
    
    'Calculate solar noon for this date
    Dim nsolNoonGMT As Double
    Dim nsolNoonLST As Double
    nsolNoonGMT = calcSolNoonUTC(nt, nLongitude)
    nsolNoonLST = nsolNoonGMT - (60 * nZone) + ndaySavings
    
    m_dateNoon = DateAdd("s", nsolNoonLST * 60, Int(m_dateSel))

    Dim nnewjd As Double
    Dim nnewtime As Double

    If bNoSunrise Then
        If ((nLatitude > 66.4) And (ndoy > 79) And _
            (ndoy < 267)) Or ((nLatitude < -66.4) And _
            ((ndoy < 83) Or (ndoy > 236))) Then

            nnewjd = findRecentSunrise(njd, nLatitude, nLongitude)
            nnewtime = calcSunriseUTC(nnewjd, nLatitude, _
                nLongitude) - (60 * nZone) + ndaySavings

            If nnewtime > 1440 Then
                nnewtime = nnewtime - 1440
                nnewjd = nnewjd + 1
            End If
            If nnewtime < 0 Then
                nnewtime = nnewtime + 1440
                nnewjd = nnewjd - 1
            End If
            
            m_dateSunrise = DateAdd("s", nnewtime * 60, _
                Int(m_dateSel))
            m_dateSunrise = DateAdd("d", nnewjd - njd, m_dateSunrise)
        
        ElseIf ((nLatitude > 66.4) And ((ndoy < 83) Or _
            (ndoy > 263))) Or ((nLatitude < -66.4) And _
            (ndoy > 79) And (ndoy < 267)) Then

            nnewjd = findNextSunrise(njd, nLatitude, nLongitude)
            nnewtime = calcSunriseUTC(nnewjd, nLatitude, _
                nLongitude) - (60 * nZone) + ndaySavings
            If nnewtime > 1440 Then
                nnewtime = nnewtime - 1440
                nnewjd = nnewjd + 1
            End If
            If nnewtime < 0 Then
                nnewtime = nnewtime + 1440
                nnewjd = nnewjd - 1
            End If
            
            m_dateSunrise = DateAdd("s", nnewtime * 60, _
                Int(m_dateSel))
            m_dateSunrise = DateAdd("d", nnewjd - njd, m_dateSunrise)
        
        End If
    End If
    
    If bNoSunset Then
        If (((nLatitude > 66.4) And (ndoy > 79) And _
            (ndoy < 267)) Or ((nLatitude < -66.4) And _
            ((ndoy < 83) Or (ndoy > 263)))) Then

            nnewjd = findNextSunset(njd, nLatitude, nLongitude)
            nnewtime = calcSunsetUTC(nnewjd, nLatitude, _
                nLongitude) - (60 * nZone) + ndaySavings
            If nnewtime > 1440 Then
                nnewtime = nnewtime - 1440
                nnewjd = nnewjd + 1
            End If
            If nnewtime < 0 Then
                nnewtime = nnewtime + 1440
                nnewjd = nnewjd - 1
            End If

            m_dateSunset = DateAdd("s", nnewtime * 60, Int(m_dateSel))
            m_dateSunset = DateAdd("d", nnewjd - njd, m_dateSunset)

        ElseIf (((nLatitude > 66.4) And ((ndoy < 83) Or _
            (ndoy > 263))) Or ((nLatitude < -66.4) And _
            (ndoy > 79) And (ndoy < 267))) Then

            nnewjd = findRecentSunset(njd, nLatitude, nLongitude)
            nnewtime = calcSunsetUTC(nnewjd, nLatitude, _
                nLongitude) - (60 * nZone) + ndaySavings
            If nnewtime > 1440 Then
                nnewtime = nnewtime - 1440
                nnewjd = nnewjd + 1
            End If
            If nnewtime < 0 Then
                nnewtime = nnewtime + 1440
                nnewjd = nnewjd - 1
            End If

            m_dateSunset = DateAdd("s", nnewtime * 60, Int(m_dateSel))
            m_dateSunset = DateAdd("d", nnewjd - njd, m_dateSunset)

        End If
    End If
End Function

' ------ End of class clsSunRiseSet
 
Sample Usage:
 
    Dim srs As New clsSunRiseSet
    srs.City = "Dallas, TX"
    srs.CalculateSun
    Debug.Print srs.Sunrise, srs.Sunset