Skip to content

Commit ceeec83

Browse files
committed
Support for temporal types
This commit makes driver able to receive temporal types as query results and send them as query parameters. All types are represented by custom date-time classes because native `Date` type is quite limited. It can't represent needed millisecond range and does not have support for things like timezone. All temporal types expose understandable properties. For example `CypherDate` has `year`, `month` and `day` properties. Driver uses code adapted from https://github.com/ThreeTen/threetenbp to perform the conversions. Main difference is that driver operates on `Integer` objects and not native JavaScript numbers.
1 parent c637770 commit ceeec83

File tree

5 files changed

+1155
-1
lines changed

5 files changed

+1155
-1
lines changed

src/v1/integer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ class Integer {
238238
* @returns {boolean}
239239
* @expose
240240
*/
241-
notEquals(other) { !this.equals(/* validates */ other); }
241+
notEquals(other) {
242+
return !this.equals(/* validates */ other);
243+
}
242244

243245
/**
244246
* Tests if this Integer's value is less than the specified's.

src/v1/internal/packstream-v2.js

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,57 @@
1919

2020
import * as v1 from './packstream-v1';
2121
import {isPoint, Point} from '../spatial-types';
22+
import {
23+
CypherDate,
24+
CypherDateTimeWithZoneId,
25+
CypherDateTimeWithZoneOffset,
26+
CypherDuration,
27+
CypherTime,
28+
isCypherDate,
29+
isCypherDateTimeWithZoneId,
30+
isCypherDateTimeWithZoneOffset,
31+
isCypherDuration,
32+
isCypherLocalDateTime,
33+
isCypherLocalTime,
34+
isCypherTime
35+
} from '../temporal-types';
2236
import {int} from '../integer';
37+
import {
38+
cypherDateToEpochDay,
39+
cypherLocalDateTimeToEpochSecond,
40+
cypherLocalTimeToNanoOfDay,
41+
epochDayToCypherDate,
42+
epochSecondAndNanoToCypherLocalDateTime,
43+
nanoOfDayToCypherLocalTime
44+
} from '../internal/temporal-util';
2345

2446
const POINT_2D = 0x58;
2547
const POINT_2D_STRUCT_SIZE = 3;
2648

2749
const POINT_3D = 0x59;
2850
const POINT_3D_STRUCT_SIZE = 4;
2951

52+
const DURATION = 0x45;
53+
const DURATION_STRUCT_SIZE = 4;
54+
55+
const LOCAL_TIME = 0x74;
56+
const LOCAL_TIME_STRUCT_SIZE = 1;
57+
58+
const TIME = 0x54;
59+
const TIME_STRUCT_SIZE = 2;
60+
61+
const DATE = 0x44;
62+
const DATE_STRUCT_SIZE = 1;
63+
64+
const LOCAL_DATE_TIME = 0x64;
65+
const LOCAL_DATE_TIME_STRUCT_SIZE = 2;
66+
67+
const DATE_TIME_WITH_ZONE_OFFSET = 0x46;
68+
const DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE = 3;
69+
70+
const DATE_TIME_WITH_ZONE_ID = 0x66;
71+
const DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE = 3;
72+
3073
export class Packer extends v1.Packer {
3174

3275
/**
@@ -44,6 +87,20 @@ export class Packer extends v1.Packer {
4487
packable(obj, onError) {
4588
if (isPoint(obj)) {
4689
return () => packPoint(obj, this, onError);
90+
} else if (isCypherDuration(obj)) {
91+
return () => packDuration(obj, this, onError);
92+
} else if (isCypherLocalTime(obj)) {
93+
return () => packLocalTime(obj, this, onError);
94+
} else if (isCypherTime(obj)) {
95+
return () => packTime(obj, this, onError);
96+
} else if (isCypherDate(obj)) {
97+
return () => packDate(obj, this, onError);
98+
} else if (isCypherLocalDateTime(obj)) {
99+
return () => packLocalDateTime(obj, this, onError);
100+
} else if (isCypherDateTimeWithZoneOffset(obj)) {
101+
return () => packDateTimeWithZoneOffset(obj, this, onError);
102+
} else if (isCypherDateTimeWithZoneId(obj)) {
103+
return () => packDateTimeWithZoneId(obj, this, onError);
47104
} else {
48105
return super.packable(obj, onError);
49106
}
@@ -66,6 +123,20 @@ export class Unpacker extends v1.Unpacker {
66123
return unpackPoint2D(this, structSize, buffer);
67124
} else if (signature == POINT_3D) {
68125
return unpackPoint3D(this, structSize, buffer);
126+
} else if (signature == DURATION) {
127+
return unpackDuration(this, structSize, buffer);
128+
} else if (signature == LOCAL_TIME) {
129+
return unpackLocalTime(this, structSize, buffer);
130+
} else if (signature == TIME) {
131+
return unpackTime(this, structSize, buffer);
132+
} else if (signature == DATE) {
133+
return unpackDate(this, structSize, buffer);
134+
} else if (signature == LOCAL_DATE_TIME) {
135+
return unpackLocalDateTime(this, structSize, buffer);
136+
} else if (signature == DATE_TIME_WITH_ZONE_OFFSET) {
137+
return unpackDateTimeWithZoneOffset(this, structSize, buffer);
138+
} else if (signature == DATE_TIME_WITH_ZONE_ID) {
139+
return unpackDateTimeWithZoneId(this, structSize, buffer);
69140
} else {
70141
return super._unpackUnknownStruct(signature, structSize, buffer);
71142
}
@@ -121,3 +192,215 @@ function unpackPoint3D(unpacker, structSize, buffer) {
121192
unpacker.unpack(buffer) // z
122193
);
123194
}
195+
196+
function packDuration(value, packer, onError) {
197+
const months = int(value.months);
198+
const days = int(value.days);
199+
const seconds = int(value.seconds);
200+
const nanoseconds = int(value.nanoseconds);
201+
202+
const packableStructFields = [
203+
packer.packable(months, onError),
204+
packer.packable(days, onError),
205+
packer.packable(seconds, onError),
206+
packer.packable(nanoseconds, onError),
207+
];
208+
packer.packStruct(DURATION, packableStructFields, onError);
209+
}
210+
211+
function unpackDuration(unpacker, structSize, buffer) {
212+
unpacker._verifyStructSize('Duration', DURATION_STRUCT_SIZE, structSize);
213+
214+
const months = unpacker.unpack(buffer);
215+
const days = unpacker.unpack(buffer);
216+
const seconds = unpacker.unpack(buffer);
217+
const nanoseconds = unpacker.unpack(buffer);
218+
219+
return new CypherDuration(months, days, seconds, nanoseconds);
220+
}
221+
222+
function packLocalTime(value, packer, onError) {
223+
const totalNanos = cypherLocalTimeToNanoOfDay(value);
224+
225+
const packableStructFields = [
226+
packer.packable(totalNanos, onError)
227+
];
228+
packer.packStruct(LOCAL_TIME, packableStructFields, onError);
229+
}
230+
231+
function unpackLocalTime(unpacker, structSize, buffer) {
232+
unpacker._verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, structSize);
233+
234+
const nanoOfDay = unpacker.unpack(buffer);
235+
return nanoOfDayToCypherLocalTime(nanoOfDay);
236+
}
237+
238+
/**
239+
* Pack given cypher time.
240+
* @param {CypherTime} value the time value to pack.
241+
* @param {Packer} packer the packer to use.
242+
* @param {function} onError the error callback.
243+
*/
244+
function packTime(value, packer, onError) {
245+
const totalNanos = cypherLocalTimeToNanoOfDay(value.localTime);
246+
const offsetSeconds = int(value.offsetSeconds);
247+
248+
const packableStructFields = [
249+
packer.packable(totalNanos, onError),
250+
packer.packable(offsetSeconds, onError)
251+
];
252+
packer.packStruct(TIME, packableStructFields, onError);
253+
}
254+
255+
/**
256+
* Unpack cypher time value using the given unpacker.
257+
* @param {Unpacker} unpacker the unpacker to use.
258+
* @param {number} structSize the retrieved struct size.
259+
* @param {BaseBuffer} buffer the buffer to unpack from.
260+
* @return {CypherTime} the unpacked cypher time value.
261+
*/
262+
function unpackTime(unpacker, structSize, buffer) {
263+
unpacker._verifyStructSize('Time', TIME_STRUCT_SIZE, structSize);
264+
265+
const nanoOfDay = unpacker.unpack(buffer);
266+
const offsetSeconds = unpacker.unpack(buffer);
267+
268+
const localTime = nanoOfDayToCypherLocalTime(nanoOfDay);
269+
return new CypherTime(localTime, offsetSeconds);
270+
}
271+
272+
/**
273+
* Pack given cypher date.
274+
* @param {CypherDate} value the date value to pack.
275+
* @param {Packer} packer the packer to use.
276+
* @param {function} onError the error callback.
277+
*/
278+
function packDate(value, packer, onError) {
279+
const epochDay = cypherDateToEpochDay(value);
280+
281+
const packableStructFields = [
282+
packer.packable(epochDay, onError)
283+
];
284+
packer.packStruct(DATE, packableStructFields, onError);
285+
}
286+
287+
/**
288+
* Unpack cypher date value using the given unpacker.
289+
* @param {Unpacker} unpacker the unpacker to use.
290+
* @param {number} structSize the retrieved struct size.
291+
* @param {BaseBuffer} buffer the buffer to unpack from.
292+
* @return {CypherDate} the unpacked cypher date value.
293+
*/
294+
function unpackDate(unpacker, structSize, buffer) {
295+
unpacker._verifyStructSize('Date', DATE_STRUCT_SIZE, structSize);
296+
297+
const epochDay = unpacker.unpack(buffer);
298+
return epochDayToCypherDate(epochDay);
299+
}
300+
301+
/**
302+
* Pack given cypher local date time.
303+
* @param {CypherLocalDateTime} value the local date time value to pack.
304+
* @param {Packer} packer the packer to use.
305+
* @param {function} onError the error callback.
306+
*/
307+
function packLocalDateTime(value, packer, onError) {
308+
const epochSecond = cypherLocalDateTimeToEpochSecond(value);
309+
const nano = int(value.localTime.nanosecond);
310+
311+
const packableStructFields = [
312+
packer.packable(epochSecond, onError),
313+
packer.packable(nano, onError)
314+
];
315+
packer.packStruct(LOCAL_DATE_TIME, packableStructFields, onError);
316+
}
317+
318+
/**
319+
* Unpack cypher local date time value using the given unpacker.
320+
* @param {Unpacker} unpacker the unpacker to use.
321+
* @param {number} structSize the retrieved struct size.
322+
* @param {BaseBuffer} buffer the buffer to unpack from.
323+
* @return {CypherLocalDateTime} the unpacked cypher local date time value.
324+
*/
325+
function unpackLocalDateTime(unpacker, structSize, buffer) {
326+
unpacker._verifyStructSize('LocalDateTime', LOCAL_DATE_TIME_STRUCT_SIZE, structSize);
327+
328+
const epochSecond = unpacker.unpack(buffer);
329+
const nano = unpacker.unpack(buffer);
330+
331+
return epochSecondAndNanoToCypherLocalDateTime(epochSecond, nano);
332+
}
333+
334+
/**
335+
* Pack given cypher date time with zone offset.
336+
* @param {CypherDateTimeWithZoneOffset} value the date time value to pack.
337+
* @param {Packer} packer the packer to use.
338+
* @param {function} onError the error callback.
339+
*/
340+
function packDateTimeWithZoneOffset(value, packer, onError) {
341+
const epochSecond = cypherLocalDateTimeToEpochSecond(value.localDateTime);
342+
const nano = int(value.localDateTime.localTime.nanosecond);
343+
const offsetSeconds = int(value.offsetSeconds);
344+
345+
const packableStructFields = [
346+
packer.packable(epochSecond, onError),
347+
packer.packable(nano, onError),
348+
packer.packable(offsetSeconds, onError)
349+
];
350+
packer.packStruct(DATE_TIME_WITH_ZONE_OFFSET, packableStructFields, onError);
351+
}
352+
353+
/**
354+
* Unpack cypher date time with zone offset value using the given unpacker.
355+
* @param {Unpacker} unpacker the unpacker to use.
356+
* @param {number} structSize the retrieved struct size.
357+
* @param {BaseBuffer} buffer the buffer to unpack from.
358+
* @return {CypherDateTimeWithZoneOffset} the unpacked cypher date time with zone offset value.
359+
*/
360+
function unpackDateTimeWithZoneOffset(unpacker, structSize, buffer) {
361+
unpacker._verifyStructSize('DateTimeWithZoneOffset', DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE, structSize);
362+
363+
const epochSecond = unpacker.unpack(buffer);
364+
const nano = unpacker.unpack(buffer);
365+
const offsetSeconds = unpacker.unpack(buffer);
366+
367+
const localDateTime = epochSecondAndNanoToCypherLocalDateTime(epochSecond, nano);
368+
return new CypherDateTimeWithZoneOffset(localDateTime, offsetSeconds);
369+
}
370+
371+
/**
372+
* Pack given cypher date time with zone id.
373+
* @param {CypherDateTimeWithZoneId} value the date time value to pack.
374+
* @param {Packer} packer the packer to use.
375+
* @param {function} onError the error callback.
376+
*/
377+
function packDateTimeWithZoneId(value, packer, onError) {
378+
const epochSecond = cypherLocalDateTimeToEpochSecond(value.localDateTime);
379+
const nano = int(value.localDateTime.localTime.nanosecond);
380+
const zoneId = value.zoneId;
381+
382+
const packableStructFields = [
383+
packer.packable(epochSecond, onError),
384+
packer.packable(nano, onError),
385+
packer.packable(zoneId, onError)
386+
];
387+
packer.packStruct(DATE_TIME_WITH_ZONE_ID, packableStructFields, onError);
388+
}
389+
390+
/**
391+
* Unpack cypher date time with zone id value using the given unpacker.
392+
* @param {Unpacker} unpacker the unpacker to use.
393+
* @param {number} structSize the retrieved struct size.
394+
* @param {BaseBuffer} buffer the buffer to unpack from.
395+
* @return {CypherDateTimeWithZoneId} the unpacked cypher date time with zone id value.
396+
*/
397+
function unpackDateTimeWithZoneId(unpacker, structSize, buffer) {
398+
unpacker._verifyStructSize('DateTimeWithZoneId', DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE, structSize);
399+
400+
const epochSecond = unpacker.unpack(buffer);
401+
const nano = unpacker.unpack(buffer);
402+
const zoneId = unpacker.unpack(buffer);
403+
404+
const localDateTime = epochSecondAndNanoToCypherLocalDateTime(epochSecond, nano);
405+
return new CypherDateTimeWithZoneId(localDateTime, zoneId);
406+
}

0 commit comments

Comments
 (0)