Skip to content
Merged
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
42 changes: 26 additions & 16 deletions neo4j/time/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,6 @@
DateTimeType,
)

import logging
from neo4j.debug import watch
watch("neo4j")

log = logging.getLogger("neo4j")


MIN_INT64 = -(2 ** 63)
MAX_INT64 = (2 ** 63) - 1

Expand Down Expand Up @@ -1157,10 +1150,15 @@ def to_native(self):
h, m, s = self.hour_minute_second
s, ns = nano_divmod(s, 1)
ms = int(nano_mul(ns, 1000000))
return time(h, m, s, ms)
tz = self.tzinfo
return time(h, m, s, ms, tz)

def iso_format(self):
return "%02d:%02d:%012.9f" % self.hour_minute_second
s = "%02d:%02d:%012.9f" % self.hour_minute_second
if self.tzinfo is not None:
offset = self.tzinfo.utcoffset(self)
s += "%+03d:%02d" % divmod(offset.total_seconds() // 60, 60)
return s

def __repr__(self):
if self.tzinfo is None:
Expand Down Expand Up @@ -1277,7 +1275,7 @@ def parse(cls, date_string, format):
def from_native(cls, dt):
""" Convert from a native Python `datetime.datetime` value.
"""
return cls.combine(Date.from_native(dt.date()), Time.from_native(dt.time()))
return cls.combine(Date.from_native(dt.date()), Time.from_native(dt.timetz()))

@classmethod
def from_clock_time(cls, clock_time, epoch):
Expand Down Expand Up @@ -1423,12 +1421,27 @@ def __sub__(self, other):
# INSTANCE METHODS #

def date(self):
"""The the date

:return: the date
:rtype: :class:`neo4j.time.Date`
"""
return self.__date

def time(self):
"""The the time without time zone info

:return: the time without time zone info
:rtype: :class:`neo4j.time.Time`
"""
return self.__time.replace(tzinfo=None)

def timetz(self):
"""The the time with time zone info

:return: the time with time zone info
:rtype: :class:`neo4j.time.Time`
"""
return self.__time

def replace(self, **kwargs):
Expand Down Expand Up @@ -1478,7 +1491,8 @@ def to_native(self):
h, m, s = self.hour_minute_second
s, ns = nano_divmod(s, 1)
ms = int(nano_mul(ns, 1000000))
return datetime(y, mo, d, h, m, s, ms)
tz = self.tzinfo
return datetime(y, mo, d, h, m, s, ms, tz)

def weekday(self):
return self.__date.weekday()
Expand All @@ -1490,11 +1504,7 @@ def iso_calendar(self):
return self.__date.iso_calendar()

def iso_format(self, sep="T"):
s = "%s%s%s" % (self.date().iso_format(), sep, self.time().iso_format())
if self.tzinfo is not None:
offset = self.tzinfo.utcoffset(self)
s += "%+03d:%02d" % divmod(offset.total_seconds() // 60, 60)
return s
return "%s%s%s" % (self.date().iso_format(), sep, self.timetz().iso_format())

def __repr__(self):
if self.tzinfo is None:
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_temporal_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ def test_time_parameter_case2(session):
# python -m pytest tests/integration/test_temporal_types.py -s -v -k test_time_parameter_case2
t1 = session.run("RETURN time('07:54:02.129790999+00:00')").single().value()
assert isinstance(t1, Time)
# assert t1.iso_format() == "07:54:02.129790999+00:00" # TODO: Broken, does not show time_zone_delta +00:00
assert t1.iso_format() == "07:54:02.129790999+00:00"
time_zone_delta = t1.utc_offset()
assert isinstance(time_zone_delta, datetime.timedelta)
assert time_zone_delta == datetime.timedelta(0)
Expand Down
61 changes: 58 additions & 3 deletions tests/unit/time/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,65 @@ def test_from_iso_format_with_negative_long_tz(self):
self.assertEqual(expected, actual)


def test_potential_rounding_error():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_potential_rounding_error
def test_iso_format_with_time_zone_case_1():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_iso_format_with_time_zone_case_1
expected = DateTime(2019, 10, 30, 7, 54, 2.129790999, tzinfo=timezone_utc)
assert expected.iso_format() == "2019-10-30T07:54:02.129790999+00:00"

assert expected.tzinfo == FixedOffset(0)
actual = DateTime.from_iso_format("2019-10-30T07:54:02.129790999+00:00")
assert expected == actual


def test_iso_format_with_time_zone_case_2():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_iso_format_with_time_zone_case_2
expected = DateTime.from_iso_format("2019-10-30T07:54:02.129790999+01:00")
assert expected.tzinfo == FixedOffset(60)
assert expected.iso_format() == "2019-10-30T07:54:02.129790999+01:00"


def test_to_native_case_1():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_to_native_case_1
dt = DateTime.from_iso_format("2019-10-30T12:34:56.789123456")
native = dt.to_native()
assert native.hour == dt.hour
assert native.minute == dt.minute
assert nano_add(native.second, nano_div(native.microsecond, 1000000)) == 56.789123
assert native.tzinfo is None
assert native.isoformat() == "2019-10-30T12:34:56.789123"


def test_to_native_case_2():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_to_native_case_2
dt = DateTime.from_iso_format("2019-10-30T12:34:56.789123456+00:00")
native = dt.to_native()
assert native.hour == dt.hour
assert native.minute == dt.minute
assert nano_add(native.second, nano_div(native.microsecond, 1000000)) == 56.789123
assert native.tzinfo == FixedOffset(0)
assert native.isoformat() == "2019-10-30T12:34:56.789123+00:00"


def test_from_native_case_1():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_from_native_case_1
native = datetime(2018, 10, 1, 12, 34, 56, 789123)
dt = DateTime.from_native(native)
assert dt.year == native.year
assert dt.month == native.month
assert dt.day == native.day
assert dt.hour == native.hour
assert dt.minute == native.minute
assert dt.second == nano_add(native.second, nano_div(native.microsecond, 1000000))
assert dt.tzinfo is None


def test_from_native_case_2():
# python -m pytest tests/unit/time/test_datetime.py -s -v -k test_from_native_case_2
native = datetime(2018, 10, 1, 12, 34, 56, 789123, FixedOffset(0))
dt = DateTime.from_native(native)
assert dt.year == native.year
assert dt.month == native.month
assert dt.day == native.day
assert dt.hour == native.hour
assert dt.minute == native.minute
assert dt.second == nano_add(native.second, nano_div(native.microsecond, 1000000))
assert dt.tzinfo == FixedOffset(0)
75 changes: 70 additions & 5 deletions tests/unit/time/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
from datetime import time
from unittest import TestCase

from pytz import timezone, FixedOffset
from pytz import (
timezone,
FixedOffset,
)

from neo4j.time import Time
from neo4j.time.arithmetic import nano_add, nano_div
from neo4j.time.arithmetic import (
nano_add,
nano_div,
)


eastern = timezone("US/Eastern")
timezone_us_eastern = timezone("US/Eastern")
timezone_utc = timezone("UTC")


class TimeTestCase(TestCase):
Expand Down Expand Up @@ -71,9 +78,9 @@ def test_now_without_tz(self):
self.assertIsInstance(t, Time)

def test_now_with_tz(self):
t = Time.now(tz=eastern)
t = Time.now(tz=timezone_us_eastern)
self.assertIsInstance(t, Time)
self.assertEqual(t.tzinfo, eastern)
self.assertEqual(t.tzinfo, timezone_us_eastern)

def test_utc_now(self):
t = Time.utc_now()
Expand Down Expand Up @@ -150,3 +157,61 @@ def test_from_iso_format_with_negative_long_tz(self):
expected = Time(12, 34, 56.123456789, tzinfo=FixedOffset(-754))
actual = Time.from_iso_format("12:34:56.123456789-12:34:56.123456")
self.assertEqual(expected, actual)


def test_iso_format_with_time_zone_case_1():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_iso_format_with_time_zone_case_1
expected = Time(7, 54, 2.129790999, tzinfo=timezone_utc)
assert expected.iso_format() == "07:54:02.129790999+00:00"
assert expected.tzinfo == FixedOffset(0)
actual = Time.from_iso_format("07:54:02.129790999+00:00")
assert expected == actual


def test_iso_format_with_time_zone_case_2():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_iso_format_with_time_zone_case_2
expected = Time.from_iso_format("07:54:02.129790999+01:00")
assert expected.tzinfo == FixedOffset(60)
assert expected.iso_format() == "07:54:02.129790999+01:00"


def test_to_native_case_1():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_to_native_case_1
t = Time(12, 34, 56.789123456)
native = t.to_native()
assert native.hour == t.hour
assert native.minute == t.minute
assert nano_add(native.second, nano_div(native.microsecond, 1000000)) == 56.789123
assert native.tzinfo is None
assert native.isoformat() == "12:34:56.789123"


def test_to_native_case_2():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_to_native_case_2
t = Time(12, 34, 56.789123456, tzinfo=timezone_utc)
native = t.to_native()
assert native.hour == t.hour
assert native.minute == t.minute
assert nano_add(native.second, nano_div(native.microsecond, 1000000)) == 56.789123
assert native.tzinfo == FixedOffset(0)
assert native.isoformat() == "12:34:56.789123+00:00"


def test_from_native_case_1():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_from_native_case_1
native = time(12, 34, 56, 789123)
t = Time.from_native(native)
assert t.hour == native.hour
assert t.minute == native.minute
assert t.second == nano_add(native.second, nano_div(native.microsecond, 1000000))
assert t.tzinfo is None


def test_from_native_case_2():
# python -m pytest tests/unit/time/test_time.py -s -v -k test_from_native_case_2
native = time(12, 34, 56, 789123, FixedOffset(0))
t = Time.from_native(native)
assert t.hour == native.hour
assert t.minute == native.minute
assert t.second == nano_add(native.second, nano_div(native.microsecond, 1000000))
assert t.tzinfo == FixedOffset(0)