Skip to content

Commit

Permalink
refactor(date)!: fail on invalid dates (#2757)
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT committed Mar 24, 2024
1 parent 45150d1 commit 3485e7e
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 119 deletions.
9 changes: 9 additions & 0 deletions docs/guide/upgrading_v9/2757.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Fail on invalid dates

Various methods in the `faker.date` module allow you to pass a `Date`-ish value:
that is, either a Javascript Date, or a timestamp number or string that can be converted to a `Date` via the `new Date()` constructor.

Previously, if you passed something which could not be parsed to a `Date`, it would fall back to the current reference date.
Now, this throws an error raising awareness of that bad value.

This affects the `refDate` parameter of the `anytime()`, `birthdate()`, `past()`, `future()`, `recent()` and `soon()`, methods as well as the `from` and `to` parameters of `between()` and `betweens()`.
117 changes: 45 additions & 72 deletions src/modules/date/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,21 @@ import { SimpleModuleBase } from '../../internal/module-base';
import { assertLocaleData } from '../../locale-proxy';

/**
* Converts date passed as a string, number or Date to a Date object.
* If nothing or a non-parsable value is passed, then it will take the value from the given fallback.
* Converts a date passed as a `string`, `number` or `Date` to a valid `Date` object.
*
* @param date The date to convert.
* @param fallback The fallback date to use if the passed date is not valid.
* @param name The reference name used for error messages. Defaults to `'refDate'`.
*
* @throws If the given date is invalid.
*/
function toDate(
date: string | Date | number | undefined,
fallback: () => Date
): Date {
if (date == null) {
return fallback();
}
function toDate(date: string | Date | number, name: string = 'refDate'): Date {
const converted = new Date(date);

date = new Date(date);
if (Number.isNaN(date.valueOf())) {
date = fallback();
if (Number.isNaN(converted.valueOf())) {
throw new FakerError(`Invalid ${name} date: ${date.toString()}`);
}

return date;
return converted;
}

/**
Expand Down Expand Up @@ -56,13 +51,12 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const { refDate } = options;

const date = toDate(refDate, this.faker.defaultRefDate);
const { refDate = this.faker.defaultRefDate() } = options;
const time = toDate(refDate).getTime();

return this.between({
from: new Date(date.getTime() - 1000 * 60 * 60 * 24 * 365),
to: new Date(date.getTime() + 1000 * 60 * 60 * 24 * 365),
from: time - 1000 * 60 * 60 * 24 * 365,
to: time + 1000 * 60 * 60 * 24 * 365,
});
}

Expand Down Expand Up @@ -98,23 +92,18 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const { years = 1, refDate } = options;
const { years = 1, refDate = this.faker.defaultRefDate() } = options;

if (years <= 0) {
throw new FakerError('Years must be greater than 0.');
}

const date = toDate(refDate, this.faker.defaultRefDate);
const range = {
min: 1000,
max: years * 365 * 24 * 3600 * 1000,
};

let past = date.getTime();
past -= this.faker.number.int(range); // some time from now to N years ago, in milliseconds
date.setTime(past);
const time = toDate(refDate).getTime();

return date;
return this.between({
from: time - years * 365 * 24 * 3600 * 1000,
to: time - 1000,
});
}

/**
Expand Down Expand Up @@ -149,23 +138,18 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const { years = 1, refDate } = options;
const { years = 1, refDate = this.faker.defaultRefDate() } = options;

if (years <= 0) {
throw new FakerError('Years must be greater than 0.');
}

const date = toDate(refDate, this.faker.defaultRefDate);
const range = {
min: 1000,
max: years * 365 * 24 * 3600 * 1000,
};
const time = toDate(refDate).getTime();

let future = date.getTime();
future += this.faker.number.int(range); // some time from now to N years later, in milliseconds
date.setTime(future);

return date;
return this.between({
from: time + 1000,
to: time + years * 365 * 24 * 3600 * 1000,
});
}

/**
Expand All @@ -192,11 +176,10 @@ export class SimpleDateModule extends SimpleModuleBase {
}): Date {
const { from, to } = options;

const fromMs = toDate(from, this.faker.defaultRefDate).getTime();
const toMs = toDate(to, this.faker.defaultRefDate).getTime();
const dateOffset = this.faker.number.int(toMs - fromMs);
const fromMs = toDate(from, 'from').getTime();
const toMs = toDate(to, 'to').getTime();

return new Date(fromMs + dateOffset);
return new Date(this.faker.number.int({ min: fromMs, max: toMs }));
}

/**
Expand Down Expand Up @@ -291,23 +274,18 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const { days = 1, refDate } = options;
const { days = 1, refDate = this.faker.defaultRefDate() } = options;

if (days <= 0) {
throw new FakerError('Days must be greater than 0.');
}

const date = toDate(refDate, this.faker.defaultRefDate);
const range = {
min: 1000,
max: days * 24 * 3600 * 1000,
};

let future = date.getTime();
future -= this.faker.number.int(range); // some time from now to N days ago, in milliseconds
date.setTime(future);
const time = toDate(refDate).getTime();

return date;
return this.between({
from: time - days * 24 * 3600 * 1000,
to: time - 1000,
});
}

/**
Expand Down Expand Up @@ -342,23 +320,18 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const { days = 1, refDate } = options;
const { days = 1, refDate = this.faker.defaultRefDate() } = options;

if (days <= 0) {
throw new FakerError('Days must be greater than 0.');
}

const date = toDate(refDate, this.faker.defaultRefDate);
const range = {
min: 1000,
max: days * 24 * 3600 * 1000,
};
const time = toDate(refDate).getTime();

let future = date.getTime();
future += this.faker.number.int(range); // some time from now to N days later, in milliseconds
date.setTime(future);

return date;
return this.between({
from: time + 1000,
to: time + days * 24 * 3600 * 1000,
});
}

/**
Expand Down Expand Up @@ -415,9 +388,9 @@ export class SimpleDateModule extends SimpleModuleBase {
refDate?: string | Date | number;
} = {}
): Date {
const mode = options.mode === 'age' ? 'age' : 'year';
const refDate = toDate(options.refDate, this.faker.defaultRefDate);
const refYear = refDate.getUTCFullYear();
const { mode = 'year', refDate = this.faker.defaultRefDate() } = options;
const date = toDate(refDate);
const refYear = date.getUTCFullYear();

// If no min or max is specified, generate a random date between (now - 80) years and (now - 18) years respectively
// So that people can still be considered as adults in most cases
Expand All @@ -426,8 +399,8 @@ export class SimpleDateModule extends SimpleModuleBase {
let min: number;
let max: number;
if (mode === 'age') {
min = new Date(refDate).setUTCFullYear(refYear - (options.max ?? 80) - 1);
max = new Date(refDate).setUTCFullYear(refYear - (options.min ?? 18));
min = new Date(date).setUTCFullYear(refYear - (options.max ?? 80) - 1);
max = new Date(date).setUTCFullYear(refYear - (options.min ?? 18));
} else {
// Avoid generating dates the first and last date of the year
// to avoid running into other years depending on the timezone.
Expand Down
48 changes: 24 additions & 24 deletions test/modules/__snapshots__/date.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,21 @@ exports[`date > 42 > month > with abbreviated = true and context = true 1`] = `"

exports[`date > 42 > month > with context = true 1`] = `"May"`;

exports[`date > 42 > past > with only Date refDate 1`] = `2020-10-08T00:10:57.898Z`;
exports[`date > 42 > past > with only Date refDate 1`] = `2020-07-08T10:07:32.524Z`;

exports[`date > 42 > past > with only number refDate 1`] = `2020-10-08T00:10:57.898Z`;
exports[`date > 42 > past > with only number refDate 1`] = `2020-07-08T10:07:32.524Z`;

exports[`date > 42 > past > with only string refDate 1`] = `2020-10-08T00:10:57.898Z`;
exports[`date > 42 > past > with only string refDate 1`] = `2020-07-08T10:07:32.524Z`;

exports[`date > 42 > past > with value 1`] = `2017-05-26T15:26:23.206Z`;
exports[`date > 42 > past > with value 1`] = `2014-11-22T18:52:07.216Z`;

exports[`date > 42 > recent > with only Date refDate 1`] = `2021-02-21T08:09:54.819Z`;
exports[`date > 42 > recent > with only Date refDate 1`] = `2021-02-21T02:08:35.603Z`;

exports[`date > 42 > recent > with only number refDate 1`] = `2021-02-21T08:09:54.819Z`;
exports[`date > 42 > recent > with only number refDate 1`] = `2021-02-21T02:08:35.603Z`;

exports[`date > 42 > recent > with only string refDate 1`] = `2021-02-21T08:09:54.819Z`;
exports[`date > 42 > recent > with only string refDate 1`] = `2021-02-21T02:08:35.603Z`;

exports[`date > 42 > recent > with value 1`] = `2021-02-17T23:15:52.423Z`;
exports[`date > 42 > recent > with value 1`] = `2021-02-15T11:02:37.999Z`;

exports[`date > 42 > soon > with only Date refDate 1`] = `2021-02-22T02:08:36.603Z`;

Expand Down Expand Up @@ -223,21 +223,21 @@ exports[`date > 1211 > month > with abbreviated = true and context = true 1`] =

exports[`date > 1211 > month > with context = true 1`] = `"December"`;

exports[`date > 1211 > past > with only Date refDate 1`] = `2020-03-19T19:19:04.066Z`;
exports[`date > 1211 > past > with only Date refDate 1`] = `2021-01-26T14:59:26.356Z`;

exports[`date > 1211 > past > with only number refDate 1`] = `2020-03-19T19:19:04.066Z`;
exports[`date > 1211 > past > with only number refDate 1`] = `2021-01-26T14:59:26.356Z`;

exports[`date > 1211 > past > with only string refDate 1`] = `2020-03-19T19:19:04.066Z`;
exports[`date > 1211 > past > with only string refDate 1`] = `2021-01-26T14:59:26.356Z`;

exports[`date > 1211 > past > with value 1`] = `2011-11-12T14:47:19.904Z`;
exports[`date > 1211 > past > with value 1`] = `2020-06-05T19:31:10.518Z`;

exports[`date > 1211 > recent > with only Date refDate 1`] = `2021-02-20T18:52:11.498Z`;
exports[`date > 1211 > recent > with only Date refDate 1`] = `2021-02-21T15:26:18.924Z`;

exports[`date > 1211 > recent > with only number refDate 1`] = `2021-02-20T18:52:11.498Z`;
exports[`date > 1211 > recent > with only number refDate 1`] = `2021-02-21T15:26:18.924Z`;

exports[`date > 1211 > recent > with only string refDate 1`] = `2021-02-20T18:52:11.498Z`;
exports[`date > 1211 > recent > with only string refDate 1`] = `2021-02-21T15:26:18.924Z`;

exports[`date > 1211 > recent > with value 1`] = `2021-02-12T10:18:34.226Z`;
exports[`date > 1211 > recent > with value 1`] = `2021-02-20T23:59:56.196Z`;

exports[`date > 1211 > soon > with only Date refDate 1`] = `2021-02-22T15:26:19.924Z`;

Expand Down Expand Up @@ -349,21 +349,21 @@ exports[`date > 1337 > month > with abbreviated = true and context = true 1`] =

exports[`date > 1337 > month > with context = true 1`] = `"April"`;

exports[`date > 1337 > past > with only Date refDate 1`] = `2020-11-18T01:49:04.822Z`;
exports[`date > 1337 > past > with only Date refDate 1`] = `2020-05-28T08:29:25.600Z`;

exports[`date > 1337 > past > with only number refDate 1`] = `2020-11-18T01:49:04.822Z`;
exports[`date > 1337 > past > with only number refDate 1`] = `2020-05-28T08:29:25.600Z`;

exports[`date > 1337 > past > with only string refDate 1`] = `2020-11-18T01:49:04.822Z`;
exports[`date > 1337 > past > with only string refDate 1`] = `2020-05-28T08:29:25.600Z`;

exports[`date > 1337 > past > with value 1`] = `2018-07-11T07:47:33.460Z`;
exports[`date > 1337 > past > with value 1`] = `2013-10-08T02:30:56.962Z`;

exports[`date > 1337 > recent > with only Date refDate 1`] = `2021-02-21T10:51:56.041Z`;
exports[`date > 1337 > recent > with only Date refDate 1`] = `2021-02-20T23:26:34.381Z`;

exports[`date > 1337 > recent > with only number refDate 1`] = `2021-02-21T10:51:56.041Z`;
exports[`date > 1337 > recent > with only number refDate 1`] = `2021-02-20T23:26:34.381Z`;

exports[`date > 1337 > recent > with only string refDate 1`] = `2021-02-21T10:51:56.041Z`;
exports[`date > 1337 > recent > with only string refDate 1`] = `2021-02-20T23:26:34.381Z`;

exports[`date > 1337 > recent > with value 1`] = `2021-02-19T02:16:05.654Z`;
exports[`date > 1337 > recent > with value 1`] = `2021-02-14T08:02:24.768Z`;

exports[`date > 1337 > soon > with only Date refDate 1`] = `2021-02-21T23:26:35.381Z`;

Expand Down

0 comments on commit 3485e7e

Please sign in to comment.