Skip to content

Commit 3e54d8c

Browse files
authored
Merge pull request #393 from lutovich/1.6-check-temporal-ranges
Value range checks for temporal types
2 parents 44f9f5a + cfcee42 commit 3e54d8c

File tree

9 files changed

+764
-30
lines changed

9 files changed

+764
-30
lines changed

src/v1/internal/temporal-util.js

+133-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
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';
22+
import {assertNumberOrInteger} from './util';
23+
import {newError} from '../error';
2224

2325
/*
2426
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';
3133
conversion functions.
3234
*/
3335

36+
class ValueRange {
37+
38+
constructor(min, max) {
39+
this._minNumber = min;
40+
this._maxNumber = max;
41+
this._minInteger = int(min);
42+
this._maxInteger = int(max);
43+
}
44+
45+
contains(value) {
46+
if (isInt(value)) {
47+
return value.greaterThanOrEqual(this._minInteger) && value.lessThanOrEqual(this._maxInteger);
48+
} else {
49+
return value >= this._minNumber && value <= this._maxNumber;
50+
}
51+
}
52+
53+
toString() {
54+
return `[${this._minNumber}, ${this._maxNumber}]`;
55+
}
56+
}
57+
58+
const YEAR_RANGE = new ValueRange(-999999999, 999999999);
59+
const MONTH_OF_YEAR_RANGE = new ValueRange(1, 12);
60+
const DAY_OF_MONTH_RANGE = new ValueRange(1, 31);
61+
const HOUR_OF_DAY_RANGE = new ValueRange(0, 23);
62+
const MINUTE_OF_HOUR_RANGE = new ValueRange(0, 59);
63+
const SECOND_OF_MINUTE_RANGE = new ValueRange(0, 59);
64+
const NANOSECOND_OF_SECOND_RANGE = new ValueRange(0, 999999999);
65+
3466
const MINUTES_PER_HOUR = 60;
3567
const SECONDS_PER_MINUTE = 60;
3668
const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
3769
const NANOS_PER_SECOND = 1000000000;
70+
const NANOS_PER_MILLISECOND = 1000000;
3871
const NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
3972
const NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
4073
const DAYS_0000_TO_1970 = 719528;
@@ -264,6 +297,105 @@ export function dateToIsoString(year, month, day) {
264297
return `${yearString}-${monthString}-${dayString}`;
265298
}
266299

300+
/**
301+
* Get the total number of nanoseconds from the milliseconds of the given standard JavaScript date and optional nanosecond part.
302+
* @param {global.Date} standardDate the standard JavaScript date.
303+
* @param {Integer|number|undefined} nanoseconds the optional number of nanoseconds.
304+
* @return {Integer|number} the total amount of nanoseconds.
305+
*/
306+
export function totalNanoseconds(standardDate, nanoseconds) {
307+
nanoseconds = (nanoseconds || 0);
308+
const nanosFromMillis = standardDate.getMilliseconds() * NANOS_PER_MILLISECOND;
309+
return isInt(nanoseconds) ? nanoseconds.add(nanosFromMillis) : nanoseconds + nanosFromMillis;
310+
}
311+
312+
/**
313+
* Get the time zone offset in seconds from the given standard JavaScript date.
314+
* @param {global.Date} standardDate the standard JavaScript date.
315+
* @return {number} the time zone offset in seconds.
316+
*/
317+
export function timeZoneOffsetInSeconds(standardDate) {
318+
return standardDate.getTimezoneOffset() * SECONDS_PER_MINUTE;
319+
}
320+
321+
/**
322+
* Assert that the year value is valid.
323+
* @param {Integer|number} year the value to check.
324+
* @return {Integer|number} the value of the year if it is valid. Exception is thrown otherwise.
325+
*/
326+
export function assertValidYear(year) {
327+
return assertValidTemporalValue(year, YEAR_RANGE, 'Year');
328+
}
329+
330+
/**
331+
* Assert that the month value is valid.
332+
* @param {Integer|number} month the value to check.
333+
* @return {Integer|number} the value of the month if it is valid. Exception is thrown otherwise.
334+
*/
335+
export function assertValidMonth(month) {
336+
return assertValidTemporalValue(month, MONTH_OF_YEAR_RANGE, 'Month');
337+
}
338+
339+
/**
340+
* Assert that the day value is valid.
341+
* @param {Integer|number} day the value to check.
342+
* @return {Integer|number} the value of the day if it is valid. Exception is thrown otherwise.
343+
*/
344+
export function assertValidDay(day) {
345+
return assertValidTemporalValue(day, DAY_OF_MONTH_RANGE, 'Day');
346+
}
347+
348+
/**
349+
* Assert that the hour value is valid.
350+
* @param {Integer|number} hour the value to check.
351+
* @return {Integer|number} the value of the hour if it is valid. Exception is thrown otherwise.
352+
*/
353+
export function assertValidHour(hour) {
354+
return assertValidTemporalValue(hour, HOUR_OF_DAY_RANGE, 'Hour');
355+
}
356+
357+
/**
358+
* Assert that the minute value is valid.
359+
* @param {Integer|number} minute the value to check.
360+
* @return {Integer|number} the value of the minute if it is valid. Exception is thrown otherwise.
361+
*/
362+
export function assertValidMinute(minute) {
363+
return assertValidTemporalValue(minute, MINUTE_OF_HOUR_RANGE, 'Minute');
364+
}
365+
366+
/**
367+
* Assert that the second value is valid.
368+
* @param {Integer|number} second the value to check.
369+
* @return {Integer|number} the value of the second if it is valid. Exception is thrown otherwise.
370+
*/
371+
export function assertValidSecond(second) {
372+
return assertValidTemporalValue(second, SECOND_OF_MINUTE_RANGE, 'Second');
373+
}
374+
375+
/**
376+
* Assert that the nanosecond value is valid.
377+
* @param {Integer|number} nanosecond the value to check.
378+
* @return {Integer|number} the value of the nanosecond if it is valid. Exception is thrown otherwise.
379+
*/
380+
export function assertValidNanosecond(nanosecond) {
381+
return assertValidTemporalValue(nanosecond, NANOSECOND_OF_SECOND_RANGE, 'Nanosecond');
382+
}
383+
384+
/**
385+
* Check if the given value is of expected type and is in the expected range.
386+
* @param {Integer|number} value the value to check.
387+
* @param {ValueRange} range the range.
388+
* @param {string} name the name of the value.
389+
* @return {Integer|number} the value if valid. Exception is thrown otherwise.
390+
*/
391+
function assertValidTemporalValue(value, range, name) {
392+
assertNumberOrInteger(value, name);
393+
if (!range.contains(value)) {
394+
throw newError(`${name} is expected to be in range ${range} but was: ${value}`);
395+
}
396+
return value;
397+
}
398+
267399
/**
268400
* Converts given local time into a single integer representing this same time in seconds of the day. Nanoseconds are skipped.
269401
* @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

0 commit comments

Comments
 (0)