diff --git a/kosmorrolib/__init__.py b/kosmorrolib/__init__.py
index d46f4ba..8fac5e3 100644
--- a/kosmorrolib/__init__.py
+++ b/kosmorrolib/__init__.py
@@ -18,5 +18,5 @@
from .model import Position, Event, AsterEphemerides, Object
from .ephemerides import get_ephemerides, get_moon_phase
-from .events import get_events
+from .events import get_events, search_events
from .enum import *
diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py
index 7bbcb14..6d26fc4 100644
--- a/kosmorrolib/events.py
+++ b/kosmorrolib/events.py
@@ -36,7 +36,7 @@ from kosmorrolib.model import (
)
from kosmorrolib.dateutil import translate_to_timezone
from kosmorrolib.enum import EventType, ObjectIdentifier, SeasonType, LunarEclipseType
-from kosmorrolib.exceptions import OutOfRangeDateError
+from kosmorrolib.exceptions import InvalidDateRangeError, OutOfRangeDateError
from kosmorrolib.core import get_timescale, get_skf_objects, flatten_list
@@ -526,3 +526,111 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]:
end_date = date(end_date.year, end_date.month, end_date.day)
raise OutOfRangeDateError(start_date, end_date) from error
+
+
+def search_events(
+ event_types: [EventType], end: date, start: date = date.today(), timezone: int = 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.
+
+ Find all events between January 27th, 2020 and January 29th, 2020:
+
+ >>> event_types = [EventType.OPPOSITION, EventType.CONJUNCTION, EventType.OCCULTATION, EventType.MAXIMAL_ELONGATION, EventType.APOGEE, EventType.PERIGEE, EventType.SEASON_CHANGE, EventType.LUNAR_ECLIPSE]
+ >>> search_events(event_types, end=date(2020, 1, 29), start=date(2020, 1, 27)) # doctest: +NORMALIZE_WHITESPACE
+ [, ] start=2020-01-27 20:00:23.242428+00:00 end=None details=None />,
+ , ] start=2020-01-28 09:33:45.000618+00:00 end=None details=None />,
+ , ] start=2020-01-28 11:01:51.909499+00:00 end=None details=None />,
+ ] start=2020-01-29 21:32:13.884314+00:00 end=None details={'distance_km': 405426.4150890029} />]
+
+ Find Apogee events between January 27th, 2020 and January 29th, 2020:
+
+ >>> search_events([EventType.APOGEE], end=date(2020, 1, 29), start=date(2020, 1, 27))
+ [] start=2020-01-29 21:32:13.884314+00:00 end=None details={'distance_km': 405426.4150890029} />]
+
+ 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)
+ [] 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.
+
+ >>> event_types = [EventType.OPPOSITION, EventType.CONJUNCTION, EventType.SEASON_CHANGE, EventType.LUNAR_ECLIPSE]
+ >>> search_events(event_types, end=date(2021, 5, 15), start=date(2021, 5, 14))
+ []
+
+ Note that the events can only be found for a date range.
+ Asking for the events with an out of range date will result in an exception:
+
+ >>> event_types = [EventType.OPPOSITION, EventType.CONJUNCTION]
+ >>> search_events(event_types, end=date(1000, 1, 2), start=date(1000, 1, 1))
+ Traceback (most recent call last):
+ ...
+ kosmorrolib.exceptions.OutOfRangeDateError: The date must be between 1899-07-28 and 2053-10-08
+
+ If the start date does not occur before the end date, an exception will be thrown
+
+ >>> event_types = [EventType.OPPOSITION, EventType.CONJUNCTION]
+ >>> search_events(event_types, end=date(2021, 1, 26), start=date(2021, 1, 28))
+ Traceback (most recent call last):
+ ...
+ kosmorrolib.exceptions.InvalidDateRangeError: The start date (2021-01-28) must be before the end date (2021-01-26)
+
+ If the start and end dates are the same, then events for that one day will be returned.
+
+ >>> event_types = [EventType.OPPOSITION, EventType.CONJUNCTION]
+ >>> search_events(event_types, end=date(2021, 1, 28), start=date(2021, 1, 28))
+ [, ] start=2021-01-28 16:18:05.483029+00:00 end=None details=None />]
+ """
+
+ 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)
+ earth_apogee_events = _search_apogee(EARTH, from_aster=sun)(
+ start, end, timezone
+ )
+ 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)
+ earth_perigee_events = _search_perigee(EARTH, from_aster=sun)(
+ start, end, timezone
+ )
+ return moon_perigee_events + earth_perigee_events
+
+ search_funcs = {
+ EventType.OPPOSITION: _search_oppositions,
+ 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.SEASON_CHANGE: _search_earth_season_change,
+ EventType.LUNAR_ECLIPSE: _search_lunar_eclipse,
+ }
+
+ 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)
+
+ try:
+ found_events = []
+ for event_type in event_types:
+ fun = search_funcs[event_type]
+ events = fun(start_time, end_time, timezone)
+ 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 = date(start_date.year, start_date.month, start_date.day)
+ end_date = date(end_date.year, end_date.month, end_date.day)
+
+ raise OutOfRangeDateError(start_date, end_date) from error
diff --git a/kosmorrolib/exceptions.py b/kosmorrolib/exceptions.py
index bc67d18..36979c9 100644
--- a/kosmorrolib/exceptions.py
+++ b/kosmorrolib/exceptions.py
@@ -30,3 +30,16 @@ class OutOfRangeDateError(ValueError):
)
self.min_date = min_date
self.max_date = max_date
+
+
+class InvalidDateRangeError(ValueError):
+ def __init__(self, start_date: date, end_date: date):
+ super().__init__(
+ "The start date (%s) must be before the end date (%s)"
+ % (
+ start_date.strftime("%Y-%m-%d"),
+ end_date.strftime("%Y-%m-%d"),
+ )
+ )
+ self.start_date = start_date
+ self.end_date = end_date
diff --git a/kosmorrolib/model.py b/kosmorrolib/model.py
index 7a04003..9a1fe20 100644
--- a/kosmorrolib/model.py
+++ b/kosmorrolib/model.py
@@ -239,6 +239,16 @@ class Event(Serializable):
self.details,
)
+ def __eq__(self, other: object) -> bool:
+ return (
+ isinstance(other, Event)
+ and self.event_type.name == other.event_type.name
+ and self.objects == other.objects
+ and self.start_time == other.start_time
+ and self.end_time == other.end_time
+ and self.details == other.details
+ )
+
@deprecated(
"kosmorrolib.Event.get_description method is deprecated since version 1.1 "
"and will be removed in version 2.0. "