Skip to content

Commit 6a58d29

Browse files
committed
Add examples for temporal types for the manual
This PR also fixes a bug in serialization of times in a timezone with a negative utc offset.
1 parent 096c42b commit 6a58d29

File tree

2 files changed

+318
-1
lines changed

2 files changed

+318
-1
lines changed

neo4j/time/hydration.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ def dehydrate_time(value):
101101
else:
102102
raise TypeError("Value must be a neo4j.time.Time or a datetime.time")
103103
if value.tzinfo:
104-
return Structure(b"T", nanoseconds, value.tzinfo.utcoffset(value).seconds)
104+
return Structure(b"T", nanoseconds,
105+
int(value.tzinfo.utcoffset(value).total_seconds()))
105106
else:
106107
return Structure(b"t", nanoseconds)
107108

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
4+
# Copyright (c) "Neo4j"
5+
# Neo4j Sweden AB [http://neo4j.com]
6+
#
7+
# This file is part of Neo4j.
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License");
10+
# you may not use this file except in compliance with the License.
11+
# You may obtain a copy of the License at
12+
#
13+
# http://www.apache.org/licenses/LICENSE-2.0
14+
#
15+
# Unless required by applicable law or agreed to in writing, software
16+
# distributed under the License is distributed on an "AS IS" BASIS,
17+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
# See the License for the specific language governing permissions and
19+
# limitations under the License.
20+
21+
# python -m pytest tests/integration/examples/test_session_example.py -s -v
22+
23+
24+
def _echo(tx, x):
25+
return tx.run("RETURN $x AS fieldName", x=x).single()
26+
27+
28+
def test_datetime(driver):
29+
# tag::temporal-types-datetime-import[]
30+
from datetime import datetime
31+
32+
from neo4j.time import DateTime
33+
import pytz
34+
# end::temporal-types-datetime-import[]
35+
36+
# tag::temporal-types-datetime[]
37+
# Create datetimes to be used as query parameters
38+
# Python's builtin datetimes works as well. However, they don't support
39+
# the full feature-set of Neo4j's durations: it has no nanosecond precision.
40+
py_dt = datetime(2021, month=11, day=2, hour=7, minute=47, microsecond=4)
41+
py_dt = pytz.timezone("US/Eastern").localize(py_dt)
42+
43+
# A DateTime can be created from a native datetime
44+
dt = DateTime.from_native(py_dt)
45+
# or directly
46+
dt = DateTime(year=2021, month=11, day=2, hour=7, minute=47,
47+
second=0.000004123)
48+
dt = pytz.timezone("US/Eastern").localize(dt)
49+
# end::temporal-types-datetime[]
50+
51+
in_dt = dt # stored for later assertions
52+
53+
with driver.session() as session:
54+
record = session.read_transaction(_echo, dt)
55+
56+
# tag::temporal-types-datetime[]
57+
58+
# Reading a DateTime from a record
59+
dt = record.get("fieldName") # type: DateTime
60+
str(dt) # '2021-11-02T07:47:09.232260000-04:00'
61+
62+
# converting DateTime to native datetime (lossy)
63+
native = dt.to_native() # type: datetime
64+
# end::temporal-types-datetime[]
65+
66+
assert isinstance(dt, DateTime)
67+
assert str(dt) == "2021-11-02T07:47:00.000004123-04:00"
68+
assert dt == in_dt
69+
assert isinstance(native, datetime)
70+
assert native == py_dt
71+
72+
with driver.session() as session:
73+
record = session.read_transaction(_echo, py_dt)
74+
75+
dt = record.get("fieldName")
76+
assert isinstance(dt, DateTime)
77+
assert dt == in_dt.to_native()
78+
79+
80+
def test_date(driver):
81+
# tag::temporal-types-date-import[]
82+
from datetime import date
83+
84+
from neo4j.time import Date
85+
# end::temporal-types-date-import[]
86+
87+
# tag::temporal-types-date[]
88+
# Create dates to be used as query parameters
89+
# Python's builtin dates works as well.
90+
py_d = date(year=2021, month=11, day=2)
91+
92+
# A Date can be created from a native date
93+
d = Date.from_native(py_d)
94+
# or directly
95+
d = Date(year=2021, month=11, day=2)
96+
# end::temporal-types-date[]
97+
98+
assert d == Date.from_native(py_d)
99+
100+
in_d = d # stored for later assertions
101+
102+
with driver.session() as session:
103+
record = session.read_transaction(_echo, d)
104+
105+
# tag::temporal-types-date[]
106+
107+
# Reading a Date from a record
108+
d = record.get("fieldName") # type: Date
109+
str(d) # '2021-11-02'
110+
111+
# converting Date to native date
112+
native = d.to_native() # type: date
113+
# end::temporal-types-date[]
114+
115+
assert isinstance(d, Date)
116+
assert str(d) == "2021-11-02"
117+
assert d == in_d
118+
assert isinstance(native, date)
119+
assert native == py_d
120+
121+
with driver.session() as session:
122+
record = session.read_transaction(_echo, py_d)
123+
124+
d = record.get("fieldName")
125+
assert isinstance(d, Date)
126+
assert d == in_d.to_native()
127+
128+
129+
def test_time(driver):
130+
# tag::temporal-types-time-import[]
131+
from datetime import time
132+
133+
from neo4j.time import Time
134+
import pytz
135+
# end::temporal-types-time-import[]
136+
137+
# tag::temporal-types-time[]
138+
# Create datetimes to be used as query parameters
139+
# Python's builtin datetimes works as well. However, they don't support
140+
# the full feature-set of Neo4j's durations: it has no nanosecond precision.
141+
py_t = time(hour=7, minute=47, microsecond=4, tzinfo=pytz.FixedOffset(-240))
142+
143+
# A Time can be created from a native time
144+
t = Time.from_native(py_t)
145+
# or directly
146+
t = Time(hour=7, minute=47, second=0.000004123,
147+
tzinfo=pytz.FixedOffset(-240))
148+
# end::temporal-types-time[]
149+
150+
in_t = t # stored for later assertions
151+
152+
with driver.session() as session:
153+
record = session.read_transaction(_echo, t)
154+
155+
# tag::temporal-types-time[]
156+
157+
# Reading a Time from a record
158+
t = record.get("fieldName") # type: Time
159+
str(t) # 'T07:47:09.232260000-04:00'
160+
161+
# converting Time to native time (lossy)
162+
native = t.to_native() # type: time
163+
# end::temporal-types-time[]
164+
165+
assert isinstance(t, Time)
166+
assert str(t) == "07:47:00.000004123-04:00"
167+
assert t == in_t
168+
assert isinstance(native, time)
169+
assert native == py_t
170+
171+
with driver.session() as session:
172+
record = session.read_transaction(_echo, py_t)
173+
174+
t = record.get("fieldName")
175+
assert isinstance(t, Time)
176+
assert t == in_t.to_native()
177+
178+
179+
def test_local_datetime(driver):
180+
# tag::temporal-types-local-datetime-import[]
181+
from datetime import datetime
182+
183+
from neo4j.time import DateTime
184+
import pytz
185+
# end::temporal-types-local-datetime-import[]
186+
187+
# tag::temporal-types-local-datetime[]
188+
# Create datetimes to be used as query parameters
189+
# Python's builtin datetimes works as well. However, they don't support
190+
# the full feature-set of Neo4j's durations: it has no nanosecond precision.
191+
py_dt = datetime(2021, month=11, day=2, hour=7, minute=47, microsecond=4)
192+
193+
# A DateTime can be created from a native datetime
194+
dt = DateTime.from_native(py_dt)
195+
# or directly
196+
dt = DateTime(year=2021, month=11, day=2, hour=7, minute=47,
197+
second=0.000004123)
198+
# end::temporal-types-local-datetime[]
199+
200+
in_dt = dt # stored for later assertions
201+
202+
with driver.session() as session:
203+
record = session.read_transaction(_echo, dt)
204+
205+
# tag::temporal-types-local-datetime[]
206+
207+
# Reading a DateTime from a record
208+
dt = record.get("fieldName") # type: DateTime
209+
str(dt) # '2021-11-02T07:47:09.232260000'
210+
211+
# converting DateTime to native datetime (lossy)
212+
native = dt.to_native() # type: datetime
213+
# end::temporal-types-local-datetime[]
214+
215+
assert isinstance(dt, DateTime)
216+
assert str(dt) == "2021-11-02T07:47:00.000004123"
217+
assert dt == in_dt
218+
assert isinstance(native, datetime)
219+
assert native == py_dt
220+
221+
with driver.session() as session:
222+
record = session.read_transaction(_echo, py_dt)
223+
224+
dt = record.get("fieldName")
225+
assert isinstance(dt, DateTime)
226+
assert dt == in_dt.to_native()
227+
228+
229+
def test_local_time(driver):
230+
# tag::temporal-types-local-time-import[]
231+
from datetime import time
232+
233+
from neo4j.time import Time
234+
import pytz
235+
# end::temporal-types-local-time-import[]
236+
237+
# tag::temporal-types-local-time[]
238+
# Create datetimes to be used as query parameters
239+
# Python's builtin datetimes works as well. However, they don't support
240+
# the full feature-set of Neo4j's durations: it has no nanosecond precision.
241+
py_t = time(hour=7, minute=47, microsecond=4)
242+
243+
# A Time can be created from a native time
244+
t = Time.from_native(py_t)
245+
# or directly
246+
t = Time(hour=7, minute=47, second=0.000004123)
247+
# end::temporal-types-local-time[]
248+
249+
in_t = t # stored for later assertions
250+
251+
with driver.session() as session:
252+
record = session.read_transaction(_echo, t)
253+
254+
# tag::temporal-types-local-time[]
255+
256+
# Reading a Time from a record
257+
t = record.get("fieldName") # type: Time
258+
str(t) # 'T07:47:09.232260000'
259+
260+
# converting Time to native time (lossy)
261+
native = t.to_native() # type: time
262+
# end::temporal-types-local-time[]
263+
264+
assert isinstance(t, Time)
265+
assert str(t) == "07:47:00.000004123"
266+
assert t == in_t
267+
assert isinstance(native, time)
268+
assert native == py_t
269+
270+
with driver.session() as session:
271+
record = session.read_transaction(_echo, py_t)
272+
273+
t = record.get("fieldName")
274+
assert isinstance(t, Time)
275+
assert t == in_t.to_native()
276+
277+
278+
def test_duration_example(driver):
279+
# tag::temporal-types-duration-import[]
280+
from datetime import timedelta
281+
282+
from neo4j.time import Duration
283+
# end::temporal-types-duration-import[]
284+
285+
# tag::temporal-types-duration[]
286+
# Creating durations to be used as query parameters
287+
duration = Duration(years=1, days=2, seconds=3, nanoseconds=4)
288+
# Python's builtin timedeltas works as well. However, they don't support
289+
# the full feature-set of Neo4j's durations,
290+
# e.g., no nanoseconds and no months.
291+
py_duration = timedelta(days=2, seconds=3, microseconds=4)
292+
# end::temporal-types-duration[]
293+
294+
in_duration = duration # stored for later assertions
295+
296+
with driver.session() as session:
297+
record = session.read_transaction(_echo, duration)
298+
299+
# tag::temporal-types-duration[]
300+
301+
# Reading a Duration from a record
302+
duration = record.get("fieldName") # type: Duration
303+
str(duration) # 'P1Y2DT3.000000004S'
304+
# end::temporal-types-duration[]
305+
306+
assert isinstance(duration, Duration)
307+
assert str(duration) == 'P1Y2DT3.000000004S'
308+
assert duration == in_duration
309+
310+
with driver.session() as session:
311+
record = session.read_transaction(_echo, py_duration)
312+
313+
duration = record.get("fieldName")
314+
assert isinstance(duration, Duration)
315+
assert str(duration) == 'P2DT3.000004S'
316+
assert Duration() + py_duration == duration

0 commit comments

Comments
 (0)