Skip to content

Commit

Permalink
Add ZonedDateTime data type to GQLAlchemy (#312)
Browse files Browse the repository at this point in the history
* Add ZonedDateTime data type to GQLAlchemy

* fix nested isinstance

* update code with fixes

* add fixes for flake test
  • Loading branch information
matea16 authored Jul 9, 2024
1 parent cb21b4a commit 8ad1670
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 4 deletions.
19 changes: 17 additions & 2 deletions gqlalchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,23 @@ def escape_value(
return "[" + ", ".join(self.escape_value(val) for val in value) + "]"
elif value_type == dict:
return "{" + ", ".join(f"{key}: {self.escape_value(val)}" for key, val in value.items()) + "}"
if isinstance(value, (timedelta, time, datetime, date)):
return f"{datetimeKwMapping[value_type]}('{_format_timedelta(value) if isinstance(value, timedelta) else value.isoformat()}')"

if isinstance(value, datetime):
if value.tzinfo is not None:
tz_offset = value.strftime("%z")
tz_name = value.tzinfo.zone
return f"datetime('{value.strftime('%Y-%m-%dT%H:%M:%S')}{tz_offset}[{tz_name}]')"
keyword = datetimeKwMapping[datetime]
formatted_value = value.isoformat()
return f"{keyword}('{formatted_value}')"
elif isinstance(value, timedelta):
formatted_value = _format_timedelta(value)
keyword = datetimeKwMapping[timedelta]
return f"{keyword}('{formatted_value}')"
elif isinstance(value, (time, date)):
formatted_value = value.isoformat()
keyword = datetimeKwMapping[type(value)]
return f"{keyword}('{formatted_value}')"
else:
raise GQLAlchemyError(
f"Unsupported value data type: {type(value)}."
Expand Down
28 changes: 26 additions & 2 deletions gqlalchemy/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from enum import Enum
import inspect
import math
import pytz
from typing import Any, Dict, List, Optional, Tuple, Union

import numpy as np
Expand Down Expand Up @@ -84,6 +85,20 @@ def _is_torch_tensor(value):
return False


def handle_datetime(value, config):
if value.tzinfo == pytz.UTC:
formatted_date = value.strftime("%Y-%m-%dT%H:%M:%SZ")
return f"datetime('{formatted_date}')"
elif value.tzinfo is not None:
tz = value.strftime("%z")
tz = f"{tz[:3]}:{tz[3:]}"
tz_name = value.tzinfo.zone
formatted_date = value.strftime(f"%Y-%m-%dT%H:%M:%S{tz}")
return f"datetime('{formatted_date}[{tz_name}]')"
else:
return f"{DatetimeKeywords.LOCALDATETIME.value}('{value.isoformat()}')"


def to_cypher_value(value: Any, config: NetworkXCypherConfig = None) -> str:
"""Converts value to a valid Cypher type."""
if config is None:
Expand All @@ -105,8 +120,17 @@ def to_cypher_value(value: Any, config: NetworkXCypherConfig = None) -> str:
if value_type == CypherVariable:
return str(value)

if isinstance(value, (timedelta, time, datetime, date)):
return f"{datetimeKwMapping[value_type]}('{_format_timedelta(value) if isinstance(value, timedelta) else value.isoformat()}')"
if isinstance(value, datetime):
return handle_datetime(value, config)

if isinstance(value, timedelta):
return f"{datetimeKwMapping[value_type]}('{_format_timedelta(value)}')"

if isinstance(value, date):
return f"{datetimeKwMapping[value_type]}('{value.isoformat()}')"

if isinstance(value, time):
return f"{datetimeKwMapping[value_type]}('{value.isoformat()}')"

if value_type == str and value.lower() in ["true", "false", "null"]:
return value
Expand Down
6 changes: 6 additions & 0 deletions tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import math
import numpy as np
import pytest
import pytz

from gqlalchemy.utilities import (
NanException,
Expand Down Expand Up @@ -68,11 +69,16 @@ def test_to_cypher_datetime():
localtime = datetime.time(12, 12, 12)
localdatetime = datetime.datetime(1999, 12, 12, 12, 12, 12)
duration = datetime.timedelta(days=1, hours=5, minutes=16, seconds=12)
zoned_dt_naive = datetime.datetime(2024, 4, 21, 14, 15, 0)
zoned_dt = pytz.timezone("America/Los_Angeles").localize(zoned_dt_naive)
zoned_dt_utc = datetime.datetime(2021, 4, 21, 14, 15, tzinfo=pytz.UTC)

assert to_cypher_value(date) == "date('1970-01-19')"
assert to_cypher_value(localtime) == "localTime('12:12:12')"
assert to_cypher_value(localdatetime) == "localDateTime('1999-12-12T12:12:12')"
assert to_cypher_value(duration) == "duration('P1DT5H16M12.0S')"
assert to_cypher_value(zoned_dt) == "datetime('2024-04-21T14:15:00-07:00[America/Los_Angeles]')"
assert to_cypher_value(zoned_dt_utc) == "datetime('2021-04-21T14:15:00Z')"


def test_to_cypher_labels_single_label():
Expand Down

0 comments on commit 8ad1670

Please sign in to comment.