diff --git a/src/astral/__init__.py b/src/astral/__init__.py index 46c4adc..f16f882 100644 --- a/src/astral/__init__.py +++ b/src/astral/__init__.py @@ -114,9 +114,9 @@ def dms_to_float( _dms_re = r"(?P\d{1,3})[°]((?P\d{1,2})[′'])?((?P\d{1,2})[″\"])?(?P[NSEW])?" # noqa dms_match = re.match(_dms_re, str(dms), flags=re.IGNORECASE) if dms_match: - deg = dms_match.group("deg") or 0.0 - min_ = dms_match.group("min") or 0.0 - sec = dms_match.group("sec") or 0.0 + deg = dms_match.group("deg") or 0 + min_ = dms_match.group("min") or 0 + sec = dms_match.group("sec") or 0 dir_ = dms_match.group("dir") or "E" res = float(deg) @@ -133,38 +133,27 @@ def dms_to_float( ) from exc if limit is not None: - if res > limit: - res = limit - elif res < -limit: - res = -limit - + res = min(max(-limit, res), limit) return res def hours_to_time(value: float) -> datetime.time: """Convert a floating point number of hours to a datetime.time""" - - hour = int(value) - value -= hour - value *= 60 - minute = int(value) - value -= minute - value *= 60 - second = int(value) - value -= second - microsecond = int(value * 1000000) - + rest = int(value * 60 * 60 * 1_000_000) + rest, microsecond = divmod(rest, 1_000_000) + rest, second = divmod(rest, 60) + hour, minute = divmod(rest, 60) return datetime.time(hour, minute, second, microsecond) def time_to_hours(value: datetime.time) -> float: """Convert a datetime.time to a floating point number of hours""" - hours = 0.0 + hours = 0 hours += value.hour hours += value.minute / 60 hours += value.second / 3600 - hours += value.microsecond / 1000000 + hours += value.microsecond / 3_600_000_000 return hours @@ -180,12 +169,12 @@ def refraction_at_zenith(zenith: float) -> float: """Calculate the degrees of refraction of the sun due to the sun's elevation.""" elevation = 90 - zenith - if elevation >= 85.0: + if elevation >= 85: return 0 - refraction_correction = 0.0 + refraction_correction = 0 te = tan(radians(elevation)) - if elevation > 5.0: + if elevation > 5: refraction_correction = ( 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te) ) @@ -193,11 +182,11 @@ def refraction_at_zenith(zenith: float) -> float: step1 = -12.79 + elevation * 0.711 step2 = 103.4 + elevation * step1 step3 = -518.2 + elevation * step2 - refraction_correction = 1735.0 + elevation * step3 + refraction_correction = 1735 + elevation * step3 else: refraction_correction = -20.774 / te - refraction_correction = refraction_correction / 3600.0 + refraction_correction = refraction_correction / 3600 return refraction_correction @@ -253,13 +242,13 @@ class Observer: latitude: Degrees = 51.4733 longitude: Degrees = -0.0008333 - elevation: Elevation = 0.0 + elevation: Elevation = 0 def __setattr__(self, name: str, value: Union[str, float, Elevation]): if name == "latitude": - value = dms_to_float(value, 90.0) + value = dms_to_float(value, 90) elif name == "longitude": - value = dms_to_float(value, 180.0) + value = dms_to_float(value, 180) elif name == "elevation": if isinstance(value, tuple): value = (float(value[0]), float(value[1])) @@ -296,15 +285,15 @@ class LocationInfo: def __setattr__(self, name: str, value: Union[Degrees, str]): if name == "latitude": - value = dms_to_float(value, 90.0) + value = dms_to_float(value, 90) elif name == "longitude": - value = dms_to_float(value, 180.0) + value = dms_to_float(value, 180) super().__setattr__(name, value) @property def observer(self): """Return an Observer at this location""" - return Observer(self.latitude, self.longitude, 0.0) + return Observer(self.latitude, self.longitude, 0) @property def tzinfo(self): # type: ignore diff --git a/src/astral/__main__.py b/src/astral/__main__.py index 9aa8f51..a7e3c4b 100644 --- a/src/astral/__main__.py +++ b/src/astral/__main__.py @@ -22,13 +22,17 @@ "-r", "--region", dest="region", default="On Earth", help="Region (free-form text)" ) options.add_argument( - "-d", "--date", dest="date", help="Date to compute times for (yyyy-mm-dd)" + "-d", "--date", + dest="date", + type=datetime.date.fromisoformat, + default=datetime.date.today(), + help="Date to compute times for (yyyy-mm-dd)", ) options.add_argument("-t", "--tzname", help="Timezone name") options.add_argument("latitude", type=float, help="Location latitude (float)") options.add_argument("longitude", type=float, help="Location longitude (float)") options.add_argument( - "elevation", nargs="?", type=float, default=0.0, help="Elevation in metres (float)" + "elevation", nargs="?", type=float, default=0, help="Elevation in metres (float)" ) args = options.parse_args() @@ -44,12 +48,7 @@ kwargs: Dict[str, Any] = {} kwargs["observer"] = obs - -if args.date is not None: - try: - kwargs["date"] = datetime.datetime.strptime(args.date, "%Y-%m-%d").date() - except: # noqa: E722 - kwargs["date"] = datetime.date.today() +kwargs["date"] = args.date sun_as_str = {} format_str = "%Y-%m-%dT%H:%M:%S" diff --git a/src/astral/geocoder.py b/src/astral/geocoder.py index b260431..f160545 100644 --- a/src/astral/geocoder.py +++ b/src/astral/geocoder.py @@ -469,8 +469,8 @@ def _locationinfo_from_str(info: str) -> LocationInfo: name=idxable[0], region=idxable[1], timezone=idxable[2], - latitude=dms_to_float(idxable[3], 90.0), - longitude=dms_to_float(idxable[4], 180.0), + latitude=dms_to_float(idxable[3], 90), + longitude=dms_to_float(idxable[4], 180), ) @@ -481,8 +481,8 @@ def _locationinfo_from_indexable( name=idxable[0], region=idxable[1], timezone=idxable[2], - latitude=dms_to_float(idxable[3], 90.0), - longitude=dms_to_float(idxable[4], 180.0), + latitude=dms_to_float(idxable[3], 90), + longitude=dms_to_float(idxable[4], 180), ) diff --git a/src/astral/julian.py b/src/astral/julian.py index dd5d481..264cf0d 100644 --- a/src/astral/julian.py +++ b/src/astral/julian.py @@ -9,12 +9,9 @@ class Calendar(Enum): def day_fraction_to_time(fraction: float) -> datetime.time: - s = fraction * (24 * 60 * 60) - h = int(s / (60 * 60)) - s -= h * 60 * 60 - m = int(s / 60) - s -= m * 60 - s = int(s) + rest = int(fraction * (24 * 60 * 60)) + rest, s = divmod(rest, 60) + h, m = divmod(rest, 60) return datetime.time(h, m, s) @@ -32,12 +29,10 @@ def _time_to_seconds(t: datetime.time) -> int: year = at.year month = at.month day = at.day - day_fraction = 0.0 + day_fraction = 0 if isinstance(at, datetime.datetime): t = _time_to_seconds(at.time()) day_fraction = t / (24 * 60 * 60) - else: - day_fraction = 0.0 if month <= 2: year -= 1 @@ -95,7 +90,7 @@ def julianday_to_datetime(jd: float) -> datetime.datetime: a = z else: alpha = int((z - 1867216.25) / 36524.25) - a = z + 1 + alpha + int(alpha / 4.0) + a = z + 1 + alpha + int(alpha / 4) b = a + 1524 c = int((b - 122.1) / 365.25) @@ -127,14 +122,14 @@ def julianday_to_datetime(jd: float) -> datetime.datetime: def julianday_to_juliancentury(julianday: float) -> float: """Convert a Julian Day number to a Julian Century""" - return (julianday - 2451545.0) / 36525.0 + return (julianday - 2451545) / 36525 def juliancentury_to_julianday(juliancentury: float) -> float: """Convert a Julian Century number to a Julian Day""" - return (juliancentury * 36525.0) + 2451545.0 + return (juliancentury * 36525) + 2451545 def julianday_2000(at: Union[datetime.datetime, datetime.date]) -> float: """Calculate the numer of Julian Days since Jan 1.5, 2000""" - return julianday(at) - 2451545.0 + return julianday(at) - 2451545 diff --git a/src/astral/location.py b/src/astral/location.py index 2162280..2334f57 100644 --- a/src/astral/location.py +++ b/src/astral/location.py @@ -60,7 +60,7 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: if self.region: - _repr = "%s/%s" % (self.name, self.region) + _repr = f"{self.name}/{self.region}" else: _repr = self.name return ( @@ -81,7 +81,7 @@ def info(self) -> LocationInfo: @property def observer(self) -> Observer: - return Observer(self.latitude, self.longitude, 0.0) + return Observer(self.latitude, self.longitude, 0) @property def name(self) -> str: @@ -117,7 +117,7 @@ def latitude(self) -> float: @latitude.setter def latitude(self, latitude: Union[float, str]) -> None: self._location_info = dataclasses.replace( - self._location_info, latitude=dms_to_float(latitude, 90.0) + self._location_info, latitude=dms_to_float(latitude, 90) ) @property @@ -138,7 +138,7 @@ def longitude(self) -> float: @longitude.setter def longitude(self, longitude: Union[float, str]) -> None: self._location_info = dataclasses.replace( - self._location_info, longitude=dms_to_float(longitude, 180.0) + self._location_info, longitude=dms_to_float(longitude, 180) ) @property @@ -157,7 +157,7 @@ def timezone(self) -> str: @timezone.setter def timezone(self, name: str) -> None: if name not in zoneinfo.available_timezones(): # type: ignore - raise ValueError("Timezone '%s' not recognized" % name) + raise ValueError(f"Timezone {name!r} not recognized") self._location_info = dataclasses.replace(self._location_info, timezone=name) @@ -170,7 +170,7 @@ def tzinfo(self) -> zoneinfo.ZoneInfo: # type: ignore return tz # type: ignore except zoneinfo.ZoneInfoNotFoundError as exc: # type: ignore raise ValueError( - "Unknown timezone '%s'" % self._location_info.timezone + f"Unknown timezone {self._location_info.timezone!r}" ) from exc tz = tzinfo @@ -199,9 +199,9 @@ def solar_depression(self, depression: Union[float, str, Depression]) -> None: if isinstance(depression, str): try: self._solar_depression = { - "civil": 6.0, - "nautical": 12.0, - "astronomical": 18.0, + "civil": 6, + "nautical": 12, + "astronomical": 18, }[depression] except KeyError: raise KeyError( @@ -226,7 +226,7 @@ def sun( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Dict[str, Any]: """Returns dawn, sunrise, noon, sunset and dusk as a dictionary. @@ -262,7 +262,7 @@ def dawn( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> datetime.datetime: """Calculates the time in the morning when the sun is a certain number of degrees below the horizon. By default this is 6 degrees but can be @@ -298,7 +298,7 @@ def sunrise( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> datetime.datetime: """Return sunrise time. @@ -363,7 +363,7 @@ def sunset( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> datetime.datetime: """Calculates sunset time (the time in the evening when the sun is a 0.833 degrees below the horizon. This is to account for refraction.) @@ -398,7 +398,7 @@ def dusk( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> datetime.datetime: """Calculates the dusk time (the time in the evening when the sun is a certain number of degrees below the horizon. By default this is 6 @@ -464,7 +464,7 @@ def daylight( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculates the daylight time (the time between sunrise and sunset) @@ -498,7 +498,7 @@ def night( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculates the night time (the time between astronomical dusk and astronomical dawn of the next day) @@ -534,7 +534,7 @@ def twilight( date: Optional[datetime.date] = None, direction: SunDirection = SunDirection.RISING, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ): """Returns the start and end times of Twilight in the UTC timezone when the sun is traversing in the specified direction. @@ -667,11 +667,11 @@ def time_at_elevation( if date is None: date = self.today(local) - if elevation > 90.0: - elevation = 180.0 - elevation + if elevation > 90: + elevation = 180 - elevation direction = SunDirection.SETTING - observer = Observer(self.latitude, self.longitude, 0.0) + observer = Observer(self.latitude, self.longitude, 0) if local: return astral.sun.time_at_elevation( @@ -684,7 +684,7 @@ def rahukaalam( self, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculates the period of rahukaalam. @@ -718,7 +718,7 @@ def golden_hour( direction: SunDirection = SunDirection.RISING, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Tuple[datetime.datetime, datetime.datetime]: """Returns the start and end times of the Golden Hour when the sun is traversing in the specified direction. @@ -761,7 +761,7 @@ def blue_hour( direction: SunDirection = SunDirection.RISING, date: Optional[datetime.date] = None, local: bool = True, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> Tuple[datetime.datetime, datetime.datetime]: """Returns the start and end times of the Blue Hour when the sun is traversing in the specified direction. @@ -802,7 +802,7 @@ def blue_hour( def solar_azimuth( self, dateandtime: Optional[datetime.datetime] = None, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> float: """Calculates the solar azimuth angle for a specific date/time. @@ -823,7 +823,7 @@ def solar_azimuth( def solar_elevation( self, dateandtime: Optional[datetime.datetime] = None, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> float: """Calculates the solar elevation angle for a specific time. @@ -845,7 +845,7 @@ def solar_elevation( def solar_zenith( self, dateandtime: Optional[datetime.datetime] = None, - observer_elevation: Elevation = 0.0, + observer_elevation: Elevation = 0, ) -> float: """Calculates the solar zenith angle for a specific time. @@ -853,7 +853,7 @@ def solar_zenith( :returns: The zenith angle in degrees from vertical. """ - return 90.0 - self.solar_elevation(dateandtime, observer_elevation) + return 90 - self.solar_elevation(dateandtime, observer_elevation) def moon_phase(self, date: Optional[datetime.date] = None, local: bool = True): """Calculates the moon phase for a specific date. diff --git a/src/astral/moon.py b/src/astral/moon.py index b2f3691..0fe32ea 100644 --- a/src/astral/moon.py +++ b/src/astral/moon.py @@ -9,7 +9,7 @@ import datetime from dataclasses import dataclass, field, replace -from math import asin, atan2, cos, degrees, fabs, pi, radians, sin, sqrt +from math import asin, atan2, cos, degrees, fabs, hypot, pi, radians, sin, sqrt from typing import Callable, List, Optional, Union try: @@ -25,7 +25,7 @@ __all__ = ["moonrise", "moonset", "phase"] # Using 1896 arc seconds as moon's apparent diameter -MOON_APPARENT_RADIUS = 1896.0 / (60.0 * 60.0) +MOON_APPARENT_RADIUS = 1896 / (60 * 60) Degrees = float Radians = float @@ -139,9 +139,9 @@ def moon_position(jd2000: float) -> AstralBodyPosition: T = jd2000 / 36525 + 1 def _calc_value(table: List[Table4Row]) -> float: - result = 0.0 + result = 0 for row in table: - revolutions: float = 0.0 + revolutions: float = 0 for arg_number, multiplier in row.argument_multiplers.items(): if multiplier != 0: arg_value = argument_values[arg_number - 1] @@ -188,7 +188,7 @@ def moon_transit_event( window: Sliding window of moon positions that covers a part of the day """ mst = radians(lmst) - hour_angle = [0.0, 0.0, 0.0] + hour_angle = [0, 0, 0] k1 = radians(15 * 1.0027379097096138907193594760917) @@ -256,11 +256,7 @@ def moon_transit_event( x = cl * sd - sl * cd * ch y = -cd * sh - az = degrees(atan2(y, x)) - if az < 0: - az += 360 - if az > 360: - az -= 360 + az = degrees(atan2(y, x)) % 360 event_time = datetime.time(h, m, 0) if window[0].distance < 0 and window[2].distance > 0: @@ -535,7 +531,7 @@ def elevation( y = -sh * cd z = ch * cd * cl + sd * sl - r = sqrt(x * x + y * y) + r = hypot(x, y) elevation = degrees(atan2(z, r)) return elevation @@ -551,24 +547,24 @@ def zenith( def _phase_asfloat(date: datetime.date) -> float: jd = julianday(date) dt = pow((jd - 2382148), 2) / (41048480 * 86400) - t = (jd + dt - 2451545.0) / 36525 + t = (jd + dt - 2451545) / 36525 t2 = pow(t, 2) t3 = pow(t, 3) d = 297.85 + (445267.1115 * t) - (0.0016300 * t2) + (t3 / 545868) - d = radians(d % 360.0) + d = radians(d % 360) m = 357.53 + (35999.0503 * t) - m = radians(m % 360.0) + m = radians(m % 360) m1 = 134.96 + (477198.8676 * t) + (0.0089970 * t2) + (t3 / 69699) - m1 = radians(m1 % 360.0) + m1 = radians(m1 % 360) elong = degrees(d) + 6.29 * sin(m1) elong -= 2.10 * sin(m) elong += 1.27 * sin(2 * d - m1) elong += 0.66 * sin(2 * d) - elong = elong % 360.0 + elong = elong % 360 elong = int(elong) moon = ((elong + 6.43) / 360) * 28 return moon @@ -595,7 +591,5 @@ def phase(date: Optional[datetime.date] = None) -> float: if date is None: date = today() - moon = _phase_asfloat(date) - if moon >= 28.0: - moon -= 28.0 + moon = _phase_asfloat(date) % 28 return moon diff --git a/src/astral/sun.py b/src/astral/sun.py index 2b53c0b..3b7e8cb 100644 --- a/src/astral/sun.py +++ b/src/astral/sun.py @@ -1,5 +1,5 @@ import datetime -from math import acos, asin, atan2, cos, degrees, fabs, radians, sin, sqrt, tan +from math import acos, asin, atan2, cos, degrees, fabs, hypot, radians, sin, tan from typing import Dict, Optional, Tuple, Union try: @@ -41,28 +41,13 @@ # Using 32 arc minutes as sun's apparent diameter -SUN_APPARENT_RADIUS = 32.0 / (60.0 * 2.0) - - -# region Backend -def minutes_to_timedelta(minutes: float) -> datetime.timedelta: - """Convert a floating point number of minutes to a - :class:`~datetime.timedelta` - """ - d = int(minutes / 1440) - minutes = minutes - (d * 1440) - minutes = minutes * 60 - s = int(minutes) - sfrac = minutes - s - us = int(sfrac * 1_000_000) - - return datetime.timedelta(days=d, seconds=s, microseconds=us) +SUN_APPARENT_RADIUS = 32 / (60 * 2) def geom_mean_long_sun(juliancentury: float) -> float: """Calculate the geometric mean longitude of the sun""" l0 = 280.46646 + juliancentury * (36000.76983 + 0.0003032 * juliancentury) - return l0 % 360.0 + return l0 % 360 def geom_mean_anomaly_sun(juliancentury: float) -> float: @@ -127,7 +112,7 @@ def mean_obliquity_of_ecliptic(juliancentury: float) -> float: seconds = 21.448 - juliancentury * ( 46.815 + juliancentury * (0.00059 - juliancentury * (0.001813)) ) - return 23.0 + (26.0 + (seconds / 60.0)) / 60.0 + return 23 + (26 + (seconds / 60)) / 60 def obliquity_correction(juliancentury: float) -> float: @@ -159,7 +144,7 @@ def sun_declination(juliancentury: float) -> float: def var_y(juliancentury: float) -> float: epsilon = obliquity_correction(juliancentury) - y = tan(radians(epsilon) / 2.0) + y = tan(radians(epsilon) / 2) return y * y @@ -170,21 +155,21 @@ def eq_of_time(juliancentury: float) -> Minutes: y = var_y(juliancentury) - sin2l0 = sin(2.0 * radians(l0)) + sin2l0 = sin(2 * radians(l0)) sinm = sin(radians(m)) - cos2l0 = cos(2.0 * radians(l0)) - sin4l0 = sin(4.0 * radians(l0)) - sin2m = sin(2.0 * radians(m)) + cos2l0 = cos(2 * radians(l0)) + sin4l0 = sin(4 * radians(l0)) + sin2m = sin(2 * radians(m)) Etime = ( y * sin2l0 - - 2.0 * e * sinm - + 4.0 * e * y * sinm * cos2l0 + - 2 * e * sinm + + 4 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m ) - return degrees(Etime) * 4.0 + return degrees(Etime) * 4 def hour_angle( @@ -241,12 +226,12 @@ def adjust_to_horizon(elevation: float) -> float: def adjust_to_obscuring_feature(elevation: Tuple[float, float]) -> float: """Calculate the number of degrees to adjust for an obscuring feature""" - if elevation[0] == 0.0: - return 0.0 + if elevation[0] == 0: + return 0 - sign = -1 if elevation[0] < 0.0 else 1 + sign = -1 if elevation[0] < 0 else 1 return sign * degrees( - acos(fabs(elevation[0]) / sqrt(pow(elevation[0], 2) + pow(elevation[1], 2))) + acos(fabs(elevation[0]) / hypot(elevation[0], elevation[1])) ) @@ -273,15 +258,10 @@ def time_of_transit( Returns: the time when the sun transits the specificed zenith """ - if observer.latitude > 89.8: - latitude = 89.8 - elif observer.latitude < -89.8: - latitude = -89.8 - else: - latitude = observer.latitude + latitude = min(max(-89.8, observer.latitude), 89.8) - adjustment_for_elevation = 0.0 - if isinstance(observer.elevation, float) and observer.elevation > 0.0: + adjustment_for_elevation = 0 + if isinstance(observer.elevation, float) and observer.elevation > 0: adjustment_for_elevation = adjust_to_horizon(observer.elevation) elif isinstance(observer.elevation, tuple): adjustment_for_elevation = adjust_to_obscuring_feature(observer.elevation) @@ -291,11 +271,11 @@ def time_of_transit( zenith + adjustment_for_elevation ) else: - adjustment_for_refraction = 0.0 + adjustment_for_refraction = 0 jd = julianday(date) - adjustment = 0.0 - timeUTC = 0.0 + adjustment = 0 + timeUTC = 0 for _ in range(2): jc = julianday_to_juliancentury(jd + adjustment) @@ -311,17 +291,15 @@ def time_of_transit( delta = -observer.longitude - degrees(hourangle) eqtime = eq_of_time(jc) - offset = delta * 4.0 - eqtime + offset = delta * 4 - eqtime - if offset < -720.0: - offset += 1440 + timeUTC = (720 + offset) % 1440 + adjustment = timeUTC / 1440 - timeUTC = 720.0 + offset - adjustment = timeUTC / 1440.0 - - td = minutes_to_timedelta(timeUTC) - dt = datetime.datetime(date.year, date.month, date.day) + td - dt = dt.replace(tzinfo=datetime.timezone.utc) # pylint: disable=E1120 + dt = ( + datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc) + + datetime.timedelta(minutes=timeUTC) + ) return dt @@ -357,8 +335,8 @@ def time_at_elevation( Date and time at which the sun is at the specified elevation. """ - if elevation > 90.0: - elevation = 180.0 - elevation + if elevation > 90: + elevation = 180 - elevation direction = SunDirection.SETTING if isinstance(tzinfo, str): @@ -408,41 +386,11 @@ def noon( jc = julianday_to_juliancentury(julianday(date)) eqtime = eq_of_time(jc) - timeUTC = (720.0 - (4 * observer.longitude) - eqtime) / 60.0 - - hour = int(timeUTC) - minute = int((timeUTC - hour) * 60) - second = int((((timeUTC - hour) * 60) - minute) * 60) - - if second > 59: - second -= 60 - minute += 1 - elif second < 0: - second += 60 - minute -= 1 - - if minute > 59: - minute -= 60 - hour += 1 - elif minute < 0: - minute += 60 - hour -= 1 - - if hour > 23: - hour -= 24 - date += datetime.timedelta(days=1) - elif hour < 0: - hour += 24 - date -= datetime.timedelta(days=1) - - noon = datetime.datetime( - date.year, - date.month, - date.day, - hour, - minute, - second, - tzinfo=datetime.timezone.utc, + timeUTC = (720 - (4 * observer.longitude) - eqtime) / 60 + + noon = ( + datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc) + + datetime.timedelta(hours=timeUTC) ) return noon.astimezone(tzinfo) # type: ignore # pylint: disable=E1120 @@ -476,42 +424,14 @@ def midnight( midday = datetime.time(12, 0, 0) jd = julianday(datetime.datetime.combine(date, midday)) - newt = julianday_to_juliancentury(jd + 0.5 + -observer.longitude / 360.0) + newt = julianday_to_juliancentury(jd + 0.5 + -observer.longitude / 360) eqtime = eq_of_time(newt) - timeUTC = (-observer.longitude * 4.0) - eqtime - - timeUTC = timeUTC / 60.0 - hour = int(timeUTC) - minute = int((timeUTC - hour) * 60) - second = int((((timeUTC - hour) * 60) - minute) * 60) - - if second > 59: - second -= 60 - minute += 1 - elif second < 0: - second += 60 - minute -= 1 - - if minute > 59: - minute -= 60 - hour += 1 - elif minute < 0: - minute += 60 - hour -= 1 - - if hour < 0: - hour += 24 - date -= datetime.timedelta(days=1) - - midnight = datetime.datetime( - date.year, - date.month, - date.day, - hour, - minute, - second, - tzinfo=datetime.timezone.utc, + timeUTC = (-observer.longitude * 4) - eqtime + timeUTC = timeUTC / 60 + midnight = ( + datetime.datetime(date.year, date.month, date.day, tzinfo=datetime.timezone.utc) + + datetime.timedelta(hours=timeUTC) ) return midnight.astimezone(tzinfo) # type: ignore @@ -521,20 +441,14 @@ def zenith_and_azimuth( dateandtime: datetime.datetime, with_refraction: bool = True, ) -> Tuple[float, float]: - if observer.latitude > 89.8: - latitude = 89.8 - elif observer.latitude < -89.8: - latitude = -89.8 - else: - latitude = observer.latitude - + latitude = min(max(-89.8, observer.latitude), 89.8) longitude = observer.longitude if dateandtime.tzinfo is None: - zone = 0.0 + zone = 0 utc_datetime = dateandtime else: - zone = -dateandtime.utcoffset().total_seconds() / 3600.0 # type: ignore + zone = -dateandtime.utcoffset().total_seconds() / 3600 # type: ignore utc_datetime = dateandtime.astimezone(datetime.timezone.utc) jd = julianday(utc_datetime) @@ -543,22 +457,21 @@ def zenith_and_azimuth( eqtime = eq_of_time(t) # 360deg * 4 == 1440 minutes, 60*24 = 1440 minutes == 1 rotation - solarTimeFix = eqtime + (4.0 * longitude) + (60 * zone) + solarTimeFix = eqtime + (4 * longitude) + (60 * zone) trueSolarTime = ( - dateandtime.hour * 60.0 + dateandtime.hour * 60 + dateandtime.minute - + dateandtime.second / 60.0 + + dateandtime.second / 60 + solarTimeFix ) # in minutes as a float, fractional part is seconds - while trueSolarTime > 1440: - trueSolarTime = trueSolarTime - 1440 + trueSolarTime %= 1440 - hourangle = trueSolarTime / 4.0 - 180.0 + hourangle = trueSolarTime / 4 - 180 # Thanks to Louis Schwarzmayr for the next line: if hourangle < -180: - hourangle = hourangle + 360.0 + hourangle = hourangle + 360 ch = cos(radians(hourangle)) # sh = sin(radians(hourangle)) @@ -568,37 +481,25 @@ def zenith_and_azimuth( cd = cos(radians(declination)) csz = cl * cd * ch + sl * sd - - if csz > 1.0: - csz = 1.0 - elif csz < -1.0: - csz = -1.0 - + csz = min(max(-1, csz), 1) zenith = degrees(acos(csz)) azDenom = cl * sin(radians(zenith)) if abs(azDenom) > 0.001: azRad = ((sl * cos(radians(zenith))) - sd) / azDenom + azRad = min(max(-1, azRad), 1) + azimuth = 180 - degrees(acos(azRad)) - if abs(azRad) > 1.0: - if azRad < 0: - azRad = -1.0 - else: - azRad = 1.0 - - azimuth = 180.0 - degrees(acos(azRad)) - - if hourangle > 0.0: + if hourangle > 0: azimuth = -azimuth else: - if latitude > 0.0: - azimuth = 180.0 + if latitude > 0: + azimuth = 180 else: - azimuth = 0.0 + azimuth = 0 - if azimuth < 0.0: - azimuth = azimuth + 360.0 + azimuth %= 360 if with_refraction: zenith -= refraction_at_zenith(zenith) @@ -677,7 +578,7 @@ def elevation( if dateandtime is None: dateandtime = now(datetime.timezone.utc) - return 90.0 - zenith(observer, dateandtime, with_refraction) + return 90 - zenith(observer, dateandtime, with_refraction) def dawn( @@ -711,7 +612,7 @@ def dawn( tzinfo = date.tzinfo or tzinfo date = date.date() - dep: float = 0.0 + dep: float = 0 if isinstance(depression, Depression): dep = depression.value else: @@ -719,7 +620,7 @@ def dawn( try: tot = time_of_transit( - observer, date, 90.0 + dep, SunDirection.RISING + observer, date, 90 + dep, SunDirection.RISING ).astimezone( tzinfo # type: ignore ) @@ -736,7 +637,7 @@ def dawn( tot = time_of_transit( observer, new_date, - 90.0 + dep, + 90 + dep, SunDirection.RISING, ).astimezone( tzinfo # type: ignore @@ -787,7 +688,7 @@ def sunrise( tot = time_of_transit( observer, date, - 90.0 + SUN_APPARENT_RADIUS, + 90 + SUN_APPARENT_RADIUS, SunDirection.RISING, ).astimezone( tzinfo # type: ignore @@ -804,7 +705,7 @@ def sunrise( tot = time_of_transit( observer, new_date, - 90.0 + SUN_APPARENT_RADIUS, + 90 + SUN_APPARENT_RADIUS, SunDirection.RISING, ).astimezone( tzinfo # type: ignore @@ -816,7 +717,7 @@ def sunrise( except ValueError as exc: if exc.args[0] == "math domain error": z = zenith(observer, noon(observer, date)) - if z > 90.0: + if z > 90: msg = "Sun is always below the horizon on this day, at this location." else: msg = "Sun is always above the horizon on this day, at this location." @@ -858,7 +759,7 @@ def sunset( tot = time_of_transit( observer, date, - 90.0 + SUN_APPARENT_RADIUS, + 90 + SUN_APPARENT_RADIUS, SunDirection.SETTING, ).astimezone( tzinfo # type: ignore @@ -875,7 +776,7 @@ def sunset( tot = time_of_transit( observer, new_date, - 90.0 + SUN_APPARENT_RADIUS, + 90 + SUN_APPARENT_RADIUS, SunDirection.SETTING, ).astimezone( tzinfo # type: ignore @@ -887,7 +788,7 @@ def sunset( except ValueError as exc: if exc.args[0] == "math domain error": z = zenith(observer, noon(observer, date)) - if z > 90.0: + if z > 90: msg = "Sun is always below the horizon on this day, at this location." else: msg = "Sun is always above the horizon on this day, at this location." @@ -928,7 +829,7 @@ def dusk( tzinfo = date.tzinfo or tzinfo date = date.date() - dep: float = 0.0 + dep: float = 0 if isinstance(depression, Depression): dep = depression.value else: @@ -936,7 +837,7 @@ def dusk( try: tot = time_of_transit( - observer, date, 90.0 + dep, SunDirection.SETTING + observer, date, 90 + dep, SunDirection.SETTING ).astimezone( tzinfo # type: ignore ) @@ -952,7 +853,7 @@ def dusk( tot = time_of_transit( observer, new_date, - 90.0 + dep, + 90 + dep, SunDirection.SETTING, ).astimezone( tzinfo # type: ignore diff --git a/src/test/almost_equal.py b/src/test/almost_equal.py index 0d3228e..972ebff 100644 --- a/src/test/almost_equal.py +++ b/src/test/almost_equal.py @@ -4,16 +4,14 @@ def datetime_almost_equal( datetime1: datetime.datetime, datetime2: datetime.datetime, seconds: int = 60 ): - if not (datetime1.tzinfo): - datetime1 = datetime1.replace(tzinfo=datetime.timezone.utc) - else: + if datetime1.tzinfo: datetime1 = datetime1.astimezone(datetime.timezone.utc) - - if not (datetime2.tzinfo): - datetime2 = datetime2.replace(tzinfo=datetime.timezone.utc) else: + datetime1 = datetime1.replace(tzinfo=datetime.timezone.utc) + + if datetime2.tzinfo: datetime2 = datetime2.astimezone(datetime.timezone.utc) + else: + datetime2 = datetime2.replace(tzinfo=datetime.timezone.utc) - dd = datetime1 - datetime2 - sd = (dd.days * 24 * 60 * 60) + dd.seconds - return abs(sd) <= seconds + return abs((datetime1 - datetime2).total_seconds()) <= seconds diff --git a/src/test/test_misc.py b/src/test/test_misc.py index 55c2cea..eefd737 100644 --- a/src/test/test_misc.py +++ b/src/test/test_misc.py @@ -1,6 +1,4 @@ # type: ignore -from datetime import timedelta - try: import zoneinfo except ImportError: @@ -10,14 +8,6 @@ from pytest import approx, raises from astral import dms_to_float, now, today -from astral.sun import minutes_to_timedelta - - -def test_MinutesToTimedelta(): - assert minutes_to_timedelta(720) == timedelta(seconds=720 * 60) - assert minutes_to_timedelta(722) == timedelta(seconds=722 * 60) - assert minutes_to_timedelta(722.2) == timedelta(seconds=722.2 * 60) - assert minutes_to_timedelta(722.5) == timedelta(seconds=722.5 * 60) class TestDMS: