From a600c8393092bcb186b8c4f69bdb99819ba23850 Mon Sep 17 00:00:00 2001 From: lutovich Date: Thu, 5 Apr 2018 12:18:28 +0200 Subject: [PATCH 1/3] Added unit tests for temporal-util --- src/v1/internal/temporal-util.js | 12 ++- test/internal/temporal-util.test.js | 132 ++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 test/internal/temporal-util.test.js diff --git a/src/v1/internal/temporal-util.js b/src/v1/internal/temporal-util.js index d3fd63b98..ba2d4c766 100644 --- a/src/v1/internal/temporal-util.js +++ b/src/v1/internal/temporal-util.js @@ -239,7 +239,7 @@ export function dateToIsoString(year, month, day) { if (isNegative) { year = year.multiply(-1); } - let yearString = year.toString().padStart(4, '0'); + let yearString = formatNumber(year, 4); if (isNegative) { yearString = '-' + yearString; } @@ -318,6 +318,12 @@ function floorMod(x, y) { * @return {string} formatted and possibly left-padded number as string. */ function formatNumber(num, stringLength = undefined) { - const result = int(num).toString(); - return stringLength ? result.padStart(stringLength, '0') : result; + num = int(num); + const isNegative = num.isNegative(); + if (isNegative) { + num = num.multiply(-1); + } + const numString = num.toString(); + const paddedNumString = stringLength ? numString.padStart(stringLength, '0') : numString; + return isNegative ? '-' + paddedNumString : paddedNumString; } diff --git a/test/internal/temporal-util.test.js b/test/internal/temporal-util.test.js new file mode 100644 index 000000000..bed24a32d --- /dev/null +++ b/test/internal/temporal-util.test.js @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {int} from '../../src/v1/integer'; +import * as util from '../../src/v1/internal/temporal-util'; +import {Date} from '../../src/v1/temporal-types'; +import {LocalDateTime, LocalTime} from '../../src/v1'; + +describe('temporal-util', () => { + + it('should convert date to ISO string', () => { + expect(util.dateToIsoString(90, 2, 5)).toEqual('0090-02-05'); + expect(util.dateToIsoString(int(1), 1, int(1))).toEqual('0001-01-01'); + expect(util.dateToIsoString(-123, int(12), int(23))).toEqual('-0123-12-23'); + expect(util.dateToIsoString(int(-999), int(9), int(10))).toEqual('-0999-09-10'); + expect(util.dateToIsoString(1999, 12, 19)).toEqual('1999-12-19'); + expect(util.dateToIsoString(int(2023), int(8), int(16))).toEqual('2023-08-16'); + expect(util.dateToIsoString(12345, 12, 31)).toEqual('12345-12-31'); + expect(util.dateToIsoString(int(19191919), int(11), int(30))).toEqual('19191919-11-30'); + expect(util.dateToIsoString(-909090, 9, 9)).toEqual('-909090-09-09'); + expect(util.dateToIsoString(int(-888999777), int(7), int(26))).toEqual('-888999777-07-26'); + }); + + it('should convert time zone offset to ISO string', () => { + expect(util.timeZoneOffsetToIsoString(0)).toEqual('Z'); + expect(util.timeZoneOffsetToIsoString(-0)).toEqual('Z'); + expect(util.timeZoneOffsetToIsoString(1)).toEqual('+00:00:01'); + expect(util.timeZoneOffsetToIsoString(-1)).toEqual('-00:00:01'); + expect(util.timeZoneOffsetToIsoString(20 * 60)).toEqual('+00:20'); + expect(util.timeZoneOffsetToIsoString(-12 * 60)).toEqual('-00:12'); + expect(util.timeZoneOffsetToIsoString(8 * 60 * 60)).toEqual('+08:00'); + expect(util.timeZoneOffsetToIsoString(-13 * 60 * 60)).toEqual('-13:00'); + expect(util.timeZoneOffsetToIsoString(8 * 60 * 60 + 59 * 60)).toEqual('+08:59'); + expect(util.timeZoneOffsetToIsoString(-12 * 60 * 60 - 31 * 60)).toEqual('-12:31'); + expect(util.timeZoneOffsetToIsoString(2 * 60 * 60 + 9 * 60 + 17)).toEqual('+02:09:17'); + expect(util.timeZoneOffsetToIsoString(-7 * 60 * 60 - 18 * 60 - 54)).toEqual('-07:18:54'); + }); + + it('should convert time to ISO string', () => { + expect(util.timeToIsoString(8, 9, 1, 0)).toEqual('08:09:01.000000000'); + expect(util.timeToIsoString(int(2), int(4), int(6), int(7))).toEqual('02:04:06.000000007'); + expect(util.timeToIsoString(22, 19, 7, 999)).toEqual('22:19:07.000000999'); + expect(util.timeToIsoString(int(17), int(2), int(59), int(909090))).toEqual('17:02:59.000909090'); + expect(util.timeToIsoString(23, 59, 59, 999999991)).toEqual('23:59:59.999999991'); + expect(util.timeToIsoString(int(23), int(22), int(21), int(111222333))).toEqual('23:22:21.111222333'); + }); + + it('should convert duration to ISO string', () => { + expect(util.durationToIsoString(0, 0, 0, 0)).toEqual('P0M0DT0.000000000S'); + expect(util.durationToIsoString(0, 0, 0, 123)).toEqual('P0M0DT0.000000123S'); + expect(util.durationToIsoString(11, 99, 100, 99901)).toEqual('P11M99DT100.000099901S'); + expect(util.durationToIsoString(int(3), int(9191), int(17), int(123456789))).toEqual('P3M9191DT17.123456789S'); + expect(util.durationToIsoString(-5, 2, -13, 123)).toEqual('P-5M2DT-13.000000123S'); + }); + + it('should convert epoch day to cypher date', () => { + expect(util.epochDayToDate(-719528)).toEqual(date(0, 1, 1)); + expect(util.epochDayToDate(-135153)).toEqual(date(1599, 12, 19)); + expect(util.epochDayToDate(7905)).toEqual(date(1991, 8, 24)); + expect(util.epochDayToDate(int(48210))).toEqual(date(2101, 12, 30)); + expect(util.epochDayToDate(int(-4310226))).toEqual(date(-9831, 1, 1)); + }); + + it('should convert cypher date to epoch day', () => { + expect(util.dateToEpochDay(date(-13, 12, 31))).toEqual(int(-723912)); + expect(util.dateToEpochDay(date(9, 9, 9))).toEqual(int(-715989)); + expect(util.dateToEpochDay(date(2015, 2, 17))).toEqual(int(16483)); + expect(util.dateToEpochDay(date(2189, 7, 19))).toEqual(int(80188)); + expect(util.dateToEpochDay(date(19999, 9, 28))).toEqual(int(6585227)); + }); + + it('should convert epoch second with nano to cypher local date-time', () => { + expect(util.epochSecondAndNanoToLocalDateTime(653165977, 999)).toEqual(localDateTime(1990, 9, 12, 18, 59, 37, 999)); + expect(util.epochSecondAndNanoToLocalDateTime(-62703676801, 12345)).toEqual(localDateTime(-18, 12, 31, 23, 59, 59, 12345)); + expect(util.epochSecondAndNanoToLocalDateTime(2678400, int(1))).toEqual(localDateTime(1970, 2, 1, 0, 0, 0, 1)); + expect(util.epochSecondAndNanoToLocalDateTime(int(3065493882737), int(1794673))).toEqual(localDateTime(99111, 8, 21, 6, 32, 17, 1794673)); + expect(util.epochSecondAndNanoToLocalDateTime(int(-37428234001), 999999111)).toEqual(localDateTime(783, 12, 12, 20, 19, 59, 999999111)); + }); + + it('should convert cypher local date-time to epoch second', () => { + expect(util.localDateTimeToEpochSecond(localDateTime(1990, 9, 12, 18, 59, 37, 999))).toEqual(int(653165977)); + expect(util.localDateTimeToEpochSecond(localDateTime(-18, 12, 31, 23, 59, 59, 12345))).toEqual(int(-62703676801)); + expect(util.localDateTimeToEpochSecond(localDateTime(1970, 2, 1, 0, 0, 0, 1))).toEqual(int(2678400)); + expect(util.localDateTimeToEpochSecond(localDateTime(99111, 8, 21, 6, 32, 17, 1794673))).toEqual(int(3065493882737)); + expect(util.localDateTimeToEpochSecond(localDateTime(783, 12, 12, 20, 19, 59, 999999111))).toEqual(int(-37428234001)); + }); + + it('should convert nanosecond of the day to cypher local time', () => { + expect(util.nanoOfDayToLocalTime(68079000012399)).toEqual(localTime(18, 54, 39, 12399)); + expect(util.nanoOfDayToLocalTime(0)).toEqual(localTime(0, 0, 0, 0)); + expect(util.nanoOfDayToLocalTime(1)).toEqual(localTime(0, 0, 0, 1)); + expect(util.nanoOfDayToLocalTime(int(86399999999999))).toEqual(localTime(23, 59, 59, 999999999)); + expect(util.nanoOfDayToLocalTime(int(46277000808080))).toEqual(localTime(12, 51, 17, 808080)); + }); + + it('should convert cypher local time to nanosecond of the day', () => { + expect(util.localTimeToNanoOfDay(localTime(18, 54, 39, 12399))).toEqual(int(68079000012399)); + expect(util.localTimeToNanoOfDay(localTime(0, 0, 0, 0))).toEqual(int(0)); + expect(util.localTimeToNanoOfDay(localTime(0, 0, 0, 1))).toEqual(int(1)); + expect(util.localTimeToNanoOfDay(localTime(23, 59, 59, 999999999))).toEqual(int(86399999999999)); + expect(util.localTimeToNanoOfDay(localTime(12, 51, 17, 808080))).toEqual(int(46277000808080)); + }); + +}); + +function date(year, month, day) { + return new Date(int(year), int(month), int(day)); +} + +function localTime(hour, minute, second, nanosecond) { + return new LocalTime(int(hour), int(minute), int(second), int(nanosecond)); +} + +function localDateTime(year, month, day, hour, minute, second, nanosecond) { + return new LocalDateTime(date(year, month, day), localTime(hour, minute, second, nanosecond)); +} From 456ae14850eac8620961678a27a86824b21c2359 Mon Sep 17 00:00:00 2001 From: lutovich Date: Thu, 5 Apr 2018 14:50:07 +0200 Subject: [PATCH 2/3] Turn all temporal types into flat objects Previously some temporal types were modeled as a combination of other temporal types. For example `Time` contained an instance of `LocalTime` and an offset in seconds. This made constructors more complicated and getters needed to access nested properties. For example `Time.hour` would need to be accessed as `Time.localTime.hour`. This commit makes `Time`, `LocalDateTime`, `DateTimeWithZoneOffset` and `DateTimeWithZoneId` contain primitive properties and not use other temporal types as building blocks. Also made all properties of spatial and temporal types readonly in TypeScript declarations. --- src/v1/internal/packstream-v2.js | 92 +++++++++++++++++++++++----- src/v1/internal/temporal-util.js | 85 +++++++++++++------------ src/v1/temporal-types.js | 81 ++++++++++++++++++------ test/internal/temporal-util.test.js | 32 +++++----- test/types/v1/temporal-types.test.ts | 79 +++++++++++++++++++----- test/v1/temporal-types.test.js | 87 ++++++++++++++++++++------ types/v1/spatial-types.d.ts | 9 +-- types/v1/temporal-types.d.ts | 68 +++++++++++++------- 8 files changed, 385 insertions(+), 148 deletions(-) diff --git a/src/v1/internal/packstream-v2.js b/src/v1/internal/packstream-v2.js index d50c0bdf5..6aea28d81 100644 --- a/src/v1/internal/packstream-v2.js +++ b/src/v1/internal/packstream-v2.js @@ -24,22 +24,22 @@ import { DateTimeWithZoneId, DateTimeWithZoneOffset, Duration, - Time, isDate, isDateTimeWithZoneId, isDateTimeWithZoneOffset, isDuration, isLocalDateTime, isLocalTime, - isTime + isTime, + Time } from '../temporal-types'; import {int} from '../integer'; import { dateToEpochDay, - localDateTimeToEpochSecond, - localTimeToNanoOfDay, epochDayToDate, epochSecondAndNanoToLocalDateTime, + localDateTimeToEpochSecond, + localTimeToNanoOfDay, nanoOfDayToLocalTime } from '../internal/temporal-util'; @@ -143,6 +143,12 @@ export class Unpacker extends v1.Unpacker { } } +/** + * Pack given 2D or 3D point. + * @param {Point} point the point value to pack. + * @param {Packer} packer the packer to use. + * @param {function} onError the error callback. + */ function packPoint(point, packer, onError) { const is2DPoint = point.z === null || point.z === undefined; if (is2DPoint) { @@ -152,6 +158,12 @@ function packPoint(point, packer, onError) { } } +/** + * Pack given 2D point. + * @param {Point} point the point value to pack. + * @param {Packer} packer the packer to use. + * @param {function} onError the error callback. + */ function packPoint2D(point, packer, onError) { const packableStructFields = [ packer.packable(int(point.srid), onError), @@ -161,6 +173,12 @@ function packPoint2D(point, packer, onError) { packer.packStruct(POINT_2D, packableStructFields, onError); } +/** + * Pack given 3D point. + * @param {Point} point the point value to pack. + * @param {Packer} packer the packer to use. + * @param {function} onError the error callback. + */ function packPoint3D(point, packer, onError) { const packableStructFields = [ packer.packable(int(point.srid), onError), @@ -171,6 +189,13 @@ function packPoint3D(point, packer, onError) { packer.packStruct(POINT_3D, packableStructFields, onError); } +/** + * Unpack 2D point value using the given unpacker. + * @param {Unpacker} unpacker the unpacker to use. + * @param {number} structSize the retrieved struct size. + * @param {BaseBuffer} buffer the buffer to unpack from. + * @return {Point} the unpacked 2D point value. + */ function unpackPoint2D(unpacker, structSize, buffer) { unpacker._verifyStructSize('Point2D', POINT_2D_STRUCT_SIZE, structSize); @@ -182,6 +207,13 @@ function unpackPoint2D(unpacker, structSize, buffer) { ); } +/** + * Unpack 3D point value using the given unpacker. + * @param {Unpacker} unpacker the unpacker to use. + * @param {number} structSize the retrieved struct size. + * @param {BaseBuffer} buffer the buffer to unpack from. + * @return {Point} the unpacked 3D point value. + */ function unpackPoint3D(unpacker, structSize, buffer) { unpacker._verifyStructSize('Point3D', POINT_3D_STRUCT_SIZE, structSize); @@ -193,6 +225,12 @@ function unpackPoint3D(unpacker, structSize, buffer) { ); } +/** + * Pack given duration. + * @param {Duration} value the duration value to pack. + * @param {Packer} packer the packer to use. + * @param {function} onError the error callback. + */ function packDuration(value, packer, onError) { const months = int(value.months); const days = int(value.days); @@ -208,6 +246,13 @@ function packDuration(value, packer, onError) { packer.packStruct(DURATION, packableStructFields, onError); } +/** + * Unpack duration value using the given unpacker. + * @param {Unpacker} unpacker the unpacker to use. + * @param {number} structSize the retrieved struct size. + * @param {BaseBuffer} buffer the buffer to unpack from. + * @return {Duration} the unpacked duration value. + */ function unpackDuration(unpacker, structSize, buffer) { unpacker._verifyStructSize('Duration', DURATION_STRUCT_SIZE, structSize); @@ -219,8 +264,14 @@ function unpackDuration(unpacker, structSize, buffer) { return new Duration(months, days, seconds, nanoseconds); } +/** + * Pack given local time. + * @param {LocalTime} value the local time value to pack. + * @param {Packer} packer the packer to use. + * @param {function} onError the error callback. + */ function packLocalTime(value, packer, onError) { - const nanoOfDay = localTimeToNanoOfDay(value); + const nanoOfDay = localTimeToNanoOfDay(value.hour, value.minute, value.second, value.nanosecond); const packableStructFields = [ packer.packable(nanoOfDay, onError) @@ -228,6 +279,13 @@ function packLocalTime(value, packer, onError) { packer.packStruct(LOCAL_TIME, packableStructFields, onError); } +/** + * Unpack local time value using the given unpacker. + * @param {Unpacker} unpacker the unpacker to use. + * @param {number} structSize the retrieved struct size. + * @param {BaseBuffer} buffer the buffer to unpack from. + * @return {LocalTime} the unpacked local time value. + */ function unpackLocalTime(unpacker, structSize, buffer) { unpacker._verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, structSize); @@ -242,7 +300,7 @@ function unpackLocalTime(unpacker, structSize, buffer) { * @param {function} onError the error callback. */ function packTime(value, packer, onError) { - const nanoOfDay = localTimeToNanoOfDay(value.localTime); + const nanoOfDay = localTimeToNanoOfDay(value.hour, value.minute, value.second, value.nanosecond); const offsetSeconds = int(value.offsetSeconds); const packableStructFields = [ @@ -266,7 +324,7 @@ function unpackTime(unpacker, structSize, buffer) { const offsetSeconds = unpacker.unpack(buffer); const localTime = nanoOfDayToLocalTime(nanoOfDay); - return new Time(localTime, offsetSeconds); + return new Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, offsetSeconds); } /** @@ -276,7 +334,7 @@ function unpackTime(unpacker, structSize, buffer) { * @param {function} onError the error callback. */ function packDate(value, packer, onError) { - const epochDay = dateToEpochDay(value); + const epochDay = dateToEpochDay(value.year, value.month, value.day); const packableStructFields = [ packer.packable(epochDay, onError) @@ -305,8 +363,8 @@ function unpackDate(unpacker, structSize, buffer) { * @param {function} onError the error callback. */ function packLocalDateTime(value, packer, onError) { - const epochSecond = localDateTimeToEpochSecond(value); - const nano = int(value.localTime.nanosecond); + const epochSecond = localDateTimeToEpochSecond(value.year, value.month, value.day, value.hour, value.minute, value.second, value.nanosecond); + const nano = int(value.nanosecond); const packableStructFields = [ packer.packable(epochSecond, onError), @@ -338,8 +396,8 @@ function unpackLocalDateTime(unpacker, structSize, buffer) { * @param {function} onError the error callback. */ function packDateTimeWithZoneOffset(value, packer, onError) { - const epochSecond = localDateTimeToEpochSecond(value.localDateTime); - const nano = int(value.localDateTime.localTime.nanosecond); + const epochSecond = localDateTimeToEpochSecond(value.year, value.month, value.day, value.hour, value.minute, value.second, value.nanosecond); + const nano = int(value.nanosecond); const offsetSeconds = int(value.offsetSeconds); const packableStructFields = [ @@ -365,7 +423,8 @@ function unpackDateTimeWithZoneOffset(unpacker, structSize, buffer) { const offsetSeconds = unpacker.unpack(buffer); const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano); - return new DateTimeWithZoneOffset(localDateTime, offsetSeconds); + return new DateTimeWithZoneOffset(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour, + localDateTime.minute, localDateTime.second, localDateTime.nanosecond, offsetSeconds); } /** @@ -375,8 +434,8 @@ function unpackDateTimeWithZoneOffset(unpacker, structSize, buffer) { * @param {function} onError the error callback. */ function packDateTimeWithZoneId(value, packer, onError) { - const epochSecond = localDateTimeToEpochSecond(value.localDateTime); - const nano = int(value.localDateTime.localTime.nanosecond); + const epochSecond = localDateTimeToEpochSecond(value.year, value.month, value.day, value.hour, value.minute, value.second, value.nanosecond); + const nano = int(value.nanosecond); const zoneId = value.zoneId; const packableStructFields = [ @@ -402,5 +461,6 @@ function unpackDateTimeWithZoneId(unpacker, structSize, buffer) { const zoneId = unpacker.unpack(buffer); const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano); - return new DateTimeWithZoneId(localDateTime, zoneId); + return new DateTimeWithZoneId(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour, + localDateTime.minute, localDateTime.second, localDateTime.nanosecond, zoneId); } diff --git a/src/v1/internal/temporal-util.js b/src/v1/internal/temporal-util.js index ba2d4c766..00ee5cbe9 100644 --- a/src/v1/internal/temporal-util.js +++ b/src/v1/internal/temporal-util.js @@ -43,14 +43,17 @@ const SECONDS_PER_DAY = 86400; /** * Converts given local time into a single integer representing this same time in nanoseconds of the day. - * @param {LocalTime} localTime the time to convert. + * @param {Integer|number|string} hour the hour of the local time to convert. + * @param {Integer|number|string} minute the minute of the local time to convert. + * @param {Integer|number|string} second the second of the local time to convert. + * @param {Integer|number|string} nanosecond the nanosecond of the local time to convert. * @return {Integer} nanoseconds representing the given local time. */ -export function localTimeToNanoOfDay(localTime) { - const hour = int(localTime.hour); - const minute = int(localTime.minute); - const second = int(localTime.second); - const nanosecond = int(localTime.nanosecond); +export function localTimeToNanoOfDay(hour, minute, second, nanosecond) { + hour = int(hour); + minute = int(minute); + second = int(second); + nanosecond = int(nanosecond); let totalNanos = hour.multiply(NANOS_PER_HOUR); totalNanos = totalNanos.add(minute.multiply(NANOS_PER_MINUTE)); @@ -80,15 +83,18 @@ export function nanoOfDayToLocalTime(nanoOfDay) { /** * Converts given local date time into a single integer representing this same time in epoch seconds UTC. - * @param {LocalDateTime} localDateTime the local date time value to convert. + * @param {Integer|number|string} year the year of the local date-time to convert. + * @param {Integer|number|string} month the month of the local date-time to convert. + * @param {Integer|number|string} day the day of the local date-time to convert. + * @param {Integer|number|string} hour the hour of the local date-time to convert. + * @param {Integer|number|string} minute the minute of the local date-time to convert. + * @param {Integer|number|string} second the second of the local date-time to convert. + * @param {Integer|number|string} nanosecond the nanosecond of the local date-time to convert. * @return {Integer} epoch second in UTC representing the given local date time. */ -export function localDateTimeToEpochSecond(localDateTime) { - const localDate = localDateTime.localDate; - const localTime = localDateTime.localTime; - - const epochDay = dateToEpochDay(localDate); - const localTimeSeconds = localTimeToSecondOfDay(localTime); +export function localDateTimeToEpochSecond(year, month, day, hour, minute, second, nanosecond) { + const epochDay = dateToEpochDay(year, month, day); + const localTimeSeconds = localTimeToSecondOfDay(hour, minute, second); return epochDay.multiply(SECONDS_PER_DAY).add(localTimeSeconds); } @@ -105,18 +111,20 @@ export function epochSecondAndNanoToLocalDateTime(epochSecond, nano) { const localDate = epochDayToDate(epochDay); const localTime = nanoOfDayToLocalTime(nanoOfDay); - return new LocalDateTime(localDate, localTime); + return new LocalDateTime(localDate.year, localDate.month, localDate.day, localTime.hour, localTime.minute, localTime.second, localTime.nanosecond); } /** * Converts given local date into a single integer representing it's epoch day. - * @param {Date} date the date to convert. + * @param {Integer|number|string} year the year of the local date to convert. + * @param {Integer|number|string} month the month of the local date to convert. + * @param {Integer|number|string} day the day of the local date to convert. * @return {Integer} epoch day representing the given date. */ -export function dateToEpochDay(date) { - const year = int(date.year); - const month = int(date.month); - const day = int(date.day); +export function dateToEpochDay(year, month, day) { + year = int(year); + month = int(month); + day = int(day); let epochDay = year.multiply(365); @@ -171,10 +179,10 @@ export function epochDayToDate(epochDay) { /** * Format given duration to an ISO 8601 string. - * @param {Integer|number} months the number of months. - * @param {Integer|number} days the number of days. - * @param {Integer|number} seconds the number of seconds. - * @param {Integer|number} nanoseconds the number of nanoseconds. + * @param {Integer|number|string} months the number of months. + * @param {Integer|number|string} days the number of days. + * @param {Integer|number|string} seconds the number of seconds. + * @param {Integer|number|string} nanoseconds the number of nanoseconds. * @return {string} ISO string that represents given duration. */ export function durationToIsoString(months, days, seconds, nanoseconds) { @@ -187,10 +195,10 @@ export function durationToIsoString(months, days, seconds, nanoseconds) { /** * Formats given time to an ISO 8601 string. - * @param {Integer|number} hour the hour value. - * @param {Integer|number} minute the minute value. - * @param {Integer|number} second the second value. - * @param {Integer|number} nanosecond the nanosecond value. + * @param {Integer|number|string} hour the hour value. + * @param {Integer|number|string} minute the minute value. + * @param {Integer|number|string} second the second value. + * @param {Integer|number|string} nanosecond the nanosecond value. * @return {string} ISO string that represents given time. */ export function timeToIsoString(hour, minute, second, nanosecond) { @@ -203,7 +211,7 @@ export function timeToIsoString(hour, minute, second, nanosecond) { /** * Formats given time zone offset in seconds to string representation like '±HH:MM', '±HH:MM:SS' or 'Z' for UTC. - * @param {Integer|number} offsetSeconds the offset in seconds. + * @param {Integer|number|string} offsetSeconds the offset in seconds. * @return {string} ISO string that represents given offset. */ export function timeZoneOffsetToIsoString(offsetSeconds) { @@ -228,9 +236,9 @@ export function timeZoneOffsetToIsoString(offsetSeconds) { /** * Formats given date to an ISO 8601 string. - * @param {Integer|number} year the date year. - * @param {Integer|number} month the date month. - * @param {Integer|number} day the date day. + * @param {Integer|number|string} year the date year. + * @param {Integer|number|string} month the date month. + * @param {Integer|number|string} day the date day. * @return {string} ISO string that represents given date. */ export function dateToIsoString(year, month, day) { @@ -251,20 +259,21 @@ export function dateToIsoString(year, month, day) { /** * Converts given local time into a single integer representing this same time in seconds of the day. Nanoseconds are skipped. - * @param {LocalTime} localTime the time to convert. + * @param {Integer|number|string} hour the hour of the local time. + * @param {Integer|number|string} minute the minute of the local time. + * @param {Integer|number|string} second the second of the local time. * @return {Integer} seconds representing the given local time. */ -function localTimeToSecondOfDay(localTime) { - const hour = int(localTime.hour); - const minute = int(localTime.minute); - const second = int(localTime.second); +function localTimeToSecondOfDay(hour, minute, second) { + hour = int(hour); + minute = int(minute); + second = int(second); let totalSeconds = hour.multiply(SECONDS_PER_HOUR); totalSeconds = totalSeconds.add(minute.multiply(SECONDS_PER_MINUTE)); return totalSeconds.add(second); } - /** * Check if given year is a leap year. Uses algorithm described here {@link https://en.wikipedia.org/wiki/Leap_year#Algorithm}. * @param {Integer|number|string} year the year to check. Will be converted to {@link Integer} for all calculations. @@ -313,7 +322,7 @@ function floorMod(x, y) { } /** - * @param {Integer|number} num the number to format. + * @param {Integer|number|string} num the number to format. * @param {number} [stringLength=undefined] the string length to left-pad to. * @return {string} formatted and possibly left-padded number as string. */ diff --git a/src/v1/temporal-types.js b/src/v1/temporal-types.js index 3f8b61661..31468203c 100644 --- a/src/v1/temporal-types.js +++ b/src/v1/temporal-types.js @@ -115,17 +115,23 @@ export class Time { /** * @constructor - * @param {LocalTime} localTime the local time for the new time with offset. + * @param {Integer|number} hour the hour for the new local time. + * @param {Integer|number} minute the minute for the new local time. + * @param {Integer|number} second the second for the new local time. + * @param {Integer|number} nanosecond the nanosecond for the new local time. * @param {Integer|number} offsetSeconds the time zone offset in seconds. */ - constructor(localTime, offsetSeconds) { - this.localTime = localTime; + constructor(hour, minute, second, nanosecond, offsetSeconds) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanosecond = nanosecond; this.offsetSeconds = offsetSeconds; Object.freeze(this); } toString() { - return this.localTime.toString() + timeZoneOffsetToIsoString(this.offsetSeconds); + return timeToIsoString(this.hour, this.minute, this.second, this.nanosecond) + timeZoneOffsetToIsoString(this.offsetSeconds); } } @@ -183,17 +189,27 @@ export class LocalDateTime { /** * @constructor - * @param {Date} localDate the local date part for the new local date-time. - * @param {LocalTime} localTime the local time part for the new local date-time. + * @param {Integer|number} year the year for the new local date. + * @param {Integer|number} month the month for the new local date. + * @param {Integer|number} day the day for the new local date. + * @param {Integer|number} hour the hour for the new local time. + * @param {Integer|number} minute the minute for the new local time. + * @param {Integer|number} second the second for the new local time. + * @param {Integer|number} nanosecond the nanosecond for the new local time. */ - constructor(localDate, localTime) { - this.localDate = localDate; - this.localTime = localTime; + constructor(year, month, day, hour, minute, second, nanosecond) { + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanosecond = nanosecond; Object.freeze(this); } toString() { - return `${this.localDate.toString()}T${this.localTime.toString()}`; + return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond); } } @@ -216,17 +232,30 @@ export class DateTimeWithZoneOffset { /** * @constructor - * @param {LocalDateTime} localDateTime the local date-time part for the new timezone-aware date-time. + * @param {Integer|number} year the year for the new local date. + * @param {Integer|number} month the month for the new local date. + * @param {Integer|number} day the day for the new local date. + * @param {Integer|number} hour the hour for the new local time. + * @param {Integer|number} minute the minute for the new local time. + * @param {Integer|number} second the second for the new local time. + * @param {Integer|number} nanosecond the nanosecond for the new local time. * @param {Integer|number} offsetSeconds the timezone offset in seconds for the new timezone-aware date-time. */ - constructor(localDateTime, offsetSeconds) { - this.localDateTime = localDateTime; + constructor(year, month, day, hour, minute, second, nanosecond, offsetSeconds) { + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanosecond = nanosecond; this.offsetSeconds = offsetSeconds; Object.freeze(this); } toString() { - return this.localDateTime.toString() + timeZoneOffsetToIsoString(this.offsetSeconds); + return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond) + + timeZoneOffsetToIsoString(this.offsetSeconds); } } @@ -249,17 +278,29 @@ export class DateTimeWithZoneId { /** * @constructor - * @param {LocalDateTime} localDateTime the local date-time part for the new timezone-aware date-time. + * @param {Integer|number} year the year for the new local date. + * @param {Integer|number} month the month for the new local date. + * @param {Integer|number} day the day for the new local date. + * @param {Integer|number} hour the hour for the new local time. + * @param {Integer|number} minute the minute for the new local time. + * @param {Integer|number} second the second for the new local time. + * @param {Integer|number} nanosecond the nanosecond for the new local time. * @param {string} zoneId the timezone identifier for the new timezone-aware date-time. */ - constructor(localDateTime, zoneId) { - this.localDateTime = localDateTime; + constructor(year, month, day, hour, minute, second, nanosecond, zoneId) { + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.nanosecond = nanosecond; this.zoneId = zoneId; Object.freeze(this); } toString() { - return `${this.localDateTime.toString()}[${this.zoneId}]`; + return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond) + `[${this.zoneId}]`; } } @@ -277,3 +318,7 @@ export function isDateTimeWithZoneId(obj) { function hasIdentifierProperty(obj, property) { return (obj && obj[property]) === true; } + +function localDateTimeToString(year, month, day, hour, minute, second, nanosecond) { + return dateToIsoString(year, month, day) + 'T' + timeToIsoString(hour, minute, second, nanosecond); +} diff --git a/test/internal/temporal-util.test.js b/test/internal/temporal-util.test.js index bed24a32d..68e000985 100644 --- a/test/internal/temporal-util.test.js +++ b/test/internal/temporal-util.test.js @@ -78,11 +78,11 @@ describe('temporal-util', () => { }); it('should convert cypher date to epoch day', () => { - expect(util.dateToEpochDay(date(-13, 12, 31))).toEqual(int(-723912)); - expect(util.dateToEpochDay(date(9, 9, 9))).toEqual(int(-715989)); - expect(util.dateToEpochDay(date(2015, 2, 17))).toEqual(int(16483)); - expect(util.dateToEpochDay(date(2189, 7, 19))).toEqual(int(80188)); - expect(util.dateToEpochDay(date(19999, 9, 28))).toEqual(int(6585227)); + expect(util.dateToEpochDay(-13, 12, 31)).toEqual(int(-723912)); + expect(util.dateToEpochDay(9, 9, 9)).toEqual(int(-715989)); + expect(util.dateToEpochDay(2015, 2, 17)).toEqual(int(16483)); + expect(util.dateToEpochDay(2189, 7, 19)).toEqual(int(80188)); + expect(util.dateToEpochDay(19999, 9, 28)).toEqual(int(6585227)); }); it('should convert epoch second with nano to cypher local date-time', () => { @@ -94,11 +94,11 @@ describe('temporal-util', () => { }); it('should convert cypher local date-time to epoch second', () => { - expect(util.localDateTimeToEpochSecond(localDateTime(1990, 9, 12, 18, 59, 37, 999))).toEqual(int(653165977)); - expect(util.localDateTimeToEpochSecond(localDateTime(-18, 12, 31, 23, 59, 59, 12345))).toEqual(int(-62703676801)); - expect(util.localDateTimeToEpochSecond(localDateTime(1970, 2, 1, 0, 0, 0, 1))).toEqual(int(2678400)); - expect(util.localDateTimeToEpochSecond(localDateTime(99111, 8, 21, 6, 32, 17, 1794673))).toEqual(int(3065493882737)); - expect(util.localDateTimeToEpochSecond(localDateTime(783, 12, 12, 20, 19, 59, 999999111))).toEqual(int(-37428234001)); + expect(util.localDateTimeToEpochSecond(1990, 9, 12, 18, 59, 37, 999)).toEqual(int(653165977)); + expect(util.localDateTimeToEpochSecond(-18, 12, 31, 23, 59, 59, 12345)).toEqual(int(-62703676801)); + expect(util.localDateTimeToEpochSecond(1970, 2, 1, 0, 0, 0, 1)).toEqual(int(2678400)); + expect(util.localDateTimeToEpochSecond(99111, 8, 21, 6, 32, 17, 1794673)).toEqual(int(3065493882737)); + expect(util.localDateTimeToEpochSecond(783, 12, 12, 20, 19, 59, 999999111)).toEqual(int(-37428234001)); }); it('should convert nanosecond of the day to cypher local time', () => { @@ -110,11 +110,11 @@ describe('temporal-util', () => { }); it('should convert cypher local time to nanosecond of the day', () => { - expect(util.localTimeToNanoOfDay(localTime(18, 54, 39, 12399))).toEqual(int(68079000012399)); - expect(util.localTimeToNanoOfDay(localTime(0, 0, 0, 0))).toEqual(int(0)); - expect(util.localTimeToNanoOfDay(localTime(0, 0, 0, 1))).toEqual(int(1)); - expect(util.localTimeToNanoOfDay(localTime(23, 59, 59, 999999999))).toEqual(int(86399999999999)); - expect(util.localTimeToNanoOfDay(localTime(12, 51, 17, 808080))).toEqual(int(46277000808080)); + expect(util.localTimeToNanoOfDay(18, 54, 39, 12399)).toEqual(int(68079000012399)); + expect(util.localTimeToNanoOfDay(0, 0, 0, 0)).toEqual(int(0)); + expect(util.localTimeToNanoOfDay(0, 0, 0, 1)).toEqual(int(1)); + expect(util.localTimeToNanoOfDay(23, 59, 59, 999999999)).toEqual(int(86399999999999)); + expect(util.localTimeToNanoOfDay(12, 51, 17, 808080)).toEqual(int(46277000808080)); }); }); @@ -128,5 +128,5 @@ function localTime(hour, minute, second, nanosecond) { } function localDateTime(year, month, day, hour, minute, second, nanosecond) { - return new LocalDateTime(date(year, month, day), localTime(hour, minute, second, nanosecond)); + return new LocalDateTime(int(year), int(month), int(day), int(hour), int(minute), int(second), int(nanosecond)); } diff --git a/test/types/v1/temporal-types.test.ts b/test/types/v1/temporal-types.test.ts index 95562c15d..9695cac59 100644 --- a/test/types/v1/temporal-types.test.ts +++ b/test/types/v1/temporal-types.test.ts @@ -19,6 +19,7 @@ import { Date, + DateTimeWithZoneId, DateTimeWithZoneOffset, Duration, isDate, @@ -58,13 +59,19 @@ const localTime2Minute1: number = localTime2.minute; const localTime2Second1: number = localTime2.second; const localTime2Nanosecond1: number = localTime2.nanosecond; -const time1: Time = new Time(localTime1, int(1)); -const localTime3: LocalTime = time1.localTime; +const time1: Time = new Time(int(1), int(1), int(1), int(1), int(1)); const offset1: Integer = time1.offsetSeconds; +const hour1: Integer = time1.hour; +const minute1: Integer = time1.minute; +const second1: Integer = time1.second; +const nanosecond1: Integer = time1.nanosecond; -const time2: Time = new Time(localTime2, 1); -const localTime4: LocalTime = time2.localTime; +const time2: Time = new Time(1, 1, 1, 1, 1); const offset2: number = time2.offsetSeconds; +const hour2: number = time2.hour; +const minute2: number = time2.minute; +const second2: number = time2.second; +const nanosecond2: number = time2.nanosecond; const date1: Date = new Date(int(1), int(1), int(1)); const date1Year1: Integer = date1.year; @@ -76,21 +83,63 @@ const date2Year1: number = date2.year; const date2Month1: number = date2.month; const date2Day1: number = date2.day; -const localDateTime1: LocalDateTime = new LocalDateTime(date1, localTime1); -const date3: Date = localDateTime1.localDate; -const localTime5: LocalTime = localDateTime1.localTime; +const localDateTime1: LocalDateTime = new LocalDateTime(int(1), int(1), int(1), int(1), int(1), int(1), int(1)); +const year1: Integer = localDateTime1.year; +const month1: Integer = localDateTime1.month; +const day1: Integer = localDateTime1.day; +const hour3: Integer = localDateTime1.hour; +const minute3: Integer = localDateTime1.minute; +const second3: Integer = localDateTime1.second; +const nanosecond3: Integer = localDateTime1.nanosecond; -const localDateTime2: LocalDateTime = new LocalDateTime(date2, localTime2); -const date4: Date = localDateTime2.localDate; -const localTime6: LocalTime = localDateTime2.localTime; +const localDateTime2: LocalDateTime = new LocalDateTime(1, 1, 1, 1, 1, 1, 1); +const year2: number = localDateTime2.year; +const month2: number = localDateTime2.month; +const day2: number = localDateTime2.day; +const hour4: number = localDateTime2.hour; +const minute4: number = localDateTime2.minute; +const second4: number = localDateTime2.second; +const nanosecond4: number = localDateTime2.nanosecond; -const dateTime1: DateTimeWithZoneOffset = new DateTimeWithZoneOffset(localDateTime1, int(1)); -const localDateTime3: LocalDateTime = dateTime1.localDateTime; +const dateTime1: DateTimeWithZoneOffset = new DateTimeWithZoneOffset(int(1), int(1), int(1), int(1), int(1), int(1), int(1), int(1)); const offset3: Integer = dateTime1.offsetSeconds; +const year3: Integer = dateTime1.year; +const month3: Integer = dateTime1.month; +const day3: Integer = dateTime1.day; +const hour5: Integer = dateTime1.hour; +const minute5: Integer = dateTime1.minute; +const second5: Integer = dateTime1.second; +const nanosecond5: Integer = dateTime1.nanosecond; -const dateTime2: DateTimeWithZoneOffset = new DateTimeWithZoneOffset(localDateTime2, 1); -const localDateTime4: LocalDateTime = dateTime2.localDateTime; +const dateTime2: DateTimeWithZoneOffset = new DateTimeWithZoneOffset(1, 1, 1, 1, 1, 1, 1, 1); const offset4: number = dateTime2.offsetSeconds; +const year4: number = dateTime2.year; +const month4: number = dateTime2.month; +const day4: number = dateTime2.day; +const hour6: number = dateTime2.hour; +const minute6: number = dateTime2.minute; +const second6: number = dateTime2.second; +const nanosecond6: number = dateTime2.nanosecond; + +const dateTime3: DateTimeWithZoneId = new DateTimeWithZoneId(int(1), int(1), int(1), int(1), int(1), int(1), int(1), "UTC"); +const zoneId1: string = dateTime3.zoneId; +const year5: Integer = dateTime3.year; +const month5: Integer = dateTime3.month; +const day5: Integer = dateTime3.day; +const hour7: Integer = dateTime3.hour; +const minute7: Integer = dateTime3.minute; +const second7: Integer = dateTime3.second; +const nanosecond7: Integer = dateTime3.nanosecond; + +const dateTime4: DateTimeWithZoneId = new DateTimeWithZoneId(1, 1, 1, 1, 1, 1, 1, "UTC"); +const zoneId2: string = dateTime4.zoneId; +const year6: number = dateTime4.year; +const month6: number = dateTime4.month; +const day6: number = dateTime4.day; +const hour8: number = dateTime4.hour; +const minute8: number = dateTime4.minute; +const second8: number = dateTime4.second; +const nanosecond8: number = dateTime4.nanosecond; const isDurationValue: boolean = isDuration(duration1); const isLocalTimeValue: boolean = isLocalTime(localTime1); @@ -98,4 +147,4 @@ const isTimeValue: boolean = isTime(time1); const isDateValue: boolean = isDate(date1); const isLocalDateTimeValue: boolean = isLocalDateTime(localDateTime1); const isDateTimeWithZoneOffsetValue: boolean = isDateTimeWithZoneOffset(dateTime1); -const isDateTimeWithZoneIdValue: boolean = isDateTimeWithZoneId(dateTime2); +const isDateTimeWithZoneIdValue: boolean = isDateTimeWithZoneId(dateTime3); diff --git a/test/v1/temporal-types.test.js b/test/v1/temporal-types.test.js index 0c4caf4f2..37cf0ae72 100644 --- a/test/v1/temporal-types.test.js +++ b/test/v1/temporal-types.test.js @@ -370,6 +370,54 @@ describe('temporal-types', () => { expect(dateTimeWithZoneId(248, 12, 30, 23, 59, 59, 3, 'CET').toString()).toEqual('0248-12-30T23:59:59.000000003[CET]'); }); + it('should expose local time components in time', () => { + const offsetTime = time(22, 12, 58, 999111222, 42); + + expect(offsetTime.hour).toEqual(neo4j.int(22)); + expect(offsetTime.minute).toEqual(neo4j.int(12)); + expect(offsetTime.second).toEqual(neo4j.int(58)); + expect(offsetTime.nanosecond).toEqual(neo4j.int(999111222)); + }); + + it('should expose local date and time components in local date-time', () => { + const dateTime = localDateTime(2025, 9, 18, 23, 22, 21, 2020); + + expect(dateTime.year).toEqual(neo4j.int(2025)); + expect(dateTime.month).toEqual(neo4j.int(9)); + expect(dateTime.day).toEqual(neo4j.int(18)); + + expect(dateTime.hour).toEqual(neo4j.int(23)); + expect(dateTime.minute).toEqual(neo4j.int(22)); + expect(dateTime.second).toEqual(neo4j.int(21)); + expect(dateTime.nanosecond).toEqual(neo4j.int(2020)); + }); + + it('should expose local date-time components in date-time with zone offset', () => { + const zonedDateTime = dateTimeWithZoneOffset(1799, 5, 19, 18, 37, 59, 875387, 3600); + + expect(zonedDateTime.year).toEqual(neo4j.int(1799)); + expect(zonedDateTime.month).toEqual(neo4j.int(5)); + expect(zonedDateTime.day).toEqual(neo4j.int(19)); + + expect(zonedDateTime.hour).toEqual(neo4j.int(18)); + expect(zonedDateTime.minute).toEqual(neo4j.int(37)); + expect(zonedDateTime.second).toEqual(neo4j.int(59)); + expect(zonedDateTime.nanosecond).toEqual(neo4j.int(875387)); + }); + + it('should expose local date-time components in date-time with zone ID', () => { + const zonedDateTime = dateTimeWithZoneId(2356, 7, 29, 23, 32, 11, 9346458, 3600, randomZoneId()); + + expect(zonedDateTime.year).toEqual(neo4j.int(2356)); + expect(zonedDateTime.month).toEqual(neo4j.int(7)); + expect(zonedDateTime.day).toEqual(neo4j.int(29)); + + expect(zonedDateTime.hour).toEqual(neo4j.int(23)); + expect(zonedDateTime.minute).toEqual(neo4j.int(32)); + expect(zonedDateTime.second).toEqual(neo4j.int(11)); + expect(zonedDateTime.nanosecond).toEqual(neo4j.int(9346458)); + }); + function testSendAndReceiveRandomTemporalValues(valueGenerator, done) { const asyncFunction = (index, callback) => { const next = () => callback(); @@ -427,25 +475,27 @@ describe('temporal-types', () => { } function randomDateTimeWithZoneOffset() { - return new neo4j.DateTimeWithZoneOffset( - randomDstSafeLocalDateTime(), - randomZoneOffsetSeconds() - ); + const dateTime = randomDstSafeLocalDateTime(); + return new neo4j.DateTimeWithZoneOffset(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.nanosecond, + randomZoneOffsetSeconds()); } function randomDateTimeWithZoneId() { - return new neo4j.DateTimeWithZoneId( - randomDstSafeLocalDateTime(), - randomZoneId() - ); + const dateTime = randomDstSafeLocalDateTime(); + return new neo4j.DateTimeWithZoneId(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.nanosecond, + randomZoneId()); } function randomDstSafeLocalDateTime() { - return new neo4j.LocalDateTime(randomDate(), randomDstSafeLocalTime()); + const date = randomDate(); + const time = randomDstSafeLocalTime(); + return new neo4j.LocalDateTime(date.year, date.month, date.day, time.hour, time.minute, time.second, time.nanosecond); } function randomLocalDateTime() { - return new neo4j.LocalDateTime(randomDate(), randomLocalTime()); + const date = randomDate(); + const time = randomLocalTime(); + return new neo4j.LocalDateTime(date.year, date.month, date.day, time.hour, time.minute, time.second, time.nanosecond); } function randomDate() { @@ -457,10 +507,8 @@ describe('temporal-types', () => { } function randomTime() { - return new neo4j.Time( - randomLocalTime(), - randomZoneOffsetSeconds(), - ); + const localTime = randomLocalTime(); + return new neo4j.Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, randomZoneOffsetSeconds()); } function randomLocalTime() { @@ -499,7 +547,7 @@ describe('temporal-types', () => { } function time(hour, minute, second, nanosecond, offsetSeconds) { - return new neo4j.Time(localTime(hour, minute, second, nanosecond), neo4j.int(offsetSeconds)); + return new neo4j.Time(neo4j.int(hour), neo4j.int(minute), neo4j.int(second), neo4j.int(nanosecond), neo4j.int(offsetSeconds)); } function date(year, month, day) { @@ -507,15 +555,18 @@ describe('temporal-types', () => { } function localDateTime(year, month, day, hour, minute, second, nanosecond) { - return new neo4j.LocalDateTime(date(year, month, day), localTime(hour, minute, second, nanosecond)); + return new neo4j.LocalDateTime(neo4j.int(year), neo4j.int(month), neo4j.int(day), + neo4j.int(hour), neo4j.int(minute), neo4j.int(second), neo4j.int(nanosecond)); } function dateTimeWithZoneOffset(year, month, day, hour, minute, second, nanosecond, offsetSeconds) { - return new neo4j.DateTimeWithZoneOffset(localDateTime(year, month, day, hour, minute, second, nanosecond), neo4j.int(offsetSeconds)); + return new neo4j.DateTimeWithZoneOffset(neo4j.int(year), neo4j.int(month), neo4j.int(day), + neo4j.int(hour), neo4j.int(minute), neo4j.int(second), neo4j.int(nanosecond), neo4j.int(offsetSeconds)); } function dateTimeWithZoneId(year, month, day, hour, minute, second, nanosecond, zoneId) { - return new neo4j.DateTimeWithZoneId(localDateTime(year, month, day, hour, minute, second, nanosecond), zoneId); + return new neo4j.DateTimeWithZoneId(neo4j.int(year), neo4j.int(month), neo4j.int(day), + neo4j.int(hour), neo4j.int(minute), neo4j.int(second), neo4j.int(nanosecond), zoneId); } function randomInt(lower, upper) { diff --git a/types/v1/spatial-types.d.ts b/types/v1/spatial-types.d.ts index 1ec8377eb..c510157d8 100644 --- a/types/v1/spatial-types.d.ts +++ b/types/v1/spatial-types.d.ts @@ -21,10 +21,11 @@ import {NumberOrInteger} from './graph-types'; import Integer from "./integer"; declare class Point { - srid: T; - x: number; - y: number; - z?: number; + + readonly srid: T; + readonly x: number; + readonly y: number; + readonly z?: number; constructor(srid: T, x: number, y: number, z?: number) } diff --git a/types/v1/temporal-types.d.ts b/types/v1/temporal-types.d.ts index dd27cae25..53d4107e3 100644 --- a/types/v1/temporal-types.d.ts +++ b/types/v1/temporal-types.d.ts @@ -21,62 +21,84 @@ import {NumberOrInteger} from './graph-types'; import Integer from "./integer"; declare class Duration { - months: T; - days: T; - seconds: T; - nanoseconds: T; + + readonly months: T; + readonly days: T; + readonly seconds: T; + readonly nanoseconds: T; constructor(months: T, days: T, seconds: T, nanoseconds: T) } declare class LocalTime { - hour: T; - minute: T; - second: T; - nanosecond: T; + + readonly hour: T; + readonly minute: T; + readonly second: T; + readonly nanosecond: T; constructor(hour: T, minute: T, second: T, nanosecond: T); } declare class Time { - localTime: LocalTime; - offsetSeconds: T; + readonly hour: T; + readonly minute: T; + readonly second: T; + readonly nanosecond: T; + readonly offsetSeconds: T; - constructor(localTime: LocalTime, offsetSeconds: T); + constructor(hour: T, minute: T, second: T, nanosecond: T, offsetSeconds: T); } declare class Date { - year: T; - month: T; - day: T; + readonly year: T; + readonly month: T; + readonly day: T; constructor(year: T, month: T, day: T); } declare class LocalDateTime { - localDate: Date; - localTime: LocalTime; + readonly year: T; + readonly month: T; + readonly day: T; + readonly hour: T; + readonly minute: T; + readonly second: T; + readonly nanosecond: T; - constructor(localDate: Date, localTime: LocalTime); + constructor(year: T, month: T, day: T, hour: T, minute: T, second: T, nanosecond: T); } declare class DateTimeWithZoneOffset { - localDateTime: LocalDateTime; - offsetSeconds: T; + readonly year: T; + readonly month: T; + readonly day: T; + readonly hour: T; + readonly minute: T; + readonly second: T; + readonly nanosecond: T; + readonly offsetSeconds: T; - constructor(localDateTime: LocalDateTime, offsetSeconds: T); + constructor(year: T, month: T, day: T, hour: T, minute: T, second: T, nanosecond: T, offsetSeconds: T); } declare class DateTimeWithZoneId { - localDateTime: LocalDateTime; - zoneId: string; + readonly year: T; + readonly month: T; + readonly day: T; + readonly hour: T; + readonly minute: T; + readonly second: T; + readonly nanosecond: T; + readonly zoneId: string; - constructor(localDateTime: LocalDateTime, zoneId: string); + constructor(year: T, month: T, day: T, hour: T, minute: T, second: T, nanosecond: T, zoneId: string); } declare function isDuration(obj: object): boolean; From f32f677301d2cbd755b464bfed67db4a298e1a6e Mon Sep 17 00:00:00 2001 From: lutovich Date: Thu, 5 Apr 2018 18:12:28 +0200 Subject: [PATCH 3/3] ITs for arrays of temporal types --- test/v1/temporal-types.test.js | 99 ++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/test/v1/temporal-types.test.js b/test/v1/temporal-types.test.js index 37cf0ae72..83095a6ad 100644 --- a/test/v1/temporal-types.test.js +++ b/test/v1/temporal-types.test.js @@ -24,6 +24,8 @@ import timesSeries from 'async/timesSeries'; import _ from 'lodash'; const RANDOM_VALUES_TO_TEST = 2000; +const MIN_TEMPORAL_ARRAY_LENGTH = 20; +const MAX_TEMPORAL_ARRAY_LENGTH = 1000; const MAX_NANO_OF_SECOND = 999999999; const MAX_YEAR = 999999999; const MIN_YEAR = -MAX_YEAR; @@ -91,17 +93,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => { - const sign = _.sample([true, false]) ? 1 : -1; // duration can be negative - return duration( - sign * _.random(0, Number.MAX_SAFE_INTEGER), - sign * _.random(0, Number.MAX_SAFE_INTEGER), - sign * _.random(0, Number.MAX_SAFE_INTEGER), - sign * _.random(0, MAX_NANO_OF_SECOND), - ); - }; + testSendAndReceiveRandomTemporalValues(() => randomDuration(), done); + }); + + it('should send and receive array of Duration', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + testSendAndReceiveArrayOfRandomTemporalValues(() => randomDuration(), done); }); it('should receive LocalTime', done => { @@ -136,9 +136,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomLocalTime(); + testSendAndReceiveRandomTemporalValues(() => randomLocalTime(), done); + }); + + it('should send and receive array of LocalTime', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + testSendAndReceiveArrayOfRandomTemporalValues(() => randomLocalTime(), done); }); it('should receive Time', done => { @@ -173,9 +179,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomTime(); + testSendAndReceiveRandomTemporalValues(() => randomTime(), done); + }); + + it('should send and receive array of Time', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + testSendAndReceiveArrayOfRandomTemporalValues(() => randomTime(), done); }); it('should receive Date', done => { @@ -210,9 +222,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomDate(); + testSendAndReceiveRandomTemporalValues(() => randomDate(), done); + }); - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + it('should send and receive array of Date', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } + + testSendAndReceiveArrayOfRandomTemporalValues(() => randomDate(), done); }); it('should receive LocalDateTime', done => { @@ -247,9 +265,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomLocalDateTime(); + testSendAndReceiveRandomTemporalValues(() => randomLocalDateTime(), done); + }); + + it('should send and receive random LocalDateTime', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + testSendAndReceiveArrayOfRandomTemporalValues(() => randomLocalDateTime(), done); }); it('should receive DateTimeWithZoneOffset', done => { @@ -284,9 +308,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomDateTimeWithZoneOffset(); + testSendAndReceiveRandomTemporalValues(() => randomDateTimeWithZoneOffset(), done); + }); - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + it('should send and receive array of DateTimeWithZoneOffset', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } + + testSendAndReceiveArrayOfRandomTemporalValues(() => randomDateTimeWithZoneOffset(), done); }); it('should receive DateTimeWithZoneId', done => { @@ -321,9 +351,15 @@ describe('temporal-types', () => { return; } - const valueGenerator = () => randomDateTimeWithZoneId(); + testSendAndReceiveRandomTemporalValues(() => randomDateTimeWithZoneId(), done); + }); + + it('should send and receive array of DateTimeWithZoneId', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } - testSendAndReceiveRandomTemporalValues(valueGenerator, done); + testSendAndReceiveArrayOfRandomTemporalValues(() => randomDateTimeWithZoneId(), done); }); it('should convert Duration to ISO string', () => { @@ -436,6 +472,15 @@ describe('temporal-types', () => { timesSeries(RANDOM_VALUES_TO_TEST, asyncFunction, doneFunction); } + function testSendAndReceiveArrayOfRandomTemporalValues(valueGenerator, done) { + const arrayLength = _.random(MIN_TEMPORAL_ARRAY_LENGTH, MAX_TEMPORAL_ARRAY_LENGTH); + const values = _.range(arrayLength).map(() => valueGenerator()); + + console.log('Generated: ' + values); + + testSendReceiveTemporalValue(values, done); + } + function testReceiveTemporalValue(query, expectedValue, done) { session.run(query).then(result => { const records = result.records; @@ -474,6 +519,16 @@ describe('temporal-types', () => { return false; } + function randomDuration() { + const sign = _.sample([true, false]) ? 1 : -1; // duration can be negative + return duration( + sign * _.random(0, Number.MAX_SAFE_INTEGER), + sign * _.random(0, Number.MAX_SAFE_INTEGER), + sign * _.random(0, Number.MAX_SAFE_INTEGER), + sign * _.random(0, MAX_NANO_OF_SECOND), + ); + } + function randomDateTimeWithZoneOffset() { const dateTime = randomDstSafeLocalDateTime(); return new neo4j.DateTimeWithZoneOffset(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.nanosecond,