Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: out-of-range date error handle #1271

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/data-type.ts
Expand Up @@ -80,7 +80,7 @@ export interface DataType {
generateTypeInfo(parameter: ParameterData, options: InternalConnectionOptions): Buffer;
generateParameterLength(parameter: ParameterData, options: InternalConnectionOptions): Buffer;
generateParameterData(parameter: ParameterData, options: InternalConnectionOptions): Generator<Buffer, void>;
validate(value: any, collation: Collation | undefined): any; // TODO: Refactor 'any' and replace with more specific type.
validate(value: any, collation: Collation | undefined, options?: InternalConnectionOptions): any; // TODO: Refactor 'any' and replace with more specific type.

hasTableName?: boolean;

Expand Down
23 changes: 21 additions & 2 deletions src/data-types/datetime.ts
@@ -1,11 +1,18 @@
import { type DataType } from '../data-type';
import DateTimeN from './datetimen';
import { ChronoUnit, LocalDate } from '@js-joda/core';
import { type InternalConnectionOptions } from '../connection';

import { Collation } from '../collation';

const EPOCH_DATE = LocalDate.ofYearDay(1900, 1);
const NULL_LENGTH = Buffer.from([0x00]);
const DATA_LENGTH = Buffer.from([0x08]);

const MIN_DATE = new Date('January 1, 1753');
const MAX_DATE = new Date('December 31, 9999');


const DateTime: DataType = {
id: 0x3D,
type: 'DATETIME',
Expand Down Expand Up @@ -34,7 +41,7 @@

const value = parameter.value as any; // Temporary solution. Remove 'any' later.

let date;
let date: LocalDate;
if (options.useUTC) {
date = LocalDate.of(value.getUTCFullYear(), value.getUTCMonth() + 1, value.getUTCDate());
} else {
Expand Down Expand Up @@ -72,7 +79,7 @@
},

// TODO: type 'any' needs to be revisited.
validate: function(value): null | number {
validate: function(value: any, collation: Collation | undefined, options: InternalConnectionOptions): null | number {
if (value == null) {
return null;
}
Expand All @@ -85,6 +92,18 @@
throw new TypeError('Invalid date.');
}

value = value as Date;

// TODO: check date range: January 1, 1753, through December 31, 9999
// : time range: 00:00:00 through 23:59:59.997
if (options && options.useUTC) {
value = new Date(value.toUTCString());

Check warning on line 100 in src/data-types/datetime.ts

View check run for this annotation

Codecov / codecov/patch

src/data-types/datetime.ts#L100

Added line #L100 was not covered by tests
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

return value;
}
};
Expand Down
26 changes: 24 additions & 2 deletions src/data-types/smalldatetime.ts
@@ -1,9 +1,13 @@
import { type InternalConnectionOptions } from '../connection';
import { type DataType } from '../data-type';
import DateTimeN from './datetimen';

import { Collation } from '../collation';
const EPOCH_DATE = new Date(1900, 0, 1);
const UTC_EPOCH_DATE = new Date(Date.UTC(1900, 0, 1));

const MIN_DATE = new Date(1900, 1, 1);
const MAX_DATE = new Date(2079, 5, 6, 23, 59, 59, 0);

const DATA_LENGTH = Buffer.from([0x04]);
const NULL_LENGTH = Buffer.from([0x00]);

Expand Down Expand Up @@ -51,7 +55,7 @@
yield buffer;
},

validate: function(value): null | Date {
validate: function(value, collation: Collation | undefined, options: InternalConnectionOptions): null | Date {
if (value == null) {
return null;
}
Expand All @@ -64,6 +68,24 @@
throw new TypeError('Invalid date.');
}

value = value as Date;

if (options && options.useUTC) {
value = new Date(value.toUTCString());

Check warning on line 74 in src/data-types/smalldatetime.ts

View check run for this annotation

Codecov / codecov/patch

src/data-types/smalldatetime.ts#L74

Added line #L74 was not covered by tests
}

if (value < EPOCH_DATE) {
throw new TypeError('Out of range.');
}

if (value.getFullYear() < 1900) {
throw new TypeError('Out of range.');

Check warning on line 82 in src/data-types/smalldatetime.ts

View check run for this annotation

Codecov / codecov/patch

src/data-types/smalldatetime.ts#L82

Added line #L82 was not covered by tests
}

if (value < MIN_DATE || value > MAX_DATE) {
throw new TypeError('Out of range.');
}

return value;
}
};
Expand Down
46 changes: 43 additions & 3 deletions test/unit/data-type.js
Expand Up @@ -246,6 +246,20 @@ describe('DateTime', function() {
assert.deepEqual(result, expected);
});
});

describe('.validate', function() {
it('returns a TypeError for dates that are below January 1, 1753', function() {
assert.throws(() => {
TYPES.DateTime.validate(new Date('January 1, 1752'));
}, TypeError, 'Out of range.');
});

it('returns a TypeError for dates that are greater than December 31, 9999', function() {
assert.throws(() => {
TYPES.DateTime.validate(new Date('December 31, 10000'));
}, TypeError, 'Out of range.');
});
});
});

describe('DateTime2', function() {
Expand Down Expand Up @@ -829,7 +843,7 @@ describe('NVarChar', function() {

describe('.generateTypeInfo', function() {
it('returns the correct type information', function() {
// Length <= Maximum Length
// Length <= Maximum Length
const type = TYPES.NVarChar;
const expected = Buffer.from([0xE7, 2, 0, 0x00, 0x00, 0x00, 0x00, 0x00]);

Expand Down Expand Up @@ -919,6 +933,32 @@ describe('SmallDateTime', function() {
assert.deepEqual(result, expected);
});
});

describe('.validate', function() {
it('throws Invalid Date error for NaN input', function() {
assert.throws(() => {
TYPES.SmallDateTime.validate('string');
}, TypeError, 'Invalid date.');
});

it('throws Out of Range error for dates out of range', function() {
assert.throws(() => {
TYPES.SmallDateTime.validate(new Date('January 1, 1899'));
}, TypeError, 'Out of range.');

assert.throws(() => {
TYPES.SmallDateTime.validate(new Date('January 1, 2080'));
}, TypeError, 'Out of range.');

assert.throws(() => {
TYPES.SmallDateTime.validate(new Date('July 1, 2079'));
}, TypeError, 'Out of range.');

assert.throws(() => {
TYPES.SmallDateTime.validate(new Date('June 7, 2079'));
}, TypeError, 'Out of range.');
});
});
});

describe('SmallInt', function() {
Expand Down Expand Up @@ -1152,7 +1192,7 @@ describe('TVP', function() {
TYPES.TVP.generateParameterLength({
value: {
columns: [{ name: 'user_id', type: TYPES.Int }],
rows: [[ 15 ], [ 16 ]]
rows: [[15], [16]]
}
}),
Buffer.from([0x01, 0x00])
Expand All @@ -1164,7 +1204,7 @@ describe('TVP', function() {
it('correctly converts TVP table values', function() {
const value = {
columns: [{ name: 'user_id', type: TYPES.Int }],
rows: [[ 15 ]]
rows: [[15]]
};
const expected = Buffer.from('0000000000002604000001040f00000000', 'hex');
const parameterValue = { value };
Expand Down