sun_times v0.1.0

Simple Crystal library for calculating sunrise and sunset given latitude, longitude, and date

☀️ sun_times

Crystal CI GitHub release Docs License

A simple Crystal library for calculating 🌅 sunrise and 🌇 sunset given latitude, longitude, and date based on the NOAA Solar Calculator and formulas from Jean Meeus' Astronomical Algorithms (2nd Edition, 1998).

📊 Accuracy

The library's astronomical constants and algorithms follow authoritative sources (NOAA, JPL, Meeus) and were verified manually against the NOAA Solar Calculator (https://gml.noaa.gov/grad/solcalc/).

Verification

  • A validation script (samples/accuracy_check.cr) compares calculated sunrise, sunset and solar noon against NOAA's apparent times for several locations.
  • The comparison was performed manually by running the script and checking the NOAA site for reference values.

Results

  • Calculated times are within the margin of error and are effectively identical to NOAA's apparent times. Differences are typically under 1 minute.
  • Tested locations include New York, London, Tokyo and Sydney. Example: New York on 2025-11-05 matched NOAA apparent sunrise/sunset closely (06:31 / 16:47 local).

📦 Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      sun_times:
        github: geocrystal/sun_times
    
  2. Run shards install

💻 Usage

require "sun_times"

# Example: Lviv, Ukraine
# SunTime.new(latitude : Float64, longitude : Float64)
sun = SunTimes::SunTime.new(49.8419, 24.0311)
location = Time::Location.load("Europe/Kyiv")
date = Time.local

astronomical_dawn_time = sun.astronomical_dawn(date, location)
nautical_dawn_time = sun.nautical_dawn(date, location)
civil_dawn_time = sun.civil_dawn(date, location)
sunrise_time = sun.sunrise(date, location)
solar_noon_time = sun.solar_noon(date, location)
sunset_time = sun.sunset(date, location)
civil_dusk_time = sun.civil_dusk(date, location)
nautical_dusk_time = sun.nautical_dusk(date, location)
astronomical_dusk_time = sun.astronomical_dusk(date, location)
daylight_length = sun.daylight_length(date, location)

puts "Timezone: #{location}"
puts "Now:      #{date}"
puts
puts "=== Twilight Periods ==="
puts "Astronomical dawn:  #{astronomical_dawn_time}"
puts "Nautical dawn:      #{nautical_dawn_time}"
puts "Civil dawn:         #{civil_dawn_time}"
puts "Sunrise:            #{sunrise_time}"
puts "Solar noon:         #{solar_noon_time}"
puts "Sunset:             #{sunset_time}"
puts "Civil dusk:         #{civil_dusk_time}"
puts "Nautical dusk:      #{nautical_dusk_time}"
puts "Astronomical dusk:  #{astronomical_dusk_time}"
puts ""
puts "=== Daylight ==="
puts "Daylight:      #{daylight_length}"

Output:

Timezone: Europe/Kyiv
Now:      2025-11-05 14:05:23 +02:00

=== Twilight Periods ===
Astronomical dawn:  2025-11-05 05:30:09 +02:00
Nautical dawn:      2025-11-05 06:07:40 +02:00
Civil dawn:         2025-11-05 06:46:02 +02:00
Sunrise:            2025-11-05 07:20:19 +02:00
Solar noon:         2025-11-05 12:07:24 +02:00
Sunset:             2025-11-05 16:54:29 +02:00
Civil dusk:         2025-11-05 17:28:46 +02:00
Nautical dusk:      2025-11-05 18:07:08 +02:00
Astronomical dusk:  2025-11-05 18:44:39 +02:00

=== Daylight ===
Daylight:      9h 34m 9s

readme

You can also get all solar events as a NamedTuple using the events method, which is useful for serialization (e.g., JSON): 📋

require "json"
require "sun_times"

sun = SunTimes::SunTime.new(49.8419, 24.0311)
location = Time::Location.load("Europe/Kyiv")
date = Time.local

sun.events(date, location).to_json

Output

{
  "astronomical_dawn":"2025-11-05T05:30:09+02:00",
  "nautical_dawn":"2025-11-05T06:07:40+02:00",
  "civil_dawn":"2025-11-05T06:46:02+02:00",
  "sunrise":"2025-11-05T07:20:19+02:00",
  "solar_noon":"2025-11-05T12:07:24+02:00",
  "sunset":"2025-11-05T16:54:29+02:00",
  "civil_dusk":"2025-11-05T17:28:46+02:00",
  "nautical_dusk":"2025-11-05T18:07:08+02:00",
  "astronomical_dusk":"2025-11-05T18:44:39+02:00"
}

🌆 Twilight Periods

The library supports three types of twilight periods:

  • Civil twilight 🌅 (sun 6° below horizon): Enough light for most outdoor activities
  • Nautical twilight ⚓ (sun 12° below horizon): Horizon is still visible for navigation
  • Astronomical twilight 🔭 (sun 18° below horizon): Sky is dark enough for astronomical observations

image

🚀 Performance

The library is fast as the speed of light! ⚡ Individual calculations are sub-microsecond, and the events method that computes all solar events is still under 4 microseconds per call.

Benchmark results were obtained on Intel(R) Core(TM) i7-8550U (8) @ 4.00 GHz.

You can run the benchmark with:

crystal run --release samples/benchmark.cr

🤝 Contributing

  1. Fork it (https://github.com/geocrystal/sun_times/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

👥 Contributors

📄 License

This library is distributed under the MIT license. Please see the LICENSE file.

Repository

sun_times

Owner
Statistic
  • 1
  • 0
  • 0
  • 1
  • 0
  • 7 days ago
  • November 4, 2025
License

MIT License

Links
Synced at

Tue, 18 Nov 2025 01:18:43 GMT

Languages