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.

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