none
Sunrise/Sunset Times RRS feed

  • General discussion

  • All,

    Somewhere around the end of last year I posted something about getting solar times (sunrise/sunset) from an API. I have limited use for it and honestly I’ve not been paying attention, but at some point after I posted that, it stopped working correctly.

    I made it a priority to find and fix that this weekend before I did anything else. Unfortunately it’s not on my end; their API is returning erroneous information. Rather than going down that road, I decided that a better way is by eliminating the web-based API and perform the calculations directly.

    This has been done many times by many people over the years so what I’ll present here is based on one them as shown on Code Project here.

    That one is done using C# and he used framework 3.5 from VS 2008. Don’t let that concern you though – it will work and you don’t have to change anything!

    If you’ll read the article so that you know how it works (it’s pretty short) then download the his assembly and source code, you only need to compile it to a .dll and you can then use it the same as any other once you add a reference to it.

    When you open it in your version of Visual Studio you’ll be prompted to convert it. Note that it’s not changing the framework version and you don’t need it to; just let VS do what it does and when it’s done, look in Solution Explorer:

    The project named “Astronomy” is the one you want. He has it defaulted to build to the \debug folder and I changed it to the \release folder but other than that, I haven’t changed it one iota and it works great.

    If you’d prefer to “just download it” then I have my version of it on my site here:

    http://www.fls-online.com/VBNet_Forum/06-10-17/Astronomy.zip

    That’s the .dll file zipped up; nothing else.

    When you read his article, one part might be confusing which is this (shown in his example code):

     

    const int LongituteTimeZone = 15;

     

    The concept of “LongitudeTimeZone” is never really explained, but it’s this: He needs to know what world time zone to offset from and he’s using 15° longitude lines to do that. On your end, you need to choose the “zone” you’re interested in by selecting one of them.

    At first that seems confusing but it’s actually brilliant!

    Have a look at the following image and view it at 100%. You’ll see what I mean:

    Standard World Time Zone Map

    The only part left is a Boolean to indicate whether or not to offset the time in order to compensate for Daylight Saving time.

    As for accuracy, is it right? This is what I found by experimenting with a few values for lat/lon and date:

     

    • Use his for a given locale and date, write down the calculated sunrise and sunset times.
    • Now use NOAA information with the same data and write down those sunrise and sunset times.
    • Lastly, use US Naval Observatory info (they present it in tables) and write those down.

     

    At the end of it all you’ll have three distinctly different sets of times but they’re all *reasonably* close – within 15 minutes or so.

    My take on it all is that they’re all just calculations so in my mind, it’s as accurate as any.

     

    *****

    My Use Of It

    I have taken his assembly and embedded it with some “helper” classes to make things easier. I set this up primarily to make it more compatible with what I had (which I have working again now) and you may also find it to be easier on your end to use mine.

    I’ll emphasize that it’s his assembly that’s doing the work, not mine. Mine just uses his a little different way, but under the hood, I’m using his as he intended.

    Continuing, with mine I’ve added a twist: I don’t want to have to change “True” to “False” and back again every few month for being in or out of Daylight Savings. What I have will make that process automatic without the need to change and recompile.

    Also, I wanted to validate the latitude/longitude coordinates to ensure that they can exist. I’m not doing anything beyond making sure that the latitude is between -90° and +90°, inclusive and that the longitude is between -180° and +180°, inclusive.

    The class diagram is shown here and if you’d like to see the source code, I have that here.

    The first thing to notice is that my main “class” isn’t a class; it’s a structure. It suits my need but you might want to change yours.

    LatLon is a class though and here’s why: I want to ensure that you’ve actually instantiated it and not just using defaults. Testing for a lat/lon of 0,0 won’t work because it’s a legit coordinate. I don’t think anyone lives there:

    …but it’s still valid. By making it a class, I can check that it’s not null.

    As for the automatic changing of Daylight Savings time, for that I need an instance of the TimeZoneInfo and with that, I can then use the built-in method to tell me whether or not that time zone is currently in Daylight savings or not.

    The way that I’m doing that is that you give it the “name” which is either of these two possibilities:

    Display Name

    Standard Name

    Using one of those – and you tell it which it is – it will then take care of the rest.

    If you set up a program and you want your users to be able to chose, I have something in there to help with that:

     

        Private Sub _

            PopulateCombo(ByVal nameType As TimeZoneData.TimeZoneNameType)

     

            Using combo As New ComboBox

                Dim tz As New TimeZoneData

     

                With combo

                    .AutoCompleteMode = AutoCompleteMode.SuggestAppend

                    .AutoCompleteSource = AutoCompleteSource.ListItems

     

                    Select Case nameType

                        Case TimeZoneData.TimeZoneNameType.DisplayName

                            .Items.AddRange(tz.GetAllDisplayNames)

     

                        Case TimeZoneData.TimeZoneNameType.StandardName

                            .Items.AddRange(tz.GetAllStandardNames)

                    End Select

                End With

            End Using

     

        End Sub

     

    Following shows a typical use of it:

     

            Dim ll As New LatLon(32.711469, -79.952603)

     

            Dim st As New  _

                SolarTimes(ll, Today, "Eastern Standard Time", _

                           TimeZoneData.TimeZoneNameType.StandardName)

     

    The .dll for mine (zipped up) is here and I hope that you might find use from it.

    :-)


    "A problem well stated is a problem half solved.” - Charles F. Kettering


    • Edited by Frank L. Smith Wednesday, January 10, 2018 2:36 PM ...updated image URL's
    Saturday, June 10, 2017 5:13 PM

All replies

  • Frank,

    I think if you come within +-10 minutes of the "correct" time that is ok. 3 minutes would be good.

    Define sunset: horizon line at top or bottom of sun at sunset? Thats 2 mins.

    Some methods are more accurate and most easy ones only work well for viewer positions between lat +-50 deg or so and beyond the err gets larger. Also depends on the time of year how large the err is etc.

    You can also use datetime.IsDaylightSavingTime???

    Fun stuff!

    Saturday, June 10, 2017 7:38 PM
  • Frank,

    I think if you come within +-10 minutes of the "correct" time that is ok. 3 minutes would be good.

    Define sunset: horizon line at top or bottom of sun at sunset? Thats 2 mins.

    Some methods are more accurate and most easy ones only work well for viewer positions between lat +-50 deg or so and beyond the err gets larger. Also depends on the time of year how large the err is etc.

    You can also use datetime.IsDaylightSavingTime???

    Fun stuff!

    Define "Correct". ;-)

    *****

    The DateTime method still goes back to TimeZoneInfo and I'd rather it be set up so that it's not implicitly your own. For what I'm using mine for, it's not "here" - it's a good 600 miles from here.

    That's certainly a viable approach if you prefer it. :)


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Saturday, June 10, 2017 7:42 PM
  • Hi Tommy, 

    I live in the center of a city, I'm not one of those lucky persons who would beside on vacation ever see a sunrise/sunset. 

    I saw with last elections in the UK that even makes who you vote. Those who could see that vote Trump and May, persons like me had a different vote. 


    Success
    Cor

    Saturday, June 10, 2017 7:46 PM
  • Hi Tommy, 

    I live in the center of a city, I'm not one of those lucky persons who would beside on vacation ever see a sunrise/sunset. 

    I saw with last elections in the UK that even makes who you vote. Those who could see that vote Trump and May, persons like me had a different vote. 


    Success
    Cor

    I had to think about what you meant there. You mean you don't see the horizon.

    Well, here either for that matter. If I go outside, it's trees all around.

    I did see the horizon last week though. I went back to where I'm from: Five miles from the Atlantic Ocean. I wanted to go the beach one last time - so I did. You can see forever it seems. :)


    "A problem well stated is a problem half solved.” - Charles F. Kettering


    Saturday, June 10, 2017 7:58 PM
  • Define "Correct". ;-)

    *****

    The DateTime method still goes back to TimeZoneInfo and I'd rather it be set up so that it's not implicitly your own. For what I'm using mine for, it's not "here" - it's a good 600 miles from here.

    That's certainly a viable approach if you prefer it. :)


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Yeah thats what I mean "correct". However there is one more or less.

    Well I just mentioned the Daylight for something to say and now I see that's what you have in the routine. Oh, I see. You mean the datetime function is based on the users system location? And you want to enter others like Paris from your system in TN.

    Now you need a graphic showing how long before sunset.


    Saturday, June 10, 2017 8:02 PM
  • Hi Tommy, 

    I live in the center of a city, I'm not one of those lucky persons who would beside on vacation ever see a sunrise/sunset. 

    I saw with last elections in the UK that even makes who you vote. Those who could see that vote Trump and May, persons like me had a different vote. 


    Success
    Cor


    Cor,

    That is funny. I guess. Or not.

    Well you need to get your *)(% out in the country more Son!

    At least stand in the cracks at Noon and look West to get some rays.

    :)

    Saturday, June 10, 2017 8:03 PM

  • Now you need a graphic showing how long before sunset.


    Oh you mean with my fantastic graphics ability?

    LOL


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Saturday, June 10, 2017 8:07 PM
  • Tommy, 

    I just saw that yesterday, and really don't know why it is. 

    But at least, can I say, most persons in the city do probably not care at all about sunset, they just watch when their alarm clock rings. 

    However, when I was young, I lived from 40 meters from the beach where the sun went down, still would that it was like that. 



    Saturday, June 10, 2017 8:30 PM

  • Now you need a graphic showing how long before sunset.


    Oh you mean with my fantastic graphics ability?

    LOL


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Here you go. Just to be complete for future generations.  :)

    I am guessing you don't like geometry therefore drawing graphics?

    Imports System.Drawing.Drawing2D
    
    Public Class Form3
        Private WithEvents Picturebox1 As New PictureBox With {.Parent = Me,
            .Location = New Point(20, 20), .Size = New Size(120, 80)}
    
        Private Sub Picturebox1_Paint(sender As Object, e As PaintEventArgs) Handles Picturebox1.Paint
            Dim dt1 As New DateTime(2017, 6, 10, 5, 28, 0)
            Dim dt2 As New DateTime(2017, 6, 10, 19, 49, 0)
            Dim thisNow As New DateTime(2017, 6, 10, 12, 49, 0)
    
            e.Graphics.Clear(Color.White)
            DrawRiseSet(e.Graphics, New Rectangle(5, 5, 110, 90), dt1, dt2, thisNow)
    
        End Sub
    
        Private Sub DrawRiseSet(g As Graphics, rect As Rectangle, riseDateTime As DateTime, setDateTime As DateTime, thisNowTime As DateTime)
            Text = "Current Time: " & thisNowTime.ToShortTimeString
    
            With g
                .SmoothingMode = SmoothingMode.AntiAlias
                .Clear(Color.DimGray)
    
                'calc the arc angle to now based on the time between rise and set
                Dim diff1 As System.TimeSpan
                diff1 = setDateTime.Subtract(riseDateTime)
                Dim dt As Double = diff1.Hours + (diff1.Minutes / 60)
                diff1 = thisNowTime.Subtract(riseDateTime)
                Dim dtNow As Double = diff1.Hours + (diff1.Minutes / 60)
                Dim a As Single = CSng(140 * dtNow / dt)
                Dim r As Integer = CInt(0.4 * rect.Height)
    
                Using p As New Pen(Color.Goldenrod, CSng(r / 10)),
                        p1 As New Pen(Color.LightGoldenrodYellow, CSng(r / 5)),
                        br As New SolidBrush(Color.AntiqueWhite),
                        f2 As New Font("arial", CSng((96 / g.DpiX) * 0.25 * r))
    
                    .FillRectangle(Brushes.Black, rect)
    
                    .TranslateTransform(CSng(rect.X + (rect.Width / 2)), CSng(rect.Y + 0.55 * rect.Height))
    
                    .DrawArc(p, -r, -r, 2 * r, 2 * r, 200, 140)
    
                    If a > 0 And a < 140 Then
                        p1.Color = Color.LightGoldenrodYellow
                        p1.EndCap = LineCap.RoundAnchor
                        .DrawArc(p1, -r, -r, 2 * r, 2 * r, 200, a)
                    End If
    
                    'rise 
                    Dim x2 As Integer = CInt(-(rect.Width / 2) + (0.1 * r))
                    .DrawString(riseDateTime.ToShortTimeString, f2, br, x2, 0)
    
                    'set
                    x2 += CInt(rect.Width / 2)
                    .DrawString(setDateTime.ToShortTimeString, f2, br, x2, 0)
    
                End Using
            End With
        End Sub
    End Class

    Sunday, June 11, 2017 12:11 AM
  • I am guessing you don't like geometry therefore drawing graphics?

    It's not that at all - I just don't have an interest in graphics, honestly.

    The last time I "used" geometry was high school; the last time I studied it was in Analytic Geometry and Calculus in the second year of college. A whole semester of mindless "why am I doing this" gibberish, but I passed.

    I don't dislike it - I just don't use it much these days...


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Sunday, June 11, 2017 12:25 AM
  • Cool Frank :)

    Here's something else which might be useful in conjunction with your code... utilizing location services to get the current lat/lon of the computer.  The success depends on location services being enabled and the accuracy depends on hardware.  The service will use GPS if available on the device and fallback on IP info or a pre-set "home" location as necessary.  Of course the OS has to have a location service running.

    Here's some example code using System.Device (add a reference):

        Private geoWatcher As New Device.Location.GeoCoordinateWatcher()
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            If geoWatcher.TryStart(False, TimeSpan.FromMilliseconds(1000)) Then
                AddHandler geoWatcher.StatusChanged, AddressOf GeoWatcher_StatusChanged
            Else
                TextBox1.Text = "Unable to start location services"
            End If
        End Sub
    
        Private Sub GeoWatcher_StatusChanged(sender As Object, e As Device.Location.GeoPositionStatusChangedEventArgs)
            Select Case e.Status
                Case Device.Location.GeoPositionStatus.Disabled
                    TextBox1.Text = "Location service disabled."
                Case Device.Location.GeoPositionStatus.Initializing
                    TextBox1.Text = "Initializing location service..."
                Case Device.Location.GeoPositionStatus.NoData
                    TextBox1.Text = "Position Unknown"
                Case Device.Location.GeoPositionStatus.Ready
                    Dim loc = geoWatcher.Position
                    TextBox1.Text = $"LAT: {loc.Location.Latitude:n5}, LON: {loc.Location.Latitude:n5}"
            End Select
            RemoveHandler geoWatcher.StatusChanged, AddressOf GeoWatcher_StatusChanged
            geoWatcher.Dispose()
        End Sub
    There's also a newer API that's just for the newer Windows 10 service, but this was the easier example since the System.Device reference assembly was already installed with the framework on VS2015.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Monday, June 12, 2017 1:41 PM
    Moderator
  • Reed,

    Sorry for the delay.

    Thanks


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Monday, June 12, 2017 6:51 PM