diff --git a/py/server/deephaven/column.py b/py/server/deephaven/column.py index 9f43abf22be..cf9a9bd0303 100644 --- a/py/server/deephaven/column.py +++ b/py/server/deephaven/column.py @@ -11,7 +11,7 @@ import jpy import deephaven.dtypes as dtypes -from deephaven import DHError +from deephaven import DHError, time from deephaven.dtypes import DType _JColumnHeader = jpy.get_type("io.deephaven.qst.column.header.ColumnHeader") @@ -196,11 +196,13 @@ def datetime_col(name: str, data: Sequence) -> InputColumn: Args: name (str): the column name - data (Any): a sequence of Datetime instances + data (Any): a sequence of Datetime instances or values that can be converted to Datetime instances + (e.g. Instant, int nanoseconds since the Epoch, str, datetime.datetime, numpy.datetime64, pandas.Timestamp). Returns: a new input column """ + data = [time.to_j_instant(d) for d in data] return InputColumn(name=name, data_type=dtypes.Instant, input_data=data) diff --git a/py/server/deephaven/replay.py b/py/server/deephaven/replay.py index fd41fbe24fb..44ec6c47a22 100644 --- a/py/server/deephaven/replay.py +++ b/py/server/deephaven/replay.py @@ -7,6 +7,10 @@ from typing import Union import jpy +import datetime +import numpy as np +import pandas as pd + from deephaven import dtypes, DHError, time from deephaven._wrapper import JObjectWrapper from deephaven.table import Table @@ -23,23 +27,21 @@ class TableReplayer(JObjectWrapper): j_object_type = _JReplayer - def __init__(self, start_time: Union[dtypes.Instant,str], end_time: Union[dtypes.Instant,str]): + 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]): """Initializes the replayer. Args: - start_time (DateTime): replay start time - end_time (DateTime): replay end time + start_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]): + replay start time. Integer values are nanoseconds since the Epoch. + end_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]): + replay end time. Integer values are nanoseconds since the Epoch. Raises: DHError """ - - if isinstance(start_time, str): - start_time = time.parse_instant(start_time) - - if isinstance(end_time, str): - end_time = time.parse_instant(end_time) - + start_time = time.to_j_instant(start_time) + end_time = time.to_j_instant(end_time) self.start_time = start_time self.end_time = end_time try: diff --git a/py/server/deephaven/table_factory.py b/py/server/deephaven/table_factory.py index 51d1533d1e7..9a24c7a2cce 100644 --- a/py/server/deephaven/table_factory.py +++ b/py/server/deephaven/table_factory.py @@ -5,13 +5,16 @@ """ This module provides various ways to make a Deephaven table. """ from typing import List, Dict, Any, Union, Sequence +import datetime import jpy +import numpy as np +import pandas as pd -from deephaven import DHError +from deephaven import DHError, time from deephaven._wrapper import JObjectWrapper from deephaven.column import InputColumn, Column -from deephaven.dtypes import DType +from deephaven.dtypes import DType, Duration, Instant from deephaven.jcompat import to_sequence from deephaven.table import Table from deephaven.update_graph import auto_locking_ctx @@ -47,14 +50,18 @@ def empty_table(size: int) -> Table: raise DHError(e, "failed to create an empty table.") from e -def time_table(period: Union[str, int], start_time: str = None, blink_table: bool = False) -> Table: +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, + blink_table: bool = False) -> Table: """Creates a table that adds a new row on a regular interval. Args: - period (Union[str, int]): time interval between new row additions, can be expressed as an integer in - nanoseconds or a time interval string, e.g. "PT00:00:00.001" or "PT1s" - start_time (str, optional): start time for adding new rows, defaults to None which means use the current time - as the start time + period (Union[dtypes.Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]): + 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, str, datetime.datetime, np.datetime64], optional): + 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 Returns: @@ -65,11 +72,19 @@ def time_table(period: Union[str, int], start_time: str = None, blink_table: boo """ try: builder = _JTableTools.timeTableBuilder() + + if not isinstance(period, str) and not isinstance(period, int): + period = time.to_j_duration(period) + builder.period(period) + if start_time: + start_time = time.to_j_instant(start_time) builder.startTime(start_time) + if blink_table: builder.blinkTable(blink_table) + return Table(j_table=builder.build()) except Exception as e: raise DHError(e, "failed to create a time table.") from e diff --git a/py/server/deephaven/time.py b/py/server/deephaven/time.py index f67ea7dc9e8..539e73cd79e 100644 --- a/py/server/deephaven/time.py +++ b/py/server/deephaven/time.py @@ -1,43 +1,42 @@ # -# Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending +# Copyright (c) 2016-2023 Deephaven Data Labs and Patent Pending # """ This module defines functions for handling Deephaven date/time data. """ from __future__ import annotations + +import datetime from typing import Union, Optional import jpy +import numpy +import pandas from deephaven import DHError -from deephaven.dtypes import Instant, LocalDate, LocalTime, ZonedDateTime, Duration, Period, TimeZone, from_jtype -from deephaven.constants import NULL_INT, NULL_LONG, NULL_DOUBLE +from deephaven.dtypes import Instant, LocalDate, LocalTime, ZonedDateTime, Duration, Period, TimeZone _JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") +_JLocalDate = jpy.get_type("java.time.LocalDate") +_JLocalTime = jpy.get_type("java.time.LocalTime") +_JInstant = jpy.get_type("java.time.Instant") +_JZonedDateTime = jpy.get_type("java.time.ZonedDateTime") +_JDuration = jpy.get_type("java.time.Duration") +_JPeriod = jpy.get_type("java.time.Period") -MICRO = 1000 #: One microsecond in nanoseconds. -MILLI = 1000000 #: One millisecond in nanosecondsl -SECOND = 1000000000 #: One second in nanoseconds. -MINUTE = 60 * SECOND #: One minute in nanoseconds. -HOUR = 60 * MINUTE #: One hour in nanoseconds. -DAY = 24 * HOUR #: One day in nanoseconds. This is one hour of wall time and does not take into account calendar adjustments. -WEEK = 7 * DAY #: One week in nanoseconds. This is 7 days of wall time and does not take into account calendar adjustments. -YEAR_365 = 365 * DAY #: One 365 day year in nanoseconds. This is 365 days of wall time and does not take into account calendar adjustments. -YEAR_AVG = 31556952000000000 #: One average year in nanoseconds. This is 365.2425 days of wall time and does not take into account calendar adjustments. - -SECONDS_PER_NANO = 1 / SECOND #: Number of seconds per nanosecond. -MINUTES_PER_NANO = 1 / MINUTE #: Number of minutes per nanosecond. -HOURS_PER_NANO = 1 / HOUR #: Number of hours per nanosecond. -DAYS_PER_NANO = 1 / DAY #: Number of days per nanosecond. -YEARS_PER_NANO_365 = 1 / YEAR_365 #: Number of 365 day years per nanosecond. -YEARS_PER_NANO_AVG = 1 / YEAR_AVG #: Number of average (365.2425 day) years per nanosecond. +_nanos_per_second = 1000000000 +_nanos_per_micro = 1000 # region Clock -def now(system: bool = False, resolution: str = 'ns') -> Instant: - """ Provides the current datetime according to a clock. +def dh_now(system: bool = False, resolution: str = 'ns') -> Instant: + """ Provides the current datetime according to the current Deephaven clock. + + Query strings should use the built-in "now" function instead of this function. + The build-in "now" function is pure Java and will be more efficient + because fewer Java/Python boundary crossings will be needed. Args: system (bool): True to use the system clock; False to use the default clock. Under most circumstances, @@ -51,7 +50,7 @@ def now(system: bool = False, resolution: str = 'ns') -> Instant: Instant Raises: - DHError + DHError, TypeError """ try: if resolution == "ns": @@ -65,18 +64,25 @@ def now(system: bool = False, resolution: str = 'ns') -> Instant: else: return _JDateTimeUtils.nowMillisResolution() else: - raise ValueError("Unsupported time resolution: " + resolution) + raise TypeError("Unsupported time resolution: " + resolution) + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def today(tz: TimeZone) -> str: - """ Provides the current date string according to the current clock. +def dh_today(tz: Optional[TimeZone] = None) -> str: + """ Provides the current date string according to the current Deephaven clock. Under most circumstances, this method will return the date according to current system time, but during replay simulations, this method can return the date according to replay time. + Query strings should use the built-in "today" function instead of this function. + The build-in "today" function is pure Java and will be more efficient + because fewer Java/Python boundary crossings will be needed. + Args: tz (TimeZone): Time zone to use when determining the date. + If None is provided, the Deephaven system default time zone is used. Returns: Date string @@ -85,21 +91,20 @@ def today(tz: TimeZone) -> str: DHError """ try: + if tz is None: + tz = _JDateTimeUtils.timeZone() + return _JDateTimeUtils.today(tz) except Exception as e: raise DHError(e) from e -# endregion - -# region Time Zone - - -def time_zone(tz: Optional[str]) -> TimeZone: - """ Gets the time zone for a time zone name. +def dh_time_zone() -> TimeZone: + """ Provides the current Deephaven system time zone. - Args: - tz (Optional[str]): Time zone name. If None is provided, the system default time zone is returned. + Query strings should use the built-in "timeZone" function instead of this function. + The build-in "timeZone" function is pure Java and will be more efficient + because fewer Java/Python boundary crossings will be needed. Returns: TimeZone @@ -108,14 +113,16 @@ def time_zone(tz: Optional[str]) -> TimeZone: DHError """ try: - if tz is None: - return _JDateTimeUtils.timeZone() - else: - return _JDateTimeUtils.timeZone(tz) + return _JDateTimeUtils.timeZone() except Exception as e: raise DHError(e) from e +# endregion + + +# region Time Zone + def time_zone_alias_add(alias: str, tz: str) -> None: """ Adds a new time zone alias. @@ -155,1884 +162,654 @@ def time_zone_alias_rm(alias: str) -> bool: # endregion -# region Conversions: Time Units +# region Conversions: Python To Java -def micros_to_nanos(micros: int) -> int: - """ Converts microseconds to nanoseconds. - - Args: - micros (int): Microseconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input microseconds converted to nanoseconds. - Raises: - DHError +def to_j_time_zone(tz: Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]) -> \ + Optional[TimeZone]: """ - try: - return _JDateTimeUtils.microsToNanos(micros) - except Exception as e: - raise DHError(e) from e - - -def millis_to_nanos(millis: int) -> int: - """ Converts milliseconds to nanoseconds. + 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: - millis (int): Milliseconds to convert. + tz (Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]): A time zone value. + If None is provided, None is returned. + If a string is provided, it is parsed as a time zone name. Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input milliseconds converted to nanoseconds. + TimeZone Raises: - DHError + DHError, TypeError """ try: - return _JDateTimeUtils.millisToNanos(millis) + if tz is None or pandas.isnull(tz): + return None + elif isinstance(tz, TimeZone.j_type): + return tz + elif isinstance(tz, str): + return _JDateTimeUtils.parseTimeZone(tz) + elif isinstance(tz, datetime.tzinfo): + return _JDateTimeUtils.parseTimeZone(str(tz)) + elif isinstance(tz, datetime.datetime): + if not tz.tzname(): + return _JDateTimeUtils.parseTimeZone(tz.astimezone().tzname()) + + return _JDateTimeUtils.parseTimeZone(tz.tzname()) + else: + raise TypeError("Unsupported conversion: " + str(type(tz)) + " -> TimeZone") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def seconds_to_nanos(seconds: int) -> int: - """ Converts seconds to nanoseconds. - - Args: - seconds (int): Seconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input seconds converted to nanoseconds. - - Raises: - DHError +def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.time, datetime.datetime, + numpy.datetime64, pandas.Timestamp]) -> Optional[LocalDate]: """ - try: - return _JDateTimeUtils.secondsToNanos(seconds) - except Exception as e: - raise DHError(e) from e - + 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.time, a datetime.datetime, + a numpy.datetime64, or a pandas.Timestamp. -def nanos_to_micros(nanos: int) -> int: - """ Converts nanoseconds to microseconds. + Date strings can be formatted according to the ISO 8601 date time format as 'YYYY-MM-DD'. Args: - nanos (int): nanoseconds to convert. + dt (Union[None, LocalDate, str, datetime.date, datetime.time, datetime.datetime, numpy.datetime64, + pandas.Timestamp]): A date time value. If None is provided, None is returned. Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input nanoseconds converted to microseconds, rounded down. + LocalDate Raises: - DHError + DHError, TypeError """ - try: - return _JDateTimeUtils.nanosToMicros(nanos) - except Exception as e: - raise DHError(e) from e - - -def millis_to_micros(millis: int) -> int: - """ Converts milliseconds to microseconds. - - Args: - millis (int): milliseconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input milliseconds converted to microseconds. - Raises: - DHError - """ try: - return _JDateTimeUtils.millisToMicros(millis) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, LocalDate.j_type): + return dt + elif isinstance(dt, str): + return _JDateTimeUtils.parseLocalDate(dt) + elif isinstance(dt, datetime.date) or isinstance(dt, datetime.datetime) or isinstance(dt, pandas.Timestamp): + return _JLocalDate.of(dt.year, dt.month, dt.day) + elif isinstance(dt, numpy.datetime64): + return to_j_local_date(dt.astype(datetime.date)) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> LocalDate") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def seconds_to_micros(seconds: int) -> int: - """ Converts seconds to microseconds. - - Args: - seconds (int): Seconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input seconds converted to microseconds. - - Raises: - DHError +def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime.datetime, + numpy.datetime64, pandas.Timestamp]) -> Optional[LocalTime]: """ - try: - return _JDateTimeUtils.secondsToMicros(seconds) - except Exception as e: - raise DHError(e) from e + 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, + a numpy.datetime64, or a pandas.Timestamp. + int values are the number of nanoseconds since the start of the day. -def nanos_to_millis(nanos: int) -> int: - """ Converts nanoseconds to milliseconds. + Time strings can be formatted as 'hh:mm:ss[.nnnnnnnnn]'. Args: - nanos (int): Nanoseconds to convert. + 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. Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input nanoseconds converted to milliseconds, rounded down. + LocalTime Raises: - DHError + DHError, TypeError """ + try: - return _JDateTimeUtils.nanosToMillis(nanos) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, LocalTime.j_type): + return dt + elif isinstance(dt, int) and not isinstance(dt, bool): + return _JLocalTime.ofNanoOfDay(dt) + elif isinstance(dt, str): + return _JDateTimeUtils.parseLocalTime(dt) + elif isinstance(dt, pandas.Timestamp): + return _JLocalTime.of(dt.hour, dt.minute, dt.second, dt.microsecond * _nanos_per_micro + dt.nanosecond) + elif isinstance(dt, datetime.time) or isinstance(dt, datetime.datetime): + return _JLocalTime.of(dt.hour, dt.minute, dt.second, dt.microsecond * _nanos_per_micro) + elif isinstance(dt, numpy.datetime64): + # Conversion only supports micros resolution + return to_j_local_time(dt.astype(datetime.time)) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> LocalTime") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def micros_to_millis(micros: int) -> int: - """ Converts microseconds to milliseconds. - - Args: - micros (int): Microseconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input microseconds converted to milliseconds, rounded down. - - Raises: - DHError +def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \ + Optional[Instant]: """ - try: - return _JDateTimeUtils.microsToMillis(micros) - except Exception as e: - raise DHError(e) from e + Converts a date time value to a Java Instant. + Date time values can be None, a Java Instant, an int, a string, a datetime.time, a datetime.datetime, + a numpy.datetime64, or a pandas.Timestamp. + int values are the number of nanoseconds since the Epoch. -def seconds_to_millis(seconds: int) -> int: - """ Converts seconds to milliseconds. + Instant strings can be formatted according to the ISO 8601 date time format + 'yyyy-MM-ddThh:mm:ss[.SSSSSSSSS] TZ' and others. + Additionally, date time strings can be integer values that are nanoseconds, milliseconds, or seconds + from the Epoch. Expected date ranges are used to infer the units. Args: - seconds (int): Seconds to convert. + dt (Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]): A date time value. + If None is provided, None is returned. Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input seconds converted to milliseconds. + Instant, TypeError Raises: DHError """ try: - return _JDateTimeUtils.secondsToMillis(seconds) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, Instant.j_type): + return dt + elif isinstance(dt, int) and not isinstance(dt, bool): + return _JDateTimeUtils.epochNanosToInstant(dt) + elif isinstance(dt, str): + return _JDateTimeUtils.parseInstant(dt) + elif isinstance(dt, datetime.datetime): + epoch_time = dt.timestamp() + epoch_sec = int(epoch_time) + nanos = int((epoch_time - epoch_sec) * _nanos_per_second) + return _JInstant.ofEpochSecond(epoch_sec, nanos) + elif isinstance(dt, numpy.datetime64): + epoch_nanos = dt.astype('datetime64[ns]').astype(numpy.int64) + epoch_sec, nanos = divmod(epoch_nanos, _nanos_per_second) + return _JInstant.ofEpochSecond(int(epoch_sec), int(nanos)) + elif isinstance(dt, pandas.Timestamp): + return _JDateTimeUtils.epochNanosToInstant(dt.value) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> Instant") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def nanos_to_seconds(nanos: int) -> int: - """ Converts nanoseconds to seconds. - - Args: - nanos (int): Nanoseconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input nanoseconds converted to seconds. - - Raises: - DHError +def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \ + Optional[ZonedDateTime]: """ - try: - return _JDateTimeUtils.nanosToSeconds(nanos) - except Exception as e: - raise DHError(e) from e + Converts a date time value to a Java ZonedDateTime. + Date time values can be None, a Java ZonedDateTime, a string, a datetime.time, a datetime.datetime, + a numpy.datetime64, or a pandas.Timestamp. + Date time strings can be formatted according to the ISO 8601 date time format + '{@code 'yyyy-MM-ddThh:mm:ss[.SSSSSSSSS] TZ' and others. + Additionally, date time strings can be integer values that are nanoseconds, milliseconds, or seconds + from the Epoch. Expected date ranges are used to infer the units. -def micros_to_seconds(micros: int) -> int: - """ Converts microseconds to seconds. + Converting a datetime.datetime or pandas.Timestamp to a ZonedDateTime will use the datetime's timezone information. + Converting a numpy.datetime64 to a ZonedDateTime will use the Deephaven default time zone. Args: - micros (int): Microseconds to convert. + dt (Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]): + A date time value. If None is provided, None is returned. Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input microseconds converted to seconds. + ZonedDateTime Raises: - DHError + DHError, TypeError """ try: - return _JDateTimeUtils.microsToSeconds(micros) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, ZonedDateTime.j_type): + return dt + elif isinstance(dt, str): + return _JDateTimeUtils.parseZonedDateTime(dt) + elif isinstance(dt, datetime.datetime) or isinstance(dt, pandas.Timestamp): + instant = to_j_instant(dt) + tz = to_j_time_zone(dt.tzinfo) + + if tz is None: + tz = dh_time_zone() + + return _JZonedDateTime.ofInstant(instant, tz) + elif isinstance(dt, numpy.datetime64): + instant = to_j_instant(dt) + tz = _JDateTimeUtils.timeZone() + return _JZonedDateTime.ofInstant(instant, tz) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> ZonedDateTime") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def millis_to_seconds(millis: int) -> int: - """ Converts milliseconds to seconds. - - Args: - millis (int): Milliseconds to convert. - - Returns: - NULL_LONG if the input is NULL_LONG; otherwise the input milliseconds converted to seconds. - - Raises: - DHError +def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \ + Optional[Duration]: """ - try: - return _JDateTimeUtils.millisToSeconds(millis) - except Exception as e: - raise DHError(e) from e + 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). + Time duration values can be None, a Java Duration, an int, a string, a datetime.timedelta, a numpy.timedelta64, + or a pandas.Timedelta. + int values are nanoseconds. -# endregion - -# region Conversions: Date Time Types + Duration strings can be formatted according to the ISO-8601 duration format as '[-]PnDTnHnMn.nS', where the + coefficients can be positive or negative. Zero coefficients can be omitted. Optionally, the string can + begin with a negative sign. -def to_instant(dt: ZonedDateTime) -> Instant: - """ Converts a date time to an Instant. + Examples: + "PT20.345S" -- parses as "20.345 seconds" + "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds) + "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) + "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) + "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" + "PT-6H3M" -- parses as "-6 hours and +3 minutes" + "-PT6H3M" -- parses as "-6 hours and -3 minutes" + "-PT-6H+3M" -- parses as "+6 hours and -3 minutes" Args: - dt (ZonedDateTime): Date time to convert. + dt (Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]): + A time duration value. If None is provided, None is returned. Returns: - Instant or None if dt is None. + Duration Raises: - DHError + DHError, TypeError """ - if not dt: - return None - try: - return _JDateTimeUtils.toInstant(dt) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, Duration.j_type): + return dt + elif isinstance(dt, int) and not isinstance(dt, bool): + return _JDuration.ofNanos(dt) + elif isinstance(dt, str): + return _JDateTimeUtils.parseDuration(dt) + elif isinstance(dt, pandas.Timedelta): + nanos = int((dt / datetime.timedelta(microseconds=1)) * _nanos_per_micro) + dt.nanoseconds + return _JDuration.ofNanos(nanos) + elif isinstance(dt, datetime.timedelta): + nanos = int((dt / datetime.timedelta(microseconds=1)) * _nanos_per_micro) + return _JDuration.ofNanos(nanos) + elif isinstance(dt, numpy.timedelta64): + nanos = int(dt.astype('timedelta64[ns]').astype(numpy.int64)) + return _JDuration.ofNanos(nanos) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> Duration") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def to_zdt(dt: Instant, tz: TimeZone) -> ZonedDateTime: - """ Converts a date time to a ZonedDateTime. - - Args: - dt (Instant): Date time to convert. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if any input is None. - - Raises: - DHError +def to_j_period(dt: Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \ + Optional[Period]: """ - if not dt or not tz: - return None - - try: - return _JDateTimeUtils.toZonedDateTime(dt, tz) - except Exception as e: - raise DHError(e) from e + 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.). + Time duration values can be None, a Java Period, a string, a datetime.timedelta, a numpy.timedelta64, + or a pandas.Timedelta. + Period strings can be formatted according to the ISO-8601 duration format as 'PnYnMnD' and 'PnW', where the + coefficients can be positive or negative. Zero coefficients can be omitted. Optionally, the string can + begin with a negative sign. -def make_instant(date: LocalDate, time: LocalTime, tz: TimeZone) -> Instant: - """ Makes an Instant. + Examples: + "P2Y" -- 2 Years + "P3M" -- 3 Months + "P4W" -- 4 Weeks + "P5D" -- 5 Days + "P1Y2M3D" -- 1 Year, 2 Months, 3 Days + "P-1Y2M" -- -1 Year, 2 Months + "-P1Y2M" -- -1 Year, -2 Months Args: - date (LocalDate): Local date. - time (LocalTime): Local time. - tz (TimeZone): Time zone. + dt (Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]): + A Python period or period string. If None is provided, None is returned. Returns: - Instant or None if any input is None. + Period Raises: - DHError + DHError, TypeError, ValueError """ - if not date or not time or not tz: - return None - try: - return _JDateTimeUtils.toInstant(date, time, tz) + if dt is None or pandas.isnull(dt): + return None + elif isinstance(dt, Period.j_type): + return dt + elif isinstance(dt, str): + return _JDateTimeUtils.parsePeriod(dt) + elif isinstance(dt, pandas.Timedelta): + if dt.seconds or dt.microseconds or dt.nanoseconds: + raise ValueError("Unsupported conversion: " + str(type(dt)) + + " -> Period: Periods must only be days or weeks") + elif dt.days: + return _JPeriod.ofDays(dt.days) + else: + raise ValueError("Unsupported conversion: " + str(type(dt)) + " -> Period") + elif isinstance(dt, datetime.timedelta): + if dt.seconds or dt.microseconds: + raise ValueError("Unsupported conversion: " + str(type(dt)) + + " -> Period: Periods must only be days or weeks") + elif dt.days: + return _JPeriod.ofDays(dt.days) + else: + raise ValueError("Unsupported conversion: " + str(type(dt)) + " -> Period") + elif isinstance(dt, numpy.timedelta64): + data = numpy.datetime_data(dt) + units = data[0] + value = int(dt.astype(numpy.int64)) + + if units == 'D': + return _JPeriod.ofDays(value) + elif units == 'W': + return _JPeriod.ofDays(value * 7) + elif units == 'M': + return _JPeriod.ofMonths(value) + elif units == 'Y': + return _JPeriod.ofYears(value) + else: + raise ValueError("Unsupported conversion: " + str( + type(dt)) + " -> Period: numpy.datetime64 must have units of 'D', 'W', 'M', or 'Y'") + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> Period") + except ValueError as e: + raise e + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def make_zdt(date: LocalDate, time: LocalTime, tz: TimeZone) -> ZonedDateTime: - """ Makes a ZonedDateTime. - - Args: - date (LocalDate): Local date. - time (LocalTime): Local time. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if any input is None. +# endregion - Raises: - DHError - """ - if not date or not time or not tz: - return None - try: - return _JDateTimeUtils.toZonedDateTime(date, time, tz) - except Exception as e: - raise DHError(e) from e +# region Conversions: Java To Python -def to_local_date(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> LocalDate: - """ Converts a date time to a LocalDate. +def to_date(dt: Union[None, LocalDate, ZonedDateTime]) -> Optional[datetime.date]: + """ + Converts a Java date time to a datetime.date. Args: - dt (Instant): Date time to convert. - tz (TimeZone): Time zone. + dt (Union[None, LocalDate, ZonedDateTime]): A Java date time. + If None is provided, None is returned. Returns: - LocalDate or None if any input is None. + datetime.date Raises: - DHError + DHError, TypeError """ try: - if not dt or not tz: + if dt is None: return None - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.toLocalDate(dt, tz) + if isinstance(dt, LocalDate.j_type): + return datetime.date(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()) + if isinstance(dt, ZonedDateTime.j_type): + return datetime.date(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.date") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def to_local_time(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> LocalTime: - """ Converts a date time to a LocalTime. +def to_time(dt: Union[None, LocalTime, ZonedDateTime]) -> Optional[datetime.time]: + """ + Converts a Java date time to a datetime.time. Args: - dt (Instant): Date time to convert. - tz (TimeZone): Time zone. + dt (Union[None, LocalTime, ZonedDateTime]): A Java date time. + If None is provided, None is returned. Returns: - LocalTime or None if any input is None. + datetime.time Raises: - DHError + DHError, TypeError """ try: - if not dt or not tz: + if dt is None: return None - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.toLocalTime(dt, tz) + elif isinstance(dt, LocalTime.j_type): + return datetime.time(dt.getHour(), dt.getMinute(), dt.getSecond(), dt.getNano() // _nanos_per_micro) + elif isinstance(dt, ZonedDateTime.j_type): + return datetime.time(dt.getHour(), dt.getMinute(), dt.getSecond(), dt.getNano() // _nanos_per_micro) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.time") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -# endregion - -# region Conversions: Epoch - -def epoch_nanos(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns nanoseconds from the Epoch for a date time value. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - - Returns: - nanoseconds since Epoch, or a NULL_LONG value if the date time is null. - - Raises: - DHError +def to_datetime(dt: Union[None, Instant, ZonedDateTime]) -> Optional[datetime.datetime]: """ - if not dt: - return NULL_LONG - - try: - return _JDateTimeUtils.epochNanos(dt) - except Exception as e: - raise DHError(e) from e - - -def epoch_micros(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns microseconds from the Epoch for a date time value. + Converts a Java date time to a datetime.datetime. Args: - dt (Union[Instant,ZonedDateTime]): Date time. + dt (Union[None, Instant, ZonedDateTime]): A Java date time. + If None is provided, None is returned. Returns: - microseconds since Epoch, or a NULL_LONG value if the date time is null. + datetime.datetime Raises: - DHError + DHError, TypeError """ - if not dt: - return NULL_LONG - try: - return _JDateTimeUtils.epochMicros(dt) + if dt is None: + return None + elif isinstance(dt, Instant.j_type): + ts = dt.getEpochSecond() + (dt.getNano() / _nanos_per_second) + return datetime.datetime.fromtimestamp(ts) + elif isinstance(dt, ZonedDateTime.j_type): + ts = dt.toEpochSecond() + (dt.getNano() / _nanos_per_second) + return datetime.datetime.fromtimestamp(ts) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.datetime") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def epoch_millis(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns milliseconds from the Epoch for a date time value. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - - Returns: - milliseconds since Epoch, or a NULL_LONG value if the date time is null. - - Raises: - DHError +def to_pd_timestamp(dt: Union[None, Instant, ZonedDateTime]) -> Optional[pandas.Timestamp]: """ - if not dt: - return NULL_LONG - - try: - return _JDateTimeUtils.epochMillis(dt) - except Exception as e: - raise DHError(e) from e - - -def epoch_seconds(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns seconds from the Epoch for a date time value. + Converts a Java date time to a pandas.Timestamp. Args: - dt (Union[Instant,ZonedDateTime]): Date time. + dt (Union[None, Instant, ZonedDateTime]): A Java date time. + If None is provided, None is returned. Returns: - seconds since Epoch, or a NULL_LONG value if the date time is null. + pandas.Timestamp Raises: - DHError + DHError, TypeError """ - if not dt: - return NULL_LONG - try: - return _JDateTimeUtils.epochSeconds(dt) + if dt is None: + return None + elif isinstance(dt, Instant.j_type) or isinstance(dt, ZonedDateTime.j_type): + ts = _JDateTimeUtils.epochNanos(dt) + return pandas.Timestamp(ts_input=ts, unit='ns') + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> pandas.Timestamp") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def epoch_nanos_to_instant(nanos: int) -> Instant: - """ Converts nanoseconds from the Epoch to an Instant. - - Args: - nanos (int): Nanoseconds since Epoch. - - Returns: - Instant or None if the input is NULL_LONG. - - Raises: - DHError +def to_np_datetime64(dt: Union[None, Instant, ZonedDateTime]) -> Optional[numpy.datetime64]: """ - try: - return _JDateTimeUtils.epochNanosToInstant(nanos) - except Exception as e: - raise DHError(e) from e - - -def epoch_micros_to_instant(micros: int) -> Instant: - """ Converts microseconds from the Epoch to an Instant. + Converts a Java date time to a numpy.datetime64. Args: - micros (int): Microseconds since Epoch. + dt (Union[None, Instant, ZonedDateTime]): A Java date time. + If None is provided, None is returned. Returns: - Instant or None if the input is NULL_LONG. + numpy.datetime64 Raises: - DHError + DHError, TypeError """ try: - return _JDateTimeUtils.epochMicrosToInstant(micros) + if dt is None: + return None + elif isinstance(dt, Instant.j_type): + ts = dt.getEpochSecond() * _nanos_per_second + dt.getNano() + return numpy.datetime64(ts, 'ns') + elif isinstance(dt, ZonedDateTime.j_type): + ts = dt.toEpochSecond() * _nanos_per_second + dt.getNano() + return numpy.datetime64(ts, 'ns') + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.datetime") + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def epoch_millis_to_instant(millis: int) -> Instant: - """ Converts milliseconds from the Epoch to an Instant. - - Args: - millis (int): Milliseconds since Epoch. - - Returns: - Instant or None if the input is NULL_LONG. - - Raises: - DHError +def to_timedelta(dt: Union[None, Duration]) -> Optional[datetime.timedelta]: """ - try: - return _JDateTimeUtils.epochMillisToInstant(millis) - except Exception as e: - raise DHError(e) from e - - -def epoch_seconds_to_instant(seconds: int) -> Instant: - """ Converts seconds from the Epoch to an Instant. + Converts a Java time duration to a datetime.timedelta. Args: - seconds (int): Seconds since Epoch. + dt (Union[None, Duration]): A Java time duration. If None is provided, None is returned. Returns: - Instant or None if the input is NULL_LONG. + datetime.timedelta Raises: - DHError + DHError, TypeError, ValueError """ try: - return _JDateTimeUtils.epochSecondsToInstant(seconds) + if dt is None: + return None + elif isinstance(dt, Duration.j_type): + return datetime.timedelta(seconds=dt.getSeconds(), microseconds=dt.getNano() // _nanos_per_micro) + elif isinstance(dt, Period.j_type): + y = dt.getYears() + m = dt.getMonths() + d = dt.getDays() + + if y or m: + raise ValueError("Unsupported conversion: " + str(type(dt)) + + " -> datetime.timedelta: Periods must only be days or weeks") + + return datetime.timedelta(days=d) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.timedelta") + except ValueError as e: + raise e + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def epoch_nanos_to_zdt(nanos: int, tz: TimeZone) -> ZonedDateTime: - """ Converts nanoseconds from the Epoch to a ZonedDateTime. +def to_pd_timedelta(dt: Union[None, Duration]) -> Optional[pandas.Timedelta]: + """ + Converts a Java time duration to a pandas.Timedelta. Args: - nanos (int): Nanoseconds since Epoch. - tz (TimeZone): Time zone. + dt (Union[None, Duration]): A Java time duration. If None is provided, None is returned. Returns: - ZonedDateTime or None if the input is NULL_LONG or None. + pandas.Timedelta Raises: - DHError + DHError, TypeError, ValueError """ try: - return _JDateTimeUtils.epochNanosToZonedDateTime(nanos, tz) + if dt is None: + return None + elif isinstance(dt, Duration.j_type): + micros, nanos = divmod(dt.getNano(), _nanos_per_micro) + return pandas.Timedelta(seconds=dt.getSeconds(), microseconds=micros, nanoseconds=nanos) + elif isinstance(dt, Period.j_type): + y = dt.getYears() + m = dt.getMonths() + d = dt.getDays() + + if y or m: + raise ValueError("Unsupported conversion: " + str(type(dt)) + + " -> datetime.timedelta: Periods must only be days or weeks") + + return pandas.Timedelta(days=d) + else: + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> pandas.Timedelta") + except ValueError as e: + raise e + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e -def epoch_micros_to_zdt(micros: int, tz: TimeZone) -> ZonedDateTime: - """ Converts microseconds from the Epoch to a ZonedDateTime. +def to_np_timedelta64(dt: Union[None, Duration, Period]) -> Optional[numpy.timedelta64]: + """ + Converts a Java time durationto a numpy.timedelta64. Args: - micros (int): Microseconds since Epoch. - tz (TimeZone): Time zone. + dt (Union[None, Duration, Period]): A Java time period. If None is provided, None is returned. Returns: - ZonedDateTime or None if the input is NULL_LONG or None. + numpy.timedelta64 Raises: - DHError + DHError, TypeError, ValueError """ try: - return _JDateTimeUtils.epochMicrosToZonedDateTime(micros, tz) - except Exception as e: - raise DHError(e) from e - - -def epoch_millis_to_zdt(millis: int, tz: TimeZone) -> ZonedDateTime: - """ Converts milliseconds from the Epoch to a ZonedDateTime. - - Args: - millis (int): Milliseconds since Epoch. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if the input is NULL_LONG or None. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.epochMillisToZonedDateTime(millis, tz) - except Exception as e: - raise DHError(e) from e - - -def epoch_seconds_to_zdt(seconds: int, tz: TimeZone) -> ZonedDateTime: - """ Converts seconds from the Epoch to a ZonedDateTime. - - Args: - seconds (int): Seconds since Epoch. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if the input is NULL_LONG or None. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.epochSecondsToZonedDateTime(seconds, tz) - except Exception as e: - raise DHError(e) from e - - -def epoch_auto_to_epoch_nanos(epoch_offset: int) -> int: - """ Converts an offset from the Epoch to a nanoseconds from the Epoch. - The offset can be in milliseconds, microseconds, or nanoseconds. - Expected date ranges are used to infer the units for the offset. - - Args: - epoch_offset (int): Time offset from the Epoch. - - Returns: - the input offset from the Epoch converted to nanoseconds from the Epoch, or NULL_LONG if the input is NULL_LONG. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.epochAutoToEpochNanos(epoch_offset) - except Exception as e: - raise DHError(e) from e - - -def epoch_auto_to_instant(epoch_offset: int) -> Instant: - """ Converts an offset from the Epoch to an Instant. - The offset can be in milliseconds, microseconds, or nanoseconds. - Expected date ranges are used to infer the units for the offset. - - Args: - epoch_offset (int): Time offset from the Epoch. - - Returns: - Instant or None if the input is NULL_LONG - - Raises: - DHError - """ - try: - return _JDateTimeUtils.epochAutoToInstant(epoch_offset) - except Exception as e: - raise DHError(e) from e - - -def epoch_auto_to_zdt(epoch_offset: int, tz: TimeZone) -> ZonedDateTime: - """ Converts an offset from the Epoch to a ZonedDateTime. - The offset can be in milliseconds, microseconds, or nanoseconds. - Expected date ranges are used to infer the units for the offset. - - Args: - epoch_offset (int): Time offset from the Epoch. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if the input is NULL_LONG - - Raises: - DHError - """ - try: - return _JDateTimeUtils.epochAutoToZonedDateTime(epoch_offset, tz) - except Exception as e: - raise DHError(e) from e - - -# endregion - -# region Conversions: Excel - -def to_excel_time(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> float: - """ Converts a date time to an Excel time represented as a double. - - Args: - dt (Instant): Date time. - tz (TimeZone): Time zone. - - Returns: - Excel time as a double or NULL_DOUBLE if any input is None - - Raises: - DHError - """ - if not dt or not tz: - return NULL_DOUBLE - - try: - if not dt or not tz: - return NULL_DOUBLE - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.toExcelTime(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def excel_to_instant(excel: float, tz: TimeZone) -> Instant: - """ Converts an Excel time represented as a double to an Instant. - - Args: - excel (float): Excel time. - tz (TimeZone): Time zone. - - Returns: - Instant or None if any input is None or NULL_DOUBLE. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.excelToInstant(excel, tz) - except Exception as e: - raise DHError(e) from e - - -def excel_to_zdt(excel: float, tz: TimeZone) -> ZonedDateTime: - """ Converts an Excel time represented as a double to a ZonedDateTime. - - Args: - excel (float): Excel time. - tz (TimeZone): Time zone. - - Returns: - ZonedDateTime or None if any input is None or NULL_DOUBLE. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.excelToZonedDateTime(excel, tz) - except Exception as e: - raise DHError(e) from e - - -# endregion - -# region Arithmetic - -def plus_period(dt: Union[Instant, ZonedDateTime], period: Union[int, Duration, Period]) -> \ - Union[Instant, ZonedDateTime]: - """ Adds a time period to a date time. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - period (Union[int, Duration, Period]): Time period to add. Integer inputs are nanoseconds. - - Returns: - Date time, or None if any input is None or NULL_LONG. - - Raises: - DHError - """ - if not dt or not period: - return None - - try: - return _JDateTimeUtils.plus(dt, period) - except Exception as e: - raise DHError(e) from e - - -def minus_period(dt: Union[Instant, ZonedDateTime], period: Union[int, Duration, Period]) -> \ - Union[Instant, ZonedDateTime]: - """ Subtracts a time period from a date time. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - period (Union[int, Duration, Period]): Time period to subtract. Integer inputs are nanoseconds. - - Returns: - Date time, or None if any input is None or NULL_LONG. - - Raises: - DHError - """ - if not dt or not period: - return None - - try: - return _JDateTimeUtils.minus(dt, period) - except Exception as e: - raise DHError(e) from e - - -def diff_nanos(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> int: - """ Returns the difference in nanoseconds between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in nanoseconds or NULL_LONG if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_LONG - - try: - return _JDateTimeUtils.diffNanos(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_micros(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> int: - """ Returns the difference in microseconds between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in microseconds or NULL_LONG if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_LONG - - try: - return _JDateTimeUtils.diffMicros(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_millis(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> int: - """ Returns the difference in milliseconds between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in milliseconds or NULL_LONG if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_LONG - - try: - return _JDateTimeUtils.diffMillis(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_seconds(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> float: - """ Returns the difference in seconds between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in seconds or NULL_DOUBLE if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_DOUBLE - - try: - return _JDateTimeUtils.diffSeconds(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_minutes(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> float: - """ Returns the difference in minutes between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in minutes or NULL_DOUBLE if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_DOUBLE - - try: - return _JDateTimeUtils.diffMinutes(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_days(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> float: - """ Returns the difference in days between two date time values. Both values must be of the same type. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in days or NULL_DOUBLE if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_DOUBLE - - try: - return _JDateTimeUtils.diffDays(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_years_365(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> float: - """ Returns the difference in years between two date time values. Both values must be of the same type. - - Years are defined in terms of 365 day years. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in years or NULL_DOUBLE if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_DOUBLE - - try: - return _JDateTimeUtils.diffYears365(start, end) - except Exception as e: - raise DHError(e) from e - - -def diff_years_avg(start: Union[Instant, ZonedDateTime], end: Union[Instant, ZonedDateTime]) -> float: - """ Returns the difference in years between two date time values. Both values must be of the same type. - - Years are defined in terms of 365.2425 day years. - - Args: - start (Union[Instant,ZonedDateTime]): Start time. - end (Union[Instant,ZonedDateTime]): End time. - - Returns: - the difference in start and end in years or NULL_DOUBLE if any input is None. - - Raises: - DHError - """ - if not start or not end: - return NULL_DOUBLE - - try: - return _JDateTimeUtils.diffYearsAvg(start, end) - except Exception as e: - raise DHError(e) from e - -# endregion - -# region Comparisons - -def is_before(dt1: Union[Instant, ZonedDateTime], dt2: Union[Instant, ZonedDateTime]) -> bool: - """ Evaluates whether one date time value is before a second date time value. - Both values must be of the same type. - - Args: - dt1 (Union[Instant,ZonedDateTime]): First date time. - dt2 (Union[Instant,ZonedDateTime]): Second date time. - - Returns: - True if dt1 is before dt2; otherwise, False if either value is null or if dt2 is equal to or before dt1. - - Raises: - DHError - """ - if not dt1 or not dt2: - return False - - try: - return _JDateTimeUtils.isBefore(dt1, dt2) - except Exception as e: - raise DHError(e) from e - - -def is_before_or_equal(dt1: Union[Instant, ZonedDateTime], dt2: Union[Instant, ZonedDateTime]) -> bool: - """ Evaluates whether one date time value is before or equal to a second date time value. - Both values must be of the same type. - - Args: - dt1 (Union[Instant,ZonedDateTime]): First date time. - dt2 (Union[Instant,ZonedDateTime]): Second date time. - - Returns: - True if dt1 is before or equal to dt2; otherwise, False if either value is null or if dt2 is before dt1. - - Raises: - DHError - """ - if not dt1 or not dt2: - return False - - try: - return _JDateTimeUtils.isBeforeOrEqual(dt1, dt2) - except Exception as e: - raise DHError(e) from e - - -def is_after(dt1: Union[Instant, ZonedDateTime], dt2: Union[Instant, ZonedDateTime]) -> bool: - """ Evaluates whether one date time value is after a second date time value. - Both values must be of the same type. - - Args: - dt1 (Union[Instant,ZonedDateTime]): First date time. - dt2 (Union[Instant,ZonedDateTime]): Second date time. - - Returns: - True if dt1 is after dt2; otherwise, False if either value is null or if dt2 is equal to or after dt1. - - Raises: - DHError - """ - if not dt1 or not dt2: - return False - - try: - return _JDateTimeUtils.isAfter(dt1, dt2) - except Exception as e: - raise DHError(e) from e - - -def is_after_or_equal(dt1: Union[Instant, ZonedDateTime], dt2: Union[Instant, ZonedDateTime]) -> bool: - """ Evaluates whether one date time value is after or equal to a second date time value. - Both values must be of the same type. - - Args: - dt1 (Union[Instant,ZonedDateTime]): First date time. - dt2 (Union[Instant,ZonedDateTime]): Second date time. - - Returns: - True if dt1 is after or equal to dt2; otherwise, False if either value is null or if dt2 is after dt1. - - Raises: - DHError - """ - if not dt1 or not dt2: - return False - - try: - return _JDateTimeUtils.isAfterOrEqual(dt1, dt2) - except Exception as e: - raise DHError(e) from e - - -# endregion - -# region Chronology - -def nanos_of_milli(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns the number of nanoseconds that have elapsed since the top of the millisecond. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - - Returns: - Number of nanoseconds that have elapsed since the top of the millisecond, or NULL_INT if the input is None. - - Raises: - DHError - """ - if not dt: - return NULL_INT - - try: - return _JDateTimeUtils.nanosOfMilli(dt) - except Exception as e: - raise DHError(e) from e - - -def micros_of_milli(dt: Union[Instant, ZonedDateTime]) -> int: - """ Returns the number of microseconds that have elapsed since the top of the millisecond. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - - Returns: - Number of microseconds that have elapsed since the top of the millisecond, or NULL_INT if the input is None. - - Raises: - DHError - """ - if not dt: - return NULL_INT - - try: - return _JDateTimeUtils.microsOfMilli(dt) - except Exception as e: - raise DHError(e) from e - - -def nanos_of_second(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of nanoseconds that have elapsed since the top of the second. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of nanoseconds that have elapsed since the top of the second, or NULL_LONG if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_LONG - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.nanosOfSecond(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def micros_of_second(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of microseconds that have elapsed since the top of the second. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of microseconds that have elapsed since the top of the second, or NULL_LONG if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_LONG - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.microsOfSecond(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def millis_of_second(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of milliseconds that have elapsed since the top of the second. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of milliseconds that have elapsed since the top of the second, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.millisOfSecond(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def second_of_minute(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of seconds that have elapsed since the top of the minute. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of seconds that have elapsed since the top of the minute, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.secondOfMinute(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def minute_of_hour(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of minutes that have elapsed since the top of the hour. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of minutes that have elapsed since the top of the hour, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.minuteOfHour(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def nanos_of_day(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of nanoseconds that have elapsed since the top of the day. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of nanoseconds that have elapsed since the top of the day, or NULL_LONG if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_LONG - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.nanosOfDay(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def millis_of_day(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of milliseconds that have elapsed since the top of the day. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of milliseconds that have elapsed since the top of the day, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.millisOfDay(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def second_of_day(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of seconds that have elapsed since the top of the day. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of seconds that have elapsed since the top of the day, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.secondOfDay(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def minute_of_day(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of minutes that have elapsed since the top of the day. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of minutes that have elapsed since the top of the day, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.minuteOfDay(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def hour_of_day(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the number of hours that have elapsed since the top of the day. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Number of hours that have elapsed since the top of the day, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.hourOfDay(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def day_of_week(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns a 1-based int value of the day of the week for a date time in the specified time zone, with 1 being - Monday and 7 being Sunday. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Day of the week, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.dayOfWeek(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def day_of_month(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns a 1-based int value of the day of the month for a date time and specified time zone. - The first day of the month returns 1, the second day returns 2, etc. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Day of the month, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.dayOfMonth(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def day_of_year(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns a 1-based int value of the day of the year (Julian date) for a date time in the specified time zone. - The first day of the year returns 1, the second day returns 2, etc. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Day of the year, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.dayOfYear(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def month_of_year(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns a 1-based int value of the month of the year (Julian date) for a date time in the specified time zone. - January is 1, February is 2, etc. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Month of the year, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.monthOfYear(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def year(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the year for a date time in the specified time zone. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Year, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.year(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def year_of_century(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> int: - """ Returns the year of the century (two-digit year) for a date time in the specified time zone. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Year of the century, or NULL_INT if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return NULL_INT - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.yearOfCentury(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def at_midnight(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> Union[Instant, ZonedDateTime]: - """ Returns a date time for the prior midnight in the specified time zone. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - date time for the prior midnight in the specified time zone, or None if any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return None - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.atMidnight(dt, tz) - except Exception as e: - raise DHError(e) from e - - -# endregion - - -# region Format - -def format_duration_nanos(nanos: int) -> str: - """ Returns a nanosecond duration formatted as a "[-]PThhh:mm:ss.nnnnnnnnn" string. - - Args: - nanos (int): Nanosecond. - - Returns: - Formatted string, or None if the input is NULL_INT. - - Raises: - DHError - """ - try: - return _JDateTimeUtils.formatDurationNanos(nanos) - except Exception as e: - raise DHError(e) from e - - -def format_datetime(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> str: - """ Returns a date time formatted as a "yyyy-MM-ddThh:mm:ss.SSSSSSSSS TZ" string. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Formatted string, or None if the any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: - return None - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.formatDateTime(dt, tz) - except Exception as e: - raise DHError(e) from e - - -def format_date(dt: Union[Instant, ZonedDateTime], tz: TimeZone) -> str: - """ Returns a date time formatted as a "yyyy-MM-dd" string. - - Args: - dt (Union[Instant,ZonedDateTime]): Date time. - tz (TimeZone): Time zone. - - Returns: - Formatted string, or None if the any input is None. - - Raises: - DHError - """ - try: - if not dt or not tz: + if dt is None: return None - - if from_jtype(dt.getClass()) == ZonedDateTime: - dt = to_instant(dt) - - return _JDateTimeUtils.formatDate(dt, tz) - except Exception as e: - raise DHError(e) from e - - -# endregion - -# region Parse - -def parse_time_zone(s: str, quiet: bool = False) -> Optional[TimeZone]: - """ Parses the string argument as a time zone. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - Time Zone - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseTimeZoneQuiet(s) - else: - return _JDateTimeUtils.parseTimeZone(s) - except Exception as e: - raise DHError(e) from e - - -def parse_duration_nanos(s: str, quiet: bool = False) -> int: - """ Parses the string argument as a time duration in nanoseconds. - - Time duration strings can be formatted as '[-]PT[-]hh:mm:[ss.nnnnnnnnn]' or as a duration string - formatted as '[-]PnDTnHnMn.nS}'. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. - False will cause NULL_LONG to be returned. - - Returns: - number of nanoseconds represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseDurationNanosQuiet(s) - else: - return _JDateTimeUtils.parseDurationNanos(s) - except Exception as e: - raise DHError(e) from e - - -def parse_period(s: str, quiet: bool = False) -> Optional[Period]: - """ Parses the string argument as a period, which is a unit of time in terms of calendar time - (days, weeks, months, years, etc.). - - Period strings are formatted according to the ISO-8601 duration format as 'PnYnMnD' and 'PnW', where the - coefficients can be positive or negative. Zero coefficients can be omitted. Optionally, the string can - begin with a negative sign. - - Examples: - "P2Y" -- 2 Years - "P3M" -- 3 Months - "P4W" -- 4 Weeks - "P5D" -- 5 Days - "P1Y2M3D" -- 1 Year, 2 Months, 3 Days - "P-1Y2M" -- -1 Year, 2 Months - "-P1Y2M" -- -1 Year, -2 Months - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - Period represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parsePeriodQuiet(s) - else: - return _JDateTimeUtils.parsePeriod(s) - except Exception as e: - raise DHError(e) from e - - -def parse_duration(s: str, quiet: bool = False) -> Optional[Duration]: - """ Parses the string argument as a duration, which is a unit of time in terms of clock time - (24-hour days, hours, minutes, seconds, and nanoseconds). - - Duration strings are formatted according to the ISO-8601 duration format as '[-]PnDTnHnMn.nS', where the - coefficients can be positive or negative. Zero coefficients can be omitted. Optionally, the string can - begin with a negative sign. - - Examples: - "PT20.345S" -- parses as "20.345 seconds" - "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds) - "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) - "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) - "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" - "PT-6H3M" -- parses as "-6 hours and +3 minutes" - "-PT6H3M" -- parses as "-6 hours and -3 minutes" - "-PT-6H+3M" -- parses as "+6 hours and -3 minutes" - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - Period represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseDurationQuiet(s) - else: - return _JDateTimeUtils.parseDuration(s) - except Exception as e: - raise DHError(e) from e - - -def parse_epoch_nanos(s: str, quiet: bool = False) -> int: - """ Parses the string argument as nanoseconds since the Epoch. - - Date time strings are formatted according to the ISO 8601 date time format - 'yyyy-MM-ddThh:mm:ss[.SSSSSSSSS] TZ' and others. - Additionally, date time strings can be integer values that are nanoseconds, milliseconds, or seconds - from the Epoch. Expected date ranges are used to infer the units. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause NULL_LONG to be returned. - - Returns: - Instant represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseEpochNanosQuiet(s) - else: - return _JDateTimeUtils.parseEpochNanos(s) - except Exception as e: - raise DHError(e) from e - - -def parse_instant(s: str, quiet: bool = False) -> Optional[Instant]: - """ Parses the string argument as an Instant. - - Date time strings are formatted according to the ISO 8601 date time format - 'yyyy-MM-ddThh:mm:ss[.SSSSSSSSS] TZ' and others. - Additionally, date time strings can be integer values that are nanoseconds, milliseconds, or seconds - from the Epoch. Expected date ranges are used to infer the units. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - Instant represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseInstantQuiet(s) - else: - return _JDateTimeUtils.parseInstant(s) - except Exception as e: - raise DHError(e) from e - - -def parse_zdt(s: str, quiet: bool = False) -> Optional[ZonedDateTime]: - """ Parses the string argument as a ZonedDateTime. - - Date time strings are formatted according to the ISO 8601 date time format - '{@code 'yyyy-MM-ddThh:mm:ss[.SSSSSSSSS] TZ' and others. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - Instant represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseZonedDateTimeQuiet(s) - else: - return _JDateTimeUtils.parseZonedDateTime(s) - except Exception as e: - raise DHError(e) from e - - -def parse_time_precision(s: str, quiet: bool = False) -> Optional[str]: - """ Returns a string indicating the level of precision in a time, datetime, or period nanos string. (e.g. 'SecondOfMinute'). - - Args: - s (str): Time string. - quiet (bool): False will cause exceptions when strings can not be parsed. False will cause None to be returned. - - Returns: - String indicating the level of precision in a time or datetime string (e.g. 'SecondOfMinute'). - - Raises: - DHError - """ - try: - if quiet: - p = _JDateTimeUtils.parseTimePrecisionQuiet(s) - - if p: - return p.toString() + elif isinstance(dt, Duration.j_type): + return numpy.timedelta64(dt.toNanos(), 'ns') + elif isinstance(dt, Period.j_type): + d = dt.getDays() + m = dt.getMonths() + y = dt.getYears() + + count = (1 if d else 0) + (1 if m else 0) + (1 if y else 0) + + if count == 0: + return numpy.timedelta64(0, 'D') + elif count > 1: + raise ValueError("Unsupported conversion: " + str(type(dt)) + + " -> datetime.timedelta64: Periods must be days, months, or years") + elif y: + return numpy.timedelta64(y, 'Y') + elif m: + return numpy.timedelta64(m, 'M') + elif d: + return numpy.timedelta64(d, 'D') else: - return None - else: - return _JDateTimeUtils.parseTimePrecision(s).toString() - except Exception as e: - raise DHError(e) from e - - -def parse_local_date(s: str, quiet: bool = False) -> Optional[LocalTime]: - """ Parses the string argument as a local date, which is a date without a time or time zone. - - Date strings are formatted according to the ISO 8601 date time format as 'YYYY-MM-DD}'. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. True will cause None to be returned. - - Returns: - LocalDate represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseLocalDateQuiet(s) - else: - return _JDateTimeUtils.parseLocalDate(s) - except Exception as e: - raise DHError(e) from e - - -def parse_local_time(s: str, quiet: bool = False) -> Optional[LocalTime]: - """ Parses the string argument as a local time, which is the time that would be read from a clock and - does not have a date or timezone. - - Local time strings can be formatted as 'hh:mm:ss[.nnnnnnnnn]'. - - Args: - s (str): String to be converted. - quiet (bool): False will cause exceptions when strings can not be parsed. True will cause None to be returned. - - Returns: - LocalTime represented by the string. - - Raises: - DHError - """ - try: - if quiet: - return _JDateTimeUtils.parseLocalTimeQuiet(s) + raise ValueError("Unsupported conversion: " + str(type(dt)) + " -> datetime.timedelta64: (" + dt + ")") else: - return _JDateTimeUtils.parseLocalTime(s) + raise TypeError("Unsupported conversion: " + str(type(dt)) + " -> datetime.timedelta64") + except ValueError as e: + raise e + except TypeError as e: + raise e except Exception as e: raise DHError(e) from e diff --git a/py/server/tests/test_arrow.py b/py/server/tests/test_arrow.py index 042e8dba6dd..411128b2f4f 100644 --- a/py/server/tests/test_arrow.py +++ b/py/server/tests/test_arrow.py @@ -16,10 +16,8 @@ from deephaven.column import byte_col, char_col, short_col, int_col, long_col, float_col, double_col, \ string_col, datetime_col, bool_col from deephaven.table import Table -from deephaven.time import epoch_nanos_to_instant from tests.testbase import BaseTestCase - class ArrowTestCase(BaseTestCase): test_table: Table @@ -36,7 +34,7 @@ def setUp(self) -> None: float_col(name="Float", data=[1.01, -1.01]), double_col(name="Double", data=[1.01, -1.01]), string_col(name="String", data=["foo", "bar"]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), epoch_nanos_to_instant(-1)]), + datetime_col(name="Datetime", data=[1, -1]), ] self.test_table = new_table(cols=cols) diff --git a/py/server/tests/test_calendar.py b/py/server/tests/test_calendar.py index 34321b35ca7..3d6a3ba07a3 100644 --- a/py/server/tests/test_calendar.py +++ b/py/server/tests/test_calendar.py @@ -4,11 +4,13 @@ import unittest -from deephaven import DHError, time +import jpy + +from deephaven import DHError from deephaven.calendar import calendar_names, default_calendar_name, BusinessCalendar, DayOfWeek -from deephaven.config import get_server_timezone from tests.testbase import BaseTestCase +_JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") class CalendarTestCase(BaseTestCase): def setUp(self) -> None: @@ -49,7 +51,7 @@ def test_calendar(self): self.assertIsNotNone(default_calendar.previous_day(current_date)) self.assertIsNotNone(default_calendar.next_day(current_date)) self.assertIsNotNone(default_calendar.day_of_week(current_date).name) - self.assertEqual(default_calendar.time_zone, time.time_zone("America/New_York")) + self.assertEqual(default_calendar.time_zone, _JDateTimeUtils.timeZone("America/New_York")) self.assertEqual(self.test_calendar.previous_day(self.b_day), self.prev_b_day) self.assertEqual(self.test_calendar.next_day(self.b_day), self.next_b_day) @@ -66,10 +68,10 @@ def test_business_period(self): b_periods = self.test_calendar.business_schedule(self.b_day1).business_periods self.assertEqual(len(b_periods), 1) p = b_periods[0] - s = time.format_datetime(p.start_time, time.time_zone('America/New_York')) - self.assertEqual(p.start_time, time.parse_instant(s)) - s = time.format_datetime(p.end_time, time.time_zone('America/New_York')) - self.assertEqual(p.end_time, time.parse_instant(s)) + s = _JDateTimeUtils.formatDateTime(p.start_time, _JDateTimeUtils.timeZone('America/New_York')) + self.assertEqual(p.start_time, _JDateTimeUtils.parseInstant(s)) + s = _JDateTimeUtils.formatDateTime(p.end_time, _JDateTimeUtils.timeZone('America/New_York')) + self.assertEqual(p.end_time, _JDateTimeUtils.parseInstant(s)) self.assertEqual(p.length, 6.5 * 60 * 60 * 10 ** 9) def test_business_schedule_business_day(self): @@ -85,14 +87,14 @@ def test_business_schedule_business_day(self): self.assertTrue(b_schedule.is_business_time(b_period.start_time)) self.assertTrue(b_schedule.is_business_time(b_period.end_time)) - non_b_time = time.minus_period(b_schedule.start_of_day, 1) + non_b_time = _JDateTimeUtils.minus(b_schedule.start_of_day, 1) self.assertFalse(b_schedule.is_business_time(non_b_time)) - non_b_time = time.plus_period(b_schedule.end_of_day, 1) + non_b_time = _JDateTimeUtils.plus(b_schedule.end_of_day, 1) self.assertFalse(b_schedule.is_business_time(non_b_time)) - b_time = time.plus_period(b_schedule.start_of_day, 10 * 10 ** 9) + b_time = _JDateTimeUtils.plus(b_schedule.start_of_day, 10 * 10 ** 9) self.assertEqual(10 * 10 ** 9, b_schedule.business_time_elapsed(b_time)) - b_time = time.plus_period(b_schedule.end_of_day, 10 * 10 ** 9) + b_time = _JDateTimeUtils.plus(b_schedule.end_of_day, 10 * 10 ** 9) self.assertEqual(b_period.length, b_schedule.business_time_elapsed(b_time)) def test_business_calendar(self): diff --git a/py/server/tests/test_column.py b/py/server/tests/test_column.py index 36271e035a9..838a1bf076f 100644 --- a/py/server/tests/test_column.py +++ b/py/server/tests/test_column.py @@ -1,18 +1,17 @@ # # Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending # - +import datetime import time import unittest from dataclasses import dataclass -from deephaven import DHError, dtypes, new_table +from deephaven import DHError, dtypes, new_table, time as dhtime from deephaven import empty_table from deephaven.column import byte_col, char_col, short_col, bool_col, int_col, long_col, float_col, double_col, \ string_col, datetime_col, jobj_col, ColumnType from deephaven.constants import MAX_BYTE, MAX_SHORT, MAX_INT, MAX_LONG from deephaven.jcompat import j_array_list -from deephaven.time import epoch_nanos_to_instant from tests.testbase import BaseTestCase @@ -53,8 +52,8 @@ def test_column_error(self): with self.assertRaises(DHError) as cm: _ = string_col(name="String", data=[1, -1.01]) - with self.assertRaises(DHError) as cm: - _ = datetime_col(name="Datetime", data=[epoch_nanos_to_instant(round(time.time())), False]) + with self.assertRaises(TypeError) as cm: + _ = datetime_col(name="Datetime", data=[round(time.time()), False]) with self.assertRaises(DHError) as cm: _ = jobj_col(name="JObj", data=[jobj, CustomClass(-1, "-1")]) @@ -130,6 +129,11 @@ def get_x(i) -> int: self.assertEqual(t_list_integers.columns[5].data_type, dtypes.float32) self.assertEqual(t_list_integers.columns[6].data_type, dtypes.float64) + def test_datetime_col(self): + inst = dhtime.to_j_instant(round(time.time())) + dt = datetime.datetime.now() + _ = datetime_col(name="Datetime", data=[inst, dt, None]) + @dataclass class CustomClass: diff --git a/py/server/tests/test_config.py b/py/server/tests/test_config.py index b2a5d46b7c8..19036c4b835 100644 --- a/py/server/tests/test_config.py +++ b/py/server/tests/test_config.py @@ -2,19 +2,19 @@ # Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending # -import os.path import unittest +import jpy from deephaven.config import get_server_timezone -from deephaven.time import time_zone from tests.testbase import BaseTestCase +_JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") class ConfigTestCase(BaseTestCase): def test_get_server_timezone(self): tz = get_server_timezone() - self.assertEqual(tz, time_zone(None)) + self.assertEqual(tz, _JDateTimeUtils.timeZone()) if __name__ == '__main__': diff --git a/py/server/tests/test_dtypes.py b/py/server/tests/test_dtypes.py index 454b07415c9..758aab3ddfa 100644 --- a/py/server/tests/test_dtypes.py +++ b/py/server/tests/test_dtypes.py @@ -13,9 +13,10 @@ from deephaven import dtypes from deephaven.constants import * from deephaven.dtypes import Instant, LocalDate, LocalTime, Duration, Period, TimeZone, ZonedDateTime -from deephaven.time import now, to_zdt, time_zone +from deephaven.time import dh_now from tests.testbase import BaseTestCase +_JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") def remap_double(v, null_value): if v != v or v == NULL_DOUBLE or v == float('inf'): @@ -201,7 +202,7 @@ def remap_char(v): def test_instant(self): dt1 = Instant.j_type.ofEpochSecond(0, round(time.time())) - dt2 = now() + dt2 = dh_now() values = [dt1, dt2, None] j_array = dtypes.array(Instant, values) self.assertTrue(all(x == y for x, y in zip(j_array, values))) @@ -238,7 +239,7 @@ def test_period(self): def test_zdt(self): dt1 = ZonedDateTime.j_type.now() - dt2 = to_zdt(now(), time_zone(None)) + dt2 = _JDateTimeUtils.toZonedDateTime(dh_now(), _JDateTimeUtils.timeZone()) values = [dt1, dt2, None] j_array = dtypes.array(ZonedDateTime, values) self.assertTrue(all(x == y for x, y in zip(j_array, values))) diff --git a/py/server/tests/test_numpy.py b/py/server/tests/test_numpy.py index 5393ab68e38..14e78d0921e 100644 --- a/py/server/tests/test_numpy.py +++ b/py/server/tests/test_numpy.py @@ -13,7 +13,6 @@ from deephaven.constants import NULL_LONG, MAX_LONG from deephaven.numpy import to_numpy, to_table from deephaven.jcompat import j_array_list -from deephaven.time import epoch_nanos_to_instant from tests.testbase import BaseTestCase @@ -39,7 +38,7 @@ def setUp(self): float_col(name="Float", data=[1.01, -1.01]), double_col(name="Double", data=[1.01, -1.01]), string_col(name="String", data=["foo", "bar"]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), epoch_nanos_to_instant(-1)]), + datetime_col(name="Datetime", data=[1, -1]), pyobj_col(name="PyObj", data=[CustomClass(1, "1"), CustomClass(-1, "-1")]), pyobj_col(name="PyObj1", data=[[1, 2, 3], CustomClass(-1, "-1")]), pyobj_col(name="PyObj2", data=[False, 'False']), diff --git a/py/server/tests/test_pandas.py b/py/server/tests/test_pandas.py index 9d1f750e048..f896c23d853 100644 --- a/py/server/tests/test_pandas.py +++ b/py/server/tests/test_pandas.py @@ -10,17 +10,15 @@ import pandas as pd import pyarrow as pa -from deephaven import dtypes, new_table, DHError +from deephaven import dtypes, new_table, DHError, time from deephaven.column import byte_col, char_col, short_col, bool_col, int_col, long_col, float_col, double_col, \ string_col, datetime_col, pyobj_col, jobj_col from deephaven.constants import NULL_LONG, NULL_SHORT, NULL_INT, NULL_BYTE, NULL_CHAR, NULL_FLOAT, NULL_DOUBLE, \ NULL_BOOLEAN from deephaven.jcompat import j_array_list from deephaven.pandas import to_pandas, to_table -from deephaven.time import parse_instant, epoch_nanos_to_instant from tests.testbase import BaseTestCase - @dataclass class CustomClass: f1: int @@ -43,7 +41,7 @@ def setUp(self): float_col(name="Float_", data=[1.01, -1.01]), double_col(name="Double_", data=[1.01, -1.01]), string_col(name="String", data=["foo", "bar"]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), epoch_nanos_to_instant(-1)]), + datetime_col(name="Datetime", data=[1,-1]), pyobj_col(name="PyObj", data=[CustomClass(1, "1"), CustomClass(-1, "-1")]), pyobj_col(name="PyObj1", data=[[1, 2, 3], CustomClass(-1, "-1")]), pyobj_col(name="PyObj2", data=[False, 'False']), @@ -131,12 +129,12 @@ def test_to_table_boolean_with_none(self): def test_to_table_datetime_with_none(self): datetime_str = "2021-12-10T23:59:59 ET" - dt = parse_instant(datetime_str) + dt = time.to_j_instant(datetime_str) datetime_str = "2021-12-10T23:59:59 US/Hawaii" - dt1 = parse_instant(datetime_str) + dt1 = time.to_j_instant(datetime_str) - input_cols = [datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), None, dt, dt1])] + input_cols = [datetime_col(name="Datetime", data=[1, None, dt, dt1])] table_with_null_dt = new_table(cols=input_cols) df = to_pandas(table_with_null_dt) @@ -157,7 +155,7 @@ def test_round_trip_with_nulls(self): long_col(name="Long_", data=[1, NULL_LONG]), float_col(name="Float_", data=[1.01, np.nan]), double_col(name="Double_", data=[1.01, np.nan]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), None]), + datetime_col(name="Datetime", data=[1, None]), pyobj_col(name="PyObj", data=[CustomClass(1, "1"), None]), ] test_table = new_table(cols=input_cols) @@ -253,7 +251,7 @@ def test_arrow_backend_nulls(self): long_col(name="Long_", data=[1, NULL_LONG]), float_col(name="Float_", data=[1.01, np.nan]), double_col(name="Double_", data=[1.01, np.nan]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), None]), + datetime_col(name="Datetime", data=[1, None]), string_col(name="String", data=["text1", None]) # pyobj_col(name="PyObj", data=[CustomClass(1, "1"), None]), #DH arrow export it as strings ] @@ -272,7 +270,7 @@ def test_np_nullable_backend_nulls(self): long_col(name="Long_", data=[1, NULL_LONG]), float_col(name="Float_", data=[1.01, np.nan]), double_col(name="Double_", data=[1.01, np.nan]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), None]), + datetime_col(name="Datetime", data=[1, None]), string_col(name="String", data=["text1", None]), # pyobj_col(name="PyObj", data=[CustomClass(1, "1"), None]), # DH arrow export it as strings ] @@ -321,7 +319,7 @@ def test_conv_null(self): long_col(name="Long_", data=[1, NULL_LONG]), float_col(name="Float_", data=[np.nan, NULL_FLOAT]), double_col(name="Double_", data=[np.nan, NULL_DOUBLE]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), None]), + datetime_col(name="Datetime", data=[1, None]), ] t = new_table(cols=input_cols) df = to_pandas(t, conv_null=True) diff --git a/py/server/tests/test_plot/test_plot.py b/py/server/tests/test_plot/test_plot.py index 5d7fce2e2f5..6c95bfd4d06 100644 --- a/py/server/tests/test_plot/test_plot.py +++ b/py/server/tests/test_plot/test_plot.py @@ -10,7 +10,7 @@ from deephaven.plot import Shape from deephaven.plot import PlotStyle from deephaven.plot import Figure -from deephaven.time import time_zone +from deephaven.time import to_j_time_zone from tests.testbase import BaseTestCase @@ -54,7 +54,7 @@ def test_axis_format(self): axis = new_f.axis(format=nanos_aix_format) self.assertIsNotNone(axis) - nanos_aix_format = NanosAxisFormat(tz=time_zone("PT")) + nanos_aix_format = NanosAxisFormat(tz=to_j_time_zone("PT")) nanos_aix_format.set_pattern("yyyy-MM-dd'T'HH:mm") axis = new_f.axis(format=nanos_aix_format) self.assertIsNotNone(axis) diff --git a/py/server/tests/test_replay.py b/py/server/tests/test_replay.py index b8eb0ef5f94..db0a2242285 100644 --- a/py/server/tests/test_replay.py +++ b/py/server/tests/test_replay.py @@ -4,17 +4,16 @@ import unittest -from deephaven import DHError, new_table, TableReplayer +from deephaven import DHError, new_table, TableReplayer, time from deephaven.column import int_col, datetime_col -from deephaven.time import parse_instant from tests.testbase import BaseTestCase class ReplayTestCase(BaseTestCase): def historical_table_replayer(self, start_time, end_time): - dt1 = parse_instant("2000-01-01T00:00:01 ET") - dt2 = parse_instant("2000-01-01T00:00:02 ET") - dt3 = parse_instant("2000-01-01T00:00:04 ET") + dt1 = time.to_j_instant("2000-01-01T00:00:01 ET") + dt2 = time.to_j_instant("2000-01-01T00:00:02 ET") + dt3 = time.to_j_instant("2000-01-01T00:00:04 ET") hist_table = new_table( [datetime_col("DateTime", [dt1, dt2, dt3]), int_col("Number", [1, 3, 6])] @@ -55,8 +54,8 @@ def historical_table_replayer(self, start_time, end_time): replayer.shutdown() def test_historical_table_replayer_instant(self): - start_time = parse_instant("2000-01-01T00:00:00 ET") - end_time = parse_instant("2000-01-01T00:00:05 ET") + start_time = time.to_j_instant("2000-01-01T00:00:00 ET") + end_time = time.to_j_instant("2000-01-01T00:00:05 ET") self.historical_table_replayer(start_time, end_time) @@ -66,5 +65,11 @@ def test_historical_table_replayer_str(self): self.historical_table_replayer(start_time, end_time) + def test_historical_table_replayer_datetime(self): + start_time = time.to_datetime(time.to_j_instant("2000-01-01T00:00:00 ET")) + end_time = time.to_datetime(time.to_j_instant("2000-01-01T00:00:05 ET")) + + self.historical_table_replayer(start_time, end_time) + if __name__ == "__main__": unittest.main() diff --git a/py/server/tests/test_table.py b/py/server/tests/test_table.py index 27cb7e9e1f7..3b6abf76b36 100644 --- a/py/server/tests/test_table.py +++ b/py/server/tests/test_table.py @@ -14,7 +14,6 @@ from deephaven.jcompat import j_hashmap from deephaven.pandas import to_pandas from deephaven.table import Table, SearchDisplayMode -from deephaven.time import epoch_nanos_to_instant from tests.testbase import BaseTestCase, table_equals @@ -944,10 +943,9 @@ def make_pairs_3(tid, a, b): def test_callable_attrs_in_query(self): input_cols = [ - datetime_col(name="DTCol", data=[epoch_nanos_to_instant(1), epoch_nanos_to_instant(10000000)]), + datetime_col(name="DTCol", data=[1,10000000]), ] test_table = new_table(cols=input_cols) - from deephaven.time import year, TimeZone rt = test_table.update("Year = (int)year(DTCol, timeZone(`ET`))") self.assertEqual(rt.size, test_table.size) diff --git a/py/server/tests/test_table_factory.py b/py/server/tests/test_table_factory.py index 2cad09ee122..c7bff4c19ec 100644 --- a/py/server/tests/test_table_factory.py +++ b/py/server/tests/test_table_factory.py @@ -8,17 +8,19 @@ import jpy import numpy as np -from deephaven import DHError, read_csv, time_table, empty_table, merge, merge_sorted, dtypes, new_table, input_table +from deephaven import DHError, read_csv, time_table, empty_table, merge, merge_sorted, dtypes, new_table, \ + input_table, time from deephaven.column import byte_col, char_col, short_col, bool_col, int_col, long_col, float_col, double_col, \ string_col, datetime_col, pyobj_col, jobj_col from deephaven.constants import NULL_DOUBLE, NULL_FLOAT, NULL_LONG, NULL_INT, NULL_SHORT, NULL_BYTE from deephaven.table_factory import DynamicTableWriter, ring_table -from deephaven.time import epoch_nanos_to_instant, format_datetime, time_zone from tests.testbase import BaseTestCase from deephaven.table import Table from deephaven.stream import blink_to_append_only, stream_to_append_only JArrayList = jpy.get_type("java.util.ArrayList") +_JBlinkTableTools = jpy.get_type("io.deephaven.engine.table.impl.BlinkTableTools") +_JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") @dataclass class CustomClass: @@ -54,7 +56,7 @@ def test_time_table(self): t = time_table("PT00:00:01", start_time="2021-11-06T13:21:00 ET") self.assertEqual(1, len(t.columns)) self.assertTrue(t.is_refreshing) - self.assertEqual("2021-11-06T13:21:00.000000000 ET", format_datetime(t.j_table.getColumnSource("Timestamp").get(0), time_zone('ET'))) + self.assertEqual("2021-11-06T13:21:00.000000000 ET", _JDateTimeUtils.formatDateTime(t.j_table.getColumnSource("Timestamp").get(0), time.to_j_time_zone('ET'))) t = time_table(1000_000_000) self.assertEqual(1, len(t.columns)) @@ -63,7 +65,18 @@ def test_time_table(self): t = time_table(1000_1000_1000, start_time="2021-11-06T13:21:00 ET") self.assertEqual(1, len(t.columns)) self.assertTrue(t.is_refreshing) - self.assertEqual("2021-11-06T13:21:00.000000000 ET", format_datetime(t.j_table.getColumnSource("Timestamp").get(0), time_zone('ET'))) + self.assertEqual("2021-11-06T13:21:00.000000000 ET", _JDateTimeUtils.formatDateTime(t.j_table.getColumnSource("Timestamp").get(0), time.to_j_time_zone('ET'))) + + p = time.to_timedelta(time.to_j_duration("PT1s")) + t = time_table(p) + self.assertEqual(1, len(t.columns)) + self.assertTrue(t.is_refreshing) + + st = time.to_datetime(time.to_j_instant("2021-11-06T13:21:00 ET")) + t = time_table(p, start_time=st) + self.assertEqual(1, len(t.columns)) + self.assertTrue(t.is_refreshing) + self.assertEqual("2021-11-06T13:21:00.000000000 ET", _JDateTimeUtils.formatDateTime(t.j_table.getColumnSource("Timestamp").get(0), time.to_j_time_zone('ET'))) def test_time_table_blink(self): t = time_table("PT1s", blink_table=True) @@ -109,7 +122,7 @@ def test_new_table(self): float_col(name="Float", data=[1.01, -1.01]), double_col(name="Double", data=[1.01, -1.01]), string_col(name="String", data=["foo", "bar"]), - datetime_col(name="Datetime", data=[epoch_nanos_to_instant(1), epoch_nanos_to_instant(-1)]), + datetime_col(name="Datetime", data=[1, -1]), pyobj_col(name="PyObj", data=[CustomClass(1, "1"), CustomClass(-1, "-1")]), pyobj_col(name="PyObj1", data=[[1, 2, 3], CustomClass(-1, "-1")]), pyobj_col(name="PyObj2", data=[False, 'False']), diff --git a/py/server/tests/test_time.py b/py/server/tests/test_time.py index 77af96f8d3d..a7ce18d46ae 100644 --- a/py/server/tests/test_time.py +++ b/py/server/tests/test_time.py @@ -5,876 +5,598 @@ import unittest from time import sleep import datetime +import pandas as pd +import numpy as np -from deephaven import dtypes -from deephaven.constants import NULL_LONG, NULL_INT from deephaven.time import * from tests.testbase import BaseTestCase +_JDateTimeUtils = jpy.get_type("io.deephaven.time.DateTimeUtils") class TimeTestCase(BaseTestCase): - # region Constants - - def test_constants(self): - self.assertEqual(1000,MICRO) - self.assertEqual(1000000,MILLI) - self.assertEqual(1000000000,SECOND) - self.assertEqual(60*1000000000,MINUTE) - self.assertEqual(60*60*1000000000,HOUR) - self.assertEqual(24*60*60*1000000000,DAY) - self.assertEqual(7*24*60*60*1000000000,WEEK) - self.assertEqual(365*24*60*60*1000000000,YEAR_365) - self.assertEqual(31556952000000000,YEAR_AVG) - - self.assertEqual(1/SECOND, SECONDS_PER_NANO) - self.assertEqual(1/MINUTE, MINUTES_PER_NANO) - self.assertEqual(1/HOUR, HOURS_PER_NANO) - self.assertEqual(1/DAY, DAYS_PER_NANO) - self.assertEqual(1/YEAR_365, YEARS_PER_NANO_365) - self.assertEqual(1/YEAR_AVG, YEARS_PER_NANO_AVG) - - # endregion - # region: CLock - def test_now(self): + def test_dh_now(self): for system in [True, False]: for resolution in ['ns', 'ms']: - dt = now(system=system, resolution=resolution) + dt = dh_now(system=system, resolution=resolution) sleep(1) - dt1 = now(system=system, resolution=resolution) - self.assertGreaterEqual(diff_nanos(dt, dt1), 100000000) + dt1 = dh_now(system=system, resolution=resolution) + self.assertGreaterEqual(_JDateTimeUtils.diffNanos(dt, dt1), 100000000) - def test_today(self): - tz = time_zone("UTC") - td = today(tz) + def test_dh_today(self): + tz = _JDateTimeUtils.timeZone("UTC") + td = dh_today(tz) target = datetime.datetime.utcnow().date().strftime('%Y-%m-%d') self.assertEqual(td, target) + def test_dh_time_zone(self): + tz = dh_time_zone() + self.assertEqual(str(tz), "Etc/UTC") + # endregion # region: Time Zone - def test_time_zone(self): - tz = time_zone("America/New_York") - self.assertEqual(str(tz), "America/New_York") - - tz = time_zone("CT") - self.assertEqual(str(tz), "America/Chicago") - - tz = time_zone(None) - self.assertEqual(str(tz), "Etc/UTC") - def test_time_zone_alias_add_rm(self): alias = "TestAlias" tz_str = "Etc/UTC" with self.assertRaises(DHError) as cm: - time_zone(alias) + to_j_time_zone(alias) self.assertFalse(time_zone_alias_rm(alias)) time_zone_alias_add(alias, tz_str) - tz = time_zone(alias) + tz = to_j_time_zone(alias) self.assertEqual(str(tz), tz_str) self.assertTrue(time_zone_alias_rm(alias)) with self.assertRaises(DHError) as cm: - time_zone(alias) + to_j_time_zone(alias) # endregion - - # region: Conversions: Time Units - - def test_micros_to_nanos(self): - t = 123456789 - self.assertEqual(t * 10 ** 3, micros_to_nanos(t)) - self.assertEqual(NULL_LONG, micros_to_nanos(NULL_LONG)) - - def test_millis_to_nanos(self): - t = 123456789 - self.assertEqual(t * 10 ** 6, millis_to_nanos(t)) - self.assertEqual(NULL_LONG, millis_to_nanos(NULL_LONG)) - - def test_seconds_to_nanos(self): - t = 123456789 - self.assertEqual(t * 10 ** 9, seconds_to_nanos(t)) - self.assertEqual(NULL_LONG, seconds_to_nanos(NULL_LONG)) - - def test_nanos_to_micros(self): - t = 123456789 - self.assertEqual(t // 10 ** 3, nanos_to_micros(t)) - self.assertEqual(NULL_LONG, nanos_to_micros(NULL_LONG)) - - def test_millis_to_micros(self): - t = 123456789 - self.assertEqual(t * 10 ** 3, millis_to_micros(t)) - self.assertEqual(NULL_LONG, millis_to_micros(NULL_LONG)) - - def test_seconds_to_micros(self): - t = 123456789 - self.assertEqual(t * 10 ** 6, seconds_to_micros(t)) - self.assertEqual(NULL_LONG, seconds_to_micros(NULL_LONG)) - - def test_nanos_to_millis(self): - t = 123456789 - self.assertEqual(t // 10 ** 6, nanos_to_millis(t)) - self.assertEqual(NULL_LONG, nanos_to_millis(NULL_LONG)) - - def test_micros_to_millis(self): - t = 123456789 - self.assertEqual(t // 10 ** 3, micros_to_millis(t)) - self.assertEqual(NULL_LONG, micros_to_millis(NULL_LONG)) - - def test_seconds_to_millis(self): - t = 123456789 - self.assertEqual(t * 10 ** 3, seconds_to_millis(t)) - self.assertEqual(NULL_LONG, seconds_to_millis(NULL_LONG)) - - def test_nanos_to_seconds(self): - t = 123456789 - self.assertEqual(t // 10 ** 9, nanos_to_seconds(t)) - self.assertEqual(NULL_LONG, nanos_to_seconds(NULL_LONG)) - - def test_micros_to_seconds(self): - t = 123456789 - self.assertEqual(t // 10 ** 6, micros_to_seconds(t)) - self.assertEqual(NULL_LONG, micros_to_seconds(NULL_LONG)) - - def test_millis_to_seconds(self): - t = 123456789 - self.assertEqual(t // 10 ** 3, millis_to_seconds(t)) - self.assertEqual(NULL_LONG, millis_to_seconds(NULL_LONG)) + # region Conversions: Python To Java - # endregion - - # region: Conversions: Date Time Types - - def test_to_instant(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_zdt("2021-12-10T14:21:17.123456789 ET") - - self.assertEqual(dt1, to_instant(dt2)) - self.assertEquals(None,to_instant(None)) - - def test_to_zdt(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_zdt("2021-12-10T14:21:17.123456789 ET") - - self.assertEqual(dt2, to_zdt(dt1, time_zone("ET"))) - self.assertEquals(None,to_zdt(None, time_zone("ET"))) - - def test_make_instant(self): - dt = parse_instant("2021-12-10T14:21:17.123456789 ET") - ld = parse_local_date("2021-12-10") - lt = parse_local_time("14:21:17.123456789") - tz = time_zone("ET") - - self.assertEqual(dt, make_instant(ld, lt, tz)) - self.assertEquals(None,make_instant(ld, lt, None)) - self.assertEquals(None,make_instant(ld, None, tz)) - self.assertEquals(None,make_instant(None, lt, tz)) - - def test_make_zdt(self): - dt = parse_zdt("2021-12-10T14:21:17.123456789 ET") - ld = parse_local_date("2021-12-10") - lt = parse_local_time("14:21:17.123456789") - tz = time_zone("ET") - - self.assertEqual(dt, make_zdt(ld, lt, tz)) - self.assertEquals(None,make_zdt(ld, lt, None)) - self.assertEquals(None,make_zdt(ld, None, tz)) - self.assertEquals(None,make_zdt(None, lt, tz)) - - def test_to_local_date(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_zdt("2021-12-10T14:21:17.123456789 ET") - tz = time_zone("ET") - ld = parse_local_date("2021-12-10") - - self.assertEqual(ld, to_local_date(dt1, tz)) - self.assertEqual(ld, to_local_date(dt2, tz)) - self.assertEquals(None,to_local_date(dt1, None)) - self.assertEquals(None,to_local_date(dt2, None)) - self.assertEquals(None,to_local_date(None, tz)) - - def test_to_local_time(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_zdt("2021-12-10T14:21:17.123456789 ET") - tz = time_zone("ET") - lt = parse_local_time("14:21:17.123456789") - - self.assertEqual(lt, to_local_time(dt1, tz)) - self.assertEqual(lt, to_local_time(dt2, tz)) - self.assertEquals(None,to_local_time(dt1, None)) - self.assertEquals(None,to_local_time(dt2, None)) - self.assertEquals(None,to_local_time(None, tz)) + def test_to_j_time_zone(self): + tz = to_j_time_zone("America/New_York") + self.assertEqual(str(tz), "America/New_York") - # endregion - - # region: Conversions: Epoch - - def test_epoch_nanos(self): - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos) - self.assertEqual(nanos, epoch_nanos(dt)) - self.assertEqual(NULL_LONG, epoch_nanos(None)) - - def test_epoch_micros(self): - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos) - self.assertEqual(nanos // 10**3, epoch_micros(dt)) - self.assertEqual(NULL_LONG, epoch_micros(None)) - - def test_epoch_millis(self): - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos) - self.assertEqual(nanos // 10**6, epoch_millis(dt)) - self.assertEqual(NULL_LONG, epoch_millis(None)) - - def test_epoch_seconds(self): - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos) - self.assertEqual(nanos // 10**9, epoch_seconds(dt)) - self.assertEqual(NULL_LONG, epoch_seconds(None)) - - def test_epoch_nanos_to_instant(self): - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos) - self.assertEqual(dt, epoch_nanos_to_instant(nanos)) - self.assertEquals(None,epoch_nanos_to_instant(NULL_LONG)) - - def test_epoch_micros_to_instant(self): - nanos = 12345678987654321 - micros = nanos // 10**3 - dt = dtypes.Instant.j_type.ofEpochSecond(0, micros * 10**3) - self.assertEqual(dt, epoch_micros_to_instant(micros)) - self.assertEquals(None,epoch_micros_to_instant(NULL_LONG)) - - def test_epoch_millis_to_instant(self): - nanos = 12345678987654321 - millis = nanos // 10**6 - dt = dtypes.Instant.j_type.ofEpochSecond(0, millis * 10**6) - self.assertEqual(dt, epoch_millis_to_instant(millis)) - self.assertEquals(None,epoch_millis_to_instant(NULL_LONG)) - - def test_epoch_seconds_to_instant(self): - nanos = 12345678987654321 - seconds = nanos // 10**9 - dt = dtypes.Instant.j_type.ofEpochSecond(0, seconds * 10**9) - self.assertEqual(dt, epoch_seconds_to_instant(seconds)) - self.assertEquals(None,epoch_seconds_to_instant(NULL_LONG)) - - def test_epoch_nanos_to_zdt(self): - tz = time_zone("ET") - nanos = 12345678987654321 - dt = dtypes.Instant.j_type.ofEpochSecond(0, nanos).atZone(tz) - self.assertEqual(dt, epoch_nanos_to_zdt(nanos, tz)) - self.assertEquals(None,epoch_nanos_to_zdt(NULL_LONG, tz)) - - def test_epoch_micros_to_zdt(self): - tz = time_zone("ET") - nanos = 12345678987654321 - micros = nanos // 10**3 - dt = dtypes.Instant.j_type.ofEpochSecond(0, micros * 10**3).atZone(tz) - self.assertEqual(dt, epoch_micros_to_zdt(micros, tz)) - self.assertEquals(None,epoch_micros_to_zdt(NULL_LONG, tz)) - - def test_epoch_millis_to_zdt(self): - tz = time_zone("ET") - nanos = 12345678987654321 - millis = nanos // 10**6 - dt = dtypes.Instant.j_type.ofEpochSecond(0, millis * 10**6).atZone(tz) - self.assertEqual(dt, epoch_millis_to_zdt(millis, tz)) - self.assertEquals(None,epoch_millis_to_zdt(NULL_LONG, tz)) - - def test_epoch_seconds_to_zdt(self): - tz = time_zone("ET") - nanos = 12345678987654321 - seconds = nanos // 10**9 - dt = dtypes.Instant.j_type.ofEpochSecond(0, seconds * 10**9).atZone(tz) - self.assertEqual(dt, epoch_seconds_to_zdt(seconds, tz)) - self.assertEquals(None,epoch_seconds_to_zdt(NULL_LONG, tz)) - - def test_epoch_auto_to_epoch_nanos(self): - nanos = 1639171277303*10**6 + 123456789 - micros = nanos // 10**3 - millis = nanos // 10**6 - seconds = nanos // 10**9 - - self.assertEqual(nanos,epoch_auto_to_epoch_nanos(nanos)) - self.assertEqual(micros * 10**3,epoch_auto_to_epoch_nanos(micros)) - self.assertEqual(millis * 10**6,epoch_auto_to_epoch_nanos(millis)) - self.assertEqual(seconds * 10**9,epoch_auto_to_epoch_nanos(seconds)) - self.assertEqual(NULL_LONG, epoch_auto_to_epoch_nanos(NULL_LONG)) - - def test_epoch_auto_to_instant(self): - nanos = 1639171277303 * 10 ** 6 + 123456789 - micros = nanos // 10 ** 3 - millis = nanos // 10 ** 6 - seconds = nanos // 10 ** 9 - - self.assertEqual(epoch_nanos_to_instant(nanos), epoch_auto_to_instant(nanos)) - self.assertEqual(epoch_nanos_to_instant(micros * 10 ** 3), epoch_auto_to_instant(micros)) - self.assertEqual(epoch_nanos_to_instant(millis * 10 ** 6), epoch_auto_to_instant(millis)) - self.assertEqual(epoch_nanos_to_instant(seconds * 10 ** 9), epoch_auto_to_instant(seconds)) - self.assertEqual(None,epoch_auto_to_instant(NULL_LONG)) - - - def test_epoch_auto_to_zdt(self): - nanos = 1639171277303 * 10 ** 6 + 123456789 - micros = nanos // 10 ** 3 - millis = nanos // 10 ** 6 - seconds = nanos // 10 ** 9 - - self.assertEqual(epoch_nanos_to_zdt(nanos, time_zone("ET")), epoch_auto_to_zdt(nanos, time_zone("ET"))) - self.assertEqual(epoch_nanos_to_zdt(micros * 10 ** 3, time_zone("ET")), epoch_auto_to_zdt(micros, time_zone("ET"))) - self.assertEqual(epoch_nanos_to_zdt(millis * 10 ** 6, time_zone("ET")), epoch_auto_to_zdt(millis, time_zone("ET"))) - self.assertEqual(epoch_nanos_to_zdt(seconds * 10 ** 9, time_zone("ET")), epoch_auto_to_zdt(seconds, time_zone("ET"))) - self.assertEqual(None,epoch_auto_to_zdt(NULL_LONG, time_zone("ET"))) + tz = to_j_time_zone("CT") + self.assertEqual(str(tz), "America/Chicago") - # endregion - - # region: Conversions: Excel + pytz = datetime.datetime.now().astimezone().tzinfo + tz = to_j_time_zone(pytz) + self.assertEqual(str(tz), "UTC") - def test_excel(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_zdt("2021-12-10T14:21:17.123456789 ET") - tz = time_zone("ET") - excel = to_excel_time(dt1, tz) + pytz = datetime.datetime.now() + tz = to_j_time_zone(pytz) + self.assertEqual(str(tz), "UTC") - self.assertTrue( abs(epoch_nanos(dt1) - epoch_nanos(excel_to_instant(excel, tz)) < 2_000_000 ) ) - self.assertTrue( abs(epoch_nanos(dt1) - epoch_nanos(excel_to_zdt(excel, tz)) < 2_000_000 ) ) - self.assertEquals(None,excel_to_instant(excel, None)) - self.assertEquals(None,excel_to_zdt(excel, None)) + pytz = datetime.datetime.now().astimezone() + tz = to_j_time_zone(pytz) + self.assertEqual(str(tz), "UTC") - # endregion - - # region: Arithmetic - - def test_plus_period(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - - dt2 = parse_instant("2021-12-10T14:21:17.123456800 ET") - dt3 = plus_period(dt1, 11) - self.assertEqual(epoch_nanos(dt2), epoch_nanos(dt3)) - - dt2 = parse_instant("2021-12-10T16:21:17.123456789 ET") - duration_str = "PT2H" - duration = parse_duration(duration_str) - dt3 = plus_period(dt1, duration) - self.assertEqual(epoch_nanos(dt2), epoch_nanos(dt3)) - - dt2 = parse_instant("2021-12-12T14:21:17.123456789 ET") - period_str = "P2D" - period = parse_period(period_str) - dt3 = plus_period(dt1, period) - self.assertEqual(epoch_nanos(dt2), epoch_nanos(dt3)) - - - def test_minus_period(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - - dt2 = parse_instant("2021-12-10T14:21:17.123456778 ET") - dt3 = minus_period(dt1, 11) - self.assertEqual(dt2, dt3) - - dt2 = parse_instant("2021-12-10T12:21:17.123456789 ET") - duration_str = "PT2H" - duration = parse_duration(duration_str) - dt3 = minus_period(dt1, duration) - self.assertEqual(dt2, dt3) - - dt2 = parse_instant("2021-12-08T14:21:17.123456789 ET") - period_str = "P2D" - period = parse_period(period_str) - dt3 = minus_period(dt1, period) - self.assertEqual(dt2, dt3) - - def test_diff_nanos(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:17.123456800 ET") - self.assertEqual(11, diff_nanos(dt1, dt2)) - self.assertEqual(-11, diff_nanos(dt2, dt1)) - self.assertEqual(NULL_LONG, diff_nanos(None, dt2)) - self.assertEqual(NULL_LONG, diff_nanos(dt1, None)) - - def test_diff_micros(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:17.123 ET") - self.assertEqual(-456, diff_micros(dt1, dt2)) - self.assertEqual(456, diff_micros(dt2, dt1)) - self.assertEqual(NULL_LONG, diff_micros(None, dt2)) - self.assertEqual(NULL_LONG, diff_micros(dt1, None)) - - def test_diff_millis(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:17 ET") - self.assertEqual(-123, diff_millis(dt1, dt2)) - self.assertEqual(123, diff_millis(dt2, dt1)) - self.assertEqual(NULL_LONG, diff_millis(None, dt2)) - self.assertEqual(NULL_LONG, diff_millis(dt1, None)) - - def test_diff_seconds(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:19.123456789 ET") - self.assertEqual(2.0, diff_seconds(dt1, dt2)) - self.assertEqual(-2.0, diff_seconds(dt2, dt1)) - self.assertEqual(NULL_DOUBLE, diff_seconds(None, dt2)) - self.assertEqual(NULL_DOUBLE, diff_seconds(dt1, None)) - - def test_diff_minutes(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:27:17.123456789 ET") - self.assertEqual(6.0, diff_minutes(dt1, dt2)) - self.assertEqual(-6.0, diff_minutes(dt2, dt1)) - self.assertEqual(NULL_DOUBLE, diff_minutes(None, dt2)) - self.assertEqual(NULL_DOUBLE, diff_minutes(dt1, None)) - - def test_diff_days(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-13T14:21:17.123456789 ET") - self.assertEqual(3.0, diff_days(dt1, dt2)) - self.assertEqual(-3.0, diff_days(dt2, dt1)) - self.assertEqual(NULL_DOUBLE, diff_days(None, dt2)) - self.assertEqual(NULL_DOUBLE, diff_days(dt1, None)) - - def test_diff_years_365(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2023-12-10T14:21:17.123456789 ET") - self.assertEqual(2.0,diff_years_365(dt1, dt2)) - self.assertEqual(-2.0,diff_years_365(dt2, dt1)) - self.assertEqual(NULL_DOUBLE, diff_years_365(None, dt2)) - self.assertEqual(NULL_DOUBLE, diff_years_365(dt1, None)) - - def test_diff_years_avg(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2023-12-10T14:21:17.123456789 ET") - self.assertAlmostEqual(2.0,diff_years_avg(dt1, dt2), delta=1e-2) - self.assertAlmostEqual(-2.0,diff_years_avg(dt2, dt1), delta=1e-2) - self.assertEqual(NULL_DOUBLE, diff_years_avg(None, dt2)) - self.assertEqual(NULL_DOUBLE, diff_years_avg(dt1, None)) + pytz = pd.Timestamp("2021-12-10T14:21:17.123456Z") + tz = to_j_time_zone(pytz) + self.assertEqual(str(tz), "UTC") - # endregion + tz = to_j_time_zone(None) + self.assertEqual(tz, None) - # region: Comparisons - - def test_is_before(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:18.123456789 ET") - self.assertTrue(is_before(dt1, dt2)) - self.assertFalse(is_before(dt2, dt1)) - self.assertFalse(is_before(dt1, dt1)) - self.assertFalse(is_before(None, dt1)) - - def test_is_before_or_equal(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:18.123456789 ET") - self.assertTrue(is_before_or_equal(dt1, dt2)) - self.assertFalse(is_before_or_equal(dt2, dt1)) - self.assertTrue(is_before_or_equal(dt1, dt1)) - self.assertFalse(is_before_or_equal(None, dt1)) - - def test_is_after(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:18.123456789 ET") - self.assertFalse(is_after(dt1, dt2)) - self.assertTrue(is_after(dt2, dt1)) - self.assertFalse(is_after(dt1, dt1)) - self.assertFalse(is_after(None, dt1)) - - def test_is_after_or_equal(self): - dt1 = parse_instant("2021-12-10T14:21:17.123456789 ET") - dt2 = parse_instant("2021-12-10T14:21:18.123456789 ET") - self.assertFalse(is_after_or_equal(dt1, dt2)) - self.assertTrue(is_after_or_equal(dt2, dt1)) - self.assertTrue(is_after_or_equal(dt1, dt1)) - self.assertFalse(is_after_or_equal(None, dt1)) + tz = to_j_time_zone(pd.Timestamp("NaT")) + self.assertEqual(tz, None) - # endregion + tz1 = to_j_time_zone("CT") + tz2 = to_j_time_zone(tz1) + self.assertEqual(tz1, tz2) - # region: Chronology - - def test_nanos_of_milli(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(456789, nanos_of_milli(dt)) - self.assertEqual(NULL_INT, nanos_of_milli(None)) - - def test_micros_of_milli(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(457, micros_of_milli(dt)) - self.assertEqual(NULL_INT, micros_of_milli(None)) - - def test_micros_of_second(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(123456, micros_of_second(dt, tz)) - self.assertEqual(NULL_LONG, micros_of_second(None, tz)) - self.assertEqual(NULL_LONG, micros_of_second(tz, None)) - - def test_millis_of_second(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(123, millis_of_second(dt, tz)) - self.assertEqual(NULL_INT, millis_of_second(None, tz)) - self.assertEqual(NULL_INT, millis_of_second(dt, None)) - - def test_second_of_minute(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(17, second_of_minute(dt, tz)) - self.assertEqual(NULL_INT, second_of_minute(None, tz)) - self.assertEqual(NULL_INT, second_of_minute(tz, None)) - - def test_minute_of_hour(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(21, minute_of_hour(dt, tz)) - self.assertEqual(NULL_INT, minute_of_hour(None, tz)) - self.assertEqual(NULL_INT, minute_of_hour(dt, None)) - - def test_nanos_of_day(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(123456789+17*SECOND+21*MINUTE+14*HOUR, nanos_of_day(dt, tz)) - self.assertEqual(NULL_LONG, nanos_of_day(None, tz)) - self.assertEqual(NULL_LONG, nanos_of_day(dt, None)) - - def test_millis_of_day(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual((123456789+17*SECOND+21*MINUTE+14*HOUR) // 10**6, millis_of_day(dt, tz)) - self.assertEqual(NULL_INT, millis_of_day(None, tz)) - self.assertEqual(NULL_INT, millis_of_day(dt, None)) - - def test_second_of_day(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual((123456789+17*SECOND+21*MINUTE+14*HOUR) // 10**9, second_of_day(dt, tz)) - self.assertEqual(NULL_INT, second_of_day(None, tz)) - self.assertEqual(NULL_INT, second_of_day(dt, None)) - - def test_minute_of_day(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(21+14*60, minute_of_day(dt, tz)) - self.assertEqual(NULL_INT, minute_of_day(None, tz)) - self.assertEqual(NULL_INT, minute_of_day(dt, None)) - - def test_hour_of_day(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(14, hour_of_day(dt, tz)) - self.assertEqual(NULL_INT, hour_of_day(None, tz)) - self.assertEqual(NULL_INT, hour_of_day(dt, None)) - - def test_day_of_week(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - # 5 - Fri - self.assertEqual(5, day_of_week(dt, tz)) - self.assertEqual(NULL_INT, day_of_week(None, tz)) - self.assertEqual(NULL_INT, day_of_week(dt, None)) - - def test_day_of_month(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(10, day_of_month(dt, tz)) - self.assertEqual(NULL_INT, day_of_month(None, tz)) - self.assertEqual(NULL_INT, day_of_month(dt, None)) - - def test_day_of_year(self): - datetime_str = "2021-02-03T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(31+3, day_of_year(dt, tz)) - self.assertEqual(NULL_INT, day_of_year(None, tz)) - self.assertEqual(NULL_INT, day_of_year(dt, None)) - - def test_month_of_year(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(12, month_of_year(dt, tz)) - self.assertEqual(NULL_INT, month_of_year(None, tz)) - self.assertEqual(NULL_INT, month_of_year(dt, None)) - - def test_year(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(2021, year(dt, tz)) - self.assertEqual(NULL_INT, year(None, tz)) - self.assertEqual(NULL_INT, year(dt, None)) - - def test_year_of_century(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertEqual(21, year_of_century(dt, tz)) - self.assertEqual(NULL_INT, year_of_century(None, tz)) - self.assertEqual(NULL_INT, year_of_century(dt, None)) - - def test_at_midnight(self): - datetime_str = "2021-12-10T02:59:59" - timezone_str = "ET" - tz_ny = time_zone("ET") - tz_pt = time_zone("PT") - dt = parse_instant(f"{datetime_str} {timezone_str}") - mid_night_time_ny = at_midnight(dt, tz_ny) - mid_night_time_pt = at_midnight(dt, tz_pt) - self.assertEqual(diff_nanos(mid_night_time_ny, mid_night_time_pt) // 10 ** 9, -21 * 60 * 60) - - # DST ended in ET but not in PT - datetime_str = "2021-11-08T02:59:59" - timezone_str = "ET" - dt = parse_instant(f"{datetime_str} {timezone_str}") - mid_night_time_ny = at_midnight(dt, tz_ny) - mid_night_time_pt = at_midnight(dt, tz_pt) - self.assertEqual(diff_nanos(mid_night_time_ny, mid_night_time_pt) // 10 ** 9, -22 * 60 * 60) + with self.assertRaises(TypeError): + to_j_time_zone(False) + self.fail("Expected TypeError") - # endregion - # region: Format + def test_to_j_local_date(self): + ld = to_j_local_date("2021-12-10") + self.assertEqual(str(ld), "2021-12-10") + + d = datetime.date(2021, 12, 10) + ld = to_j_local_date(d) + self.assertEqual(str(ld), "2021-12-10") + + d = datetime.datetime(2021, 12, 10, 14, 21, 17, 123456) + ld = to_j_local_date(d) + self.assertEqual(str(ld), "2021-12-10") + + d = np.datetime64("2021-12-10") + ld = to_j_local_date(d) + self.assertEqual(str(ld), "2021-12-10") + + d = pd.Timestamp("2021-12-10T14:21:17.123456Z") + ld = to_j_local_date(d) + self.assertEqual(str(ld), "2021-12-10") + + ld = to_j_local_date(None) + self.assertEqual(ld, None) + + ld = to_j_local_date(pd.Timestamp("NaT")) + self.assertEqual(ld, None) + + ld1 = to_j_local_date("2021-12-10") + ld2 = to_j_local_date(ld1) + self.assertEqual(ld1, ld2) + + with self.assertRaises(TypeError): + to_j_local_date(False) + self.fail("Expected TypeError") + + + def test_to_j_local_time(self): + lt = to_j_local_time("14:21:17.123456") + self.assertEqual(str(lt), "14:21:17.123456") + + ltn = lt.toNanoOfDay() + lt = to_j_local_time(ltn) + self.assertEqual(str(lt), "14:21:17.123456") + + t = datetime.time(14, 21, 17, 123456) + lt = to_j_local_time(t) + self.assertEqual(str(lt), "14:21:17.123456") + + t = datetime.datetime(2021, 12, 10, 14, 21, 17, 123456) + lt = to_j_local_time(t) + self.assertEqual(str(lt), "14:21:17.123456") + + t = np.datetime64(t) + lt = to_j_local_time(t) + self.assertEqual(str(lt), "14:21:17.123456") + + t = pd.Timestamp("2021-12-10T14:21:17.123456789Z") + lt = to_j_local_time(t) + self.assertEqual(str(lt), "14:21:17.123456789") + + lt = to_j_local_time(None) + self.assertEqual(lt, None) + + lt = to_j_local_time(np.datetime64("NaT")) + self.assertEqual(lt, None) + + lt = to_j_local_time(pd.Timestamp("NaT")) + self.assertEqual(lt, None) + + lt1 = to_j_local_time("14:21:17.123456") + lt2 = to_j_local_time(lt1) + self.assertEqual(lt1, lt2) + + with self.assertRaises(TypeError): + to_j_local_time(False) + self.fail("Expected TypeError") + + def test_to_j_instant(self): + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T19:21:17.123456Z") + + dt = to_j_instant("2021-12-10T14:21:17.123456 ET") + self.assertEqual(str(target), str(dt)) + + dtn = _JDateTimeUtils.epochNanos(dt) + dt = to_j_instant(dtn) + self.assertEqual(str(target), str(dt)) + + # 1 ns is a rounding error + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T19:21:17.123456001Z") + x = datetime.datetime(2021, 12, 10, 19, 21, 17, 123456, tzinfo=datetime.timezone.utc) + dt = to_j_instant(x) + self.assertEqual(str(target), str(dt)) + + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T19:21:17.123456Z") + x = np.datetime64(x) + dt = to_j_instant(x) + self.assertEqual(str(target), str(dt)) + + # 1 ns is a rounding error + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T19:21:17.123456001Z") + x = pd.Timestamp(x) + dt = to_j_instant(x) + self.assertEqual(str(target), str(dt)) + + dt = to_j_instant(None) + self.assertEqual(dt, None) + + dt = to_j_instant(pd.NaT) + self.assertEqual(dt, None) + + dt = to_j_instant(np.datetime64("NaT")) + self.assertEqual(dt, None) + + dt1 = target.toInstant() + dt2 = to_j_instant(dt1) + self.assertEqual(dt1, dt2) + + with self.assertRaises(TypeError): + to_j_instant(False) + self.fail("Expected TypeError") + + + def test_to_j_zdt(self): + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456-05:00[America/New_York]") + + dt = to_j_zdt("2021-12-10T14:21:17.123456 ET") + self.assertEqual(str(target), str(dt)) + + # 1 ns is a rounding error + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456001Z[Etc/UTC]") + x = datetime.datetime(2021, 12, 10, 14, 21, 17, 123456) + dt = to_j_zdt(x) + self.assertEqual(str(target), str(dt)) - def test_format_duration_nanos(self): - nanos = 123456789 - ns_str = format_duration_nanos(nanos) - self.assertEqual("PT0:00:00.123456789", ns_str) + # 1 ns is a rounding error + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456001Z[Etc/UTC]") + x = pd.Timestamp(x) + dt = to_j_zdt(x) + self.assertEqual(str(target), str(dt)) - def test_format_datetime(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - dt_str = format_datetime(dt, tz) - self.assertEqual(f"{datetime_str} {timezone_str}", dt_str) + target = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456Z[Etc/UTC]") + x = np.datetime64(x) + dt = to_j_zdt(x) + self.assertEqual(str(target), str(dt)) - def test_format_date(self): - datetime_str = "2021-12-10T14:21:17.123456789" - timezone_str = "ET" - tz = time_zone(timezone_str) - dt = parse_instant(f"{datetime_str} {timezone_str}") - dt_str = format_date(dt, tz) - self.assertEqual("2021-12-10", dt_str) + dt = to_j_zdt(None) + self.assertEqual(dt, None) + + dt = to_j_zdt(np.datetime64("NaT")) + self.assertEqual(dt, None) + + dt = to_j_zdt(pd.Timestamp("NaT")) + self.assertEqual(dt, None) + + dt = to_j_zdt(target) + self.assertEqual(dt, target) + + with self.assertRaises(TypeError): + to_j_zdt(False) + self.fail("Expected TypeError") + + + def test_to_j_duration(self): + d = to_j_duration("PT1H") + self.assertEqual(str(d), "PT1H") + + d = to_j_duration(2) + self.assertEqual(str(d), "PT0.000000002S") + + x = datetime.timedelta(hours=1, minutes=2, seconds=3, milliseconds=4, microseconds=5) + dt = to_j_duration(x) + self.assertEqual(dt, _JDateTimeUtils.parseDuration("PT1H2M3.004005S")) + + x = np.timedelta64(x) + dt = to_j_duration(x) + self.assertEqual(dt, _JDateTimeUtils.parseDuration("PT1H2M3.004005S")) + + x = pd.Timedelta(x) + dt = to_j_duration(x) + self.assertEqual(dt, _JDateTimeUtils.parseDuration("PT1H2M3.004005S")) + + d = to_j_duration(None) + self.assertEqual(d, None) + + d = to_j_duration(np.timedelta64("NaT")) + self.assertEqual(d, None) + + d = to_j_duration(pd.Timedelta("NaT")) + self.assertEqual(d, None) + + d1 = to_j_duration("PT1H") + d2 = to_j_duration(d1) + self.assertEqual(d1, d2) + + with self.assertRaises(TypeError): + to_j_duration(False) + self.fail("Expected TypeError") + + + def test_to_j_period(self): + p = to_j_period("P2W") + self.assertEqual(str(p), "P14D") + + x = datetime.timedelta(days=2) + p = to_j_period(x) + self.assertEqual(str(p), "P2D") + + x = pd.Timedelta(days=2) + p = to_j_period(x) + self.assertEqual(str(p), "P2D") + + x = np.timedelta64(2, 'D') + p = to_j_period(x) + self.assertEqual(str(p), "P2D") + + x = np.timedelta64(2, 'W') + p = to_j_period(x) + self.assertEqual(str(p), "P14D") + + x = np.timedelta64(2, 'M') + p = to_j_period(x) + self.assertEqual(str(p), "P2M") + + x = np.timedelta64(2, 'Y') + p = to_j_period(x) + self.assertEqual(str(p), "P2Y") + + p = to_j_period(None) + self.assertEqual(p, None) + + d = to_j_period(np.timedelta64("NaT")) + self.assertEqual(d, None) + + d = to_j_period(pd.Timedelta("NaT")) + self.assertEqual(d, None) + + p1 = to_j_period("P2W") + p2 = to_j_period(p1) + self.assertEqual(p1, p2) + + with self.assertRaises(TypeError): + to_j_period(False) + self.fail("Expected TypeError") + + with self.assertRaises(ValueError): + x = datetime.timedelta(days=2, seconds=1) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = datetime.timedelta(days=2, microseconds=1) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = datetime.timedelta(days=2.3) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = pd.Timedelta(days=2, seconds=1) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = pd.Timedelta(days=2, microseconds=1) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = pd.Timedelta(days=2, nanoseconds=1) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = pd.Timedelta(days=2.3) + to_j_period(x) + self.fail("Expected ValueError") + + with self.assertRaises(ValueError): + x = np.timedelta64(2, 'h') + to_j_period(x) + self.fail("Expected ValueError") - dt = now() - self.assertEqual(3, len(format_date(dt, time_zone("PT")).split("-"))) # endregion - - # region: Parse - def test_parse_time_zone(self): - tz = parse_time_zone("America/New_York") - self.assertEqual(str(tz), "America/New_York") - tz = parse_time_zone("CT") - self.assertEqual(str(tz), "America/Chicago") + # region Conversions: Java To Python - with self.assertRaises(DHError) as cm: - tz = parse_time_zone(None) - self.assertTrue(cm.exception.root_cause) - self.assertIn("Cannot parse", cm.exception.compact_traceback) + def test_to_date(self): + target = datetime.date(2021, 12, 10) - with self.assertRaises(DHError) as cm: - tz = parse_time_zone("JUNK") - self.assertTrue(cm.exception.root_cause) - self.assertIn("Cannot parse time zone", cm.exception.compact_traceback) + ld = _JDateTimeUtils.parseLocalDate("2021-12-10") + dt = to_date(ld) + self.assertEqual(dt, target) - tz = parse_time_zone("JUNK", quiet=True) - self.assertEqual(None, tz) + lt = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456 ET") + dt = to_date(lt) + self.assertEqual(dt, target) - def test_parse_duration_nanos(self): - time_str = "PT530000:59:39.123456789" - in_nanos = parse_duration_nanos(time_str) - self.assertEqual(str(in_nanos), "1908003579123456789") + dt = to_date(None) + self.assertEqual(dt, None) - with self.assertRaises(DHError) as cm: - time_str = "PT530000:59:39.X" - in_nanos = parse_duration_nanos(time_str) - self.assertIn("DateTimeParseException", str(cm.exception)) - - time_str = "PT00:59:39.X" - in_nanos = parse_duration_nanos(time_str, quiet=True) - self.assertEqual(in_nanos, NULL_LONG) - - time_str = "PT1:02:03" - in_nanos = parse_duration_nanos(time_str) - time_str2 = format_duration_nanos(in_nanos) - self.assertEqual(time_str2, time_str) - - time_str = "PT1h" - in_nanos = parse_duration_nanos(time_str) - time_str2 = format_duration_nanos(in_nanos) - self.assertEqual(time_str2, "PT1:00:00") - - def test_parse_period(self): - period_str = "P1W" - period = parse_period(period_str) - # Java Period normalizes weeks to days in toString() - self.assertEqual(str(period).upper(), "P7D") - - period_str = "P6D" - period = parse_period(period_str) - self.assertEqual(str(period).upper(), period_str) - - period_str = "P1M" - period = parse_period(period_str) - self.assertEqual(str(period).upper(), period_str) + with self.assertRaises(TypeError): + to_date(False) + self.fail("Expected TypeError") - with self.assertRaises(DHError) as cm: - period_str = "PT1Y" - period = parse_period(period_str) - self.assertIn("DateTimeParseException", str(cm.exception)) + def test_to_time(self): + target = datetime.time(14, 21, 17, 123456) - period = parse_period(period_str, quiet=True) - self.assertEquals(None,period) + lt = _JDateTimeUtils.parseLocalTime("14:21:17.123456") + dt = to_time(lt) + self.assertEqual(dt, target) - def test_parse_duration(self): - duration_str = "PT1M" - duration = parse_duration(duration_str) - self.assertEqual(str(duration).upper(), duration_str) + lt = _JDateTimeUtils.parseZonedDateTime("2023-07-11T14:21:17.123456 ET") + dt = to_time(lt) + self.assertEqual(dt, target) - duration_str = "PT1H" - duration = parse_duration(duration_str) - self.assertEqual(str(duration).upper(), duration_str) + dt = to_time(None) + self.assertEqual(dt, None) - with self.assertRaises(DHError) as cm: - duration = parse_duration("T1Q") - self.assertIn("DateTimeParseException", str(cm.exception)) + with self.assertRaises(TypeError): + to_time(False) + self.fail("Expected TypeError") - duration = parse_duration("T1Q", quiet=True) - self.assertEquals(None,duration) + def test_to_datetime(self): + target = datetime.datetime(2021, 12, 10, 14, 21, 17, 123456) - def test_parse_epoch_nanos(self): - datetime_str = "2021-12-10T23:59:59" - timezone_str = "ET" - dt = parse_instant(f"{datetime_str} {timezone_str}") - n = parse_epoch_nanos(f"{datetime_str} {timezone_str}") - self.assertEqual(epoch_nanos(dt), n) + dt = _JDateTimeUtils.parseInstant("2021-12-10T14:21:17.123456Z") + dt = to_datetime(dt) + self.assertEqual(dt, target) - with self.assertRaises(DHError) as cm: - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_epoch_nanos(f"{datetime_str} {timezone_str}") - self.assertIn("RuntimeException", str(cm.exception)) - - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_epoch_nanos(f"{datetime_str} {timezone_str}", quiet=True) - self.assertEquals(NULL_LONG,dt) - - def test_parse_instant(self): - datetime_str = "2021-12-10T23:59:59" - timezone_str = "ET" - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertTrue(format_datetime(dt, time_zone("ET")).startswith(datetime_str)) + dt = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456Z") + dt = to_datetime(dt) + self.assertEqual(dt, target) - with self.assertRaises(DHError) as cm: - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_instant(f"{datetime_str} {timezone_str}") - self.assertIn("RuntimeException", str(cm.exception)) - - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_instant(f"{datetime_str} {timezone_str}", quiet=True) - self.assertEquals(None,dt) - - def test_parse_zdt(self): - datetime_str = "2021-12-10T23:59:59" - timezone_str = "ET" - dt = parse_zdt(f"{datetime_str} {timezone_str}") - self.assertTrue(str(dt).startswith(datetime_str)) + dt = to_datetime(None) + self.assertEqual(dt, None) - with self.assertRaises(DHError) as cm: - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_zdt(f"{datetime_str} {timezone_str}") - self.assertIn("RuntimeException", str(cm.exception)) - - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - dt = parse_zdt(f"{datetime_str} {timezone_str}", quiet=True) - self.assertEquals(None,dt) - - def test_parse_time_precision(self): - datetime_str = "2021-12-10T23:59:59" - timezone_str = "ET" - tp = parse_time_precision(f"{datetime_str} {timezone_str}") - self.assertEqual(tp, "SecondOfMinute") + with self.assertRaises(TypeError): + to_datetime(False) + self.fail("Expected TypeError") - with self.assertRaises(DHError) as cm: - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - tp = parse_time_precision(f"{datetime_str} {timezone_str}") - self.assertIn("RuntimeException", str(cm.exception)) + def test_to_pd_timestamp(self): + target = pd.Timestamp(year=2021, month=12, day=10, hour=14, minute=21, second=17, microsecond=123456, nanosecond=789) - datetime_str = "2021-12-10T23:59:59" - timezone_str = "--" - tp = parse_time_precision(f"{datetime_str} {timezone_str}", quiet=True) - self.assertEquals(None,tp) + dt = _JDateTimeUtils.parseInstant("2021-12-10T14:21:17.123456789Z") + dt = to_pd_timestamp(dt) + self.assertEqual(dt, target) - def test_parse_local_date(self): - date_str = "2021-12-10" - dt = parse_local_date(date_str) - self.assertTrue(str(dt), date_str) + dt = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456789Z") + dt = to_pd_timestamp(dt) + self.assertEqual(dt, target) - with self.assertRaises(DHError) as cm: - date_str = "2021-x12-10" - dt = parse_local_date(date_str) - self.assertIn("DateTimeParseException", str(cm.exception)) + dt = to_pd_timestamp(None) + self.assertEqual(dt, None) - date_str = "2021-x12-10" - dt = parse_local_date(date_str, quiet=True) - self.assertEquals(None,dt) + with self.assertRaises(TypeError): + to_pd_timestamp(False) - def test_parse_local_time(self): - time_str = "23:59:59" - dt = parse_local_time(time_str) - self.assertTrue(str(dt), time_str) + def test_to_np_datetime64(self): + target = np.datetime64("2021-12-10T14:21:17.123456Z") - with self.assertRaises(DHError) as cm: - time_str = "23:59x:59" - dt = parse_local_time(time_str) - self.assertIn("DateTimeParseException", str(cm.exception)) + dt = _JDateTimeUtils.parseInstant("2021-12-10T14:21:17.123456Z") + dt = to_np_datetime64(dt) + self.assertEqual(dt, target) + + dt = _JDateTimeUtils.parseZonedDateTime("2021-12-10T14:21:17.123456Z") + dt = to_np_datetime64(dt) + self.assertEqual(dt, target) + + dt = to_np_datetime64(None) + self.assertEqual(dt, None) + + with self.assertRaises(TypeError): + to_np_datetime64(False) + self.fail("Expected TypeError") + + def test_to_timedelta(self): + target = datetime.timedelta(hours=1, minutes=2, seconds=3, milliseconds=4, microseconds=5) + + d = _JDateTimeUtils.parseDuration("PT1H2M3.004005S") + dt = to_timedelta(d) + self.assertEqual(dt, target) + + target = datetime.timedelta(days=2) + d = _JDateTimeUtils.parsePeriod("P2D") + dt = to_timedelta(d) + self.assertEqual(dt, target) + + target = datetime.timedelta(days=14) + d = _JDateTimeUtils.parsePeriod("P2W") + dt = to_timedelta(d) + self.assertEqual(dt, target) + + d = to_timedelta(None) + self.assertEqual(d, None) + + d = _JDateTimeUtils.parsePeriod("P1Y") + with self.assertRaises(ValueError): + to_timedelta(d) + self.fail("Expected ValueError") - time_str = "23:59x:59" - dt = parse_local_time(time_str, quiet=True) - self.assertEquals(None,dt) + d = _JDateTimeUtils.parsePeriod("P1M") + with self.assertRaises(ValueError): + to_timedelta(d) + self.fail("Expected ValueError") + + with self.assertRaises(TypeError): + to_timedelta(False) + self.fail("Expected TypeError") + + def test_to_pd_timedelta(self): + target = pd.Timedelta(hours=1, minutes=2, seconds=3, milliseconds=4, microseconds=5) + + d = _JDateTimeUtils.parseDuration("PT1H2M3.004005S") + dt = to_pd_timedelta(d) + self.assertEqual(dt, target) + + target = datetime.timedelta(days=2) + d = _JDateTimeUtils.parsePeriod("P2D") + dt = to_pd_timedelta(d) + self.assertEqual(dt, target) + + target = datetime.timedelta(days=14) + d = _JDateTimeUtils.parsePeriod("P2W") + dt = to_pd_timedelta(d) + self.assertEqual(dt, target) + + d = to_pd_timedelta(None) + self.assertEqual(d, None) + + d = _JDateTimeUtils.parsePeriod("P1Y") + with self.assertRaises(ValueError): + to_pd_timedelta(d) + self.fail("Expected ValueError") + + d = _JDateTimeUtils.parsePeriod("P1M") + with self.assertRaises(ValueError): + to_pd_timedelta(d) + self.fail("Expected ValueError") + + with self.assertRaises(TypeError): + to_pd_timedelta(False) + self.fail("Expected TypeError") + + def test_to_np_timedelta64(self): + target = np.timedelta64(1, 'h') + np.timedelta64(2, 'm') + np.timedelta64(3, 's') + np.timedelta64(4, 'ms') + np.timedelta64(5, 'us') + + d = _JDateTimeUtils.parseDuration("PT1H2M3.004005S") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + target = np.timedelta64(2, 'D') + d = _JDateTimeUtils.parsePeriod("P2D") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + target = np.timedelta64(14, 'D') + d = _JDateTimeUtils.parsePeriod("P2W") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + d = to_np_timedelta64(None) + self.assertEqual(d, None) + + target = np.timedelta64(2, 'Y') + d = _JDateTimeUtils.parsePeriod("P2Y") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + target = np.timedelta64(2, 'M') + d = _JDateTimeUtils.parsePeriod("P2M") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + target = np.timedelta64(0, 'D') + d = _JDateTimeUtils.parsePeriod("P0M") + dt = to_np_timedelta64(d) + self.assertEqual(dt, target) + + d = _JDateTimeUtils.parsePeriod("P1Y1M") + with self.assertRaises(ValueError): + to_np_timedelta64(d) + self.fail("Expected ValueError") + + d = _JDateTimeUtils.parsePeriod("P1Y1D") + with self.assertRaises(ValueError): + to_np_timedelta64(d) + self.fail("Expected ValueError") + + d = _JDateTimeUtils.parsePeriod("P1M1D") + with self.assertRaises(ValueError): + to_np_timedelta64(d) + self.fail("Expected ValueError") + + with self.assertRaises(TypeError): + to_np_timedelta64(False) + self.fail("Expected TypeError") # endregion diff --git a/py/server/tests/test_update_graph.py b/py/server/tests/test_update_graph.py index b0143634d24..2cdf0d116cc 100644 --- a/py/server/tests/test_update_graph.py +++ b/py/server/tests/test_update_graph.py @@ -2,7 +2,6 @@ # Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending # -import jpy import unittest from deephaven import time_table, DHError, merge