SolCalc 1.0.0

dotnet add package SolCalc --version 1.0.0
NuGet\Install-Package SolCalc -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SolCalc" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SolCalc --version 1.0.0
#r "nuget: SolCalc, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SolCalc as a Cake Addin
#addin nuget:?package=SolCalc&version=1.0.0

// Install SolCalc as a Cake Tool
#tool nuget:?package=SolCalc&version=1.0.0

☀ SolCalc

NuGet GitHub Workflow Status Testspace Coveralls

Find when sunrise, sunset, and different twilights happen for a given location, based on the NOAA ESRL Solar Calculator.

Features high accuracy across several millenia, atmospheric refraction, a simple enumeration-based API, and multiple/missing events during polar night/day/twilight at extreme latitudes.

  1. Prerequisites
  2. Installation
  3. Quick Start
  4. Concepts
  5. Usage
  6. Algorithm

Sun

Prerequisites

  • .NET runtime that conforms to .NET Standard 2.0 or later
    • .NET 5 or later
    • .NET Core 2.0 or later
    • .NET Framework 4.6.1 or later

Installation

The SolCalc package is available on NuGet Gallery.

dotnet add package SolCalc

Quick Start

using NodaTime;
using NodaTime.Extensions;
using SolCalc;
using SolCalc.Data;

ZonedDateTime now = SystemClock.Instance.InZone(DateTimeZoneProviders.Tzdb["America/Los_Angeles"]).GetCurrentZonedDateTime();
const double  lat = 37.35, lon = -121.95;

SunlightLevel currentSunlight = SunlightCalculator.GetSunlightAt(now, lat, lon);
SunlightChange nextSunrise = SunlightCalculator.GetSunlightChanges(now, lat, lon)
    .First(change => change.Name == SolarTimeOfDay.Sunrise);

Console.WriteLine($"It is currently {currentSunlight} in Santa Clara, CA, US.");
Console.WriteLine($"The next sunrise will be {nextSunrise.Time:l<F> x}.");
// It is currently AstronomicalTwilight in Santa Clara, CA, US.
// The next sunrise will be Tuesday, April 23, 2024 6:23:01 am PDT.

Concepts

For more information and diagrams, see "Twilight" on Wikipedia.

Sunlight levels

Quantized levels of sunlight brightness based on the sun's angle above the horizon at 0°. These represent durations from one solar time of day to the next. Each twilight generally occurs twice per day.

Level Solar elevation range Description
Daylight [0°, 90°] Sun is visible
Civil twilight [−6°, 0°) Objects are visible
Nautical twilight [−12°, −6°) Silhouettes are visible
Astronomical twilight [−18°, −12°) Sunlight is only detectable with instruments
Night [−90°, −18°) No sunlight

Solar times of day

Instants in a day when the sunlight level changes from one level to another.

Time of day Sun direction Previous light level New light level Solar elevation
Astronomical dawn Rising Night Astronomical twilight −18°
Nautical dawn Rising Astronomical twilight Nautical twilight −12°
Civil dawn Rising Nautical twilight Civil twilight −6°
Sunrise Rising Civil twilight Daylight
Sunset Setting Daylight Civil twilight
Civil dusk Setting Civil twilight Nautical twilight −6°
Nautical dusk Setting Nautical twilight Astronomical twilight −12°
Astronomical dusk Setting Astronomical twilight Night −18°

Usage

The main entry point into this library is the SolCalc.SunlightCalculator static class.

Get the sunlight level at a time and location

Use SunlightCalculator.GetSunlightAt(ZonedDateTime, double, double) to return the sunlight level at the given geographic coordinates at the given instant.

The instant's time zone must be the same zone that the given location observes, otherwise the result will be wrong. In the example below, Santa Clara, CA is in the America/Los_Angeles time zone.

using NodaTime;
using NodaTime.Extensions;
using SolCalc;
using SolCalc.Data;

DateTimeZone  zone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];
ZonedDateTime now  = SystemClock.Instance.InZone(zone).GetCurrentZonedDateTime();

SunlightLevel level = SunlightCalculator.GetSunlightAt(now, latitude: 37.35, longitude: -121.95);
Console.WriteLine($"It is currently {level} in Santa Clara, CA, US.");
// It is currently Night in Santa Clara, CA, US.

Get the series of sunlight level changes at a location starting after a time

Use SunlightCalculator.GetSunlightChanges(ZonedDateTime, double, double) to get an infinite series of all future sunlight level changes at a particular location, starting after a given time.

The instant's time zone must be the same zone that the given location observes, otherwise the result will be wrong. In the example below, Santa Clara, CA is in the America/Los_Angeles time zone.

The returned IEnumerable<SunlightChange> is infinitely long because the sun will always rise again. It is not bounded by the end of the day. This means you should not try to call .ToList(), .Count(), or any other method that fully enumerates it, because they will never end. Instead, use filtering to get just the items you want, using methods like .TakeWhile(), .SkipWhile(), .Where(), and .First().

Get the next sunlight level change at a location
DateTimeZone  zone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];
ZonedDateTime now  = SystemClock.Instance.InZone(zone).GetCurrentZonedDateTime();

SunlightChange nextChange = SunlightCalculator.GetSunlightChanges(now, latitude: 37.35, longitude: -121.95).First();
Console.WriteLine($"At {nextChange.Time}, {nextChange.Name} will start when {nextChange.PreviousSunlightLevel} changes to {nextChange.NewSunlightLevel} in Santa Clara, CA, US.");
// At 2024-04-22T04:48:01 America/Los_Angeles (-07), AstronomicalDawn will start when Night changes to AstronomicalTwilight in Santa Clara, CA, US.
Get sunrise and sunset on a date at a location
DateTimeZone  zone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];
ZonedDateTime now  = SystemClock.Instance.InZone(zone).GetCurrentZonedDateTime();
IEnumerable<SunlightChange> dailySunriseAndSunset = SunlightCalculator.GetSunlightChanges(now, 37.35, -121.95)
    .TakeWhile(s => s.Time.Date == now.Date).ToList();

ZonedDateTime? sunrise = dailySunriseAndSunset.FirstOrNull(s => s.Name == SolarTimeOfDay.Sunrise)?.Time;
ZonedDateTime? sunset  = dailySunriseAndSunset.FirstOrNull(s => s.Name == SolarTimeOfDay.Sunset)?.Time;
Wait for each sunlight level change
DateTimeZone zone  = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];
ZonedClock   clock = SystemClock.Instance.InZone(zone);

IEnumerable<SunlightChange> sunlightChanges = SunlightCalculator.GetSunlightChanges(clock.GetCurrentZonedDateTime(), 37.35, -121.95);
foreach (SunlightChange sunlightChange in sunlightChanges) {
    Duration delay = sunlightChange.Time.Minus(clock.GetCurrentZonedDateTime());
    Console.WriteLine($"Waiting {delay} for {sunlightChange.Name} at {sunlightChange.Time}");
    await Task.Delay(delay.ToTimeSpan());

    Console.WriteLine($"It is currently {sunlightChange.Name} at {clock.GetCurrentZonedDateTime()} in Santa Clara, CA, US");
}

Algorithm

This library uses the solar position calculation algorithm from the National Oceanic and Atmospheric Administration's Earth System Research Laboratories' Global Monitoring Laboratory's web-based Solar Calculator.

[!WARNING]
For research and recreational use only. Not for legal use.

If you want to calculate solar position or solar noon, you can use the SolarCalculator static class.

Accuracy

  • Time accuracy decreases from ≤ ±1 minute at latitudes ≤ ±72°, to ≤ ±10 minutes at latitudes > ±72°.
  • Atmospheric refraction is taken in account.
  • Clouds, air pressure, humidity, dust, other atmospheric conditions, observer's altitude, and solar eclipses are not taken into account.
  • Years between −2000 and 3000 can be handled.
  • Dates before October 15, 1582 might not use the correct calendar system.
  • Years between 1800 and 2100 have the highest accuracy results. Years between −1000 and 3000 have medium accuracy results. Years outside those ranges have lower accuracy results.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 87 4/23/2024
0.0.0-SNAPSHOT1 65 4/21/2024