Skip to content

Commit

Permalink
Merge pull request #393 from lutovich/1.6-check-temporal-ranges
Browse files Browse the repository at this point in the history
Value range checks for temporal types
  • Loading branch information
lutovich committed Jun 28, 2018
2 parents 44f9f5a + cfcee42 commit 3e54d8c
Show file tree
Hide file tree
Showing 9 changed files with 764 additions and 30 deletions.
134 changes: 133 additions & 1 deletion src/v1/internal/temporal-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions src/v1/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -112,6 +122,7 @@ export {
assertString,
assertNumber,
assertNumberOrInteger,
assertValidDate,
validateStatementAndParameters,
ENCRYPTION_ON,
ENCRYPTION_OFF
Expand Down
Loading

0 comments on commit 3e54d8c

Please sign in to comment.