Skip to content

Commit

Permalink
feat: Create deephaven.time time-type aliases (#5269)
Browse files Browse the repository at this point in the history
This adds `TimeZoneLike`, `LocalDateLike`, `InstantLike`,
`ZonedDateTimeLike`, `DurationLike`, and `PeriodLike` as aliased union
types for objects that can be coerced into said type. This makes it
easier for public APIs to reference the proper time-types.
`S3Instructions`, `json.instant_val`, `TableReplayer`, `time_table`, and
the `time.to_j_` APIs have been updated to reference the new types.
  • Loading branch information
devinrsmith authored Aug 28, 2024
1 parent e75b192 commit ed3a523
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 70 deletions.
21 changes: 8 additions & 13 deletions py/server/deephaven/experimental/s3.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
#
# Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending
#
import datetime
from typing import Optional, Union

import jpy
import numpy as np
import pandas as pd

from deephaven import time, DHError
from deephaven import DHError
from deephaven._wrapper import JObjectWrapper
from deephaven.dtypes import Duration
from deephaven.time import DurationLike, to_j_duration

# If we move S3 to a permanent module, we should remove this try/except block and just import the types directly.
try:
Expand Down Expand Up @@ -38,10 +35,8 @@ def __init__(self,
max_concurrent_requests: Optional[int] = None,
read_ahead_count: Optional[int] = None,
fragment_size: Optional[int] = None,
connection_timeout: Union[
Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta, None] = None,
read_timeout: Union[
Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta, None] = None,
connection_timeout: Optional[DurationLike] = None,
read_timeout: Optional[DurationLike] = None,
access_key_id: Optional[str] = None,
secret_access_key: Optional[str] = None,
anonymous_access: bool = False,
Expand All @@ -62,11 +57,11 @@ def __init__(self,
fragment. Defaults to 32, which means fetch the next 32 fragments in advance when reading the current fragment.
fragment_size (int): the maximum size of each fragment to read, defaults to 64 KiB. If there are fewer bytes
remaining in the file, the fetched fragment can be smaller.
connection_timeout (Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]):
connection_timeout (DurationLike):
the amount of time to wait when initially establishing a connection before giving up and timing out, can
be expressed as an integer in nanoseconds, a time interval string, e.g. "PT00:00:00.001" or "PT1s", or
other time duration types. Default to 2 seconds.
read_timeout (Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]):
read_timeout (DurationLike):
the amount of time to wait when reading a fragment before giving up and timing out, can be expressed as
an integer in nanoseconds, a time interval string, e.g. "PT00:00:00.001" or "PT1s", or other time
duration types. Default to 2 seconds.
Expand Down Expand Up @@ -111,10 +106,10 @@ def __init__(self,
builder.fragmentSize(fragment_size)

if connection_timeout is not None:
builder.connectionTimeout(time.to_j_duration(connection_timeout))
builder.connectionTimeout(to_j_duration(connection_timeout))

if read_timeout is not None:
builder.readTimeout(time.to_j_duration(read_timeout))
builder.readTimeout(to_j_duration(read_timeout))

if ((access_key_id is not None and secret_access_key is None) or
(access_key_id is None and secret_access_key is not None)):
Expand Down
11 changes: 5 additions & 6 deletions py/server/deephaven/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@

from deephaven import dtypes
from deephaven._wrapper import JObjectWrapper
from deephaven.time import to_j_instant
from deephaven.time import InstantLike, to_j_instant
from deephaven._jpy import strict_cast


Expand Down Expand Up @@ -1056,14 +1056,13 @@ def string_val(
return JsonValue(builder.build())


# TODO(deephaven-core#5269): Create deephaven.time time-type aliases
def instant_val(
allow_missing: bool = True,
allow_null: bool = True,
number_format: Literal[None, "s", "ms", "us", "ns"] = None,
allow_decimal: bool = False,
on_missing: Optional[Any] = None,
on_null: Optional[Any] = None,
on_missing: Optional[InstantLike] = None,
on_null: Optional[InstantLike] = None,
) -> JsonValue:
"""Creates an Instant value. For example, the JSON string
Expand Down Expand Up @@ -1110,8 +1109,8 @@ def instant_val(
the epoch. When not set, a JSON string in the ISO-8601 format is expected.
allow_decimal (bool): if the Instant value is allowed to be a JSON decimal type, default is False. Only valid
when number_format is specified.
on_missing (Optional[Any]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[Any]): the value to use when the JSON value is null and allow_null is True, default is None.
on_missing (Optional[InstantLike]): the value to use when the JSON value is missing and allow_missing is True, default is None.
on_null (Optional[InstantLike]): the value to use when the JSON value is null and allow_null is True, default is None.
Returns:
the Instant value
Expand Down
15 changes: 5 additions & 10 deletions py/server/deephaven/replay.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@

""" This module provides support for replaying historical data. """

from typing import Union
import jpy

import datetime
import numpy as np
import pandas as pd

from deephaven import dtypes, DHError, time
from deephaven import DHError, time
from deephaven._wrapper import JObjectWrapper
from deephaven.table import Table
from deephaven.time import InstantLike

_JReplayer = jpy.get_type("io.deephaven.engine.table.impl.replay.Replayer")

Expand All @@ -27,14 +23,13 @@ class TableReplayer(JObjectWrapper):

j_object_type = _JReplayer

def __init__(self, start_time: Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp],
end_time: Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]):
def __init__(self, start_time: InstantLike, end_time: InstantLike):
"""Initializes the replayer.
Args:
start_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]):
start_time (InstantLike):
replay start time. Integer values are nanoseconds since the Epoch.
end_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]):
end_time (InstantLike):
replay end time. Integer values are nanoseconds since the Epoch.
Raises:
Expand Down
24 changes: 11 additions & 13 deletions py/server/deephaven/table_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@

""" This module provides various ways to make a Deephaven table. """

import datetime
from typing import Callable, List, Dict, Any, Union, Sequence, Tuple, Mapping, Optional

import jpy
import numpy as np
import pandas as pd

from deephaven import execution_context, DHError, time
from deephaven import execution_context, DHError
from deephaven._wrapper import JObjectWrapper
from deephaven.column import InputColumn
from deephaven.dtypes import DType, Duration, Instant
from deephaven.dtypes import DType
from deephaven.execution_context import ExecutionContext
from deephaven.jcompat import j_lambda, j_list_to_list, to_sequence
from deephaven.table import Table, TableDefinition, TableDefinitionLike
from deephaven.time import DurationLike, InstantLike, to_j_duration, to_j_instant
from deephaven.update_graph import auto_locking_ctx

_JTableFactory = jpy.get_type("io.deephaven.engine.table.TableFactory")
Expand Down Expand Up @@ -53,16 +52,16 @@ def empty_table(size: int) -> Table:
raise DHError(e, "failed to create an empty table.") from e


def time_table(period: Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta],
start_time: Union[None, Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp] = None,
def time_table(period: DurationLike,
start_time: Optional[InstantLike] = None,
blink_table: bool = False) -> Table:
"""Creates a table that adds a new row on a regular interval.
Args:
period (Union[dtypes.Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]):
period (DurationLike):
time interval between new row additions, can be expressed as an integer in nanoseconds,
a time interval string, e.g. "PT00:00:00.001" or "PT1s", or other time duration types.
start_time (Union[None, Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp], optional):
start_time (Optional[InstantLike]):
start time for adding new rows, defaults to None which means use the current time
as the start time.
blink_table (bool, optional): if the time table should be a blink table, defaults to False
Expand All @@ -76,14 +75,13 @@ def time_table(period: Union[Duration, int, str, datetime.timedelta, np.timedelt
try:
builder = _JTableTools.timeTableBuilder()

if not isinstance(period, str) and not isinstance(period, int):
period = time.to_j_duration(period)
if period is None:
raise ValueError("period must be specified")

builder.period(period)
builder.period(to_j_duration(period))

if start_time:
start_time = time.to_j_instant(start_time)
builder.startTime(start_time)
builder.startTime(to_j_instant(start_time))

if blink_table:
builder.blinkTable(blink_table)
Expand Down
85 changes: 58 additions & 27 deletions py/server/deephaven/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,50 @@
_NANOS_PER_SECOND = 1000000000
_NANOS_PER_MICRO = 1000

if sys.version_info >= (3, 10):
from typing import TypeAlias # novermin

TimeZoneLike : TypeAlias = Union[TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]
"""A Union representing objects that can be coerced into a TimeZone."""

LocalDateLike : TypeAlias = Union[LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a LocalDate."""

LocalTimeLike : TypeAlias = Union[LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a LocalTime."""

InstantLike : TypeAlias = Union[Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into an Instant."""

ZonedDateTimeLike : TypeAlias = Union[ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a ZonedDateTime."""

DurationLike : TypeAlias = Union[Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]
"""A Union representing objects that can be coerced into a Duration."""

PeriodLike : TypeAlias = Union[Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]
"""A Union representing objects that can be coerced into a Period."""
else:
TimeZoneLike = Union[TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]
"""A Union representing objects that can be coerced into a TimeZone."""

LocalDateLike = Union[LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a LocalDate."""

LocalTimeLike = Union[LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a LocalTime."""

InstantLike = Union[Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into an Instant."""

ZonedDateTimeLike = Union[ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
"""A Union representing objects that can be coerced into a ZonedDateTime."""

DurationLike = Union[Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]
"""A Union representing objects that can be coerced into a Duration."""

PeriodLike = Union[Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]
"""A Union representing objects that can be coerced into a Period."""

# region Clock

Expand Down Expand Up @@ -223,15 +267,14 @@ def _tzinfo_to_j_time_zone(tzi: datetime.tzinfo) -> TimeZone:
raise TypeError(f"Unsupported conversion: {str(type(tzi))} -> TimeZone\n\tDetails:\n\t{details}")


def to_j_time_zone(tz: Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]) -> \
Optional[TimeZone]:
def to_j_time_zone(tz: Optional[TimeZoneLike]) -> Optional[TimeZone]:
"""
Converts a time zone value to a Java TimeZone.
Time zone values can be None, a Java TimeZone, a string, a datetime.tzinfo, a datetime.datetime,
or a pandas.Timestamp.
Args:
tz (Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]): A time zone value.
tz (Optional[TimeZoneLike]): A time zone value.
If None is provided, None is returned.
If a string is provided, it is parsed as a time zone name.
Expand Down Expand Up @@ -266,8 +309,7 @@ def to_j_time_zone(tz: Union[None, TimeZone, str, datetime.tzinfo, datetime.date
raise DHError(e) from e


def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.datetime,
numpy.datetime64, pandas.Timestamp]) -> Optional[LocalDate]:
def to_j_local_date(dt: Optional[LocalDateLike]) -> Optional[LocalDate]:
"""
Converts a date time value to a Java LocalDate.
Date time values can be None, a Java LocalDate, a string, a datetime.date, a datetime.datetime,
Expand All @@ -276,8 +318,7 @@ def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.date
Date strings can be formatted according to the ISO 8601 date time format as 'YYYY-MM-DD'.
Args:
dt (Union[None, LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]):
A date time value. If None is provided, None is returned.
dt (Optional[LocalDateLike]): A date time value. If None is provided, None is returned.
Returns:
LocalDate
Expand Down Expand Up @@ -305,8 +346,7 @@ def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.date
raise DHError(e) from e


def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime.datetime,
numpy.datetime64, pandas.Timestamp]) -> Optional[LocalTime]:
def to_j_local_time(dt: Optional[LocalTimeLike]) -> Optional[LocalTime]:
"""
Converts a date time value to a Java LocalTime.
Date time values can be None, a Java LocalTime, an int, a string, a datetime.time, a datetime.datetime,
Expand All @@ -317,8 +357,7 @@ def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime
Time strings can be formatted as 'hh:mm:ss[.nnnnnnnnn]'.
Args:
dt (Union[None, LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp]):
A date time value. If None is provided, None is returned.
dt (Optional[LocalTimeLike]): A date time value. If None is provided, None is returned.
Returns:
LocalTime
Expand Down Expand Up @@ -351,8 +390,7 @@ def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime
raise DHError(e) from e


def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \
Optional[Instant]:
def to_j_instant(dt: Optional[InstantLike]) -> Optional[Instant]:
"""
Converts a date time value to a Java Instant.
Date time values can be None, a Java Instant, an int, a string, a datetime.datetime,
Expand All @@ -366,8 +404,7 @@ def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.dat
from the Epoch. Expected date ranges are used to infer the units.
Args:
dt (Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]): A date time value.
If None is provided, None is returned.
dt (Optional[InstantLike]): A date time value. If None is provided, None is returned.
Returns:
Instant, TypeError
Expand Down Expand Up @@ -403,8 +440,7 @@ def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.dat
raise DHError(e) from e


def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \
Optional[ZonedDateTime]:
def to_j_zdt(dt: Optional[ZonedDateTimeLike]) -> Optional[ZonedDateTime]:
"""
Converts a date time value to a Java ZonedDateTime.
Date time values can be None, a Java ZonedDateTime, a string, a datetime.datetime,
Expand All @@ -419,8 +455,7 @@ def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.dateti
Converting a numpy.datetime64 to a ZonedDateTime will use the Deephaven default time zone.
Args:
dt (Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]):
A date time value. If None is provided, None is returned.
dt (Optional[ZonedDateTimeLike]): A date time value. If None is provided, None is returned.
Returns:
ZonedDateTime
Expand Down Expand Up @@ -455,8 +490,7 @@ def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.dateti
raise DHError(e) from e


def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \
Optional[Duration]:
def to_j_duration(dt: Optional[DurationLike]) -> Optional[Duration]:
"""
Converts a time duration value to a Java Duration,
which is a unit of time in terms of clock time (24-hour days, hours, minutes, seconds, and nanoseconds).
Expand All @@ -480,8 +514,7 @@ def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy.
| "-PT-6H+3M" -- parses as "+6 hours and -3 minutes"
Args:
dt (Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]):
A time duration value. If None is provided, None is returned.
dt (Optional[DurationLike]): A time duration value. If None is provided, None is returned.
Returns:
Duration
Expand Down Expand Up @@ -515,8 +548,7 @@ def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy.
raise DHError(e) from e


def to_j_period(dt: Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \
Optional[Period]:
def to_j_period(dt: Optional[PeriodLike]) -> Optional[Period]:
"""
Converts a time duration value to a Java Period,
which is a unit of time in terms of calendar time (days, weeks, months, years, etc.).
Expand All @@ -537,8 +569,7 @@ def to_j_period(dt: Union[None, Period, str, datetime.timedelta, numpy.timedelta
| "-P1Y2M" -- -1 Year, -2 Months
Args:
dt (Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]):
A Python period or period string. If None is provided, None is returned.
dt (Optional[PeriodLike]): A Python period or period string. If None is provided, None is returned.
Returns:
Period
Expand Down
Loading

0 comments on commit ed3a523

Please sign in to comment.