Files
GreenHomeBack/src/GreenHome.Infrastructure/SunCalculatorService.cs
2025-12-16 16:52:40 +03:30

126 lines
5.2 KiB
C#

using GreenHome.Application;
namespace GreenHome.Infrastructure;
/// <summary>
/// سرویس محاسبه طلوع و غروب خورشید
/// </summary>
public sealed class SunCalculatorService : ISunCalculatorService
{
public bool IsDaytime(DateTime dateTime, decimal latitude, decimal longitude)
{
var lat = (double)latitude;
var lng = (double)longitude;
// Calculate sunrise and sunset times
var (sunrise, sunset) = CalculateSunriseSunset(dateTime, lat, lng);
// Check if current time is between sunrise and sunset
var currentTime = dateTime.TimeOfDay;
return currentTime >= sunrise && currentTime <= sunset;
}
private (TimeSpan sunrise, TimeSpan sunset) CalculateSunriseSunset(DateTime date, double latitude, double longitude)
{
// Julian day calculation
var julianDay = CalculateJulianDay(date);
var julianCentury = (julianDay - 2451545.0) / 36525.0;
// Sun's mean longitude
var sunMeanLongitude = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
// Sun's mean anomaly
var sunMeanAnomaly = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
// Earth's orbit eccentricity
var eccentricity = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
// Sun's equation of center
var sunCenter = Math.Sin(ToRadians(sunMeanAnomaly)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury))
+ Math.Sin(ToRadians(2 * sunMeanAnomaly)) * (0.019993 - 0.000101 * julianCentury)
+ Math.Sin(ToRadians(3 * sunMeanAnomaly)) * 0.000289;
// Sun's true longitude
var sunTrueLongitude = sunMeanLongitude + sunCenter;
// Sun's apparent longitude
var sunApparentLongitude = sunTrueLongitude - 0.00569 - 0.00478 * Math.Sin(ToRadians(125.04 - 1934.136 * julianCentury));
// Mean oblique ecliptic
var meanOblique = 23.0 + (26.0 + ((21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813)))) / 60.0) / 60.0;
// Oblique correction
var obliqueCorrection = meanOblique + 0.00256 * Math.Cos(ToRadians(125.04 - 1934.136 * julianCentury));
// Sun's declination
var declination = ToDegrees(Math.Asin(Math.Sin(ToRadians(obliqueCorrection)) * Math.Sin(ToRadians(sunApparentLongitude))));
// Equation of time
var y = Math.Tan(ToRadians(obliqueCorrection / 2.0)) * Math.Tan(ToRadians(obliqueCorrection / 2.0));
var equationOfTime = 4.0 * ToDegrees(y * Math.Sin(2.0 * ToRadians(sunMeanLongitude))
- 2.0 * eccentricity * Math.Sin(ToRadians(sunMeanAnomaly))
+ 4.0 * eccentricity * y * Math.Sin(ToRadians(sunMeanAnomaly)) * Math.Cos(2.0 * ToRadians(sunMeanLongitude))
- 0.5 * y * y * Math.Sin(4.0 * ToRadians(sunMeanLongitude))
- 1.25 * eccentricity * eccentricity * Math.Sin(2.0 * ToRadians(sunMeanAnomaly)));
// Hour angle sunrise (civil twilight: sun 6 degrees below horizon)
var zenith = 90.833; // Official: 90 degrees 50 minutes
var hourAngle = ToDegrees(Math.Acos(
(Math.Cos(ToRadians(zenith)) / (Math.Cos(ToRadians(latitude)) * Math.Cos(ToRadians(declination))))
- Math.Tan(ToRadians(latitude)) * Math.Tan(ToRadians(declination))
));
// Calculate sunrise and sunset in minutes
var solarNoon = (720.0 - 4.0 * longitude - equationOfTime) / 1440.0;
var sunriseTime = solarNoon - hourAngle * 4.0 / 1440.0;
var sunsetTime = solarNoon + hourAngle * 4.0 / 1440.0;
// Convert to local time (assume UTC offset for Iran: +3:30 = 210 minutes)
// You should ideally calculate timezone offset based on longitude
var utcOffsetMinutes = Math.Round(longitude / 15.0) * 60.0;
var sunriseMinutes = sunriseTime * 1440.0 + utcOffsetMinutes;
var sunsetMinutes = sunsetTime * 1440.0 + utcOffsetMinutes;
// Handle edge cases
if (sunriseMinutes < 0) sunriseMinutes += 1440;
if (sunriseMinutes >= 1440) sunriseMinutes -= 1440;
if (sunsetMinutes < 0) sunsetMinutes += 1440;
if (sunsetMinutes >= 1440) sunsetMinutes -= 1440;
var sunrise = TimeSpan.FromMinutes(sunriseMinutes);
var sunset = TimeSpan.FromMinutes(sunsetMinutes);
return (sunrise, sunset);
}
private double CalculateJulianDay(DateTime date)
{
var year = date.Year;
var month = date.Month;
var day = date.Day;
if (month <= 2)
{
year -= 1;
month += 12;
}
var a = year / 100;
var b = 2 - a + (a / 4);
return Math.Floor(365.25 * (year + 4716)) + Math.Floor(30.6001 * (month + 1)) + day + b - 1524.5;
}
private double ToRadians(double degrees)
{
return degrees * Math.PI / 180.0;
}
private double ToDegrees(double radians)
{
return radians * 180.0 / Math.PI;
}
}