Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/chronify/time_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
from datetime import datetime, timedelta, tzinfo
from typing import Union, Literal, Optional
from pydantic import Field, field_validator
from pydantic import Field, field_validator, field_serializer
from typing_extensions import Annotated

from chronify.base_models import ChronifyBaseModel
Expand All @@ -16,6 +16,7 @@
list_representative_time_columns,
)
from chronify.exceptions import InvalidValue, InvalidParameter
from chronify.time_utils import get_tzname

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -121,11 +122,18 @@ def get_time_zones(self) -> list[tzinfo | None]:
@classmethod
def check_duplicated_time_zones(cls, time_zones: list[tzinfo | None]) -> list[tzinfo | None]:
if len(set(time_zones)) < len(time_zones):
msg = ("DatetimeRangeWithTZColumn.time_zones has duplicates: ", time_zones)
msg = f"DatetimeRangeWithTZColumn.time_zones has duplicates: {time_zones}"
raise InvalidValue(msg)
return time_zones

# Lixi TODO: ensure table schema has time_zone col?
@field_serializer("time_zones")
def serialize_time_zones(self, time_zones: list[tzinfo | None]) -> list[str]:
"""Serialize tzinfo objects to their string names for Pydantic serialization."""
return [get_tzname(tz) for tz in time_zones]

# Note: Validation that the table schema contains the time_zone_column
# is deferred to runtime when the table is accessed, as schema validation
# occurs separately in the table schema validation logic.


DatetimeRanges = Union[
Expand Down Expand Up @@ -243,7 +251,10 @@ def get_time_zone_column(self) -> str:
return self.time_zone_column

def get_time_zones(self) -> list[tzinfo | None]:
return [] # LIXI TODO
# Time zones are determined by values in the time_zone_column for each row.
# The actual time zones are not available at the configuration level and must
# be retrieved from the data itself. Returns an empty list to indicate this.
return []


IndexTimeRanges = Union[
Expand Down Expand Up @@ -286,7 +297,10 @@ def get_time_zone_column(self) -> str:
return self.time_zone_column

def get_time_zones(self) -> list[tzinfo | None]:
return [] # LIXI TODO
# Time zones are determined by values in the time_zone_column for each row.
# The actual time zones are not available at the configuration level and must
# be retrieved from the data itself. Returns an empty list to indicate this.
return []


class ColumnRepresentativeBase(TimeBaseModel):
Expand Down
2 changes: 1 addition & 1 deletion src/chronify/time_series_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _check_expected_timestamps_with_external_time_zone(self) -> int:

if sorted(expected_dct.keys()) != sorted(actual_dct.keys()):
msg = "Time zone records do not match between expected and actual from table "
msg += f"\nexpected: {sorted(expected_dct.keys())} vs. \neactual: {sorted(actual_dct.keys())}"
msg += f"\nexpected: {sorted(expected_dct.keys())} vs. \nactual: {sorted(actual_dct.keys())}"
raise InvalidTable(msg)

for tz_name in expected_dct.keys():
Expand Down
2 changes: 1 addition & 1 deletion src/chronify/time_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,4 @@ def get_tzname(tz: tzinfo | None) -> str:
if isinstance(tz, ZoneInfo):
return tz.key
ts = datetime(year=2020, month=1, day=1, tzinfo=tz)
return tz.tzname(ts) # type: ignore # LIXI TODO
return tz.tzname(ts) # type: ignore # tz is guaranteed to be a tzinfo instance here, but mypy cannot infer this from the isinstance check above
8 changes: 4 additions & 4 deletions src/chronify/time_zone_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ def convert_time_zone_by_column(
sqlalchemy engine
metadata
sqlalchemy metadata
srd_schema
src_schema
Defines the source table in the database.
time_zone_column
Column name in the source table that contains the time zone information.
wrap_time_allowed
If False, the converted timestamps will aligned with the original timestamps in real time scale
If False, the converted timestamps will be aligned with the original timestamps in real time scale
E.g. 2018-01-01 00:00 ~ 2018-12-31 23:00 in US/Eastern becomes
2017-12-31 23:00 ~ 2018-12-31 22:00 in US/Central
If True, the converted timestamps will fit into the time range of the src_schema in tz-naive clock time
Expand Down Expand Up @@ -304,8 +304,8 @@ def generate_to_time_config(self) -> DatetimeRangeBase:

def generate_to_schema(self) -> TableSchema:
id_cols = self._from_schema.time_array_id_columns
if "time_zone" not in id_cols:
id_cols.append("time_zone")
if self.time_zone_column not in id_cols:
id_cols.append(self.time_zone_column)
to_schema: TableSchema = self._from_schema.model_copy(
update={
"name": f"{self._from_schema.name}_tz_converted",
Expand Down