Skip to content

Commit

Permalink
feat: enrich and fix time conversion functions (#313)
Browse files Browse the repository at this point in the history
* feat: enrich and fix time conversion functions

* test: enrich time conversion test to consider different time scales

* docs: fix type

* test: add time conversion type error test cases

* chore: apply PR suggestions

---------

Co-authored-by: Pau Hebrero <pau.hebrero@gmail.com>
  • Loading branch information
phc1990 and Pau Hebrero authored Jan 8, 2024
1 parent 785fbb5 commit 2bb01de
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 12 deletions.
221 changes: 215 additions & 6 deletions bindings/python/test/test_converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Apache License 2.0

import pytest

from datetime import datetime, timedelta, timezone

import numpy as np
Expand All @@ -17,6 +19,7 @@

from ostk.astrodynamics.converters import coerce_to_datetime
from ostk.astrodynamics.converters import coerce_to_instant
from ostk.astrodynamics.converters import coerce_to_iso
from ostk.astrodynamics.converters import coerce_to_interval
from ostk.astrodynamics.converters import coerce_to_duration
from ostk.astrodynamics.converters import coerce_to_position
Expand All @@ -25,25 +28,231 @@


def test_coerce_to_datetime_success_instant():
value = Instant.date_time(DateTime(2020, 1, 1), Scale.UTC)
assert coerce_to_datetime(value) == datetime(2020, 1, 1, tzinfo=timezone.utc)
value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC)
assert coerce_to_datetime(value) == datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc
)

value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.TAI)
assert coerce_to_datetime(value) == datetime(
2020, 1, 2, 3, 3, 28, 123456, tzinfo=timezone.utc
)


def test_coerce_to_datetime_success_datetime():
value = datetime(2020, 1, 1, tzinfo=timezone.utc)
value = datetime(2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc)
assert coerce_to_datetime(value) == value

value = datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone(timedelta(seconds=3600))
)
assert coerce_to_datetime(value) == value


def test_coerce_to_datetime_success_iso():
value = "2020-01-02T03:04:05.123456+00:00"
assert coerce_to_datetime(value) == datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc
)

value = "2020-01-02T03:04:05+00:00"
assert coerce_to_datetime(value) == datetime(2020, 1, 2, 3, 4, 5, tzinfo=timezone.utc)

value = "2020-01-02T03:04:05.123456+01:00"
assert coerce_to_datetime(value) == datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone(timedelta(seconds=3600))
)

value = "2020-01-02T03:04:05.123456Z"
assert coerce_to_datetime(value) == datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc
)

value = "2020-01-02T03:04:05Z"
assert coerce_to_datetime(value) == datetime(2020, 1, 2, 3, 4, 5, tzinfo=timezone.utc)


def test_coerce_to_datetime_failure():
with pytest.raises(TypeError):
coerce_to_datetime(False)

with pytest.raises(Exception):
coerce_to_datetime("some_ill_formed_iso")


def test_coerce_to_instant_success_datetime():
value = datetime(2020, 1, 1, tzinfo=timezone.utc)
assert coerce_to_instant(value) == Instant.date_time(DateTime(2020, 1, 1), Scale.UTC)
value = datetime(2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc)
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC
)

value = datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone(timedelta(seconds=3600))
)
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 2, 4, 5, 123, 456), Scale.UTC
)


def test_coerce_to_instant_success_instant():
value = Instant.date_time(DateTime(2020, 1, 1), Scale.UTC)
value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC)
assert coerce_to_instant(value) == value

value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.TAI)
assert coerce_to_instant(value) == value


def test_coerce_to_instant_success_iso():
value = "2020-01-02T03:04:05.123456+00:00"
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC
)

value = "2020-01-02T03:04:05+00:00"
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 3, 4, 5), Scale.UTC
)

value = "2020-01-02T03:04:05.123456+01:00"
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 2, 4, 5, 123, 456), Scale.UTC
)

value = "2020-01-02T03:04:05.123456Z"
assert coerce_to_instant(value) == Instant.date_time(
DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC
)

value = "2020-01-02T03:04:05Z"
assert coerce_to_instant(value) == Instant.date_time(
DateTime(
2020,
1,
2,
3,
4,
5,
),
Scale.UTC,
)


def test_coerce_to_instant_failure():
with pytest.raises(TypeError):
coerce_to_instant(False)

with pytest.raises(Exception):
coerce_to_instant("some_ill_formed_iso")


def test_coerce_to_iso_success_datetime():
value = datetime(2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123456+00:00"
)

value = datetime(
2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone(timedelta(seconds=3600))
)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123456+01:00"
)

value = datetime(2020, 1, 2, 3, 4, 5, 123456, tzinfo=timezone.utc)
assert (
coerce_to_iso(value, timespec="milliseconds") == "2020-01-02T03:04:05.123+00:00"
)

value = datetime(2020, 1, 2, 3, 4, 5, 0, tzinfo=timezone.utc)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.000000+00:00"
)


def test_coerce_to_iso_success_instant():
value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123456+00:00"
)

value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.TAI)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:03:28.123456+00:00"
)

value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123, 456), Scale.UTC)
assert (
coerce_to_iso(value, timespec="milliseconds") == "2020-01-02T03:04:05.123+00:00"
)

value = Instant.date_time(DateTime(2020, 1, 2, 3, 4, 5, 123), Scale.UTC)
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123000+00:00"
)


def test_coerce_to_iso_success_iso():
value = "2020-01-02T03:04:05.123456+00:00"
assert coerce_to_iso(value, timespec="microseconds") == value

value = "2020-01-02T03:04:05.123456+01:00"
assert coerce_to_iso(value, timespec="microseconds") == value

value = "2020-01-02T03:04:05.123456+00:00"
assert (
coerce_to_iso(value, timespec="milliseconds") == "2020-01-02T03:04:05.123+00:00"
)

value = "2020-01-02T03:04:05.123+00:00"
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123000+00:00"
)

value = "2020-01-02T03:04:05+00:00"
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.000000+00:00"
)

value = "2020-01-02T03:04:05.123456Z"
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123456+00:00"
)

value = "2020-01-02T03:04:05.123456Z"
assert (
coerce_to_iso(value, timespec="milliseconds") == "2020-01-02T03:04:05.123+00:00"
)

value = "2020-01-02T03:04:05.123Z"
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.123000+00:00"
)

value = "2020-01-02T03:04:05Z"
assert (
coerce_to_iso(value, timespec="microseconds")
== "2020-01-02T03:04:05.000000+00:00"
)


def test_coerce_to_iso_failure():
with pytest.raises(TypeError):
coerce_to_iso(False)

with pytest.raises(Exception):
coerce_to_iso("some_ill_formed_iso")


def test_coerce_to_interval_success_interval():
value = Interval.closed(
Instant.date_time(DateTime(2020, 1, 1), Scale.UTC),
Expand Down
50 changes: 44 additions & 6 deletions bindings/python/tools/python/ostk/astrodynamics/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
from ostk.physics.coordinate import Frame


def coerce_to_datetime(value: Instant | datetime) -> datetime:
def coerce_to_datetime(value: Instant | datetime | str) -> datetime:
"""
Return datetime from value.
Args:
value (Instant | datetime): A value to coerce.
value (Instant | datetime | str): A value to coerce.
Returns:
datetime: The coerced datetime.
Expand All @@ -29,15 +29,21 @@ def coerce_to_datetime(value: Instant | datetime) -> datetime:
if isinstance(value, datetime):
return value

return value.get_date_time(Scale.UTC).replace(tzinfo=timezone.utc)
if isinstance(value, Instant):
return value.get_date_time(Scale.UTC).replace(tzinfo=timezone.utc)

if isinstance(value, str):
return datetime.fromisoformat(value)

raise TypeError("Argument must be a datetime, an Instant, or a str.")


def coerce_to_instant(value: Instant | datetime) -> Instant:
def coerce_to_instant(value: Instant | datetime | str) -> Instant:
"""
Return Instant from value.
Args:
value (Instant | datetime): A value to coerce.
value (Instant | datetime | str): A value to coerce.
Returns:
Instant: The coerced Instant.
Expand All @@ -46,7 +52,39 @@ def coerce_to_instant(value: Instant | datetime) -> Instant:
if isinstance(value, Instant):
return value

return Instant.date_time(value, Scale.UTC)
if isinstance(value, datetime):
return Instant.date_time(value.astimezone(tz=timezone.utc), Scale.UTC)

if isinstance(value, str):
return coerce_to_instant(coerce_to_datetime(value))

raise TypeError("Argument must be a datetime, an Instant, or a str.")


def coerce_to_iso(
value: Instant | datetime | str, timespec: str = "microseconds"
) -> Instant:
"""
Return an ISO string from value.
Args:
value (Instant | datetime | str): A value to coerce.
timespec (str): A time resolution. Defaults to "microseconds".
Returns:
str: The coerced ISO string.
"""

if isinstance(value, str):
return coerce_to_iso(coerce_to_datetime(value), timespec=timespec)

if isinstance(value, datetime):
return value.isoformat(timespec=timespec)

if isinstance(value, Instant):
return coerce_to_iso(coerce_to_datetime(value), timespec=timespec)

raise TypeError("Argument must be a datetime, an Instant, or a str.")


def coerce_to_interval(
Expand Down

0 comments on commit 2bb01de

Please sign in to comment.