Created
August 19, 2026 15:22
-
-
Save yetimdasturchi/8973a67dd7df53870b748b125e01932b to your computer and use it in GitHub Desktop.
Calculate sunrise and sunset times using geographic coordinates and date.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Check whether the given year is a leap year. | |
| */ | |
| function isLeapYear(int $year): bool | |
| { | |
| return $year % 400 === 0 | |
| || ($year % 4 === 0 && $year % 100 !== 0); | |
| } | |
| /** | |
| * Convert a date to the day number within the year. | |
| * | |
| * Examples: | |
| * January 1 = 1 | |
| * January 31 = 31 | |
| * February 1 = 32 | |
| */ | |
| function dayOfYear(int $year, int $month, int $day): int | |
| { | |
| $daysInMonths = [ | |
| 31, 28, 31, 30, 31, 30, | |
| 31, 31, 30, 31, 30, 31, | |
| ]; | |
| if (isLeapYear($year)) { | |
| $daysInMonths[1] = 29; | |
| } | |
| $result = $day; | |
| for ($i = 0; $i < $month - 1; $i++) { | |
| $result += $daysInMonths[$i]; | |
| } | |
| return $result; | |
| } | |
| /** | |
| * Convert decimal hours to HH:MM:SS. | |
| * | |
| * Example: | |
| * 5.5 -> 05:30:00 | |
| */ | |
| function hoursToTime(float $hours): string | |
| { | |
| $hours = fmod($hours, 24); | |
| if ($hours < 0) { | |
| $hours += 24; | |
| } | |
| $totalSeconds = (int) round($hours * 3600) % 86400; | |
| $hour = intdiv($totalSeconds, 3600); | |
| $minute = intdiv($totalSeconds % 3600, 60); | |
| $second = $totalSeconds % 60; | |
| return sprintf('%02d:%02d:%02d', $hour, $minute, $second); | |
| } | |
| /** | |
| * Calculate sunrise and sunset times. | |
| */ | |
| function calculateSun( | |
| int $year, | |
| int $month, | |
| int $day, | |
| float $latitude, | |
| float $longitude, | |
| float $timezone, | |
| ): array { | |
| // 1. Day of the year | |
| $dayOfYear = dayOfYear($year, $month, $day); | |
| // 2. Fractional year angle, in radians | |
| $gamma = (2 * M_PI / 365) * ($dayOfYear - 1); | |
| // 3. Difference between apparent solar time and clock time, in minutes | |
| $equationOfTime = 229.18 * ( | |
| 0.000075 | |
| + 0.001868 * cos($gamma) | |
| - 0.032077 * sin($gamma) | |
| - 0.014615 * cos(2 * $gamma) | |
| - 0.040849 * sin(2 * $gamma) | |
| ); | |
| // 4. Solar declination, in radians | |
| $declination = | |
| 0.006918 | |
| - 0.399912 * cos($gamma) | |
| + 0.070257 * sin($gamma) | |
| - 0.006758 * cos(2 * $gamma) | |
| + 0.000907 * sin(2 * $gamma) | |
| - 0.002697 * cos(3 * $gamma) | |
| + 0.001480 * sin(3 * $gamma); | |
| // 5. Convert latitude to radians | |
| $latitudeRad = deg2rad($latitude); | |
| /** | |
| * 6. Sunrise / sunset zenith | |
| * | |
| * 90° = geometric horizon | |
| * 0.266° = apparent solar radius | |
| * 0.567° = atmospheric refraction | |
| * | |
| * Total: 90.833° | |
| */ | |
| $zenith = deg2rad(90.833); | |
| // 7. Calculate the hour angle at the horizon | |
| $cosHourAngle = | |
| cos($zenith) / (cos($latitudeRad) * cos($declination)) | |
| - tan($latitudeRad) * tan($declination); | |
| // Polar night: the Sun does not rise | |
| if ($cosHourAngle > 1) { | |
| return [ | |
| 'sunrise' => null, | |
| 'sunset' => null, | |
| 'status' => 'Sun does not rise', | |
| ]; | |
| } | |
| // Midnight Sun: the Sun does not set | |
| if ($cosHourAngle < -1) { | |
| return [ | |
| 'sunrise' => null, | |
| 'sunset' => null, | |
| 'status' => 'Sun does not set', | |
| ]; | |
| } | |
| // 8. Convert hour angle from radians to degrees | |
| $hourAngle = rad2deg(acos($cosHourAngle)); | |
| // 9. Solar peak time, in minutes from midnight | |
| $solarPeakMinutes = | |
| 720 | |
| - 4 * $longitude | |
| - $equationOfTime | |
| + $timezone * 60; | |
| /** | |
| * Earth rotates 360° in 24 hours: | |
| * | |
| * 360 / 24 = 15° per hour | |
| * 1° = 4 minutes | |
| */ | |
| $offsetMinutes = $hourAngle * 4; | |
| // 10. Sunrise and sunset times | |
| $sunriseMinutes = $solarPeakMinutes - $offsetMinutes; | |
| $sunsetMinutes = $solarPeakMinutes + $offsetMinutes; | |
| return [ | |
| 'sunrise' => hoursToTime($sunriseMinutes / 60), | |
| 'sunset' => hoursToTime($sunsetMinutes / 60), | |
| // Debug values | |
| 'day_of_year' => $dayOfYear, | |
| 'equation_of_time' => $equationOfTime, | |
| 'declination_deg' => rad2deg($declination), | |
| 'solar_peak' => hoursToTime($solarPeakMinutes / 60), | |
| 'status' => 'OK', | |
| ]; | |
| } | |
| // ------------------------------------------------------ | |
| // Example: Tashkent, August 19, 2026 | |
| // ------------------------------------------------------ | |
| $result = calculateSun( | |
| year: 2026, | |
| month: 8, | |
| day: 19, | |
| latitude: 41.2995, | |
| longitude: 69.2401, | |
| timezone: 5, | |
| ); | |
| echo "Sunrise: {$result['sunrise']}" . PHP_EOL; | |
| echo "Sunset: {$result['sunset']}" . PHP_EOL; | |
| echo PHP_EOL; | |
| echo "Solar peak: {$result['solar_peak']}" . PHP_EOL; | |
| echo "Declination: {$result['declination_deg']}°" . PHP_EOL; | |
| echo "Time difference: {$result['equation_of_time']} min" . PHP_EOL; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment