From 437f8c81bb591e8dd86143e3e6c72b31a2c51f3e Mon Sep 17 00:00:00 2001 From: Deuchnord Date: Sun, 1 Jun 2025 10:53:08 +0200 Subject: [PATCH] feat: deprecate `timezone` argument in favor of `utc_offset` --- kosmorrolib/dateutil.py | 10 +-- kosmorrolib/ephemerides.py | 99 ++++++++++++++++++++------- kosmorrolib/events.py | 137 ++++++++++++++++++++++++------------- 3 files changed, 167 insertions(+), 79 deletions(-) diff --git a/kosmorrolib/dateutil.py b/kosmorrolib/dateutil.py index 6d0c96a..929d3b3 100644 --- a/kosmorrolib/dateutil.py +++ b/kosmorrolib/dateutil.py @@ -19,18 +19,18 @@ from datetime import datetime, timezone, timedelta -def translate_to_timezone(date: datetime, to_tz: int): - """Convert a datetime from a timezone to another. +def translate_to_utc_offset(date: datetime, to_tz: int): + """Convert a datetime from a UTC offset to another. - >>> translate_to_timezone(datetime(2021, 6, 9, 5, 0, 0, tzinfo=timezone.utc), 2) + >>> translate_to_utc_offset(datetime(2021, 6, 9, 5, 0, 0, tzinfo=timezone.utc), 2) datetime.datetime(2021, 6, 9, 7, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200))) - >>> translate_to_timezone(datetime(2021, 6, 9, 5, 0, 0, tzinfo=timezone(timedelta(hours=1))), 2) + >>> translate_to_utc_offset(datetime(2021, 6, 9, 5, 0, 0, tzinfo=timezone(timedelta(hours=1))), 2) datetime.datetime(2021, 6, 9, 6, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200))) If the datetime has no timezone information, then it is interpreted as UTC: - >>> translate_to_timezone(datetime(2021, 6, 9, 5, 0, 0), 2) + >>> translate_to_utc_offset(datetime(2021, 6, 9, 5, 0, 0), 2) datetime.datetime(2021, 6, 9, 7, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200))) """ source_tz = date.tzinfo if date.tzinfo is not None else timezone.utc diff --git a/kosmorrolib/ephemerides.py b/kosmorrolib/ephemerides.py index ff211bb..88a7ed9 100644 --- a/kosmorrolib/ephemerides.py +++ b/kosmorrolib/ephemerides.py @@ -25,8 +25,14 @@ from skyfield.errors import EphemerisRangeError from .model import Position, AsterEphemerides, MoonPhase, Object, ASTERS -from .dateutil import translate_to_timezone, normalize_datetime -from .core import get_skf_objects, get_timescale, get_iau2000b +from .dateutil import translate_to_utc_offset, normalize_datetime +from .core import ( + get_skf_objects, + get_timescale, + get_iau2000b, + deprecated, + alert_deprecation, +) from .enum import MoonPhaseType from .exceptions import OutOfRangeDateError @@ -34,7 +40,7 @@ def _get_skyfield_to_moon_phase( - times: [Time], vals: [int], now: Time, timezone: int + times: [Time], vals: [int], now: Time, utc_offset: Union[int, float] ) -> Union[MoonPhase, None]: tomorrow = get_timescale().utc( now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1 @@ -58,21 +64,27 @@ def _get_skyfield_to_moon_phase( MoonPhaseType.FULL_MOON, MoonPhaseType.LAST_QUARTER, ]: - current_phase_time = translate_to_timezone(times[i].utc_datetime(), timezone) + current_phase_time = translate_to_utc_offset( + times[i].utc_datetime(), utc_offset + ) else: current_phase_time = None # Find the next moon phase for j in range(i + 1, len(times)): if vals[j] in [0, 2, 4, 6]: - next_phase_time = translate_to_timezone(times[j].utc_datetime(), timezone) + next_phase_time = translate_to_utc_offset( + times[j].utc_datetime(), utc_offset + ) break return MoonPhase(current_phase, current_phase_time, next_phase_time) -def get_moon_phase(for_date: date = date.today(), timezone: int = 0) -> MoonPhase: - """Calculate and return the moon phase for the given date, adjusted to the given timezone if any. +def get_moon_phase( + for_date: date = date.today(), utc_offset: Union[int, float] = 0, **argv +) -> MoonPhase: + """Calculate and return the moon phase for the given date, adjusted to the given UTC offset if any. Get the moon phase for the 27 March, 2021: @@ -85,7 +97,12 @@ def get_moon_phase(for_date: date = date.today(), timezone: int = 0) -> MoonPhas >>> get_moon_phase(datetime(2021, 3, 28)) - Get the moon phase for the 27 March, 2021, in the UTC+2 timezone: + Get the moon phase for the 27 March, 2021, in UTC+2: + + >>> get_moon_phase(date(2021, 3, 27), utc_offset=2) + + + 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. >>> get_moon_phase(date(2021, 3, 27), timezone=2) @@ -98,6 +115,13 @@ def get_moon_phase(for_date: date = date.today(), timezone: int = 0) -> MoonPhas ... kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-08-09 and 2053-09-26 """ + + if argv.get("timezone") is not None: + alert_deprecation( + "'timezone' argument of the get_moon_phase() function is deprecated. Use utc_offset instead." + ) + utc_offset = argv.get("timezone") + earth = get_skf_objects()["earth"] moon = get_skf_objects()["moon"] sun = get_skf_objects()["sun"] @@ -117,11 +141,11 @@ def moon_phase_at(time: Time): try: times, phases = find_discrete(start_time, end_time, moon_phase_at) - return _get_skyfield_to_moon_phase(times, phases, today, timezone) + return _get_skyfield_to_moon_phase(times, phases, today, utc_offset) except EphemerisRangeError as error: - start = translate_to_timezone(error.start_time.utc_datetime(), timezone) - end = translate_to_timezone(error.end_time.utc_datetime(), timezone) + start = translate_to_utc_offset(error.start_time.utc_datetime(), utc_offset) + end = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset) start = date(start.year, start.month, start.day) + timedelta(days=12) end = date(end.year, end.month, end.day) - timedelta(days=12) @@ -130,9 +154,12 @@ def moon_phase_at(time: Time): def get_ephemerides( - position: Position, for_date: date = date.today(), timezone: int = 0 + position: Position, + for_date: date = date.today(), + utc_offset: Union[int, float] = 0, + **argv ) -> [AsterEphemerides]: - """Compute and return the ephemerides for the given position and date, adjusted to the given timezone if any. + """Compute and return the ephemerides for the given position and date, adjusted to the given UTC offset if any. Compute the ephemerides for July 7th, 2022: @@ -148,9 +175,9 @@ def get_ephemerides( >, >] - Timezone can be optionnaly set to adapt the hours to your location: + UTC offset can be optionnaly set to adapt the hours to your location: - >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7), timezone=2) + >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7), utc_offset=2) [>, >, >, @@ -163,6 +190,20 @@ def get_ephemerides( >] + 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. + + >>> get_ephemerides(Position(36.6794, 4.8555), date(2022, 7, 7), timezone=2) + [>, + >, + >, + >, + >, + >, + >, + >, + >, + >] + Objects may not rise or set on the given date (e.g. they rise the previous day or set the next day). In this case, you will get `None` values on the rise or set time. @@ -226,10 +267,16 @@ def get_ephemerides( ... kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-07-29 and 2053-10-07 - - The date given in parameter is considered as being given from the point of view of the given timezone. - Using a timezone that does not correspond to the place's actual one can impact the returned times. + - The date given in parameter is considered as being given from the point of view of the given UTC offset. + Using a UTC offset that does not correspond to the place's actual one can impact the returned times. """ + if argv.get("timezone") is not None: + alert_deprecation( + "'timezone' argument of the get_ephemerides() function is deprecated. Use utc_offset instead." + ) + utc_offset = argv.get("timezone") + def get_angle(for_aster: Object): def fun(time: Time) -> float: return ( @@ -251,15 +298,15 @@ def fun(time: Time) -> bool: fun.rough_period = 0.5 return fun - # The date given in argument is supposed to be given in the given timezone (more natural for a human), - # but we need it in UTC. Subtracting the timezone to get it in UTC. + # The date given in argument is supposed to be given in the given UTC offset (more natural for a human), + # but we need it in UTC. Subtracting the offset to get it in UTC. start_time = get_timescale().utc( - for_date.year, for_date.month, for_date.day, -timezone + for_date.year, for_date.month, for_date.day, -utc_offset ) end_time = get_timescale().utc( - for_date.year, for_date.month, for_date.day + 1, -timezone + for_date.year, for_date.month, for_date.day + 1, -utc_offset ) ephemerides = [] @@ -276,7 +323,7 @@ def fun(time: Time) -> bool: for i, time in enumerate(times): time_dt = normalize_datetime( - translate_to_timezone(time.utc_datetime(), to_tz=timezone) + translate_to_utc_offset(time.utc_datetime(), to_tz=utc_offset) ) if time_dt is not None and time_dt.day != for_date.day: @@ -289,9 +336,9 @@ def fun(time: Time) -> bool: if culmination_time is not None: culmination_time = normalize_datetime( - translate_to_timezone( + translate_to_utc_offset( culmination_time.utc_datetime(), - to_tz=timezone, + to_tz=utc_offset, ) ) @@ -299,8 +346,8 @@ def fun(time: Time) -> bool: AsterEphemerides(rise_time, culmination_time, set_time, aster=aster) ) except EphemerisRangeError as error: - start = translate_to_timezone(error.start_time.utc_datetime(), timezone) - end = translate_to_timezone(error.end_time.utc_datetime(), timezone) + start = translate_to_utc_offset(error.start_time.utc_datetime(), utc_offset) + end = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset) start = date(start.year, start.month, start.day + 1) end = date(end.year, end.month, end.day - 1) diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py index 08b5ca6..8b7fa61 100644 --- a/kosmorrolib/events.py +++ b/kosmorrolib/events.py @@ -17,6 +17,7 @@ # along with this program. If not, see . from datetime import date, timedelta +from typing import Union from skyfield.errors import EphemerisRangeError from skyfield.timelib import Time @@ -34,14 +35,20 @@ ASTERS, EARTH, ) -from kosmorrolib.dateutil import translate_to_timezone +from kosmorrolib.dateutil import translate_to_utc_offset from kosmorrolib.enum import EventType, ObjectIdentifier, SeasonType, LunarEclipseType from kosmorrolib.exceptions import InvalidDateRangeError, OutOfRangeDateError -from kosmorrolib.core import get_timescale, get_skf_objects, flatten_list +from kosmorrolib.core import ( + get_timescale, + get_skf_objects, + flatten_list, + deprecated, + alert_deprecation, +) def _search_conjunctions_occultations( - start_time: Time, end_time: Time, timezone: int + start_time: Time, end_time: Time, utc_offset: Union[int, float] ) -> [Event]: """Function to search conjunction. @@ -118,7 +125,9 @@ def is_in_conjunction(time: Time): Event( EventType.OCCULTATION, occulting_aster, - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset( + time.utc_datetime(), utc_offset + ), ) ) else: @@ -126,7 +135,9 @@ def is_in_conjunction(time: Time): Event( EventType.CONJUNCTION, [aster1, aster2], - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset( + time.utc_datetime(), utc_offset + ), ) ) @@ -135,7 +146,9 @@ def is_in_conjunction(time: Time): return events -def _search_oppositions(start_time: Time, end_time: Time, timezone: int) -> [Event]: +def _search_oppositions( + start_time: Time, end_time: Time, utc_offset: Union[int, float] +) -> [Event]: """Function to search oppositions. **Warning:** this is an internal function, not intended for use by end-developers. @@ -198,7 +211,7 @@ def get_angle(time: Time): Event( EventType.OPPOSITION, [aster], - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset(time.utc_datetime(), utc_offset), ) ) @@ -206,7 +219,7 @@ def get_angle(time: Time): def _search_maximal_elongations( - start_time: Time, end_time: Time, timezone: int + start_time: Time, end_time: Time, utc_offset: Union[int, float] ) -> [Event]: """Function to search oppositions. @@ -249,7 +262,7 @@ def f(time: Time): Event( EventType.MAXIMAL_ELONGATION, [planet], - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset(time.utc_datetime(), utc_offset), details={"deg": float(elongation)}, ) ) @@ -283,7 +296,7 @@ def _search_apogee(to_aster: Object, from_aster: Object = EARTH) -> callable: [] start=2021-07-05 22:35:42.148792+00:00 end=None details={'distance_km': 152100521.91712126} />] """ - def f(start_time: Time, end_time: Time, timezone: int) -> [Event]: + def f(start_time: Time, end_time: Time, utc_offset: Union[int, float]) -> [Event]: events = [] times, distances = find_maxima( @@ -298,7 +311,7 @@ def f(start_time: Time, end_time: Time, timezone: int) -> [Event]: Event( EventType.APOGEE, [to_aster], - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset(time.utc_datetime(), utc_offset), details={"distance_km": float(distances[i])}, ) ) @@ -322,7 +335,7 @@ def _search_perigee(aster: Object, from_aster: Object = EARTH) -> callable: [] start=2021-01-02 13:59:00.495905+00:00 end=None details={'distance_km': 147093166.1686309} />] """ - def f(start_time: Time, end_time: Time, timezone: int) -> [Event]: + def f(start_time: Time, end_time: Time, utc_offset: Union[int, float]) -> [Event]: events = [] times, distances = find_minima( @@ -337,7 +350,7 @@ def f(start_time: Time, end_time: Time, timezone: int) -> [Event]: Event( EventType.PERIGEE, [aster], - translate_to_timezone(time.utc_datetime(), timezone), + translate_to_utc_offset(time.utc_datetime(), utc_offset), details={"distance_km": float(distances[i])}, ) ) @@ -348,7 +361,7 @@ def f(start_time: Time, end_time: Time, timezone: int) -> [Event]: def _search_earth_season_change( - start_time: Time, end_time: Time, timezone: int + start_time: Time, end_time: Time, utc_offset: Union[int, float] ) -> [Event]: """Function to find earth season change event. @@ -379,14 +392,16 @@ def _search_earth_season_change( Event( EventType.SEASON_CHANGE, [], - translate_to_timezone(event_time.utc_datetime()[0], timezone), + translate_to_utc_offset(event_time.utc_datetime()[0], utc_offset), details={"season": SeasonType(event_id[0])}, ) ) return events -def _search_lunar_eclipse(start_time: Time, end_time: Time, timezone: int) -> [Event]: +def _search_lunar_eclipse( + start_time: Time, end_time: Time, utc_offset: Union[int, float] +) -> [Event]: """Function to detect lunar eclipses. **Warning:** this is an internal function, not intended for use by end-developers. @@ -444,13 +459,15 @@ def is_in_penumbra(time: Time): Event( EventType.LUNAR_ECLIPSE, [moon], - start_time=translate_to_timezone( - eclipse_start[0].utc_datetime(), timezone + start_time=translate_to_utc_offset( + eclipse_start[0].utc_datetime(), utc_offset + ), + end_time=translate_to_utc_offset( + eclipse_end[0].utc_datetime(), utc_offset ), - end_time=translate_to_timezone(eclipse_end[0].utc_datetime(), timezone), details={ "type": LunarEclipseType(yi), - "maximum": translate_to_timezone(ti.utc_datetime(), timezone), + "maximum": translate_to_utc_offset(ti.utc_datetime(), utc_offset), }, ) ) @@ -458,24 +475,31 @@ def is_in_penumbra(time: Time): return events -def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: - """Calculate and return a list of events for the given date, adjusted to the given timezone if any. +def get_events( + for_date: date = date.today(), utc_offset: Union[int, float] = 0, **argv +) -> [Event]: + """Calculate and return a list of events for the given date, adjusted to the given UTC offset if any. Find events that happen on April 4th, 2020 (show hours in UTC): >>> get_events(date(2020, 4, 4)) [, ] start=2020-04-04 01:14:39.063308+00:00 end=None details=None />] - Find events that happen on April 4th, 2020 (show timezones in UTC+2): + Find events that happen on April 4th, 2020 (displayed in UTC+2): - >>> get_events(date(2020, 4, 4), 2) + >>> get_events(date(2020, 4, 4), utc_offset=2) [, ] start=2020-04-04 03:14:39.063267+02:00 end=None details=None />] - Find events that happen on April 3rd, 2020 (show timezones in UTC-2): + Find events that happen on April 3rd, 2020 (displayed in UTC-2): - >>> get_events(date(2020, 4, 3), -2) + >>> get_events(date(2020, 4, 3), utc_offset=-2) [, ] start=2020-04-03 23:14:39.063388-02:00 end=None details=None />] + 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. + + >>> get_events(date(2020, 4, 4), timezone=2) + [, ] start=2020-04-04 03:14:39.063267+02:00 end=None details=None />] + If there is no events for the given date, then an empty list is returned: >>> get_events(date(2021, 4, 20)) @@ -490,15 +514,21 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-07-28 and 2053-10-08 :param for_date: the date for which the events must be calculated - :param timezone: the timezone to adapt the results to. If not given, defaults to 0. + :param utc_offset: the UTC offset to adapt the results to. If not given, defaults to 0. :return: a list of events found for the given date. """ + if argv.get("timezone") is not None: + alert_deprecation( + "'timezone' argument of the get_events() function is deprecated. Use utc_offset instead." + ) + utc_offset = argv.get("timezone") + start_time = get_timescale().utc( - for_date.year, for_date.month, for_date.day, -timezone + for_date.year, for_date.month, for_date.day, -utc_offset ) end_time = get_timescale().utc( - for_date.year, for_date.month, for_date.day + 1, -timezone + for_date.year, for_date.month, for_date.day + 1, -utc_offset ) try: @@ -515,12 +545,14 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: _search_earth_season_change, _search_lunar_eclipse, ]: - found_events.append(fun(start_time, end_time, timezone)) + found_events.append(fun(start_time, end_time, utc_offset)) return sorted(flatten_list(found_events), key=lambda event: event.start_time) except EphemerisRangeError as error: - start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone) - end_date = translate_to_timezone(error.end_time.utc_datetime(), timezone) + start_date = translate_to_utc_offset( + error.start_time.utc_datetime(), utc_offset + ) + end_date = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset) start_date = date(start_date.year, start_date.month, start_date.day) end_date = date(end_date.year, end_date.month, end_date.day) @@ -529,9 +561,12 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: def search_events( - event_types: [EventType], end: date, start: date = date.today(), timezone: int = 0 + event_types: [EventType], + end: date, + start: date = date.today(), + utc_offset: Union[int, float] = 0, ) -> [Event]: - """Search between `start` and `end` dates, and return a list of matching events for the given time range, adjusted to a given timezone. + """Search between `start` and `end` dates, and return a list of matching events for the given time range, adjusted to a given UTC offset. Find all events between January 27th, 2020 and January 29th, 2020: @@ -549,7 +584,7 @@ def search_events( Find Apogee events between January 27th, 2020 and January 29th, 2020 (show times in UTC-6): - >>> search_events([EventType.APOGEE], end=date(2020, 1, 29), start=date(2020, 1, 27), timezone=-6) + >>> search_events([EventType.APOGEE], end=date(2020, 1, 29), start=date(2020, 1, 27), utc_offset=-6) [] start=2020-01-29 15:32:13.884314-06:00 end=None details={'distance_km': 405426.4150890029} />] If no events occurred in the given time range, an empty list is returned. @@ -585,17 +620,21 @@ def search_events( moon = ASTERS[1] sun = ASTERS[0] - def search_all_apogee_events(start: date, end: date, timezone: int = 0) -> [Event]: - moon_apogee_events = _search_apogee(moon)(start, end, timezone) + def _search_all_apogee_events( + start: date, end: date, utc_offset: Union[int, float] = 0 + ) -> [Event]: + moon_apogee_events = _search_apogee(moon)(start, end, utc_offset) earth_apogee_events = _search_apogee(EARTH, from_aster=sun)( - start, end, timezone + start, end, utc_offset ) return moon_apogee_events + earth_apogee_events - def search_all_perigee_events(start: date, end: date, timezone: int = 0) -> [Event]: - moon_perigee_events = _search_perigee(moon)(start, end, timezone) + def _search_all_perigee_events( + start: date, end: date, utc_offset: Union[int, float] = 0 + ) -> [Event]: + moon_perigee_events = _search_perigee(moon)(start, end, utc_offset) earth_perigee_events = _search_perigee(EARTH, from_aster=sun)( - start, end, timezone + start, end, utc_offset ) return moon_perigee_events + earth_perigee_events @@ -604,8 +643,8 @@ def search_all_perigee_events(start: date, end: date, timezone: int = 0) -> [Eve EventType.CONJUNCTION: _search_conjunctions_occultations, EventType.OCCULTATION: _search_conjunctions_occultations, EventType.MAXIMAL_ELONGATION: _search_maximal_elongations, - EventType.APOGEE: search_all_apogee_events, - EventType.PERIGEE: search_all_perigee_events, + EventType.APOGEE: _search_all_apogee_events, + EventType.PERIGEE: _search_all_perigee_events, EventType.SEASON_CHANGE: _search_earth_season_change, EventType.LUNAR_ECLIPSE: _search_lunar_eclipse, } @@ -613,22 +652,24 @@ def search_all_perigee_events(start: date, end: date, timezone: int = 0) -> [Eve if start > end: raise InvalidDateRangeError(start, end) - start_time = get_timescale().utc(start.year, start.month, start.day, -timezone) - end_time = get_timescale().utc(end.year, end.month, end.day + 1, -timezone) + start_time = get_timescale().utc(start.year, start.month, start.day, -utc_offset) + end_time = get_timescale().utc(end.year, end.month, end.day + 1, -utc_offset) try: found_events = [] for event_type in event_types: fun = search_funcs[event_type] - events = fun(start_time, end_time, timezone) + events = fun(start_time, end_time, utc_offset) for event in events: if event not in found_events: found_events.append(event) return sorted(flatten_list(found_events), key=lambda event: event.start_time) except EphemerisRangeError as error: - start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone) - end_date = translate_to_timezone(error.end_time.utc_datetime(), timezone) + start_date = translate_to_utc_offset( + error.start_time.utc_datetime(), utc_offset + ) + end_date = translate_to_utc_offset(error.end_time.utc_datetime(), utc_offset) start_date = date(start_date.year, start_date.month, start_date.day) end_date = date(end_date.year, end_date.month, end_date.day)