diff --git a/src/v1/internal/temporal-util.js b/src/v1/internal/temporal-util.js index ca6a63bfa..f94a642e6 100644 --- a/src/v1/internal/temporal-util.js +++ b/src/v1/internal/temporal-util.js @@ -17,8 +17,10 @@ * limitations under the License. */ -import {int} from '../integer'; +import {int, isInt} from '../integer'; import {Date, LocalDateTime, LocalTime} from '../temporal-types'; +import {assertNumberOrInteger} from './util'; +import {newError} from '../error'; /* Code in this util should be compatible with code in the database that uses JSR-310 java.time APIs. @@ -31,10 +33,41 @@ import {Date, LocalDateTime, LocalTime} from '../temporal-types'; conversion functions. */ +class ValueRange { + + constructor(min, max) { + this._minNumber = min; + this._maxNumber = max; + this._minInteger = int(min); + this._maxInteger = int(max); + } + + contains(value) { + if (isInt(value)) { + return value.greaterThanOrEqual(this._minInteger) && value.lessThanOrEqual(this._maxInteger); + } else { + return value >= this._minNumber && value <= this._maxNumber; + } + } + + toString() { + return `[${this._minNumber}, ${this._maxNumber}]`; + } +} + +const YEAR_RANGE = new ValueRange(-999999999, 999999999); +const MONTH_OF_YEAR_RANGE = new ValueRange(1, 12); +const DAY_OF_MONTH_RANGE = new ValueRange(1, 31); +const HOUR_OF_DAY_RANGE = new ValueRange(0, 23); +const MINUTE_OF_HOUR_RANGE = new ValueRange(0, 59); +const SECOND_OF_MINUTE_RANGE = new ValueRange(0, 59); +const NANOSECOND_OF_SECOND_RANGE = new ValueRange(0, 999999999); + 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 +297,105 @@ 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; +} + +/** + * Assert that the year value is valid. + * @param {Integer|number} year the value to check. + * @return {Integer|number} the value of the year if it is valid. Exception is thrown otherwise. + */ +export function assertValidYear(year) { + return assertValidTemporalValue(year, YEAR_RANGE, 'Year'); +} + +/** + * Assert that the month value is valid. + * @param {Integer|number} month the value to check. + * @return {Integer|number} the value of the month if it is valid. Exception is thrown otherwise. + */ +export function assertValidMonth(month) { + return assertValidTemporalValue(month, MONTH_OF_YEAR_RANGE, 'Month'); +} + +/** + * Assert that the day value is valid. + * @param {Integer|number} day the value to check. + * @return {Integer|number} the value of the day if it is valid. Exception is thrown otherwise. + */ +export function assertValidDay(day) { + return assertValidTemporalValue(day, DAY_OF_MONTH_RANGE, 'Day'); +} + +/** + * Assert that the hour value is valid. + * @param {Integer|number} hour the value to check. + * @return {Integer|number} the value of the hour if it is valid. Exception is thrown otherwise. + */ +export function assertValidHour(hour) { + return assertValidTemporalValue(hour, HOUR_OF_DAY_RANGE, 'Hour'); +} + +/** + * Assert that the minute value is valid. + * @param {Integer|number} minute the value to check. + * @return {Integer|number} the value of the minute if it is valid. Exception is thrown otherwise. + */ +export function assertValidMinute(minute) { + return assertValidTemporalValue(minute, MINUTE_OF_HOUR_RANGE, 'Minute'); +} + +/** + * Assert that the second value is valid. + * @param {Integer|number} second the value to check. + * @return {Integer|number} the value of the second if it is valid. Exception is thrown otherwise. + */ +export function assertValidSecond(second) { + return assertValidTemporalValue(second, SECOND_OF_MINUTE_RANGE, 'Second'); +} + +/** + * Assert that the nanosecond value is valid. + * @param {Integer|number} nanosecond the value to check. + * @return {Integer|number} the value of the nanosecond if it is valid. Exception is thrown otherwise. + */ +export function assertValidNanosecond(nanosecond) { + return assertValidTemporalValue(nanosecond, NANOSECOND_OF_SECOND_RANGE, 'Nanosecond'); +} + +/** + * Check if the given value is of expected type and is in the expected range. + * @param {Integer|number} value the value to check. + * @param {ValueRange} range the range. + * @param {string} name the name of the value. + * @return {Integer|number} the value if valid. Exception is thrown otherwise. + */ +function assertValidTemporalValue(value, range, name) { + assertNumberOrInteger(value, name); + if (!range.contains(value)) { + throw newError(`${name} is expected to be in range ${range} but was: ${value}`); + } + return value; +} + /** * 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..641ca8797 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 = { @@ -87,13 +87,30 @@ export class LocalTime { * @param {Integer|number} nanosecond the nanosecond for the new local time. */ constructor(hour, minute, second, nanosecond) { - this.hour = assertNumberOrInteger(hour, 'Hour'); - this.minute = assertNumberOrInteger(minute, 'Minute'); - this.second = assertNumberOrInteger(second, 'Second'); - this.nanosecond = assertNumberOrInteger(nanosecond, 'Nanosecond'); + this.hour = util.assertValidHour(hour); + this.minute = util.assertValidMinute(minute); + this.second = util.assertValidSecond(second); + this.nanosecond = util.assertValidNanosecond(nanosecond); 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); } @@ -125,14 +142,32 @@ export class Time { * @param {Integer|number} timeZoneOffsetSeconds the time zone offset in seconds. */ constructor(hour, minute, second, nanosecond, timeZoneOffsetSeconds) { - this.hour = assertNumberOrInteger(hour, 'Hour'); - this.minute = assertNumberOrInteger(minute, 'Minute'); - this.second = assertNumberOrInteger(second, 'Second'); - this.nanosecond = assertNumberOrInteger(nanosecond, 'Nanosecond'); + this.hour = util.assertValidHour(hour); + this.minute = util.assertValidMinute(minute); + this.second = util.assertValidSecond(second); + this.nanosecond = util.assertValidNanosecond(nanosecond); this.timeZoneOffsetSeconds = assertNumberOrInteger(timeZoneOffsetSeconds, 'Time zone offset in seconds'); 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); } @@ -162,12 +197,27 @@ export class Date { * @param {Integer|number} day the day for the new local date. */ constructor(year, month, day) { - this.year = assertNumberOrInteger(year, 'Year'); - this.month = assertNumberOrInteger(month, 'Month'); - this.day = assertNumberOrInteger(day, 'Day'); + this.year = util.assertValidYear(year); + this.month = util.assertValidMonth(month); + this.day = util.assertValidDay(day); 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); } @@ -201,16 +251,36 @@ export class LocalDateTime { * @param {Integer|number} nanosecond the nanosecond for the new local time. */ constructor(year, month, day, hour, minute, second, nanosecond) { - this.year = assertNumberOrInteger(year, 'Year'); - this.month = assertNumberOrInteger(month, 'Month'); - this.day = assertNumberOrInteger(day, 'Day'); - this.hour = assertNumberOrInteger(hour, 'Hour'); - this.minute = assertNumberOrInteger(minute, 'Minute'); - this.second = assertNumberOrInteger(second, 'Second'); - this.nanosecond = assertNumberOrInteger(nanosecond, 'Nanosecond'); + this.year = util.assertValidYear(year); + this.month = util.assertValidMonth(month); + this.day = util.assertValidDay(day); + this.hour = util.assertValidHour(hour); + this.minute = util.assertValidMinute(minute); + this.second = util.assertValidSecond(second); + this.nanosecond = util.assertValidNanosecond(nanosecond); 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); } @@ -246,13 +316,13 @@ export class DateTime { * @param {string|null} timeZoneId the time zone id for the new date-time. Either this argument or timeZoneOffsetSeconds should be defined. */ constructor(year, month, day, hour, minute, second, nanosecond, timeZoneOffsetSeconds, timeZoneId) { - this.year = assertNumberOrInteger(year, 'Year'); - this.month = assertNumberOrInteger(month, 'Month'); - this.day = assertNumberOrInteger(day, 'Day'); - this.hour = assertNumberOrInteger(hour, 'Hour'); - this.minute = assertNumberOrInteger(minute, 'Minute'); - this.second = assertNumberOrInteger(second, 'Second'); - this.nanosecond = assertNumberOrInteger(nanosecond, 'Nanosecond'); + this.year = util.assertValidYear(year); + this.month = util.assertValidMonth(month); + this.day = util.assertValidDay(day); + this.hour = util.assertValidHour(hour); + this.minute = util.assertValidMinute(minute); + this.second = util.assertValidSecond(second); + this.nanosecond = util.assertValidNanosecond(nanosecond); const [offset, id] = verifyTimeZoneArguments(timeZoneOffsetSeconds, timeZoneId); this.timeZoneOffsetSeconds = offset; @@ -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..ee3610a4b 100644 --- a/test/internal/temporal-util.test.js +++ b/test/internal/temporal-util.test.js @@ -169,6 +169,154 @@ 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); + }); + + it('should verify year', () => { + expect(util.assertValidYear(-1)).toEqual(-1); + expect(util.assertValidYear(-2010)).toEqual(-2010); + expect(util.assertValidYear(int(-42))).toEqual(int(-42)); + expect(util.assertValidYear(int(-2019))).toEqual(int(-2019)); + + expect(util.assertValidYear(0)).toEqual(0); + expect(util.assertValidYear(1)).toEqual(1); + expect(util.assertValidYear(int(2015))).toEqual(int(2015)); + expect(util.assertValidYear(int(99999))).toEqual(int(99999)); + + expect(() => util.assertValidYear(1000000000)).toThrow(); + expect(() => util.assertValidYear(1999999999)).toThrow(); + expect(() => util.assertValidYear(int(2000000000))).toThrow(); + expect(() => util.assertValidYear(int(3999999999))).toThrow(); + + expect(() => util.assertValidYear(-1000000001)).toThrow(); + expect(() => util.assertValidYear(-1888888888)).toThrow(); + expect(() => util.assertValidYear(int(-2000000001))).toThrow(); + expect(() => util.assertValidYear(int(-3888888888))).toThrow(); + }); + + it('should verify month', () => { + for (let i = 1; i <= 12; i++) { + expect(util.assertValidMonth(i)).toEqual(i); + expect(util.assertValidMonth(int(i))).toEqual(int(i)); + } + + expect(() => util.assertValidMonth(0)).toThrow(); + expect(() => util.assertValidMonth(int(0))).toThrow(); + expect(() => util.assertValidMonth(-1)).toThrow(); + expect(() => util.assertValidMonth(int(-1))).toThrow(); + expect(() => util.assertValidMonth(-42)).toThrow(); + expect(() => util.assertValidMonth(int(-42))).toThrow(); + expect(() => util.assertValidMonth(13)).toThrow(); + expect(() => util.assertValidMonth(int(13))).toThrow(); + expect(() => util.assertValidMonth(42)).toThrow(); + expect(() => util.assertValidMonth(int(42))).toThrow(); + }); + + it('should verify day', () => { + for (let i = 1; i <= 31; i++) { + expect(util.assertValidDay(i)).toEqual(i); + expect(util.assertValidDay(int(i))).toEqual(int(i)); + } + + expect(() => util.assertValidDay(0)).toThrow(); + expect(() => util.assertValidDay(int(0))).toThrow(); + expect(() => util.assertValidDay(-1)).toThrow(); + expect(() => util.assertValidDay(int(-1))).toThrow(); + expect(() => util.assertValidDay(-42)).toThrow(); + expect(() => util.assertValidDay(int(-42))).toThrow(); + expect(() => util.assertValidDay(42)).toThrow(); + expect(() => util.assertValidDay(int(42))).toThrow(); + }); + + it('should verify hour', () => { + for (let i = 0; i <= 23; i++) { + expect(util.assertValidHour(i)).toEqual(i); + expect(util.assertValidHour(int(i))).toEqual(int(i)); + } + + expect(() => util.assertValidHour(-1)).toThrow(); + expect(() => util.assertValidHour(int(-1))).toThrow(); + expect(() => util.assertValidHour(-42)).toThrow(); + expect(() => util.assertValidHour(int(-42))).toThrow(); + expect(() => util.assertValidHour(24)).toThrow(); + expect(() => util.assertValidHour(int(24))).toThrow(); + expect(() => util.assertValidHour(42)).toThrow(); + expect(() => util.assertValidHour(int(42))).toThrow(); + }); + + it('should verify minute', () => { + for (let i = 0; i <= 59; i++) { + expect(util.assertValidMinute(i)).toEqual(i); + expect(util.assertValidMinute(int(i))).toEqual(int(i)); + } + + expect(() => util.assertValidMinute(-1)).toThrow(); + expect(() => util.assertValidMinute(int(-1))).toThrow(); + expect(() => util.assertValidMinute(-42)).toThrow(); + expect(() => util.assertValidMinute(int(-42))).toThrow(); + expect(() => util.assertValidMinute(60)).toThrow(); + expect(() => util.assertValidMinute(int(60))).toThrow(); + expect(() => util.assertValidMinute(91023)).toThrow(); + expect(() => util.assertValidMinute(int(1234))).toThrow(); + }); + + it('should verify second', () => { + for (let i = 0; i <= 59; i++) { + expect(util.assertValidSecond(i)).toEqual(i); + expect(util.assertValidSecond(int(i))).toEqual(int(i)); + } + + expect(() => util.assertValidSecond(-1)).toThrow(); + expect(() => util.assertValidSecond(int(-1))).toThrow(); + expect(() => util.assertValidSecond(-42)).toThrow(); + expect(() => util.assertValidSecond(int(-42))).toThrow(); + expect(() => util.assertValidSecond(60)).toThrow(); + expect(() => util.assertValidSecond(int(60))).toThrow(); + expect(() => util.assertValidSecond(123)).toThrow(); + expect(() => util.assertValidSecond(int(321))).toThrow(); + }); + + it('should verify nanosecond', () => { + expect(util.assertValidNanosecond(0)).toEqual(0); + expect(util.assertValidNanosecond(1)).toEqual(1); + expect(util.assertValidNanosecond(42)).toEqual(42); + expect(util.assertValidNanosecond(999)).toEqual(999); + expect(util.assertValidNanosecond(123456789)).toEqual(123456789); + expect(util.assertValidNanosecond(999999999)).toEqual(999999999); + + expect(() => util.assertValidNanosecond(-1)).toThrow(); + expect(() => util.assertValidNanosecond(int(-1))).toThrow(); + expect(() => util.assertValidNanosecond(-42)).toThrow(); + expect(() => util.assertValidNanosecond(int(-42))).toThrow(); + expect(() => util.assertValidNanosecond(1000000000)).toThrow(); + expect(() => util.assertValidNanosecond(int(1000000000))).toThrow(); + expect(() => util.assertValidNanosecond(1999999999)).toThrow(); + expect(() => util.assertValidNanosecond(int(1222222222))).toThrow(); + }); + }); function date(year, month, day) { @@ -182,3 +330,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..f008b5624 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'; @@ -462,7 +463,7 @@ describe('temporal-types', () => { it('should convert LocalDateTime to ISO string', () => { expect(localDateTime(1992, 11, 8, 9, 42, 17, 22).toString()).toEqual('1992-11-08T09:42:17.000000022'); expect(localDateTime(-10, 7, 15, 8, 15, 33, 500).toString()).toEqual('-0010-07-15T08:15:33.000000500'); - expect(localDateTime(0, 0, 0, 0, 0, 0, 1).toString()).toEqual('0000-00-00T00:00:00.000000001'); + expect(localDateTime(0, 1, 1, 0, 0, 0, 1).toString()).toEqual('0000-01-01T00:00:00.000000001'); }); it('should convert DateTime with time zone offset to ISO string', () => { @@ -658,6 +659,261 @@ 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); + }); + + it('should fail to create LocalTime with out of range values', () => { + expect(() => localTime(999, 1, 1, 1)).toThrow(); + expect(() => localTime(1, 999, 1, 1)).toThrow(); + expect(() => localTime(1, 1, 999, 1)).toThrow(); + expect(() => localTime(1, 1, 1, -999)).toThrow(); + expect(() => localTime(1, 1, 1, 1000000000)).toThrow(); + }); + + it('should fail to create Time with out of range values', () => { + expect(() => time(999, 1, 1, 1, 1)).toThrow(); + expect(() => time(1, 999, 1, 1, 1)).toThrow(); + expect(() => time(1, 1, 999, 1, 1)).toThrow(); + expect(() => time(1, 1, 1, -999, 1)).toThrow(); + expect(() => time(1, 1, 1, 1000000000, 1)).toThrow(); + }); + + it('should fail to create Date with out of range values', () => { + expect(() => date(1000000000, 1, 1)).toThrow(); + expect(() => date(1, 0, 1)).toThrow(); + expect(() => date(1, 13, 1)).toThrow(); + expect(() => date(1, 1, 0)).toThrow(); + expect(() => date(1, 1, -1)).toThrow(); + expect(() => date(1, 1, 33)).toThrow(); + }); + + it('should fail to create LocalDateTime with out of range values', () => { + expect(() => localDateTime(1000000000, 1, 1, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 0, 1, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 13, 1, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, -1, 1, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 0, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, -1, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 33, 1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, -1, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 24, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 42, 1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, -1, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 60, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 999, 1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 1, -1, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 1, 60, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 1, 99, 1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 1, 1, -1)).toThrow(); + expect(() => localDateTime(1, 1, 1, 1, 1, 1, 1000000000)).toThrow(); + }); + + it('should fail to create DateTime with out of range values', () => { + expect(() => dateTimeWithZoneOffset(1000000000, 1, 1, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 0, 1, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 13, 1, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, -1, 1, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 0, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, -1, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 33, 1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, -1, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 24, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 42, 1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, -1, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 60, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 999, 1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 1, -1, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 1, 60, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 1, 99, 1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 1, 1, -1, 0)).toThrow(); + expect(() => dateTimeWithZoneOffset(1, 1, 1, 1, 1, 1, 1000000000, 0)).toThrow(); + }); + function testSendAndReceiveRandomTemporalValues(valueGenerator, done) { const asyncFunction = (index, callback) => { const next = () => callback(); @@ -844,4 +1100,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;