solar

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 6 Imported by: 0

README

go-solar ☀️

CI CodeQL Go Report Card GoDoc Go Version Release License

A modern, well-tested Go package for calculating sunrise, sunset, and solar elevation at any location on Earth. Uses the sunrise equation method with support for edge cases like polar nights and midnight sun.

✨ Features

  • 🌅 Calculate sunrise and sunset times for any location
  • 🌄 Calculate dawn and dusk with civil, nautical, and astronomical twilight
  • 📐 Determine solar elevation and azimuth angles
  • 🧭 Calculate solar azimuth (compass direction of the sun)
  • 🛰️ Parse NMEA GPS sentences (GGA, RMC) for location-based calculations
  • 🌍 Handle edge cases (polar night, midnight sun)
  • 🚀 High performance with zero allocations for core functions
  • ✅ 94%+ test coverage on production code
  • 🔧 Generic helper functions (Go 1.18+)
  • 📚 Comprehensive documentation and examples

Forked from Nathan Osman's package go-sunrise with modern enhancements.

📦 Installation

go get github.com/mstephenholl/go-solar

Requirements: Go 1.21 or later

🚀 Quick Start

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/mstephenholl/go-solar"
)

func main() {
    // Create location for Toronto
    loc := solar.NewLocation(43.65, -79.38)

    // Create time for January 1, 2000
    t := solar.NewTime(2000, time.January, 1)

    // Calculate sunrise
    sunrise, err := solar.Sunrise(loc, t)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Sunrise: %s\n", sunrise.Format("15:04:05 MST"))
    // Output: Sunrise: 12:51:00 UTC

    // Calculate sunset
    sunset, err := solar.Sunset(loc, t)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Sunset: %s\n", sunset.Format("15:04:05 MST"))
    // Output: Sunset: 21:50:36 UTC

    // Or get both at once (more efficient)
    sunrise, sunset, err = solar.SunriseSunset(loc, t)
    if err != nil {
        log.Fatal(err)
    }
}

📖 Usage Examples

Creating Locations and Times
import "github.com/mstephenholl/go-solar"

// Create a location from latitude and longitude
loc := solar.NewLocation(40.7128, -74.0060) // New York City

// Create a time from date components
t := solar.NewTime(2024, time.June, 21)

// Or create from a time.Time object
now := time.Now()
t := solar.NewTimeFromDateTime(now)
Individual Sunrise or Sunset
// Create location and time
loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)

// Get just sunrise
sunrise, err := solar.Sunrise(loc, t)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Sunrise: %s\n", sunrise.Format("15:04 MST"))

// Get just sunset
sunset, err := solar.Sunset(loc, t)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Sunset: %s\n", sunset.Format("15:04 MST"))
Both Sunrise and Sunset
loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)

// Calculate both at once (more efficient)
sunrise, sunset, err := solar.SunriseSunset(loc, t)
if err != nil {
    // Handle polar region cases
    if err == solar.ErrSunNeverRises {
        fmt.Println("Polar night - sun never rises on this day")
    } else if err == solar.ErrSunNeverSets {
        fmt.Println("Midnight sun - sun never sets on this day")
    }
    return
}
fmt.Printf("Sunrise: %s, Sunset: %s\n", sunrise.Format("15:04"), sunset.Format("15:04"))
Solar Elevation Angle
// Get sun's elevation at a specific time
loc := solar.NewLocation(40.7128, -74.0060)
when := time.Date(2024, time.June, 21, 12, 0, 0, 0, time.UTC)
elevation := solar.Elevation(loc, when)
fmt.Printf("Sun elevation: %.2f degrees\n", elevation)
Solar Azimuth Angle
// Get sun's azimuth (compass direction) at a specific time
// Azimuth: 0° = North, 90° = East, 180° = South, 270° = West
loc := solar.NewLocation(40.7128, -74.0060)
when := time.Date(2024, time.June, 21, 12, 0, 0, 0, time.UTC)
azimuth := solar.Azimuth(loc, when)
fmt.Printf("Sun azimuth: %.2f degrees\n", azimuth)
Dawn and Dusk (Twilight Times)

Calculate dawn and dusk using civil, nautical, or astronomical twilight definitions:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)

// Calculate civil dawn and dusk (default, -6° sun angle)
dawn, dusk := solar.DawnDusk(loc, t)
fmt.Printf("Dawn: %s, Dusk: %s\n", dawn.Format("15:04"), dusk.Format("15:04"))

// Or get them individually
dawn := solar.Dawn(loc, t)
dusk := solar.Dusk(loc, t)

// Nautical twilight (-12° sun angle, for marine navigation)
dawn, dusk := solar.DawnDusk(loc, t, solar.Nautical)

// Astronomical twilight (-18° sun angle, for astronomy)
dawn, dusk := solar.DawnDusk(loc, t, solar.Astronomical)

Twilight Types:

  • Civil (-6°): Default. Enough light for outdoor activities without artificial lighting
  • Nautical (-12°): Horizon visible at sea for navigation, general ground outlines visible
  • Astronomical (-18°): Sky dark enough for astronomical observations
Custom Elevation Times
loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)

// Find when sun reaches a specific elevation (e.g., golden hour at 6°)
morning, evening := solar.TimeOfElevation(loc, 6.0, t)
fmt.Printf("Golden hour: %s to %s\n", morning.Format("15:04"), evening.Format("15:04"))
Working with NMEA GPS Sentences

The package supports parsing location and time data from NMEA GPS sentences, which you can then use with any solar calculation function.

Supported NMEA sentence types:

  • RMC (Recommended Minimum): Includes date, no external date needed
  • GGA (GPS Fix Data): Requires external date parameters

Features:

  • Automatic checksum validation
  • Supports both hemispheres (N/S, E/W)
  • Handles 2-digit year conversion (00-49 → 2000-2049, 50-99 → 1950-1999)
  • Detailed error messages for debugging
Parsing NMEA Sentences
// Using RMC sentence (includes date)
nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*71"

// Parse location from NMEA
loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0)  // For RMC, date params ignored
if err != nil {
    log.Fatal(err)
}

// Parse time from NMEA
t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)  // For RMC, date params ignored
if err != nil {
    log.Fatal(err)
}

// Using GGA sentence (requires external date)
nmea = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*5C"
loc, err = solar.NewLocationFromNMEA(nmea, 2024, time.June, 21)  // Must provide date for GGA
if err != nil {
    log.Fatal(err)
}
t, err = solar.NewTimeFromNMEA(nmea, 2024, time.June, 21)
if err != nil {
    log.Fatal(err)
}
Using NMEA Data with Solar Functions

Once you've parsed the NMEA sentence into Location and Time, you can use them with any solar calculation function:

nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*71"

// Parse NMEA once
loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0)
if err != nil {
    log.Fatal(err)
}
t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)
if err != nil {
    log.Fatal(err)
}

// Use with any solar function
sunrise, err := solar.Sunrise(loc, t)
sunset, err := solar.Sunset(loc, t)
noon := solar.MeanSolarNoon(loc, t)
dawn := solar.Dawn(loc, t, solar.Civil)
dusk := solar.Dusk(loc, t, solar.Nautical)
azimuth := solar.Azimuth(loc, t.DateTime())
elevation := solar.Elevation(loc, t.DateTime())

// For time-sensitive calculations (azimuth, elevation),
// use time.Now() or the GPS time from NMEA
currentAzimuth := solar.Azimuth(loc, time.Now())

Benefits of this approach:

  • Parse NMEA sentence once, use with multiple calculations
  • Compose any combination of Location/Time sources
  • Clearer separation between parsing and calculation logic
  • More flexible for complex GPS data processing workflows
Using Generic Helpers
// Generic absolute value - works with any signed type
fmt.Println(solar.Abs(-42))      // int: 42
fmt.Println(solar.Abs(-3.14))    // float64: 3.14

// Floating-point comparison with tolerance
if solar.AlmostEqual(1.0, 1.00001, 0.001) {
    fmt.Println("Values are approximately equal")
}

🧪 Development

Running Tests
# Run all tests
make test

# Run with coverage
make coverage

# Run benchmarks
make test-bench

# Run all CI checks
make ci
Code Quality
# Format code
make fmt

# Run linters
make lint

# Security scan
make security

📊 Performance

All generic helper functions are highly optimized:

BenchmarkAbs-11          1000000000   0.25 ns/op   0 B/op   0 allocs/op
BenchmarkAlmostEqual-11  1000000000   0.25 ns/op   0 B/op   0 allocs/op
BenchmarkMin-11          1000000000   0.25 ns/op   0 B/op   0 allocs/op
BenchmarkMax-11          1000000000   0.25 ns/op   0 B/op   0 allocs/op

🚀 Releases

This project uses automated releases with Calendar Versioning (CalVer).

Version Format

Releases follow the CalVer pattern: YYYY.MM.MICRO

  • YYYY - Full year (e.g., 2025)
  • MM - Zero-padded month (01-12)
  • MICRO - Incrementing number for releases within the same month (0, 1, 2, ...)

Examples:

  • v2025.10.0 - First release in October 2025
  • v2025.10.1 - Second release in October 2025
  • v2025.11.0 - First release in November 2025

Why CalVer? This format provides clear, chronological versioning that makes it easy to understand when a release was created. Similar to Ubuntu's versioning scheme (e.g., 22.04, 24.04).

Automated Release Process

Every successful push to the master branch automatically triggers:

  1. CI Quality Gates (must pass before release):

    • ✅ Runs full test suite across multiple OS and Go versions
    • ✅ Runs linter checks (golangci-lint)
    • ✅ Runs security scans (gosec, govulncheck)
    • ✅ Builds the package
    • ✅ Runs benchmarks
  2. Release Creation (only if CI passes):

    • 🏷️ Creates a CalVer git tag (YYYY.MM.MICRO)
    • 📝 Generates categorized changelog from commits
    • 🎉 Creates GitHub release with notes

Quality First: Releases are only created when all CI quality gates pass successfully.

Changelog

Each release includes:

  • Categorized commits (Features, Enhancements, Bug Fixes, etc.)
  • Installation instructions
  • Links to documentation and full changelog
Installing a Specific Version
# Latest release
go get github.com/mstephenholl/go-solar

# Specific version (CalVer format)
go get github.com/mstephenholl/go-solar@v2025.10.0

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

📝 License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

🙏 Acknowledgments

  • Original go-sunrise package by Nathan Osman
  • Based on the sunrise equation algorithm
  • Modernized with Go 1.18+ generics and comprehensive testing

📚 Documentation

Full documentation is available at pkg.go.dev.

🐛 Issues & Support

Documentation

Overview

Package solar provides functions for calculating sunrise, sunset, and solar position for any location on Earth. It uses astronomical algorithms to compute solar elevation angles and times when the sun crosses specific elevation thresholds.

Index

Examples

Constants

View Source
const (
	// Degree provides a precise fraction for converting between degrees and
	// radians.
	Degree = math.Pi / 180

	// J2000 is the Julian date for January 1, 2000, 12:00:00 TT.
	// This is the standard epoch used in modern astronomical calculations.
	J2000 = 2451545

	// SolarMeanAnomalyBase is the mean anomaly of the sun at J2000 epoch (in degrees).
	// This represents the angular distance from perihelion at the reference epoch.
	SolarMeanAnomalyBase = 357.5291

	// SolarMeanAnomalyRate is the daily change in mean anomaly (degrees per day).
	// This accounts for Earth's orbital motion around the sun.
	SolarMeanAnomalyRate = 0.98560028

	// FullCircleDegrees represents a complete rotation in degrees.
	FullCircleDegrees = 360.0

	// HalfCircleDegrees represents a semicircle in degrees.
	HalfCircleDegrees = 180.0

	// EquationOfCenterC1 is the first coefficient in the equation of center calculation.
	// This is the dominant term in the Earth's orbital eccentricity correction.
	EquationOfCenterC1 = 1.9148

	// EquationOfCenterC2 is the second coefficient in the equation of center calculation.
	// This accounts for higher-order orbital perturbations.
	EquationOfCenterC2 = 0.0200

	// EquationOfCenterC3 is the third coefficient in the equation of center calculation.
	// This is a small correction term for orbital accuracy.
	EquationOfCenterC3 = 0.0003

	// SinDeclinationCoefficient is sin(ε) where ε is Earth's axial tilt (obliquity).
	// Earth's axial tilt is approximately 23.44°, and sin(23.44°) ≈ 0.39779
	SinDeclinationCoefficient = 0.39779

	// EquationOfTimeC1 is the first coefficient in the equation of time calculation.
	// This accounts for the eccentricity of Earth's orbit.
	EquationOfTimeC1 = 0.0053

	// EquationOfTimeC2 is the second coefficient in the equation of time calculation.
	// This accounts for the obliquity of the ecliptic.
	EquationOfTimeC2 = 0.0069

	// SunriseCorrectionAngle is the correction angle for sunrise/sunset calculations.
	// This is precisely -0.833° (-50 arc minutes), accounting for:
	// - Atmospheric refraction (34 arc minutes)
	// - Sun's angular diameter (16 arc minutes)
	// Calculated as: -0.833 * (π/180) = -0.0145385927 radians
	SunriseCorrectionAngle = -0.0145385927

	// PerihelionBase is the argument of perihelion at J2000 epoch (in degrees).
	// This is the angle from the vernal equinox to perihelion.
	PerihelionBase = 102.93005

	// PerihelionRate is the rate of change of perihelion per Julian century.
	// This accounts for the precession of Earth's orbit.
	PerihelionRate = 0.3179526

	// JulianCenturyDays is the number of days in a Julian century.
	JulianCenturyDays = 36525.0

	// LongitudeDivisor is used in mean solar noon calculation.
	// Dividing longitude by 360 converts it to a fractional day offset.
	LongitudeDivisor = 360.0

	// CivilTwilightAngle is the solar elevation angle for civil twilight (-6°).
	// Civil twilight occurs when the sun is between the horizon and 6° below it.
	// During civil twilight, there is enough natural light for most outdoor activities
	// without artificial lighting. This is the most commonly used definition for
	// dawn and dusk in everyday contexts.
	CivilTwilightAngle = -6.0

	// NauticalTwilightAngle is the solar elevation angle for nautical twilight (-12°).
	// Nautical twilight occurs when the sun is between 6° and 12° below the horizon.
	// During nautical twilight, the horizon is still visible at sea, allowing sailors
	// to take reliable readings using a marine sextant. The general outline of ground
	// objects may be distinguishable, but detailed outdoor work is difficult.
	NauticalTwilightAngle = -12.0

	// AstronomicalTwilightAngle is the solar elevation angle for astronomical twilight (-18°).
	// Astronomical twilight occurs when the sun is between 12° and 18° below the horizon.
	// During astronomical twilight, the sky is dark enough for most astronomical
	// observations of point sources of light such as stars, though the Sun's light
	// may still interfere with observations of extremely faint objects.
	AstronomicalTwilightAngle = -18.0
)
View Source
const DefaultPlaces = 5

DefaultPlaces specifies the default precision for rounding.

Variables

View Source
var (
	// ErrInvalidNMEA is returned when the NMEA sentence is malformed or unsupported.
	ErrInvalidNMEA = errors.New("invalid NMEA sentence")

	// ErrInvalidChecksum is returned when the NMEA checksum validation fails.
	ErrInvalidChecksum = errors.New("invalid NMEA checksum")

	// ErrUnsupportedSentence is returned when the NMEA sentence type is not supported.
	ErrUnsupportedSentence = errors.New("unsupported NMEA sentence type")

	// ErrInvalidPosition is returned when latitude or longitude cannot be parsed.
	ErrInvalidPosition = errors.New("invalid position data")

	// ErrInvalidDate is returned when date/time cannot be parsed from NMEA sentence.
	ErrInvalidDate = errors.New("invalid date/time data")
)
View Source
var (
	// ErrSunNeverRises is returned when the sun never rises at the given location and date (polar night).
	ErrSunNeverRises = errors.New("sun never rises at this location on this date")
	// ErrSunNeverSets is returned when the sun never sets at the given location and date (midnight sun).
	ErrSunNeverSets = errors.New("sun never sets at this location on this date")
)

Functions

func Abs

func Abs[T Signed](x T) T

Abs returns the absolute value of x for any numeric type. This is a generic replacement for type-specific abs functions.

Example:

Abs(-5)     // returns 5 (int)
Abs(-5.5)   // returns 5.5 (float64)
Abs(int64(-10)) // returns 10 (int64)
Example

ExampleAbs demonstrates the generic Abs function with different types.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Works with int
	fmt.Println(solar.Abs(-5))

	// Works with int64
	fmt.Println(solar.Abs(int64(-100)))

	// Works with float64
	fmt.Println(solar.Abs(-3.14))

}
Output:
5
100
3.14

func AlmostEqual

func AlmostEqual[T Float](a, b, tolerance T) bool

AlmostEqual checks if two floating-point numbers are within a given tolerance. This is useful for comparing floating-point results where exact equality is not possible due to rounding errors.

Example:

AlmostEqual(1.0, 1.00001, 0.001)  // true
AlmostEqual(1.0, 1.1, 0.001)      // false
Example

ExampleAlmostEqual demonstrates floating-point comparison with tolerance.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	a := 1.0
	b := 1.00001

	// These are almost equal within tolerance
	fmt.Println(solar.AlmostEqual(a, b, 0.001))

	// But not with stricter tolerance
	fmt.Println(solar.AlmostEqual(a, b, 0.00001))

}
Output:
true
false

func Azimuth

func Azimuth(loc Location, when time.Time) float64

Azimuth calculates the solar azimuth angle at a specific time and location. The azimuth is the sun's compass direction, measured clockwise from true north.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • when: The datetime at which to calculate the azimuth (use time.Date, time.Now(), or Time.DateTime())

Returns:

  • The solar azimuth angle in degrees (0° = North, 90° = East, 180° = South, 270° = West)

The calculation uses the solar hour angle and declination to determine the sun's position in the sky. The azimuth is measured clockwise from north, ranging from 0° to 360°.

Note: All calculations assume UTC time. Ensure the input time is in UTC timezone.

Example:

loc := solar.NewLocation(43.65, -79.38)
when := time.Date(2000, time.January, 1, 17, 0, 0, 0, time.UTC)
azimuth := solar.Azimuth(loc, when)
// azimuth is in degrees: 0°=North, 90°=East, 180°=South, 270°=West
Example

ExampleAzimuth demonstrates calculating the solar azimuth angle. The azimuth is the sun's compass direction measured clockwise from north.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for Toronto
	loc := solar.NewLocation(43.65, -79.38)

	// Calculate azimuth for January 1, 2000 at 5:00 PM UTC (noon local time)
	when := time.Date(2000, time.January, 1, 17, 0, 0, 0, time.UTC)
	azimuth := solar.Azimuth(loc, when)

	fmt.Printf("Azimuth: %.1f degrees (South)\n", azimuth)
}
Output:
Azimuth: 174.8 degrees (South)

func Dawn

func Dawn(loc Location, t Time, twilightType ...TwilightType) time.Time

Dawn calculates the dawn time for a given location and date. Dawn is the beginning of morning twilight, when the sun reaches the specified angle below the horizon and natural light begins to appear.

By default, civil twilight (-6°) is used, which is the most common definition of dawn in everyday contexts. You can optionally specify Nautical or Astronomical twilight types for specialized applications.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()
  • twilightType: Optional twilight type (Civil, Nautical, or Astronomical). Defaults to Civil.

Returns:

  • Dawn time in UTC (time.Time{} if the sun never reaches the twilight angle on this day)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
// Calculate civil dawn (default)
dawn := solar.Dawn(loc, t)

// Calculate nautical dawn
dawn := solar.Dawn(loc, t, solar.Nautical)

// Calculate astronomical dawn
dawn := solar.Dawn(loc, t, solar.Astronomical)
Example

ExampleDawn demonstrates calculating civil dawn (beginning of morning twilight).

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Toronto coordinates
	loc := solar.NewLocation(43.65, -79.38)
	t := solar.NewTime(2000, time.January, 1)

	// Calculate civil dawn (default)
	dawn := solar.Dawn(loc, t)

	fmt.Printf("Civil dawn: %s\n", dawn.Format("15:04 MST"))
}
Output:
Civil dawn: 12:18 UTC
Example (Nautical)

ExampleDawn_nautical demonstrates calculating nautical dawn.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Toronto coordinates
	loc := solar.NewLocation(43.65, -79.38)
	t := solar.NewTime(2000, time.January, 1)

	// Calculate nautical dawn (sun at -12° below horizon)
	nauticalDawn := solar.Dawn(loc, t, solar.Nautical)

	fmt.Printf("Nautical dawn: %s\n", nauticalDawn.Format("15:04 MST"))
}
Output:
Nautical dawn: 11:42 UTC

func DawnDusk

func DawnDusk(loc Location, t Time, twilightType ...TwilightType) (dawn, dusk time.Time)

DawnDusk calculates both dawn and dusk times for a given location and date. This is more efficient than calling Dawn() and Dusk() separately.

By default, civil twilight (-6°) is used. You can optionally specify Nautical or Astronomical twilight types for specialized applications.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()
  • twilightType: Optional twilight type (Civil, Nautical, or Astronomical). Defaults to Civil.

Returns:

  • dawn: Dawn time in UTC (time.Time{} if never occurs)
  • dusk: Dusk time in UTC (time.Time{} if never occurs)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
// Calculate civil dawn and dusk (default)
dawn, dusk := solar.DawnDusk(loc, t)

// Calculate nautical dawn and dusk
dawn, dusk := solar.DawnDusk(loc, t, solar.Nautical)
Example

ExampleDawnDusk demonstrates calculating both dawn and dusk times.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Toronto coordinates
	loc := solar.NewLocation(43.65, -79.38)
	t := solar.NewTime(2000, time.January, 1)

	// Calculate civil dawn and dusk
	dawn, dusk := solar.DawnDusk(loc, t)

	fmt.Printf("Dawn: %s\n", dawn.Format("15:04 MST"))
	fmt.Printf("Dusk: %s\n", dusk.Format("15:04 MST"))
}
Output:
Dawn: 12:18 UTC
Dusk: 22:23 UTC
Example (Astronomical)

ExampleDawnDusk_astronomical demonstrates calculating astronomical twilight.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Toronto coordinates
	loc := solar.NewLocation(43.65, -79.38)
	t := solar.NewTime(2000, time.January, 1)

	// Calculate astronomical dawn and dusk (sun at -18° below horizon)
	dawn, dusk := solar.DawnDusk(loc, t, solar.Astronomical)

	fmt.Printf("Astronomical dawn: %s\n", dawn.Format("15:04 MST"))
	fmt.Printf("Astronomical dusk: %s\n", dusk.Format("15:04 MST"))
}
Output:
Astronomical dawn: 11:07 UTC
Astronomical dusk: 23:34 UTC

func Dusk

func Dusk(loc Location, t Time, twilightType ...TwilightType) time.Time

Dusk calculates the dusk time for a given location and date. Dusk is the end of evening twilight, when the sun reaches the specified angle below the horizon and natural light fades to darkness.

By default, civil twilight (-6°) is used, which is the most common definition of dusk in everyday contexts. You can optionally specify Nautical or Astronomical twilight types for specialized applications.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()
  • twilightType: Optional twilight type (Civil, Nautical, or Astronomical). Defaults to Civil.

Returns:

  • Dusk time in UTC (time.Time{} if the sun never reaches the twilight angle on this day)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
// Calculate civil dusk (default)
dusk := solar.Dusk(loc, t)

// Calculate nautical dusk
dusk := solar.Dusk(loc, t, solar.Nautical)

// Calculate astronomical dusk
dusk := solar.Dusk(loc, t, solar.Astronomical)
Example

ExampleDusk demonstrates calculating civil dusk (end of evening twilight).

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Toronto coordinates
	loc := solar.NewLocation(43.65, -79.38)
	t := solar.NewTime(2000, time.January, 1)

	// Calculate civil dusk (default)
	dusk := solar.Dusk(loc, t)

	fmt.Printf("Civil dusk: %s\n", dusk.Format("15:04 MST"))
}
Output:
Civil dusk: 22:23 UTC

func Elevation

func Elevation(loc Location, when time.Time) float64

Elevation calculates the angle of the sun above the horizon at a given moment at the specified location.

The time parameter should be in UTC. If you have a local time, convert it to UTC first using time.UTC() or time.In(time.UTC).

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • when: The moment in time to calculate elevation (in UTC)

Returns:

  • Solar elevation angle in degrees (positive above horizon, negative below)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
elevation := solar.Elevation(loc, time.Now().UTC())
// elevation is in degrees, positive above horizon, negative below
Example

ExampleElevation demonstrates calculating the sun's elevation angle at a specific time and location.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for New York City
	loc := solar.NewLocation(40.7128, -74.0060)

	// Check sun elevation at noon UTC on summer solstice
	when := time.Date(2022, time.June, 21, 12, 0, 0, 0, time.UTC)
	elevation := solar.Elevation(loc, when)

	fmt.Printf("Sun elevation: %.1f degrees\n", elevation)
}
Output:
Sun elevation: 26.5 degrees

func JulianDayToTime

func JulianDayToTime(d float64) time.Time

JulianDayToTime converts a Julian day number into a time.Time.

The returned time is always in UTC timezone. This is the standard for astronomical calculations.

Parameters:

  • d: Julian day number as float64

Returns:

  • Time in UTC corresponding to the Julian day

Example:

t := solar.JulianDayToTime(2451545.0)  // J2000.0 epoch
// Returns 2000-01-01 12:00:00 +0000 UTC

func MeanSolarNoon

func MeanSolarNoon(loc Location, t Time) time.Time

MeanSolarNoon calculates the time at which the sun is at its highest altitude (solar noon) for the given location and date.

All times are calculated and returned in UTC. To convert to local time, use time.In() with the appropriate timezone.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()

Returns:

  • Solar noon time in UTC

Example:

loc := solar.NewLocation(43.65, -79.38)
t := solar.NewTime(2024, time.June, 21)
noon := solar.MeanSolarNoon(loc, t)
// noon is in UTC - convert to local time if needed

func Round

func Round(f float64, places int) float64

Round takes the provided float and rounds it to the specified number of decimal places. This function is adapted from user korya on GitHub (https://gist.github.com/DavidVaini/10308388#gistcomment-1391788).

func Sunrise

func Sunrise(loc Location, t Time) (time.Time, error)

Sunrise calculates when the sun will rise on the given day at the specified location.

All times are calculated and returned in UTC. To convert to local time, use time.In() with the appropriate timezone.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()

Returns:

  • Sunrise time in UTC
  • error if the sun does not rise on this day (e.g., polar night)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
sunrise, err := solar.Sunrise(loc, t)
if err != nil {
    // Handle polar night or other errors
}
// sunrise is in UTC - convert to local time if needed
localTime := sunrise.In(time.Local)
Example

ExampleSunrise demonstrates calculating just the sunrise time for Toronto, Canada on January 1, 2000.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for Toronto
	loc := solar.NewLocation(43.65, -79.38)

	// Create time for January 1, 2000
	t := solar.NewTime(2000, time.January, 1)

	// Calculate sunrise
	rise, err := solar.Sunrise(loc, t)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Sunrise: %s\n", rise.Format("15:04:05 MST"))
}
Output:
Sunrise: 12:50:59 UTC

func SunriseSunset

func SunriseSunset(loc Location, t Time) (time.Time, time.Time, error)

SunriseSunset calculates when the sun will rise and when it will set on the given day at the specified location.

All times are calculated and returned in UTC. To convert to local time, use time.In() with the appropriate timezone.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()

Returns:

  • sunrise: Sunrise time in UTC
  • sunset: Sunset time in UTC
  • error if the sun does not rise or set (e.g., polar night or midnight sun)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
sunrise, sunset, err := solar.SunriseSunset(loc, t)
if err != nil {
    // Handle polar night or midnight sun
}
// Both times are in UTC - convert to local time if needed
Example

ExampleSunriseSunset demonstrates basic sunrise and sunset calculation for Toronto, Canada on January 1, 2000.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for Toronto
	loc := solar.NewLocation(43.65, -79.38)

	// Create time for January 1, 2000
	t := solar.NewTime(2000, time.January, 1)

	// Calculate sunrise and sunset
	rise, set, err := solar.SunriseSunset(loc, t)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Sunrise: %s\n", rise.Format("15:04:05 MST"))
	fmt.Printf("Sunset: %s\n", set.Format("15:04:05 MST"))
}
Output:
Sunrise: 12:50:59 UTC
Sunset: 21:50:37 UTC
Example
// Using an RMC sentence to get both sunrise and sunset
nmea := "$GPRMC,123519,A,4339.192,N,07922.992,W,022.4,084.4,230394,003.1,W*71"
loc, err := NewLocationFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
tm, err := NewTimeFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
sunrise, sunset, err := SunriseSunset(loc, tm)
if err != nil {
	panic(err)
}

_, _ = sunrise, sunset // Use the times
Example (PolarNight)

ExampleSunriseSunset_polarNight demonstrates the case where the sun never rises (polar night).

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for Igloolik, Nunavut
	loc := solar.NewLocation(69.3321443, -81.6781126)

	// Create time for June 25, 2020 (midnight sun period)
	t := solar.NewTime(2020, time.June, 25)

	// Calculate sunrise and sunset
	_, _, err := solar.SunriseSunset(loc, t)

	// Check for error (sun never rises or sets)
	if err != nil {
		fmt.Println("The sun does not rise or set on this day")
	}
}
Output:
The sun does not rise or set on this day

func Sunset

func Sunset(loc Location, t Time) (time.Time, error)

Sunset calculates when the sun will set on the given day at the specified location.

All times are calculated and returned in UTC. To convert to local time, use time.In() with the appropriate timezone.

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()

Returns:

  • Sunset time in UTC
  • error if the sun does not set on this day (e.g., midnight sun)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
sunset, err := solar.Sunset(loc, t)
if err != nil {
    // Handle midnight sun or other errors
}
// sunset is in UTC - convert to local time if needed
localTime := sunset.In(time.Local)
Example

ExampleSunset demonstrates calculating just the sunset time for Toronto, Canada on January 1, 2000.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for Toronto
	loc := solar.NewLocation(43.65, -79.38)

	// Create time for January 1, 2000
	t := solar.NewTime(2000, time.January, 1)

	// Calculate sunset
	set, err := solar.Sunset(loc, t)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Sunset: %s\n", set.Format("15:04:05 MST"))
}
Output:
Sunset: 21:50:37 UTC
Example
// Using an RMC sentence
nmea := "$GPRMC,123519,A,4339.192,N,07922.992,W,022.4,084.4,230394,003.1,W*71"
loc, err := NewLocationFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
tm, err := NewTimeFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
sunset, err := Sunset(loc, tm)
if err != nil {
	panic(err)
}

_ = sunset // Use the sunset time

func TimeOfElevation

func TimeOfElevation(loc Location, elevation float64, t Time) (morning, evening time.Time)

TimeOfElevation calculates the times of day when the sun is at a given elevation above the horizon on a given day at the specified location.

All times are calculated and returned in UTC. Useful for calculating twilight times, golden hour, etc.

Common elevation angles:

  • -0.833°: Official sunrise/sunset (accounts for atmospheric refraction)
  • -6°: Civil twilight
  • -12°: Nautical twilight
  • -18°: Astronomical twilight
  • 6°: Golden hour

Parameters:

  • loc: Location created via NewLocation() or NewLocationFromNMEA()
  • elevation: Solar elevation angle in degrees (negative for below horizon)
  • t: Time created via NewTime(), NewTimeFromDateTime(), or NewTimeFromNMEA()

Returns:

  • morning: Time in UTC when sun reaches elevation in the morning (time.Time{} if never reached)
  • evening: Time in UTC when sun reaches elevation in the evening (time.Time{} if never reached)

Example:

loc := solar.NewLocation(40.7128, -74.0060)
t := solar.NewTime(2024, time.June, 21)
// Calculate civil twilight times
morning, evening := solar.TimeOfElevation(loc, -6.0, t)
Example

ExampleTimeOfElevation demonstrates finding when the sun reaches a specific elevation angle.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Create location for London
	loc := solar.NewLocation(51.5072, -0.1276)
	t := solar.NewTime(2022, time.June, 21)

	// Find when sun is at 10 degrees above horizon
	morning, evening := solar.TimeOfElevation(loc, 10.0, t)

	fmt.Printf("Morning: %s\n", morning.Format("15:04 MST"))
	fmt.Printf("Evening: %s\n", evening.Format("15:04 MST"))
}
Output:
Morning: 05:06 UTC
Evening: 18:58 UTC

func TimeToJulianDay

func TimeToJulianDay(t time.Time) float64

TimeToJulianDay converts a time.Time into a Julian day number.

The input time should be in UTC for accurate astronomical calculations. The timezone of the input time is preserved in the calculation via Unix timestamp.

Parameters:

  • t: Time to convert (should be in UTC for astronomical calculations)

Returns:

  • Julian day number as float64

Example:

jd := solar.TimeToJulianDay(time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC))
// Returns 2451545.0 (J2000.0 epoch)

Types

type Float

type Float interface {
	~float32 | ~float64
}

Float represents all floating-point types.

type Location

type Location struct {
	// contains filtered or unexported fields
}

Location represents a geographical location. It can be created from direct latitude/longitude coordinates or parsed from an NMEA GPS sentence.

func NewLocation

func NewLocation(latitude, longitude float64) Location

NewLocation creates a Location from latitude and longitude coordinates.

Parameters:

  • latitude: Decimal degrees, positive north, negative south (-90 to +90)
  • longitude: Decimal degrees, positive east, negative west (-180 to +180)

Example:

loc := solar.NewLocation(43.65, -79.38) // Toronto, Canada

func NewLocationFromNMEA

func NewLocationFromNMEA(nmea string, year int, month time.Month, day int) (Location, error)

NewLocationFromNMEA creates a Location from an NMEA GPS sentence.

Supported NMEA sentence types:

  • GGA (Global Positioning System Fix Data)
  • RMC (Recommended Minimum Specific GPS/Transit Data)

The year, month, and day parameters are required for GGA sentences (which don't include date information). For RMC sentences, these parameters are ignored as the date is parsed from the sentence.

Parameters:

  • nmea: NMEA sentence string (e.g., "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47")
  • year: Year (e.g., 2025) - ignored for RMC sentences
  • month: Month (e.g., time.January) - ignored for RMC sentences
  • day: Day of month (e.g., 15) - ignored for RMC sentences

Returns:

  • Location: The parsed location
  • error: Any error encountered during parsing

Example:

// From GGA sentence (requires date)
nmea := "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
loc, err := solar.NewLocationFromNMEA(nmea, 2025, time.January, 15)

// From RMC sentence (includes date, parameters ignored)
nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0)
Example

Example tests for documentation

// Using an RMC sentence (includes date: March 23, 1994)
nmea := "$GPRMC,123519,A,4339.192,N,07922.992,W,022.4,084.4,230394,003.1,W*71"
loc, err := NewLocationFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
tm, err := NewTimeFromNMEA(nmea, 0, 0, 0)
if err != nil {
	panic(err)
}
sunrise, err := Sunrise(loc, tm)
if err != nil {
	panic(err)
}

// Sunrise is returned in UTC
_ = sunrise // Use the sunrise time
Example

ExampleNewLocationFromNMEA demonstrates parsing location from an NMEA GPS sentence.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Parse an NMEA RMC sentence (includes date)
	nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
	loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0) // Date ignored for RMC
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Latitude: %.4f\n", loc.Latitude())
	fmt.Printf("Longitude: %.4f\n", loc.Longitude())
}
Output:
Latitude: 48.1173
Longitude: 11.5167
Example (Gga)

ExampleNewLocationFromNMEA_gga demonstrates parsing a GGA sentence which requires an external date.

package main

import (
	"fmt"
	"time"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Parse an NMEA GGA sentence (requires external date)
	nmea := "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
	loc, err := solar.NewLocationFromNMEA(nmea, 2024, time.June, 21) // Must provide date
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Latitude: %.4f\n", loc.Latitude())
	fmt.Printf("Longitude: %.4f\n", loc.Longitude())
}
Output:
Latitude: 48.1173
Longitude: 11.5167
Example (Gga)
// Using a GGA sentence (requires external date)
nmea := "$GPGGA,123519,4339.192,N,07922.992,W,1,08,0.9,545.4,M,46.9,M,,*5C"
loc, err := NewLocationFromNMEA(nmea, 2024, time.June, 21)
if err != nil {
	panic(err)
}
tm, err := NewTimeFromNMEA(nmea, 2024, time.June, 21)
if err != nil {
	panic(err)
}
sunrise, err := Sunrise(loc, tm)
if err != nil {
	panic(err)
}

_ = sunrise // Use the sunrise time
Example (MultipleCalculations)

ExampleNewLocationFromNMEA_multipleCalculations demonstrates the efficiency of parsing NMEA once and reusing the parsed data for multiple calculations.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"

	// Parse NMEA once
	loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	// Use with multiple calculations
	sunrise, _ := solar.Sunrise(loc, t)
	sunset, _ := solar.Sunset(loc, t)
	noon := solar.MeanSolarNoon(loc, t)
	dawn, dusk := solar.DawnDusk(loc, t)

	fmt.Printf("Dawn: %s\n", dawn.Format("15:04"))
	fmt.Printf("Sunrise: %s\n", sunrise.Format("15:04"))
	fmt.Printf("Solar Noon: %s\n", noon.Format("15:04"))
	fmt.Printf("Sunset: %s\n", sunset.Format("15:04"))
	fmt.Printf("Dusk: %s\n", dusk.Format("15:04"))
}
Output:
Dawn: 04:39
Sunrise: 05:10
Solar Noon: 11:13
Sunset: 17:30
Dusk: 18:01
Example (WithSolarCalculation)

ExampleNewLocationFromNMEA_withSolarCalculation demonstrates the complete pattern of parsing NMEA data and using it with solar calculations.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// NMEA sentence from GPS
	nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"

	// Parse location from NMEA
	loc, err := solar.NewLocationFromNMEA(nmea, 0, 0, 0)
	if err != nil {
		fmt.Printf("Error parsing location: %v\n", err)
		return
	}

	// Parse time from NMEA
	t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)
	if err != nil {
		fmt.Printf("Error parsing time: %v\n", err)
		return
	}

	// Now use with any solar calculation
	sunrise, err := solar.Sunrise(loc, t)
	if err != nil {
		fmt.Printf("Error calculating sunrise: %v\n", err)
		return
	}

	sunset, err := solar.Sunset(loc, t)
	if err != nil {
		fmt.Printf("Error calculating sunset: %v\n", err)
		return
	}

	fmt.Printf("Sunrise: %s\n", sunrise.Format("15:04 MST"))
	fmt.Printf("Sunset: %s\n", sunset.Format("15:04 MST"))
}
Output:
Sunrise: 05:10 UTC
Sunset: 17:30 UTC

func (Location) Latitude

func (l Location) Latitude() float64

Latitude returns the latitude in decimal degrees. Positive values are north, negative values are south.

func (Location) Longitude

func (l Location) Longitude() float64

Longitude returns the longitude in decimal degrees. Positive values are east, negative values are west.

func (Location) String

func (l Location) String() string

String returns a string representation of the Location.

type Signed

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
}

Signed represents all signed numeric types.

type Time

type Time struct {
	// contains filtered or unexported fields
}

Time represents a specific date for solar calculations. It can be created from individual date components or from a time.Time object. All times are treated as UTC.

func NewTime

func NewTime(year int, month time.Month, day int) Time

NewTime creates a Time from individual date components. The time is set to midnight (00:00:00) UTC.

Parameters:

  • year: Year (e.g., 2025)
  • month: Month (e.g., time.January)
  • day: Day of month (e.g., 15)

Example:

t := solar.NewTime(2025, time.January, 15)

func NewTimeFromDateTime

func NewTimeFromDateTime(when time.Time) Time

NewTimeFromDateTime creates a Time from a time.Time object. The time is converted to UTC if it isn't already.

Parameters:

  • when: A time.Time object

Example:

now := time.Now()
t := solar.NewTimeFromDateTime(now)

func NewTimeFromNMEA

func NewTimeFromNMEA(nmea string, year int, month time.Month, day int) (Time, error)

NewTimeFromNMEA creates a Time from an NMEA GPS sentence. The time is extracted from the NMEA sentence and combined with the provided date.

For RMC sentences, the date is parsed from the sentence and the provided year, month, day parameters are ignored. For GGA sentences, the provided date parameters are used.

Parameters:

  • nmea: NMEA sentence string
  • year: Year (e.g., 2025) - ignored for RMC sentences
  • month: Month (e.g., time.January) - ignored for RMC sentences
  • day: Day of month (e.g., 15) - ignored for RMC sentences

Returns:

  • Time: The parsed time
  • error: Any error encountered during parsing

Example:

nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)
Example

ExampleNewTimeFromNMEA demonstrates parsing time from an NMEA GPS sentence.

package main

import (
	"fmt"

	"github.com/mstephenholl/go-solar"
)

func main() {
	// Parse time from an NMEA RMC sentence
	nmea := "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
	t, err := solar.NewTimeFromNMEA(nmea, 0, 0, 0)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Year: %d\n", t.Year())
	fmt.Printf("Month: %s\n", t.Month())
	fmt.Printf("Day: %d\n", t.Day())
}
Output:
Year: 1994
Month: March
Day: 23

func (Time) DateTime

func (t Time) DateTime() time.Time

DateTime returns the underlying time.Time value in UTC.

func (Time) Day

func (t Time) Day() int

Day returns the day of month component.

func (Time) Month

func (t Time) Month() time.Month

Month returns the month component.

func (Time) String

func (t Time) String() string

String returns a string representation of the Time.

func (Time) Year

func (t Time) Year() int

Year returns the year component.

type TwilightType

type TwilightType int

TwilightType represents the type of twilight for dawn/dusk calculations. Twilight is the period between daylight and darkness (or vice versa) when the sun is below the horizon but its light is still visible.

const (
	// Civil twilight occurs when the sun is between 0° and 6° below the horizon.
	// This is the most commonly used definition for dawn and dusk in everyday contexts.
	// During civil twilight, there is enough natural light for most outdoor activities
	// without artificial lighting. The brightest stars and planets are visible.
	Civil TwilightType = iota

	// Nautical twilight occurs when the sun is between 6° and 12° below the horizon.
	// During nautical twilight, the horizon is still visible at sea, allowing sailors
	// to take reliable star sights for navigation. General ground outlines are visible,
	// but detailed outdoor work is difficult.
	Nautical

	// Astronomical twilight occurs when the sun is between 12° and 18° below the horizon.
	// During astronomical twilight, the sky is dark enough for most astronomical
	// observations, though the Sun's light may still interfere with observing
	// extremely faint objects. Beyond astronomical twilight is true night.
	Astronomical
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL