Skip to content

Commit c6c1a08

Browse files
committed
Allow to create temporal objects from standard JS Date
This commit adds `#fromStandardDate()` functions to all temporal types except `Duration`. Such functions allow to create temporal objects from the provided standard JavaScript `Date`.
1 parent 0ce603e commit c6c1a08

9 files changed

+392
-4
lines changed

src/v1/internal/temporal-util.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* limitations under the License.
1818
*/
1919

20-
import {int} from '../integer';
20+
import {int, isInt} from '../integer';
2121
import {Date, LocalDateTime, LocalTime} from '../temporal-types';
2222

2323
/*
@@ -35,6 +35,7 @@ const MINUTES_PER_HOUR = 60;
3535
const SECONDS_PER_MINUTE = 60;
3636
const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
3737
const NANOS_PER_SECOND = 1000000000;
38+
const NANOS_PER_MILLISECOND = 1000000;
3839
const NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
3940
const NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
4041
const DAYS_0000_TO_1970 = 719528;
@@ -264,6 +265,27 @@ export function dateToIsoString(year, month, day) {
264265
return `${yearString}-${monthString}-${dayString}`;
265266
}
266267

268+
/**
269+
* Get the total number of nanoseconds from the milliseconds of the given standard JavaScript date and optional nanosecond part.
270+
* @param {global.Date} standardDate the standard JavaScript date.
271+
* @param {Integer|number|undefined} nanoseconds the optional number of nanoseconds.
272+
* @return {Integer|number} the total amount of nanoseconds.
273+
*/
274+
export function totalNanoseconds(standardDate, nanoseconds) {
275+
nanoseconds = (nanoseconds || 0);
276+
const nanosFromMillis = standardDate.getMilliseconds() * NANOS_PER_MILLISECOND;
277+
return isInt(nanoseconds) ? nanoseconds.add(nanosFromMillis) : nanoseconds + nanosFromMillis;
278+
}
279+
280+
/**
281+
* Get the total number of nanoseconds from the given standard JavaScript date.
282+
* @param {global.Date} standardDate the standard JavaScript date.
283+
* @return {number} the total amount of nanoseconds.
284+
*/
285+
export function timeZoneOffsetInSeconds(standardDate) {
286+
return standardDate.getTimezoneOffset() * SECONDS_PER_MINUTE;
287+
}
288+
267289
/**
268290
* Converts given local time into a single integer representing this same time in seconds of the day. Nanoseconds are skipped.
269291
* @param {Integer|number|string} hour the hour of the local time.

src/v1/internal/util.js

+11
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ function assertNumberOrInteger(obj, objName) {
8787
return obj;
8888
}
8989

90+
function assertValidDate(obj, objName) {
91+
if (Object.prototype.toString.call(obj) !== '[object Date]') {
92+
throw new TypeError(objName + ' expected to be a standard JavaScript Date but was: ' + JSON.stringify(obj));
93+
}
94+
if (Number.isNaN(obj.getTime())) {
95+
throw new TypeError(objName + ' expected to be valid JavaScript Date but its time was NaN: ' + JSON.stringify(obj));
96+
}
97+
return obj;
98+
}
99+
90100
function assertCypherStatement(obj) {
91101
assertString(obj, 'Cypher statement');
92102
if (obj.trim().length === 0) {
@@ -112,6 +122,7 @@ export {
112122
assertString,
113123
assertNumber,
114124
assertNumberOrInteger,
125+
assertValidDate,
115126
validateStatementAndParameters,
116127
ENCRYPTION_ON,
117128
ENCRYPTION_OFF

src/v1/temporal-types.js

+99-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
import * as util from './internal/temporal-util';
21-
import {assertNumberOrInteger, assertString} from './internal/util';
21+
import {assertNumberOrInteger, assertString, assertValidDate} from './internal/util';
2222
import {newError} from './error';
2323

2424
const IDENTIFIER_PROPERTY_ATTRIBUTES = {
@@ -94,6 +94,23 @@ export class LocalTime {
9494
Object.freeze(this);
9595
}
9696

97+
/**
98+
* Create a local time object from the given standard JavaScript <code>Date</code> and optional nanoseconds.
99+
* Year, month, day and time zone offset components of the given date are ignored.
100+
* @param {global.Date} standardDate the standard JavaScript date to convert.
101+
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
102+
* @return {LocalTime} new local time.
103+
*/
104+
static fromStandardDate(standardDate, nanosecond) {
105+
verifyStandardDateAndNanos(standardDate, nanosecond);
106+
107+
return new LocalTime(
108+
standardDate.getHours(),
109+
standardDate.getMinutes(),
110+
standardDate.getSeconds(),
111+
util.totalNanoseconds(standardDate, nanosecond));
112+
}
113+
97114
toString() {
98115
return util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond);
99116
}
@@ -133,6 +150,24 @@ export class Time {
133150
Object.freeze(this);
134151
}
135152

153+
/**
154+
* Create a time object from the given standard JavaScript <code>Date</code> and optional nanoseconds.
155+
* Year, month and day components of the given date are ignored.
156+
* @param {global.Date} standardDate the standard JavaScript date to convert.
157+
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
158+
* @return {Time} new time.
159+
*/
160+
static fromStandardDate(standardDate, nanosecond) {
161+
verifyStandardDateAndNanos(standardDate, nanosecond);
162+
163+
return new Time(
164+
standardDate.getHours(),
165+
standardDate.getMinutes(),
166+
standardDate.getSeconds(),
167+
util.totalNanoseconds(standardDate, nanosecond),
168+
util.timeZoneOffsetInSeconds(standardDate));
169+
}
170+
136171
toString() {
137172
return util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond) + util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds);
138173
}
@@ -168,6 +203,21 @@ export class Date {
168203
Object.freeze(this);
169204
}
170205

206+
/**
207+
* Create a date object from the given standard JavaScript <code>Date</code>.
208+
* Hour, minute, second, millisecond and time zone offset components of the given date are ignored.
209+
* @param {global.Date} standardDate the standard JavaScript date to convert.
210+
* @return {Date} new date.
211+
*/
212+
static fromStandardDate(standardDate) {
213+
verifyStandardDateAndNanos(standardDate, null);
214+
215+
return new Date(
216+
standardDate.getFullYear(),
217+
standardDate.getMonth(),
218+
standardDate.getDate());
219+
}
220+
171221
toString() {
172222
return util.dateToIsoString(this.year, this.month, this.day);
173223
}
@@ -211,6 +261,26 @@ export class LocalDateTime {
211261
Object.freeze(this);
212262
}
213263

264+
/**
265+
* Create a local date-time object from the given standard JavaScript <code>Date</code> and optional nanoseconds.
266+
* Time zone offset component of the given date is ignored.
267+
* @param {global.Date} standardDate the standard JavaScript date to convert.
268+
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
269+
* @return {LocalDateTime} new local date-time.
270+
*/
271+
static fromStandardDate(standardDate, nanosecond) {
272+
verifyStandardDateAndNanos(standardDate, nanosecond);
273+
274+
return new LocalDateTime(
275+
standardDate.getFullYear(),
276+
standardDate.getMonth(),
277+
standardDate.getDate(),
278+
standardDate.getHours(),
279+
standardDate.getMinutes(),
280+
standardDate.getSeconds(),
281+
util.totalNanoseconds(standardDate, nanosecond));
282+
}
283+
214284
toString() {
215285
return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond);
216286
}
@@ -261,6 +331,27 @@ export class DateTime {
261331
Object.freeze(this);
262332
}
263333

334+
/**
335+
* Create a date-time object from the given standard JavaScript <code>Date</code> and optional nanoseconds.
336+
* @param {global.Date} standardDate the standard JavaScript date to convert.
337+
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
338+
* @return {DateTime} new date-time.
339+
*/
340+
static fromStandardDate(standardDate, nanosecond) {
341+
verifyStandardDateAndNanos(standardDate, nanosecond);
342+
343+
return new DateTime(
344+
standardDate.getFullYear(),
345+
standardDate.getMonth(),
346+
standardDate.getDate(),
347+
standardDate.getHours(),
348+
standardDate.getMinutes(),
349+
standardDate.getSeconds(),
350+
util.totalNanoseconds(standardDate, nanosecond),
351+
util.timeZoneOffsetInSeconds(standardDate),
352+
null /* no time zone id */);
353+
}
354+
264355
toString() {
265356
const localDateTimeStr = localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond);
266357
const timeZoneStr = this.timeZoneId ? `[${this.timeZoneId}]` : util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds);
@@ -303,3 +394,10 @@ function verifyTimeZoneArguments(timeZoneOffsetSeconds, timeZoneId) {
303394
throw newError(`Unable to create DateTime without either time zone offset or id. Please specify either of them. Given offset: ${timeZoneOffsetSeconds} and id: ${timeZoneId}`);
304395
}
305396
}
397+
398+
function verifyStandardDateAndNanos(standardDate, nanosecond) {
399+
assertValidDate(standardDate, 'Standard date');
400+
if (nanosecond !== null && nanosecond !== undefined) {
401+
assertNumberOrInteger(nanosecond, 'Nanosecond');
402+
}
403+
}

test/internal/temporal-util.test.js

+32
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,32 @@ describe('temporal-util', () => {
169169
expect(util.localTimeToNanoOfDay(12, 51, 17, 808080)).toEqual(int(46277000808080));
170170
});
171171

172+
it('should get total nanoseconds from standard date', () => {
173+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0))).toEqual(0);
174+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1))).toEqual(1000000);
175+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 23))).toEqual(23000000);
176+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999))).toEqual(999000000);
177+
178+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 0)).toEqual(0);
179+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 1)).toEqual(1);
180+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), 999)).toEqual(999);
181+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1), 999)).toEqual(1000999);
182+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999), 111)).toEqual(999000111);
183+
184+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(0))).toEqual(int(0));
185+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(1))).toEqual(int(1));
186+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 0), int(999))).toEqual(int(999));
187+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 1), int(999))).toEqual(int(1000999));
188+
expect(util.totalNanoseconds(new Date(2000, 1, 1, 1, 1, 1, 999), int(111))).toEqual(int(999000111));
189+
});
190+
191+
it('should get timezone offset in seconds from standard date', () => {
192+
expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(0))).toEqual(0);
193+
expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(2))).toEqual(120);
194+
expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(10))).toEqual(600);
195+
expect(util.timeZoneOffsetInSeconds(fakeStandardDateWithOffset(101))).toEqual(6060);
196+
});
197+
172198
});
173199

174200
function date(year, month, day) {
@@ -182,3 +208,9 @@ function localTime(hour, minute, second, nanosecond) {
182208
function localDateTime(year, month, day, hour, minute, second, nanosecond) {
183209
return new types.LocalDateTime(int(year), int(month), int(day), int(hour), int(minute), int(second), int(nanosecond));
184210
}
211+
212+
function fakeStandardDateWithOffset(offsetMinutes) {
213+
const date = new Date();
214+
date.getTimezoneOffset = () => offsetMinutes;
215+
return date;
216+
}

test/internal/util.test.js

+25
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ describe('util', () => {
135135
verifyInvalidNumberOrInteger({value: 42});
136136
});
137137

138+
it('should check dates', () => {
139+
verifyValidDate(new Date());
140+
verifyValidDate(new Date(0));
141+
verifyValidDate(new Date(-1));
142+
verifyValidDate(new Date(2000, 10, 10));
143+
verifyValidDate(new Date(2000, 10, 10, 10, 10, 10, 10));
144+
145+
verifyInvalidDate(new Date('not a valid date'));
146+
verifyInvalidDate(new Date({}));
147+
verifyInvalidDate(new Date([]));
148+
149+
verifyInvalidDate({});
150+
verifyInvalidDate([]);
151+
verifyInvalidDate('2007-04-05T12:30-02:00');
152+
verifyInvalidDate(2019);
153+
});
154+
138155
function verifyValidString(str) {
139156
expect(util.assertString(str, 'Test string')).toBe(str);
140157
}
@@ -171,4 +188,12 @@ describe('util', () => {
171188
expect(() => util.validateStatementAndParameters('RETURN 1', obj)).toThrowError(TypeError);
172189
}
173190

191+
function verifyValidDate(obj) {
192+
expect(util.assertValidDate(obj, 'Test date')).toBe(obj);
193+
}
194+
195+
function verifyInvalidDate(obj) {
196+
expect(() => util.assertValidDate(obj, 'Test date')).toThrowError(TypeError);
197+
}
198+
174199
});

test/types/v1/temporal-types.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
Time
3333
} from "../../../types/v1/temporal-types";
3434
import Integer, {int} from "../../../types/v1/integer";
35+
import {StandardDate} from "../../../types/v1/graph-types";
3536

3637
const duration1: Duration = new Duration(int(1), int(1), int(1), int(1));
3738
const months1: Integer = duration1.months;
@@ -149,3 +150,15 @@ const isTimeValue: boolean = isTime(time1);
149150
const isDateValue: boolean = isDate(date1);
150151
const isLocalDateTimeValue: boolean = isLocalDateTime(localDateTime1);
151152
const isDateTimeValue: boolean = isDateTime(dateTime1);
153+
154+
const dummy: any = null;
155+
const standardDate: StandardDate = dummy;
156+
const localTime3: LocalTime<number> = LocalTime.fromStandardDate(standardDate);
157+
const localTime4: LocalTime<number> = LocalTime.fromStandardDate(standardDate, 42);
158+
const time3: Time<number> = Time.fromStandardDate(standardDate);
159+
const time4: Time<number> = Time.fromStandardDate(standardDate, 42);
160+
const date3: Date<number> = Date.fromStandardDate(standardDate);
161+
const localDateTime3: LocalDateTime<number> = LocalDateTime.fromStandardDate(standardDate);
162+
const localDateTime4: LocalDateTime<number> = LocalDateTime.fromStandardDate(standardDate, 42);
163+
const dateTime5: DateTime<number> = DateTime.fromStandardDate(standardDate);
164+
const dateTime6: DateTime<number> = DateTime.fromStandardDate(standardDate, 42);

0 commit comments

Comments
 (0)