def night( observer: Observer, date: Optional[datetime.date] = None, tzinfo: datetime.tzinfo = pytz.utc, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculate night start and end times. Night is calculated to be between astronomical dusk on the date specified and astronomical dawn of the next day. Args: observer: Observer to calculate night for date: Date to calculate for. Default is today's date for the specified tzinfo. tzinfo: Timezone to return times in. Default is UTC. Returns: A tuple of the date and time at which night starts and ends. Raises: ValueError: if dawn does not occur on the specified date or dusk on the following day """ if date is None: date = today(tzinfo) start = dusk(observer, date, 6, tzinfo) tomorrow = date + datetime.timedelta(days=1) end = dawn(observer, tomorrow, 6, tzinfo) return start, end
def sun( observer: Observer, date: Optional[datetime.date] = None, dawn_dusk_depression: Union[float, Depression] = Depression.CIVIL, tzinfo: datetime.tzinfo = pytz.utc, ) -> Dict: """Calculate all the info for the sun at once. Args: observer: Observer for which to calculate the times of the sun date: Date to calculate for. Default is today's date in the timezone `tzinfo`. dawn_dusk_depression: Depression to use to calculate dawn and dusk. Default is for Civil dusk i.e. 6.0 tzinfo: Timezone to return times in. Default is UTC. Returns: Dictionary with keys ``dawn``, ``sunrise``, ``noon``, ``sunset`` and ``dusk`` whose values are the results of the corresponding functions. Raises: ValueError: if passed through from any of the functions """ if date is None: date = today(tzinfo) return { "dawn": dawn(observer, date, dawn_dusk_depression, tzinfo), "sunrise": sunrise(observer, date, tzinfo), "noon": noon(observer, date, tzinfo), "sunset": sunset(observer, date, tzinfo), "dusk": dusk(observer, date, dawn_dusk_depression, tzinfo), }
def daylight( observer: Observer, date: Optional[datetime.date] = None, tzinfo: datetime.tzinfo = pytz.utc, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculate daylight start and end times. Args: observer: Observer to calculate daylight for date: Date to calculate for. Default is today's date in the timezone `tzinfo`. tzinfo: Timezone to return times in. Default is UTC. Returns: A tuple of the date and time at which daylight starts and ends. Raises: ValueError: if the sun does not rise or does not set """ if date is None: date = today(tzinfo) start = sunrise(observer, date, tzinfo) end = sunset(observer, date, tzinfo) return start, end
def phase(date: Optional[datetime.date] = None) -> float: """Calculates the phase of the moon on the specified date. Args: date: The date to calculate the phase for. Dates are always in the UTC timezone. If not specified then today's date is used. Returns: A number designating the phase. ============ ============== 0 .. 6.99 New moon 7 .. 13.99 First quarter 14 .. 20.99 Full moon 21 .. 27.99 Last quarter ============ ============== """ if date is None: date = today() moon = _phase_asfloat(date) if moon >= 28.0: moon -= 28.0 return moon
def midnight( observer: Observer, date: Optional[datetime.date] = None, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> datetime.datetime: """Calculate solar midnight time. Note: This calculates the solar midnight that is closest to 00:00:00 of the specified date i.e. it may return a time that is on the previous day. Args: observer: An observer viewing the sun at a specific, latitude, longitude and elevation date: Date to calculate for. Default is today for the specified tzinfo. tzinfo: Timezone to return times in. Default is UTC. Returns: Date and time at which midnight occurs. """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) jd = julianday(date) newt = jday_to_jcentury(jd + 0.5 + -observer.longitude / 360.0) 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) return pytz.utc.localize(midnight).astimezone(tzinfo) # pylint: disable=E1120
def noon( observer: Observer, date: Optional[datetime.date] = None, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> datetime.datetime: """Calculate solar noon time when the sun is at its highest point. Args: observer: An observer viewing the sun at a specific, latitude, longitude and elevation date: Date to calculate for. Default is today for the specified tzinfo. tzinfo: Timezone to return times in. Default is UTC. Returns: Date and time at which noon occurs. """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) jc = jday_to_jcentury(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) return pytz.utc.localize(noon).astimezone(tzinfo) # pylint: disable=E1120
def page7(): global lastday global sunrise_m global sunset_m now = time.localtime() hh = now.tm_hour mm = now.tm_min today = now.tm_mday ## partial clock setup daybegin = 8 dayend = 23 daymin = (hh * 60) + mm minbegin = daybegin * 60 minend = dayend * 60 if ((daymin >= minbegin) and (daymin <= minend)): tleft = (minend - daymin) / float(minend - minbegin) else: tleft = 0 ## angle for partial clock pangle = (180 * (1 - tleft)) - 90 ## angle for 24h clock cangle = 90 + (-360 * (1 - (daymin / float(24 * 60)))) ## sunrise sunset calculation if (lastday != today): lastday = today mysun = sun(observer, astral.today()) sunrise = mysun['sunrise'].astimezone(Berlin_tz) sunset = mysun['sunset'].astimezone(Berlin_tz) sunrise_m = int((sunrise.hour * 60) + sunrise.minute) sunset_m = int((sunset.hour * 60) + sunset.minute) now_m = (now.tm_hour * 60) + now.tm_min if ((now_m < sunrise_m) or (now_m > sunset_m)): sunscale = 0 else: sunscale = 1 - ((now_m - sunrise_m) / (sunset_m - sunrise_m)) sunangle = (180 * sunscale) + 90 ## draw dr.rectangle((0, 0, 128, 64), fill=0) dr.arc((-30, 0, 30, 60), pangle, 90, fill=1, width=20) dr.arc((34, 0, 94, 60), cangle, 90, fill=1, width=20) dr.arc((98, 0, 158, 60), 90, sunangle, fill=1, width=20)
def time_at_elevation( observer: Observer, elevation: float, date: Optional[datetime.date] = None, direction: SunDirection = SunDirection.RISING, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> datetime.datetime: """Calculates the time when the sun is at the specified elevation on the specified date. Note: This method uses positive elevations for those above the horizon. Elevations greater than 90 degrees are converted to a setting sun i.e. an elevation of 110 will calculate a setting sun at 70 degrees. Args: elevation: Elevation of the sun in degrees above the horizon to calculate for. observer: Observer to calculate for date: Date to calculate for. Default is today's date in the timezone `tzinfo`. direction: Determines whether the calculated time is for the sun rising or setting. Use ``SunDirection.RISING`` or ``SunDirection.SETTING``. Default is rising. tzinfo: Timezone to return times in. Default is UTC. Returns: Date and time at which the sun is at the specified elevation. """ if elevation > 90.0: elevation = 180.0 - elevation direction = SunDirection.SETTING if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) zenith = 90 - elevation try: return time_of_transit(observer, date, zenith, direction).astimezone(tzinfo) except ValueError as exc: if exc.args[0] == "math domain error": raise ValueError( f"Sun never reaches an elevation of {elevation} degrees " "at this location.") from exc else: raise
def rahukaalam( observer: Observer, date: Optional[datetime.date] = None, daytime: bool = True, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> Tuple[datetime.datetime, datetime.datetime]: """Calculate ruhakaalam times. Args: observer: Observer to calculate rahukaalam for date: Date to calculate for. Default is today's date in the timezone `tzinfo`. daytime: If True calculate for the day time else calculate for the night time. tzinfo: Timezone to return times in. Default is UTC. Returns: Tuple containing the start and end times for Rahukaalam. Raises: ValueError: if the sun does not rise or does not set """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) if daytime: start = sunrise(observer, date, tzinfo) end = sunset(observer, date, tzinfo) else: start = sunset(observer, date, tzinfo) oneday = datetime.timedelta(days=1) end = sunrise(observer, date + oneday, tzinfo) octant_duration = datetime.timedelta(seconds=(end - start).seconds / 8) # Mo,Sa,Fr,We,Th,Tu,Su octant_index = [1, 6, 4, 5, 3, 2, 7] weekday = date.weekday() octant = octant_index[weekday] start = start + (octant_duration * octant) end = start + octant_duration return start, end
def twilight( observer: Observer, date: Optional[datetime.date] = None, direction: SunDirection = SunDirection.RISING, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> Tuple[datetime.datetime, datetime.datetime]: """Returns the start and end times of Twilight when the sun is traversing in the specified direction. This method defines twilight as being between the time when the sun is at -6 degrees and sunrise/sunset. Args: observer: Observer to calculate twilight for date: Date for which to calculate the times. Default is today's date in the timezone `tzinfo`. direction: Determines whether the time is for the sun rising or setting. Use ``astral.SunDirection.RISING`` or ``astral.SunDirection.SETTING``. tzinfo: Timezone to return times in. Default is UTC. Returns: A tuple of the date and time at which twilight starts and ends. Raises: ValueError: if the sun does not rise or does not set """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) start = time_of_transit(observer, date, 90 + 6, direction).astimezone(tzinfo) if direction == SunDirection.RISING: end = sunrise(observer, date, tzinfo).astimezone(tzinfo) else: end = sunset(observer, date, tzinfo).astimezone(tzinfo) if direction == SunDirection.RISING: return start, end else: return end, start
def dusk( observer: Observer, date: Optional[datetime.date] = None, depression: Union[float, Depression] = Depression.CIVIL, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> datetime.datetime: """Calculate dusk time. Args: observer: Observer to calculate dusk for date: Date to calculate for. Default is today's date in the timezone `tzinfo`. depression: Number of degrees below the horizon to use to calculate dusk. Default is for Civil dusk i.e. 6.0 tzinfo: Timezone to return times in. Default is UTC. Returns: Date and time at which dusk occurs. Raises: ValueError: if dusk does not occur on the specified date """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) dep: float = 0.0 if isinstance(depression, Depression): dep = depression.value else: dep = depression try: return time_of_transit(observer, date, 90.0 + dep, SunDirection.SETTING).astimezone(tzinfo) except ValueError as exc: if exc.args[0] == "math domain error": raise ValueError( f"Sun never reaches {dep} degrees below the horizon, at this location." ) from exc else: raise
def golden_hour( observer: Observer, date: Optional[datetime.date] = None, direction: SunDirection = SunDirection.RISING, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> Tuple[datetime.datetime, datetime.datetime]: """Returns the start and end times of the Golden Hour when the sun is traversing in the specified direction. This method uses the definition from PhotoPills i.e. the golden hour is when the sun is between 4 degrees below the horizon and 6 degrees above. Args: observer: Observer to calculate the golden hour for date: Date for which to calculate the times. Default is today's date in the timezone `tzinfo`. direction: Determines whether the time is for the sun rising or setting. Use ``SunDirection.RISING`` or ``SunDirection.SETTING``. tzinfo: Timezone to return times in. Default is UTC. Returns: A tuple of the date and time at which the Golden Hour starts and ends. Raises: ValueError: if the sun does not transit the elevations -4 & +6 degrees """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) start = time_of_transit(observer, date, 90 + 4, direction).astimezone(tzinfo) end = time_of_transit(observer, date, 90 - 6, direction).astimezone(tzinfo) if direction == SunDirection.RISING: return start, end else: return end, start
def sunset( observer: Observer, date: Optional[datetime.date] = None, tzinfo: Union[str, datetime.tzinfo] = pytz.utc, ) -> datetime.datetime: """Calculate sunset time. Args: observer: Observer to calculate sunset for date: Date to calculate for. Default is today's date in the timezone `tzinfo`. tzinfo: Timezone to return times in. Default is UTC. Returns: Date and time at which sunset occurs. Raises: ValueError: if the sun does not reach the horizon """ if isinstance(tzinfo, str): tzinfo = pytz.timezone(tzinfo) if date is None: date = today(tzinfo) try: return time_of_transit( observer, date, 90.0 + SUN_APPARENT_RADIUS, SunDirection.SETTING, ).astimezone(tzinfo) except ValueError as exc: if exc.args[0] == "math domain error": z = zenith(observer, noon(observer, date)) if z > 90.0: 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." raise ValueError(msg) from exc else: raise
def test_australia(self): assert today(timezone("Australia/Melbourne")).day == 2
def test_adak(self): assert today(timezone("America/Adak")).day == 1
def test_ElevationEqualsTimeAtElevation(elevation, london): o = london.observer td = today() et = sun.time_at_elevation(o, elevation, td) assert sun.elevation(o, et) == pytest.approx(elevation, abs=0.05)
def today(self, local: bool = True) -> datetime.date: if local: return today(self.tzinfo) else: return today()
def test_default_timezone(self): td = today() assert td.year == 2020 assert td.month == 1 assert td.day == 1