A library that computes the ephemerides.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

358 lignes
21 KiB

  1. #!/usr/bin/env python3
  2. # Kosmorrolib - The Library To Compute Your Ephemerides
  3. # Copyright (C) 2021 Jérôme Deuchnord <jerome@deuchnord.fr>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. from datetime import date, datetime, timedelta
  18. from typing import Union
  19. from skyfield.searchlib import find_discrete, find_maxima
  20. from skyfield.timelib import Time
  21. from skyfield.constants import tau
  22. from skyfield.errors import EphemerisRangeError
  23. from .model import Position, AsterEphemerides, MoonPhase, Object, ASTERS
  24. from .dateutil import translate_to_utc_offset, normalize_datetime
  25. from .core import (
  26. get_skf_objects,
  27. get_timescale,
  28. get_iau2000b,
  29. deprecated,
  30. alert_deprecation,
  31. )
  32. from .enum import MoonPhaseType
  33. from .exceptions import OutOfRangeDateError
  34. RISEN_ANGLE = -0.8333
  35. def _get_skyfield_to_moon_phase(
  36. times: [Time], vals: [int], now: Time, utc_offset: Union[int, float]
  37. ) -> Union[MoonPhase, None]:
  38. tomorrow = get_timescale().utc(
  39. now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1
  40. )
  41. next_phase_time = None
  42. i = 0
  43. # Find the current moon phase:
  44. for i, time in enumerate(times):
  45. if now.utc_datetime() <= time.utc_datetime():
  46. if time.utc_datetime() >= tomorrow.utc_datetime():
  47. i -= 1
  48. break
  49. current_phase = MoonPhaseType(vals[i])
  50. if current_phase in [
  51. MoonPhaseType.NEW_MOON,
  52. MoonPhaseType.FIRST_QUARTER,
  53. MoonPhaseType.FULL_MOON,
  54. MoonPhaseType.LAST_QUARTER,
  55. ]:
  56. current_phase_time = translate_to_utc_offset(
  57. times[i].utc_datetime(), utc_offset
  58. )
  59. else:
  60. current_phase_time = None
  61. # Find the next moon phase
  62. for j in range(i + 1, len(times)):
  63. if vals[j] in [0, 2, 4, 6]:
  64. next_phase_time = translate_to_utc_offset(
  65. times[j].utc_datetime(), utc_offset
  66. )
  67. break
  68. return MoonPhase(current_phase, current_phase_time, next_phase_time)
  69. def get_moon_phase(
  70. for_date: date = date.today(), utc_offset: Union[int, float] = 0, **argv
  71. ) -> MoonPhase:
  72. """Calculate and return the moon phase for the given date, adjusted to the given UTC offset if any.
  73. Get the moon phase for the 27 March, 2021:
  74. >>> get_moon_phase(date(2021, 3, 27))
  75. <MoonPhase phase_type=MoonPhaseType.WAXING_GIBBOUS time=None next_phase_date=2021-03-28 18:48:10.902298+00:00>
  76. When the moon phase is a new moon, a first quarter, a full moon or a last quarter, you get the exact time
  77. of its happening too:
  78. >>> get_moon_phase(datetime(2021, 3, 28))
  79. <MoonPhase phase_type=MoonPhaseType.FULL_MOON time=2021-03-28 18:48:10.902298+00:00 next_phase_date=2021-04-04 10:02:27.393689+00:00>
  80. Get the moon phase for the 27 March, 2021, in UTC+2:
  81. >>> get_moon_phase(date(2021, 3, 27), utc_offset=2)
  82. <MoonPhase phase_type=MoonPhaseType.WAXING_GIBBOUS time=None next_phase_date=2021-03-28 20:48:10.902298+02:00>
  83. Note that the `utc_offset` argument was named `timezone` before version 1.1. The old name still works, but will be dropped in the future.
  84. >>> get_moon_phase(date(2021, 3, 27), timezone=2)
  85. <MoonPhase phase_type=MoonPhaseType.WAXING_GIBBOUS time=None next_phase_date=2021-03-28 20:48:10.902298+02:00>
  86. Note that the moon phase can only be computed for a date range.
  87. Asking for the moon phase with an out of range date will result in an exception:
  88. >>> get_moon_phase(date(1000, 1, 1))
  89. Traceback (most recent call last):
  90. ...
  91. kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-08-09 and 2053-09-26
  92. """
  93. if argv.get("timezone") is not None:
  94. alert_deprecation(
  95. "'timezone' argument of the get_moon_phase() function is deprecated. Use utc_offset instead."
  96. )
  97. utc_offset = argv.get("timezone")
  98. earth = get_skf_objects()["earth"]
  99. moon = get_skf_objects()["moon"]
  100. sun = get_skf_objects()["sun"]
  101. def moon_phase_at(time: Time):
  102. time._nutation_angles = get_iau2000b(time)
  103. current_earth = earth.at(time)
  104. _, mlon, _ = current_earth.observe(moon).apparent().ecliptic_latlon("date")
  105. _, slon, _ = current_earth.observe(sun).apparent().ecliptic_latlon("date")
  106. return (((mlon.radians - slon.radians) // (tau / 8)) % 8).astype(int)
  107. moon_phase_at.rough_period = 7.0 # one lunar phase per week
  108. today = get_timescale().utc(for_date.year, for_date.month, for_date.day)
  109. start_time = get_timescale().utc(for_date.year, for_date.month, for_date.day - 10)
  110. end_time = get_timescale().utc(for_date.year, for_date.month, for_date.day + 10)
  111. try:
  112. times, phases = find_discrete(start_time, end_time, moon_phase_at)
  113. return _get_skyfield_to_moon_phase(times, phases, today, utc_offset)
  114. except EphemerisRangeError as error:
  115. start = translate_to_utc_offset(error.start_time.utc_datetime(), utc_offset)
  116. end = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset)
  117. start = date(start.year, start.month, start.day) + timedelta(days=12)
  118. end = date(end.year, end.month, end.day) - timedelta(days=12)
  119. raise OutOfRangeDateError(start, end) from error
  120. def get_ephemerides(
  121. position: Position,
  122. for_date: date = date.today(),
  123. utc_offset: Union[int, float] = 0,
  124. **argv
  125. ) -> [AsterEphemerides]:
  126. """Compute and return the ephemerides for the given position and date, adjusted to the given UTC offset if any.
  127. Compute the ephemerides for July 7th, 2022:
  128. >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7))
  129. [<AsterEphemerides rise_time=2022-07-07 04:29:00 culmination_time=2022-07-07 11:46:00 set_time=2022-07-07 19:02:00 aster=<Object type=STAR name=SUN />>,
  130. <AsterEphemerides rise_time=2022-07-07 12:16:00 culmination_time=2022-07-07 18:06:00 set_time=2022-07-07 23:54:00 aster=<Object type=SATELLITE name=MOON />>,
  131. <AsterEphemerides rise_time=2022-07-07 03:36:00 culmination_time=2022-07-07 10:58:00 set_time=2022-07-07 18:20:00 aster=<Object type=PLANET name=MERCURY />>,
  132. <AsterEphemerides rise_time=2022-07-07 02:30:00 culmination_time=2022-07-07 09:44:00 set_time=2022-07-07 16:58:00 aster=<Object type=PLANET name=VENUS />>,
  133. <AsterEphemerides rise_time=2022-07-07 00:05:00 culmination_time=2022-07-07 06:39:00 set_time=2022-07-07 13:14:00 aster=<Object type=PLANET name=MARS />>,
  134. <AsterEphemerides rise_time=2022-07-07 22:59:00 culmination_time=2022-07-07 05:11:00 set_time=2022-07-07 11:20:00 aster=<Object type=PLANET name=JUPITER />>,
  135. <AsterEphemerides rise_time=2022-07-07 21:06:00 culmination_time=2022-07-07 02:29:00 set_time=2022-07-07 07:48:00 aster=<Object type=PLANET name=SATURN />>,
  136. <AsterEphemerides rise_time=2022-07-07 00:47:00 culmination_time=2022-07-07 07:42:00 set_time=2022-07-07 14:38:00 aster=<Object type=PLANET name=URANUS />>,
  137. <AsterEphemerides rise_time=2022-07-07 22:27:00 culmination_time=2022-07-07 04:25:00 set_time=2022-07-07 10:20:00 aster=<Object type=PLANET name=NEPTUNE />>,
  138. <AsterEphemerides rise_time=2022-07-07 19:46:00 culmination_time=2022-07-07 00:41:00 set_time=2022-07-07 05:33:00 aster=<Object type=PLANET name=PLUTO />>]
  139. UTC offset can be optionnaly set to adapt the hours to your location:
  140. >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7), utc_offset=2)
  141. [<AsterEphemerides rise_time=2022-07-07 06:29:00 culmination_time=2022-07-07 13:46:00 set_time=2022-07-07 21:02:00 aster=<Object type=STAR name=SUN />>,
  142. <AsterEphemerides rise_time=2022-07-07 14:16:00 culmination_time=2022-07-07 20:06:00 set_time=2022-07-07 01:27:00 aster=<Object type=SATELLITE name=MOON />>,
  143. <AsterEphemerides rise_time=2022-07-07 05:36:00 culmination_time=2022-07-07 12:58:00 set_time=2022-07-07 20:20:00 aster=<Object type=PLANET name=MERCURY />>,
  144. <AsterEphemerides rise_time=2022-07-07 04:30:00 culmination_time=2022-07-07 11:44:00 set_time=2022-07-07 18:58:00 aster=<Object type=PLANET name=VENUS />>,
  145. <AsterEphemerides rise_time=2022-07-07 02:05:00 culmination_time=2022-07-07 08:39:00 set_time=2022-07-07 15:14:00 aster=<Object type=PLANET name=MARS />>,
  146. <AsterEphemerides rise_time=2022-07-07 01:02:00 culmination_time=2022-07-07 07:11:00 set_time=2022-07-07 13:20:00 aster=<Object type=PLANET name=JUPITER />>,
  147. <AsterEphemerides rise_time=2022-07-07 23:06:00 culmination_time=2022-07-07 04:29:00 set_time=2022-07-07 09:48:00 aster=<Object type=PLANET name=SATURN />>,
  148. <AsterEphemerides rise_time=2022-07-07 02:47:00 culmination_time=2022-07-07 09:42:00 set_time=2022-07-07 16:38:00 aster=<Object type=PLANET name=URANUS />>,
  149. <AsterEphemerides rise_time=2022-07-07 00:31:00 culmination_time=2022-07-07 06:25:00 set_time=2022-07-07 12:20:00 aster=<Object type=PLANET name=NEPTUNE />>,
  150. <AsterEphemerides rise_time=2022-07-07 21:46:00 culmination_time=2022-07-07 02:41:00 set_time=2022-07-07 07:33:00 aster=<Object type=PLANET name=PLUTO />>]
  151. Note that the `utc_offset` argument was named `timezone` before version 1.1. The old name still works, but will be dropped in the future.
  152. >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7), timezone=2)
  153. [<AsterEphemerides rise_time=2022-07-07 06:29:00 culmination_time=2022-07-07 13:46:00 set_time=2022-07-07 21:02:00 aster=<Object type=STAR name=SUN />>,
  154. <AsterEphemerides rise_time=2022-07-07 14:16:00 culmination_time=2022-07-07 20:06:00 set_time=2022-07-07 01:27:00 aster=<Object type=SATELLITE name=MOON />>,
  155. <AsterEphemerides rise_time=2022-07-07 05:36:00 culmination_time=2022-07-07 12:58:00 set_time=2022-07-07 20:20:00 aster=<Object type=PLANET name=MERCURY />>,
  156. <AsterEphemerides rise_time=2022-07-07 04:30:00 culmination_time=2022-07-07 11:44:00 set_time=2022-07-07 18:58:00 aster=<Object type=PLANET name=VENUS />>,
  157. <AsterEphemerides rise_time=2022-07-07 02:05:00 culmination_time=2022-07-07 08:39:00 set_time=2022-07-07 15:14:00 aster=<Object type=PLANET name=MARS />>,
  158. <AsterEphemerides rise_time=2022-07-07 01:02:00 culmination_time=2022-07-07 07:11:00 set_time=2022-07-07 13:20:00 aster=<Object type=PLANET name=JUPITER />>,
  159. <AsterEphemerides rise_time=2022-07-07 23:06:00 culmination_time=2022-07-07 04:29:00 set_time=2022-07-07 09:48:00 aster=<Object type=PLANET name=SATURN />>,
  160. <AsterEphemerides rise_time=2022-07-07 02:47:00 culmination_time=2022-07-07 09:42:00 set_time=2022-07-07 16:38:00 aster=<Object type=PLANET name=URANUS />>,
  161. <AsterEphemerides rise_time=2022-07-07 00:31:00 culmination_time=2022-07-07 06:25:00 set_time=2022-07-07 12:20:00 aster=<Object type=PLANET name=NEPTUNE />>,
  162. <AsterEphemerides rise_time=2022-07-07 21:46:00 culmination_time=2022-07-07 02:41:00 set_time=2022-07-07 07:33:00 aster=<Object type=PLANET name=PLUTO />>]
  163. Objects may not rise or set on the given date (e.g. they rise the previous day or set the next day).
  164. In this case, you will get `None` values on the rise or set time.
  165. If an objet does not rise nor set due to your latitude, then both rise and set will be `None`:
  166. >>> north_pole = Position(70, 20)
  167. >>> south_pole = Position(-70, 20)
  168. >>> get_ephemerides(north_pole, date(2021, 6, 20))
  169. [<AsterEphemerides rise_time=None culmination_time=2021-06-20 10:42:00 set_time=None aster=<Object type=STAR name=SUN />>,
  170. <AsterEphemerides rise_time=2021-06-20 14:30:00 culmination_time=2021-06-20 18:44:00 set_time=2021-06-20 22:53:00 aster=<Object type=SATELLITE name=MOON />>,
  171. <AsterEphemerides rise_time=2021-06-20 22:56:00 culmination_time=2021-06-20 09:47:00 set_time=2021-06-20 20:34:00 aster=<Object type=PLANET name=MERCURY />>,
  172. <AsterEphemerides rise_time=None culmination_time=2021-06-20 12:20:00 set_time=None aster=<Object type=PLANET name=VENUS />>,
  173. <AsterEphemerides rise_time=None culmination_time=2021-06-20 13:17:00 set_time=None aster=<Object type=PLANET name=MARS />>,
  174. <AsterEphemerides rise_time=2021-06-20 23:06:00 culmination_time=2021-06-20 03:04:00 set_time=2021-06-20 06:58:00 aster=<Object type=PLANET name=JUPITER />>,
  175. <AsterEphemerides rise_time=2021-06-20 23:28:00 culmination_time=2021-06-20 01:48:00 set_time=2021-06-20 04:05:00 aster=<Object type=PLANET name=SATURN />>,
  176. <AsterEphemerides rise_time=2021-06-20 21:53:00 culmination_time=2021-06-20 07:29:00 set_time=2021-06-20 17:02:00 aster=<Object type=PLANET name=URANUS />>,
  177. <AsterEphemerides rise_time=2021-06-20 22:51:00 culmination_time=2021-06-20 04:22:00 set_time=2021-06-20 09:50:00 aster=<Object type=PLANET name=NEPTUNE />>,
  178. <AsterEphemerides rise_time=None culmination_time=2021-06-20 00:40:00 set_time=None aster=<Object type=PLANET name=PLUTO />>]
  179. >>> get_ephemerides(north_pole, date(2021, 12, 21))
  180. [<AsterEphemerides rise_time=None culmination_time=2021-12-21 10:38:00 set_time=None aster=<Object type=STAR name=SUN />>,
  181. <AsterEphemerides rise_time=None culmination_time=2021-12-21 00:04:00 set_time=None aster=<Object type=SATELLITE name=MOON />>,
  182. <AsterEphemerides rise_time=None culmination_time=2021-12-21 11:33:00 set_time=None aster=<Object type=PLANET name=MERCURY />>,
  183. <AsterEphemerides rise_time=2021-12-21 11:58:00 culmination_time=2021-12-21 12:33:00 set_time=2021-12-21 13:08:00 aster=<Object type=PLANET name=VENUS />>,
  184. <AsterEphemerides rise_time=None culmination_time=2021-12-21 08:54:00 set_time=None aster=<Object type=PLANET name=MARS />>,
  185. <AsterEphemerides rise_time=2021-12-21 11:07:00 culmination_time=2021-12-21 14:43:00 set_time=2021-12-21 18:19:00 aster=<Object type=PLANET name=JUPITER />>,
  186. <AsterEphemerides rise_time=2021-12-21 11:32:00 culmination_time=2021-12-21 13:33:00 set_time=2021-12-21 15:33:00 aster=<Object type=PLANET name=SATURN />>,
  187. <AsterEphemerides rise_time=2021-12-21 09:54:00 culmination_time=2021-12-21 19:13:00 set_time=2021-12-21 04:37:00 aster=<Object type=PLANET name=URANUS />>,
  188. <AsterEphemerides rise_time=2021-12-21 10:49:00 culmination_time=2021-12-21 16:05:00 set_time=2021-12-21 21:21:00 aster=<Object type=PLANET name=NEPTUNE />>,
  189. <AsterEphemerides rise_time=None culmination_time=2021-12-21 12:31:00 set_time=None aster=<Object type=PLANET name=PLUTO />>]
  190. >>> get_ephemerides(south_pole, date(2021, 6, 20))
  191. [<AsterEphemerides rise_time=None culmination_time=2021-06-20 10:42:00 set_time=None aster=<Object type=STAR name=SUN />>,
  192. <AsterEphemerides rise_time=2021-06-20 11:10:00 culmination_time=2021-06-20 19:06:00 set_time=2021-06-20 01:20:00 aster=<Object type=SATELLITE name=MOON />>,
  193. <AsterEphemerides rise_time=2021-06-20 07:47:00 culmination_time=2021-06-20 09:47:00 set_time=2021-06-20 11:48:00 aster=<Object type=PLANET name=MERCURY />>,
  194. <AsterEphemerides rise_time=None culmination_time=2021-06-20 12:20:00 set_time=None aster=<Object type=PLANET name=VENUS />>,
  195. <AsterEphemerides rise_time=2021-06-20 12:14:00 culmination_time=2021-06-20 13:17:00 set_time=2021-06-20 14:21:00 aster=<Object type=PLANET name=MARS />>,
  196. <AsterEphemerides rise_time=2021-06-20 18:32:00 culmination_time=2021-06-20 03:04:00 set_time=2021-06-20 11:32:00 aster=<Object type=PLANET name=JUPITER />>,
  197. <AsterEphemerides rise_time=2021-06-20 15:20:00 culmination_time=2021-06-20 01:48:00 set_time=2021-06-20 12:12:00 aster=<Object type=PLANET name=SATURN />>,
  198. <AsterEphemerides rise_time=2021-06-20 04:32:00 culmination_time=2021-06-20 07:29:00 set_time=2021-06-20 10:26:00 aster=<Object type=PLANET name=URANUS />>,
  199. <AsterEphemerides rise_time=2021-06-20 21:28:00 culmination_time=2021-06-20 04:22:00 set_time=2021-06-20 11:13:00 aster=<Object type=PLANET name=NEPTUNE />>,
  200. <AsterEphemerides rise_time=None culmination_time=2021-06-20 00:40:00 set_time=None aster=<Object type=PLANET name=PLUTO />>]
  201. >>> get_ephemerides(south_pole, date(2021, 12, 22))
  202. [<AsterEphemerides rise_time=None culmination_time=2021-12-22 10:39:00 set_time=None aster=<Object type=STAR name=SUN />>,
  203. <AsterEphemerides rise_time=None culmination_time=2021-12-22 01:01:00 set_time=None aster=<Object type=SATELLITE name=MOON />>,
  204. <AsterEphemerides rise_time=None culmination_time=2021-12-22 11:35:00 set_time=None aster=<Object type=PLANET name=MERCURY />>,
  205. <AsterEphemerides rise_time=None culmination_time=2021-12-22 12:27:00 set_time=None aster=<Object type=PLANET name=VENUS />>,
  206. <AsterEphemerides rise_time=None culmination_time=2021-12-22 08:53:00 set_time=None aster=<Object type=PLANET name=MARS />>,
  207. <AsterEphemerides rise_time=2021-12-22 05:52:00 culmination_time=2021-12-22 14:40:00 set_time=2021-12-22 23:26:00 aster=<Object type=PLANET name=JUPITER />>,
  208. <AsterEphemerides rise_time=2021-12-22 02:41:00 culmination_time=2021-12-22 13:29:00 set_time=2021-12-22 00:21:00 aster=<Object type=PLANET name=SATURN />>,
  209. <AsterEphemerides rise_time=2021-12-22 16:01:00 culmination_time=2021-12-22 19:09:00 set_time=2021-12-22 22:17:00 aster=<Object type=PLANET name=URANUS />>,
  210. <AsterEphemerides rise_time=2021-12-22 08:59:00 culmination_time=2021-12-22 16:01:00 set_time=2021-12-22 23:04:00 aster=<Object type=PLANET name=NEPTUNE />>,
  211. <AsterEphemerides rise_time=None culmination_time=2021-12-22 12:27:00 set_time=None aster=<Object type=PLANET name=PLUTO />>]
  212. Please note:
  213. - The ephemerides can only be computed for a date range. Asking for the ephemerides with an out of range date will result in an exception:
  214. >>> get_ephemerides(Position(50.5824, 3.0624), date(1000, 1, 1))
  215. Traceback (most recent call last):
  216. ...
  217. kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-07-29 and 2053-10-07
  218. - The date given in parameter is considered as being given from the point of view of the given UTC offset.
  219. Using a UTC offset that does not correspond to the place's actual one can impact the returned times.
  220. """
  221. if argv.get("timezone") is not None:
  222. alert_deprecation(
  223. "'timezone' argument of the get_ephemerides() function is deprecated. Use utc_offset instead."
  224. )
  225. utc_offset = argv.get("timezone")
  226. def get_angle(for_aster: Object):
  227. def fun(time: Time) -> float:
  228. return (
  229. position.get_planet_topos()
  230. .at(time)
  231. .observe(for_aster.skyfield_object)
  232. .apparent()
  233. .altaz()[0]
  234. .degrees
  235. )
  236. fun.rough_period = 1.0
  237. return fun
  238. def is_risen(for_aster: Object):
  239. def fun(time: Time) -> bool:
  240. return get_angle(for_aster)(time) > RISEN_ANGLE
  241. fun.rough_period = 0.5
  242. return fun
  243. # The date given in argument is supposed to be given in the given UTC offset (more natural for a human),
  244. # but we need it in UTC. Subtracting the offset to get it in UTC.
  245. start_time = get_timescale().utc(
  246. for_date.year, for_date.month, for_date.day, -utc_offset
  247. )
  248. end_time = get_timescale().utc(
  249. for_date.year, for_date.month, for_date.day + 1, -utc_offset
  250. )
  251. ephemerides = []
  252. try:
  253. for aster in ASTERS:
  254. times, risen_info = find_discrete(start_time, end_time, is_risen(aster))
  255. culmination_time, _ = find_maxima(start_time, end_time, get_angle(aster))
  256. rise_time, set_time = None, None
  257. culmination_time = (
  258. culmination_time[0] if len(culmination_time) == 1 else None
  259. )
  260. for i, time in enumerate(times):
  261. time_dt = normalize_datetime(
  262. translate_to_utc_offset(time.utc_datetime(), to_tz=utc_offset)
  263. )
  264. if time_dt is not None and time_dt.day != for_date.day:
  265. continue
  266. if risen_info[i]:
  267. rise_time = time_dt
  268. else:
  269. set_time = time_dt
  270. if culmination_time is not None:
  271. culmination_time = normalize_datetime(
  272. translate_to_utc_offset(
  273. culmination_time.utc_datetime(),
  274. to_tz=utc_offset,
  275. )
  276. )
  277. ephemerides.append(
  278. AsterEphemerides(rise_time, culmination_time, set_time, aster=aster)
  279. )
  280. except EphemerisRangeError as error:
  281. start = translate_to_utc_offset(error.start_time.utc_datetime(), utc_offset)
  282. end = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset)
  283. start = date(start.year, start.month, start.day + 1)
  284. end = date(end.year, end.month, end.day - 1)
  285. raise OutOfRangeDateError(start, end) from error
  286. return ephemerides