using GreenHome.Application; namespace GreenHome.Infrastructure; /// /// سرویس محاسبه طلوع و غروب خورشید /// 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; } }