diff --git a/src/v1/internal/temporal-util.js b/src/v1/internal/temporal-util.js index ca6a63bfa..291878ae2 100644 --- a/src/v1/internal/temporal-util.js +++ b/src/v1/internal/temporal-util.js @@ -17,7 +17,7 @@ * limitations under the License. */ -import {int} from '../integer'; +import {int, isInt} from '../integer'; import {Date, LocalDateTime, LocalTime} from '../temporal-types'; /* @@ -35,6 +35,7 @@ const MINUTES_PER_HOUR = 60; const SECONDS_PER_MINUTE = 60; const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; const NANOS_PER_SECOND = 1000000000; +const NANOS_PER_MILLISECOND = 1000000; const NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE; const NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR; const DAYS_0000_TO_1970 = 719528; @@ -264,6 +265,27 @@ export function dateToIsoString(year, month, day) { return `${yearString}-${monthString}-${dayString}`; } +/** + * Get the total number of nanoseconds from the milliseconds of the given standard JavaScript date and optional nanosecond part. + * @param {global.Date} standardDate the standard JavaScript date. + * @param {Integer|number|undefined} nanoseconds the optional number of nanoseconds. + * @return {Integer|number} the total amount of nanoseconds. + */ +export function totalNanoseconds(standardDate, nanoseconds) { + nanoseconds = (nanoseconds || 0); + const nanosFromMillis = standardDate.getMilliseconds() * NANOS_PER_MILLISECOND; + return isInt(nanoseconds) ? nanoseconds.add(nanosFromMillis) : nanoseconds + nanosFromMillis; +} + +/** + * Get the time zone offset in seconds from the given standard JavaScript date. + * @param {global.Date} standardDate the standard JavaScript date. + * @return {number} the time zone offset in seconds. + */ +export function timeZoneOffsetInSeconds(standardDate) { + return standardDate.getTimezoneOffset() * SECONDS_PER_MINUTE; +} + /** * Converts given local time into a single integer representing this same time in seconds of the day. Nanoseconds are skipped. * @param {Integer|number|string} hour the hour of the local time. diff --git a/src/v1/internal/util.js b/src/v1/internal/util.js index 437be20be..aa858f87c 100644 --- a/src/v1/internal/util.js +++ b/src/v1/internal/util.js @@ -87,6 +87,16 @@ function assertNumberOrInteger(obj, objName) { return obj; } +function assertValidDate(obj, objName) { + if (Object.prototype.toString.call(obj) !== '[object Date]') { + throw new TypeError(objName + ' expected to be a standard JavaScript Date but was: ' + JSON.stringify(obj)); + } + if (Number.isNaN(obj.getTime())) { + throw new TypeError(objName + ' expected to be valid JavaScript Date but its time was NaN: ' + JSON.stringify(obj)); + } + return obj; +} + function assertCypherStatement(obj) { assertString(obj, 'Cypher statement'); if (obj.trim().length === 0) { @@ -112,6 +122,7 @@ export { assertString, assertNumber, assertNumberOrInteger, + assertValidDate, validateStatementAndParameters, ENCRYPTION_ON, ENCRYPTION_OFF diff --git a/src/v1/temporal-types.js b/src/v1/temporal-types.js index 0ca11b7df..7f77deb15 100644 --- a/src/v1/temporal-types.js +++ b/src/v1/temporal-types.js @@ -18,7 +18,7 @@ */ import * as util from './internal/temporal-util'; -import {assertNumberOrInteger, assertString} from './internal/util'; +import {assertNumberOrInteger, assertString, assertValidDate} from './internal/util'; import {newError} from './error'; const IDENTIFIER_PROPERTY_ATTRIBUTES = { @@ -94,6 +94,23 @@ export class LocalTime { Object.freeze(this); } + /** + * Create a local time object from the given standard JavaScript Date and optional nanoseconds. + * Year, month, day and time zone offset components of the given date are ignored. + * @param {global.Date} standardDate the standard JavaScript date to convert. + * @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds. + * @return {LocalTime} new local time. + */ + static fromStandardDate(standardDate, nanosecond) { + verifyStandardDateAndNanos(standardDate, nanosecond); + + return new LocalTime( + standardDate.getHours(), + standardDate.getMinutes(), + standardDate.getSeconds(), + util.totalNanoseconds(standardDate, nanosecond)); + } + toString() { return util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond); } @@ -133,6 +150,24 @@ export class Time { Object.freeze(this); } + /** + * Create a time object from the given standard JavaScript Date and optional nanoseconds. + * Year, month and day components of the given date are ignored. + * @param {global.Date} standardDate the standard JavaScript date to convert. + * @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds. + * @return {Time} new time. + */ + static fromStandardDate(standardDate, nanosecond) { + verifyStandardDateAndNanos(standardDate, nanosecond); + + return new Time( + standardDate.getHours(), + standardDate.getMinutes(), + standardDate.getSeconds(), + util.totalNanoseconds(standardDate, nanosecond), + util.timeZoneOffsetInSeconds(standardDate)); + } + toString() { return util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond) + util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds); } @@ -168,6 +203,21 @@ export class Date { Object.freeze(this); } + /** + * Create a date object from the given standard JavaScript Date. + * Hour, minute, second, millisecond and time zone offset components of the given date are ignored. + * @param {global.Date} standardDate the standard JavaScript date to convert. + * @return {Date} new date. + */ + static fromStandardDate(standardDate) { + verifyStandardDateAndNanos(standardDate, null); + + return new Date( + standardDate.getFullYear(), + standardDate.getMonth() + 1, + standardDate.getDate()); + } + toString() { return util.dateToIsoString(this.year, this.month, this.day); } @@ -211,6 +261,26 @@ export class LocalDateTime { Object.freeze(this); } + /** + * Create a local date-time object from the given standard JavaScript Date and optional nanoseconds. + * Time zone offset component of the given date is ignored. + * @param {global.Date} standardDate the standard JavaScript date to convert. + * @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds. + * @return {LocalDateTime} new local date-time. + */ + static fromStandardDate(standardDate, nanosecond) { + verifyStandardDateAndNanos(standardDate, nanosecond); + + return new LocalDateTime( + standardDate.getFullYear(), + standardDate.getMonth() + 1, + standardDate.getDate(), + standardDate.getHours(), + standardDate.getMinutes(), + standardDate.getSeconds(), + util.totalNanoseconds(standardDate, nanosecond)); + } + toString() { return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond); } @@ -261,6 +331,27 @@ export class DateTime { Object.freeze(this); } + /** + * Create a date-time object from the given standard JavaScript Date and optional nanoseconds. + * @param {global.Date} standardDate the standard JavaScript date to convert. + * @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds. + * @return {DateTime} new date-time. + */ + static fromStandardDate(standardDate, nanosecond) { + verifyStandardDateAndNanos(standardDate, nanosecond); + + return new DateTime( + standardDate.getFullYear(), + standardDate.getMonth() + 1, + standardDate.getDate(), + standardDate.getHours(), + standardDate.getMinutes(), + standardDate.getSeconds(), + util.totalNanoseconds(standardDate, nanosecond), + util.timeZoneOffsetInSeconds(standardDate), + null /* no time zone id */); + } + toString() { const localDateTimeStr = localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond); const timeZoneStr = this.timeZoneId ? `[${this.timeZoneId}]` : util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds); @@ -303,3 +394,10 @@ function verifyTimeZoneArguments(timeZoneOffsetSeconds, timeZoneId) { throw newError(`Unable to create DateTime without either time zone offset or id. Please specify either of them. Given offset: ${timeZoneOffsetSeconds} and id: ${timeZoneId}`); } } + +function verifyStandardDateAndNanos(standardDate, nanosecond) { + assertValidDate(standardDate, 'Standard date'); + if (nanosecond !== null && nanosecond !== undefined) { + assertNumberOrInteger(nanosecond, 'Nanosecond'); + } +} diff --git a/test/internal/temporal-util.test.js b/test/internal/temporal-util.test.js index c9108cee1..f0d05c387 100644 --- a/test/internal/temporal-util.test.js +++ b/test/internal/temporal-util.test.js @@ -169,6 +169,32 @@ describe('temporal-util', () => { expect(util.localTimeToNanoOfDay(12, 51, 17, 808080)).toEqual(int(46277000808080)); }); + it('should get total nanoseconds from standard date', () => { + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0))).toEqual(0); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1))).toEqual(1000000); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 23))).toEqual(23000000); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999))).toEqual(999000000); + + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 0)).toEqual(0); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 1)).toEqual(1); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 999)).toEqual(999); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1), 999)).toEqual(1000999); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999), 111)).toEqual(999000111); + + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(0))).toEqual(int(0)); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(1))).toEqual(int(1)); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(999))).toEqual(int(999)); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1), int(999))).toEqual(int(1000999)); + expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999), int(111))).toEqual(int(999000111)); + }); + + it('should get timezone offset in seconds from standard date', () => { + expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(0))).toEqual(0); + expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(2))).toEqual(120); + expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(10))).toEqual(600); + expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(101))).toEqual(6060); + }); + }); function date(year, month, day) { @@ -182,3 +208,9 @@ function localTime(hour, minute, second, nanosecond) { function localDateTime(year, month, day, hour, minute, second, nanosecond) { return new types.LocalDateTime(int(year), int(month), int(day), int(hour), int(minute), int(second), int(nanosecond)); } + +function fakeStandardDateWithOffset(offsetMinutes) { + const date = new Date(); + date.getTimezoneOffset = () => offsetMinutes; + return date; +} diff --git a/test/internal/util.test.js b/test/internal/util.test.js index 58cc873d6..6fc420ca9 100644 --- a/test/internal/util.test.js +++ b/test/internal/util.test.js @@ -135,6 +135,23 @@ describe('util', () => { verifyInvalidNumberOrInteger({value: 42}); }); + it('should check dates', () => { + verifyValidDate(new Date()); + verifyValidDate(new Date(0)); + verifyValidDate(new Date(-1)); + verifyValidDate(new Date(2000, 10, 10)); + verifyValidDate(new Date(2000, 10, 10, 10, 10, 10, 10)); + + verifyInvalidDate(new Date('not a valid date')); + verifyInvalidDate(new Date({})); + verifyInvalidDate(new Date([])); + + verifyInvalidDate({}); + verifyInvalidDate([]); + verifyInvalidDate('2007-04-05T12:30-02:00'); + verifyInvalidDate(2019); + }); + function verifyValidString(str) { expect(util.assertString(str, 'Test string')).toBe(str); } @@ -171,4 +188,12 @@ describe('util', () => { expect(() => util.validateStatementAndParameters('RETURN 1', obj)).toThrowError(TypeError); } + function verifyValidDate(obj) { + expect(util.assertValidDate(obj, 'Test date')).toBe(obj); + } + + function verifyInvalidDate(obj) { + expect(() => util.assertValidDate(obj, 'Test date')).toThrowError(TypeError); + } + }); diff --git a/test/types/v1/temporal-types.test.ts b/test/types/v1/temporal-types.test.ts index 996b73208..30e000b8c 100644 --- a/test/types/v1/temporal-types.test.ts +++ b/test/types/v1/temporal-types.test.ts @@ -32,6 +32,7 @@ import { Time } from "../../../types/v1/temporal-types"; import Integer, {int} from "../../../types/v1/integer"; +import {StandardDate} from "../../../types/v1/graph-types"; const duration1: Duration = new Duration(int(1), int(1), int(1), int(1)); const months1: Integer = duration1.months; @@ -149,3 +150,15 @@ const isTimeValue: boolean = isTime(time1); const isDateValue: boolean = isDate(date1); const isLocalDateTimeValue: boolean = isLocalDateTime(localDateTime1); const isDateTimeValue: boolean = isDateTime(dateTime1); + +const dummy: any = null; +const standardDate: StandardDate = dummy; +const localTime3: LocalTime = LocalTime.fromStandardDate(standardDate); +const localTime4: LocalTime = LocalTime.fromStandardDate(standardDate, 42); +const time3: Time = Time.fromStandardDate(standardDate); +const time4: Time = Time.fromStandardDate(standardDate, 42); +const date3: Date = Date.fromStandardDate(standardDate); +const localDateTime3: LocalDateTime = LocalDateTime.fromStandardDate(standardDate); +const localDateTime4: LocalDateTime = LocalDateTime.fromStandardDate(standardDate, 42); +const dateTime5: DateTime = DateTime.fromStandardDate(standardDate); +const dateTime6: DateTime = DateTime.fromStandardDate(standardDate, 42); diff --git a/test/v1/temporal-types.test.js b/test/v1/temporal-types.test.js index cbc926f12..eea25e741 100644 --- a/test/v1/temporal-types.test.js +++ b/test/v1/temporal-types.test.js @@ -19,6 +19,7 @@ import neo4j from '../../src'; import sharedNeo4j from '../internal/shared-neo4j'; +import {totalNanoseconds} from '../../src/v1/internal/temporal-util'; import {ServerVersion, VERSION_3_4_0} from '../../src/v1/internal/server-version'; import timesSeries from 'async/timesSeries'; import _ from 'lodash'; @@ -658,6 +659,194 @@ describe('temporal-types', () => { expect(() => new neo4j.types.DateTime(1, 2, 3, 4, 5, 6, 7, 8, 'UK')).toThrow(); }); + it('should convert standard Date to neo4j LocalTime', () => { + testStandardDateToLocalTimeConversion(new Date(2000, 1, 1, 0, 0, 0, 0)); + testStandardDateToLocalTimeConversion(new Date(1456, 7, 12, 12, 0, 0, 0)); + testStandardDateToLocalTimeConversion(new Date(2121, 11, 27, 21, 56, 0, 0)); + testStandardDateToLocalTimeConversion(new Date(1392, 2, 2, 3, 14, 59, 0)); + testStandardDateToLocalTimeConversion(new Date(1102, 6, 5, 17, 12, 32, 99)); + testStandardDateToLocalTimeConversion(new Date(2019, 2, 7, 0, 0, 0, 1)); + + testStandardDateToLocalTimeConversion(new Date(1351, 4, 7, 0, 0, 0, 0), neo4j.int(1)); + testStandardDateToLocalTimeConversion(new Date(3841, 1, 19, 0, 0, 0, 0), neo4j.int(99)); + testStandardDateToLocalTimeConversion(new Date(2222, 3, 29, 0, 0, 0, 0), neo4j.int(999999999)); + }); + + it('should fail to convert invalid standard Date to neo4j LocalTime', () => { + const LocalTime = neo4j.types.LocalTime; + + expect(() => LocalTime.fromStandardDate()).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate('2007-04-05T12:30-02:00')).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate({})).toThrowError(TypeError); + + expect(() => LocalTime.fromStandardDate(new Date({}))).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate(new Date([]))).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate(new Date(NaN))).toThrowError(TypeError); + + expect(() => LocalTime.fromStandardDate(new Date(), '1')).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate(new Date(), {nanosecond: 1})).toThrowError(TypeError); + expect(() => LocalTime.fromStandardDate(new Date(), [1])).toThrowError(TypeError); + }); + + it('should convert standard Date to neo4j Time', () => { + testStandardDateToTimeConversion(new Date(2000, 1, 1, 0, 0, 0, 0)); + testStandardDateToTimeConversion(new Date(1456, 7, 12, 12, 0, 0, 0)); + testStandardDateToTimeConversion(new Date(2121, 11, 27, 21, 56, 0, 0)); + testStandardDateToTimeConversion(new Date(1392, 2, 2, 3, 14, 59, 0)); + testStandardDateToTimeConversion(new Date(1102, 6, 5, 17, 12, 32, 99)); + testStandardDateToTimeConversion(new Date(2019, 2, 7, 0, 0, 0, 1)); + + testStandardDateToTimeConversion(new Date(1351, 4, 7, 0, 0, 0, 0), neo4j.int(1)); + testStandardDateToTimeConversion(new Date(3841, 1, 19, 0, 0, 0, 0), neo4j.int(99)); + testStandardDateToTimeConversion(new Date(2222, 3, 29, 0, 0, 0, 0), neo4j.int(999999999)); + }); + + it('should fail to convert invalid standard Date to neo4j Time', () => { + const Time = neo4j.types.Time; + + expect(() => Time.fromStandardDate()).toThrowError(TypeError); + expect(() => Time.fromStandardDate('2007-04-05T12:30-02:00')).toThrowError(TypeError); + expect(() => Time.fromStandardDate({})).toThrowError(TypeError); + + expect(() => Time.fromStandardDate(new Date({}))).toThrowError(TypeError); + expect(() => Time.fromStandardDate(new Date([]))).toThrowError(TypeError); + expect(() => Time.fromStandardDate(new Date(NaN))).toThrowError(TypeError); + + expect(() => Time.fromStandardDate(new Date(), '1')).toThrowError(TypeError); + expect(() => Time.fromStandardDate(new Date(), {nanosecond: 1})).toThrowError(TypeError); + expect(() => Time.fromStandardDate(new Date(), [1])).toThrowError(TypeError); + }); + + it('should convert standard Date to neo4j Date', () => { + testStandardDateToNeo4jDateConversion(new Date(2000, 1, 1)); + testStandardDateToNeo4jDateConversion(new Date(1456, 7, 12)); + testStandardDateToNeo4jDateConversion(new Date(2121, 11, 27)); + testStandardDateToNeo4jDateConversion(new Date(1392, 2, 2)); + testStandardDateToNeo4jDateConversion(new Date(1102, 6, 5)); + testStandardDateToNeo4jDateConversion(new Date(2019, 2, 7)); + + testStandardDateToNeo4jDateConversion(new Date(1351, 4, 7)); + testStandardDateToNeo4jDateConversion(new Date(3841, 1, 19)); + testStandardDateToNeo4jDateConversion(new Date(2222, 3, 29)); + + testStandardDateToNeo4jDateConversion(new Date(1567, 0, 29)); + }); + + it('should fail to convert invalid standard Date to neo4j Date', () => { + const Neo4jDate = neo4j.types.Date; + + expect(() => Neo4jDate.fromStandardDate()).toThrowError(TypeError); + expect(() => Neo4jDate.fromStandardDate('2007-04-05T12:30-02:00')).toThrowError(TypeError); + expect(() => Neo4jDate.fromStandardDate({})).toThrowError(TypeError); + + expect(() => Neo4jDate.fromStandardDate(new Date({}))).toThrowError(TypeError); + expect(() => Neo4jDate.fromStandardDate(new Date([]))).toThrowError(TypeError); + expect(() => Neo4jDate.fromStandardDate(new Date(NaN))).toThrowError(TypeError); + }); + + it('should convert standard Date to neo4j LocalDateTime', () => { + testStandardDateToLocalDateTimeConversion(new Date(2011, 9, 18)); + testStandardDateToLocalDateTimeConversion(new Date(1455, 0, 1)); + testStandardDateToLocalDateTimeConversion(new Date(0)); + testStandardDateToLocalDateTimeConversion(new Date(2056, 5, 22, 21, 59, 12, 999)); + + testStandardDateToLocalDateTimeConversion(new Date(0), 1); + testStandardDateToLocalDateTimeConversion(new Date(0), 999999999); + testStandardDateToLocalDateTimeConversion(new Date(1922, 1, 22, 23, 23, 45, 123), 456789); + + testStandardDateToLocalDateTimeConversion(new Date(1999, 1, 1, 10, 10, 10), neo4j.int(999)); + + testStandardDateToLocalDateTimeConversion(new Date(2192, 0, 17, 20, 30, 40)); + testStandardDateToLocalDateTimeConversion(new Date(2239, 0, 9, 1, 2, 3), 4); + }); + + it('should fail to convert invalid standard Date to neo4j LocalDateTime', () => { + const LocalDateTime = neo4j.types.LocalDateTime; + + expect(() => LocalDateTime.fromStandardDate()).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate('2007-04-05T12:30-02:00')).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate({})).toThrowError(TypeError); + + expect(() => LocalDateTime.fromStandardDate(new Date({}))).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate(new Date([]))).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate(new Date(NaN))).toThrowError(TypeError); + + expect(() => LocalDateTime.fromStandardDate(new Date(), '1')).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate(new Date(), {nanosecond: 1})).toThrowError(TypeError); + expect(() => LocalDateTime.fromStandardDate(new Date(), [1])).toThrowError(TypeError); + }); + + it('should convert standard Date to neo4j DateTime', () => { + testStandardDateToDateTimeConversion(new Date(2011, 9, 18)); + testStandardDateToDateTimeConversion(new Date(1455, 0, 1)); + testStandardDateToDateTimeConversion(new Date(0)); + testStandardDateToDateTimeConversion(new Date(2056, 5, 22, 21, 59, 12, 999)); + + testStandardDateToDateTimeConversion(new Date(0), 1); + testStandardDateToDateTimeConversion(new Date(0), 999999999); + + testStandardDateToDateTimeConversion(new Date(1922, 1, 22, 23, 23, 45, 123), 456789); + testStandardDateToDateTimeConversion(new Date(1999, 1, 1, 10, 10, 10), neo4j.int(999)); + + testStandardDateToDateTimeConversion(new Date(1899, 0, 7, 7, 7, 7, 7)); + testStandardDateToDateTimeConversion(new Date(2005, 0, 1, 2, 3, 4, 5), 100); + }); + + it('should fail to convert invalid standard Date to neo4j DateTime', () => { + const DateTime = neo4j.types.DateTime; + + expect(() => DateTime.fromStandardDate()).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate('2007-04-05T12:30-02:00')).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate({})).toThrowError(TypeError); + + expect(() => DateTime.fromStandardDate(new Date({}))).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate(new Date([]))).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate(new Date(NaN))).toThrowError(TypeError); + + expect(() => DateTime.fromStandardDate(new Date(), '1')).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate(new Date(), {nanosecond: 1})).toThrowError(TypeError); + expect(() => DateTime.fromStandardDate(new Date(), [1])).toThrowError(TypeError); + }); + + it('should send and receive neo4j Date created from standard Date with zero month', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } + + // return numbers and not integers to simplify the equality comparison + session = driverWithNativeNumbers.session(); + + const standardDate = new Date(2000, 0, 1); + const neo4jDate = neo4j.types.Date.fromStandardDate(standardDate); + testSendReceiveTemporalValue(neo4jDate, done); + }); + + it('should send and receive neo4j LocalDateTime created from standard Date with zero month', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } + + // return numbers and not integers to simplify the equality comparison + session = driverWithNativeNumbers.session(); + + const standardDate = new Date(2121, 0, 7, 10, 20, 30, 40); + const neo4jLocalDateTime = neo4j.types.LocalDateTime.fromStandardDate(standardDate); + testSendReceiveTemporalValue(neo4jLocalDateTime, done); + }); + + it('should send and receive neo4j DateTime created from standard Date with zero month', done => { + if (neo4jDoesNotSupportTemporalTypes(done)) { + return; + } + + // return numbers and not integers to simplify the equality comparison + session = driverWithNativeNumbers.session(); + + const standardDate = new Date(1756, 0, 29, 23, 15, 59, 12); + const neo4jDateTime = neo4j.types.DateTime.fromStandardDate(standardDate); + testSendReceiveTemporalValue(neo4jDateTime, done); + }); + function testSendAndReceiveRandomTemporalValues(valueGenerator, done) { const asyncFunction = (index, callback) => { const next = () => callback(); @@ -844,4 +1033,37 @@ describe('temporal-types', () => { function randomInt(lower, upper) { return neo4j.int(_.random(lower, upper)); } + + function testStandardDateToLocalTimeConversion(date, nanosecond) { + const converted = neo4j.types.LocalTime.fromStandardDate(date, nanosecond); + const expected = new neo4j.types.LocalTime(date.getHours(), date.getMinutes(), date.getSeconds(), totalNanoseconds(date, nanosecond)); + expect(converted).toEqual(expected); + } + + function testStandardDateToTimeConversion(date, nanosecond) { + const converted = neo4j.types.Time.fromStandardDate(date, nanosecond); + const expected = new neo4j.types.Time(date.getHours(), date.getMinutes(), date.getSeconds(), totalNanoseconds(date, nanosecond), + date.getTimezoneOffset() * 60); + expect(converted).toEqual(expected); + } + + function testStandardDateToNeo4jDateConversion(date) { + const converted = neo4j.types.Date.fromStandardDate(date); + const expected = new neo4j.types.Date(date.getFullYear(), date.getMonth() + 1, date.getDate()); + expect(converted).toEqual(expected); + } + + function testStandardDateToLocalDateTimeConversion(date, nanosecond) { + const converted = neo4j.types.LocalDateTime.fromStandardDate(date, nanosecond); + const expected = new neo4j.types.LocalDateTime(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), + date.getSeconds(), totalNanoseconds(date, nanosecond)); + expect(converted).toEqual(expected); + } + + function testStandardDateToDateTimeConversion(date, nanosecond) { + const converted = neo4j.types.DateTime.fromStandardDate(date, nanosecond); + const expected = new neo4j.types.DateTime(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), + totalNanoseconds(date, nanosecond), date.getTimezoneOffset() * 60); + expect(converted).toEqual(expected); + } }); diff --git a/types/v1/graph-types.d.ts b/types/v1/graph-types.d.ts index 333f5044b..58f2f5792 100644 --- a/types/v1/graph-types.d.ts +++ b/types/v1/graph-types.d.ts @@ -19,6 +19,7 @@ import Integer from "./integer"; +declare type StandardDate = Date; declare type NumberOrInteger = number | Integer; declare class Node { @@ -90,5 +91,6 @@ export { UnboundRelationship, Path, PathSegment, - NumberOrInteger + NumberOrInteger, + StandardDate } diff --git a/types/v1/temporal-types.d.ts b/types/v1/temporal-types.d.ts index f8760f965..c9af663f1 100644 --- a/types/v1/temporal-types.d.ts +++ b/types/v1/temporal-types.d.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import {NumberOrInteger} from './graph-types'; +import {NumberOrInteger, StandardDate} from './graph-types'; import Integer from "./integer"; declare class Duration { @@ -38,6 +38,8 @@ declare class LocalTime { readonly nanosecond: T; constructor(hour: T, minute: T, second: T, nanosecond: T); + + static fromStandardDate(standardDate: StandardDate, nanosecond?: number): LocalTime; } declare class Time { @@ -49,6 +51,8 @@ declare class Time { readonly timeZoneOffsetSeconds: T; constructor(hour: T, minute: T, second: T, nanosecond: T, timeZoneOffsetSeconds: T); + + static fromStandardDate(standardDate: StandardDate, nanosecond?: number): Time; } declare class Date { @@ -58,6 +62,8 @@ declare class Date { readonly day: T; constructor(year: T, month: T, day: T); + + static fromStandardDate(standardDate: StandardDate): Date; } declare class LocalDateTime { @@ -71,6 +77,8 @@ declare class LocalDateTime { readonly nanosecond: T; constructor(year: T, month: T, day: T, hour: T, minute: T, second: T, nanosecond: T); + + static fromStandardDate(standardDate: StandardDate, nanosecond?: number): LocalDateTime; } declare class DateTime { @@ -86,6 +94,8 @@ declare class DateTime { readonly timeZoneId?: string; constructor(year: T, month: T, day: T, hour: T, minute: T, second: T, nanosecond: T, timeZoneOffsetSeconds?: T, timeZoneId?: string); + + static fromStandardDate(standardDate: StandardDate, nanosecond?: number): DateTime; } declare function isDuration(obj: object): boolean;