Hi,
We have had some discussions on datetime(python) to absolutedate(orekit) conversions, and would like to share some reminders on this conversion. We have now added both to the JCC and jpype version new conversions which do not use float but integer parts as in new orekit v13. The previous way of conversion of seconds as floats could in some cases give some floating point noise that led to some issues when comparing dates.
I think it is important in this to highlight that Python datetime is a very rudimentary class. The precision is only down to microsecond, which is far better in AbsoluteDate (attosecond). In many cases this does not matter but in some cases such as comparison of signals or arrival times it may have a significant impact.
What also became clear is that datetime is a non-physical class - it does not care about leap seconds etc and cannot be used to compare distance in time reliable. In the conversion the leap second will be in datetime the following second, which then will appear again one second step later. i.e. two different AbsoluteDate will convert to same datetime date… There are other time classes in Python with better accuracy, Astropy and Pandas for example, but then there is yet another library to involve.
The recommendation in orekit python is to keep as long as posisble the time and date handling in AbsoluteDate classes. This keeps all the precision and can provide a physical interpretation of time. And especially avoid going back and forth between datetime and AbsoluteDate, precision will be lost. The new conversion manages this better than previous but still loss of precision.
In the update we also added the possibility to use timezone-aware datetime, which is fixed to UTC for conversion to datetime. For the conversion to absolutedate it will check if a timezone is provided and then convert in that case to UTC before converting to AbsoluteDate.
The updated functions below.
Related to discussions: AbstractTime constructor different behaviour?
Regards
MICROSECOND_MULTIPLIER = 1000000
def absolutedate_to_datetime(orekit_absolutedate: AbsoluteDate, tz_aware=False) -> datetime:
""" Converts from orekit.AbsoluteDate objects
to python datetime objects (utc).
Args:
orekit_absolutedate (AbsoluteDate): orekit AbsoluteDate object to convert
tz_aware (bool): If True, the returned datetime will be timezone-aware (UTC). Default is False.
Returns:
datetime: time in python datetime format (UTC)
"""
utc = TimeScalesFactory.getUTC()
or_comp = orekit_absolutedate.getComponents(utc)
or_date = or_comp.getDate()
or_time = or_comp.getTime()
us = or_time.getSplitSecond().getRoundedTime(TimeUnit.MICROSECONDS)
dt = datetime(or_date.getYear(),
or_date.getMonth(),
or_date.getDay(),
or_time.getHour(),
or_time.getMinute()) + timedelta(microseconds=us)
if tz_aware:
dt = dt.replace(tzinfo=timezone.utc)
return dt
def datetime_to_absolutedate(dt_date: datetime) -> AbsoluteDate:
"""
Converts from python datetime objects to orekit AbsoluteDate objects.
Args:
dt_date (datetime): datetime object to convert
Returns:
AbsoluteDate: time in orekit AbsoluteDate format
"""
if dt_date.tzinfo is not None and dt_date.tzinfo.utcoffset(dt_date) is not None:
# If the datetime is timezone-aware, convert it to UTC
dt_date = dt_date.astimezone(timezone.utc)
utc = TimeScalesFactory.getUTC()
return AbsoluteDate(dt_date.year,
dt_date.month,
dt_date.day,
dt_date.hour,
dt_date.minute,
TimeOffset(dt_date.second*MICROSECOND_MULTIPLIER+dt_date.microsecond, TimeOffset.MICROSECOND),
utc)