aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/guide/upgrading_v9/2756.md6
-rw-r--r--src/modules/date/index.ts237
-rw-r--r--test/modules/__snapshots__/date.spec.ts.snap30
-rw-r--r--test/modules/date.spec.ts115
4 files changed, 257 insertions, 131 deletions
diff --git a/docs/guide/upgrading_v9/2756.md b/docs/guide/upgrading_v9/2756.md
new file mode 100644
index 00000000..da0be8eb
--- /dev/null
+++ b/docs/guide/upgrading_v9/2756.md
@@ -0,0 +1,6 @@
+### Changed default mode from birthdate
+
+Previously, the method had defaults that were unclear in their specific impact.
+Now, the method requires either none or all of the `min`, `max` and `mode` options.
+
+We also improved the error messages in case of invalid min/max age/year ranges.
diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts
index 19dea55c..f851c62e 100644
--- a/src/modules/date/index.ts
+++ b/src/modules/date/index.ts
@@ -357,90 +357,199 @@ export class SimpleDateModule extends SimpleModuleBase {
}
/**
- * Returns a random birthdate.
+ * Returns a random birthdate. By default, the birthdate is generated for an adult between 18 and 80 years old.
+ * But you can customize the `'age'` range or the `'year'` range to generate a more specific birthdate.
*
- * @param options The options to use to generate the birthdate. If no options are set, an age between 18 and 80 (inclusive) is generated.
- * @param options.min The minimum age or year to generate a birthdate.
- * @param options.max The maximum age or year to generate a birthdate.
- * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `now`.
- * @param options.mode The mode to generate the birthdate. Supported modes are `'age'` and `'year'` .
+ * @param options The options to use to generate the birthdate.
+ * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `faker.defaultRefDate()`.
+ *
+ * @example
+ * faker.date.birthdate() // 1977-07-10T01:37:30.719Z
+ *
+ * @since 7.0.0
+ */
+ birthdate(options?: {
+ /**
+ * The date to use as reference point for the newly generated date.
+ *
+ * @default faker.defaultRefDate()
+ */
+ refDate?: string | Date | number;
+ }): Date;
+ /**
+ * Returns a random birthdate for a given age range.
+ *
+ * @param options The options to use to generate the birthdate.
+ * @param options.mode `'age'` to generate a birthdate based on the age range. It is also possible to generate a birthdate based on a `'year'` range.
+ * @param options.min The minimum age to generate a birthdate for.
+ * @param options.max The maximum age to generate a birthdate for.
+ * @param options.refDate The date to use as reference point for the newly generated date. Defaults to `faker.defaultRefDate()`.
+ *
+ * @example
+ * faker.date.birthdate({ mode: 'age', min: 18, max: 65 }) // 2003-11-02T20:03:20.116Z
*
- * There are two modes available `'age'` and `'year'`:
- * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`).
- * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`).
+ * @since 7.0.0
+ */
+ birthdate(options: {
+ /**
+ * `'age'` to generate a birthdate based on the age range.
+ * It is also possible to generate a birthdate based on a `'year'` range.
+ */
+ mode: 'age';
+ /**
+ * The minimum age to generate a birthdate for.
+ */
+ min: number;
+ /**
+ * The maximum age to generate a birthdate for.
+ */
+ max: number;
+ /**
+ * The date to use as reference point for the newly generated date.
+ *
+ * @default faker.defaultRefDate()
+ */
+ refDate?: string | Date | number;
+ }): Date;
+ /**
+ * Returns a random birthdate in the given range of years.
+ *
+ * @param options The options to use to generate the birthdate.
+ * @param options.mode `'year'` to generate a birthdate based on the year range. It is also possible to generate a birthdate based on a `'age'` range.
+ * @param options.min The minimum year to generate a birthdate in.
+ * @param options.max The maximum year to generate a birthdate in.
+ *
+ * @example
+ * faker.date.birthdate({ mode: 'year', min: 1900, max: 2000 }) // 1940-08-20T08:53:07.538Z
+ *
+ * @since 7.0.0
+ */
+ birthdate(options: {
+ /**
+ * `'year'` to generate a birthdate based on the year range.
+ * It is also possible to generate a birthdate based on an `'age'` range.
+ */
+ mode: 'year';
+ /**
+ * The minimum year to generate a birthdate in.
+ */
+ min: number;
+ /**
+ * The maximum year to generate a birthdate in.
+ */
+ max: number;
+ }): Date;
+ /**
+ * Returns a random birthdate. By default, the birthdate is generated for an adult between 18 and 80 years old.
+ * But you can customize the `'age'` range or the `'year'` range to generate a more specific birthdate.
*
- * Defaults to `year`.
+ * @param options The options to use to generate the birthdate.
+ * @param options.mode Either `'age'` or `'year'` to generate a birthdate based on the age or year range.
+ * @param options.min The minimum age or year to generate a birthdate in.
+ * @param options.max The maximum age or year to generate a birthdate in.
+ * @param options.refDate The date to use as reference point for the newly generated date.
+ * Only used when `mode` is `'age'`.
+ * Defaults to `faker.defaultRefDate()`.
*
* @example
* faker.date.birthdate() // 1977-07-10T01:37:30.719Z
- * faker.date.birthdate({ min: 18, max: 65, mode: 'age' }) // 2003-11-02T20:03:20.116Z
- * faker.date.birthdate({ min: 1900, max: 2000, mode: 'year' }) // 1940-08-20T08:53:07.538Z
+ * faker.date.birthdate({ mode: 'age', min: 18, max: 65 }) // 2003-11-02T20:03:20.116Z
+ * faker.date.birthdate({ mode: 'year', min: 1900, max: 2000 }) // 1940-08-20T08:53:07.538Z
*
* @since 7.0.0
*/
birthdate(
+ options?:
+ | {
+ /**
+ * The date to use as reference point for the newly generated date.
+ *
+ * @default faker.defaultRefDate()
+ */
+ refDate?: string | Date | number;
+ }
+ | {
+ /**
+ * Either `'age'` or `'year'` to generate a birthdate based on the age or year range.
+ */
+ mode: 'age' | 'year';
+ /**
+ * The minimum age/year to generate a birthdate for/in.
+ */
+ min: number;
+ /**
+ * The maximum age/year to generate a birthdate for/in.
+ */
+ max: number;
+ /**
+ * The date to use as reference point for the newly generated date.
+ * Only used when `mode` is `'age'`.
+ *
+ * @default faker.defaultRefDate()
+ */
+ refDate?: string | Date | number;
+ }
+ ): Date;
+ birthdate(
options: {
- /**
- * The minimum age or year to generate a birthdate.
- *
- * @default 18
- */
+ mode?: 'age' | 'year';
min?: number;
- /**
- * The maximum age or year to generate a birthdate.
- *
- * @default 80
- */
max?: number;
- /**
- * The mode to generate the birthdate. Supported modes are `'age'` and `'year'` .
- *
- * There are two modes available `'age'` and `'year'`:
- * - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`).
- * - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`).
- *
- * @default 'year'
- */
- mode?: 'age' | 'year';
- /**
- * The date to use as reference point for the newly generated date.
- *
- * @default faker.defaultRefDate()
- */
refDate?: string | Date | number;
} = {}
): Date {
- 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
-
- // Convert to epoch timestamps
- let min: number;
- let max: number;
- if (mode === 'age') {
- 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.
- min = new Date(Date.UTC(0, 0, 2)).setUTCFullYear(
- options.min ?? refYear - 80
- );
- max = new Date(Date.UTC(0, 11, 30)).setUTCFullYear(
- options.max ?? refYear - 19
- );
- }
-
- if (max < min) {
+ const {
+ mode = 'age',
+ min = 18,
+ max = 80,
+ refDate: rawRefDate = this.faker.defaultRefDate(),
+ mode: originalMode,
+ min: originalMin,
+ max: originalMax,
+ } = options;
+
+ // TODO @ST-DDT 2024-03-17: Remove check in v10
+ const optionsSet = [originalMin, originalMax, originalMode].filter(
+ (x) => x != null
+ ).length;
+ if (optionsSet % 3 !== 0) {
throw new FakerError(
- `Max ${options.max} should be larger than or equal to min ${options.min}.`
+ "The 'min', 'max', and 'mode' options must be set together."
);
}
- return new Date(this.faker.number.int({ min, max }));
+ const refDate = toDate(rawRefDate);
+ const refYear = refDate.getUTCFullYear();
+
+ switch (mode) {
+ case 'age': {
+ const from = new Date(refDate).setUTCFullYear(refYear - max - 1);
+ const to = new Date(refDate).setUTCFullYear(refYear - min);
+
+ if (from > to) {
+ throw new FakerError(
+ `Max age ${max} should be greater than or equal to min age ${min}.`
+ );
+ }
+
+ return this.between({ from, to });
+ }
+
+ case 'year': {
+ // Avoid generating dates on the first and last date of the year
+ // to avoid running into other years depending on the timezone.
+ const from = new Date(Date.UTC(0, 0, 2)).setUTCFullYear(min);
+ const to = new Date(Date.UTC(0, 11, 30)).setUTCFullYear(max);
+
+ if (from > to) {
+ throw new FakerError(
+ `Max year ${max} should be greater than or equal to min year ${min}.`
+ );
+ }
+
+ return this.between({ from, to });
+ }
+ }
}
}
diff --git a/test/modules/__snapshots__/date.spec.ts.snap b/test/modules/__snapshots__/date.spec.ts.snap
index f67a339d..d73d48cd 100644
--- a/test/modules/__snapshots__/date.spec.ts.snap
+++ b/test/modules/__snapshots__/date.spec.ts.snap
@@ -67,17 +67,13 @@ exports[`date > 42 > betweens > with string dates and count 1`] = `
exports[`date > 42 > birthdate > with age and refDate 1`] = `1980-07-07T19:06:53.165Z`;
-exports[`date > 42 > birthdate > with age mode and refDate 1`] = `1963-09-27T06:10:42.813Z`;
-
exports[`date > 42 > birthdate > with age range and refDate 1`] = `1962-12-27T20:14:08.437Z`;
-exports[`date > 42 > birthdate > with only refDate 1`] = `1964-03-22T08:05:48.849Z`;
-
-exports[`date > 42 > birthdate > with year and refDate 1`] = `0020-07-07T19:06:53.165Z`;
+exports[`date > 42 > birthdate > with only refDate 1`] = `1963-09-27T06:10:42.813Z`;
-exports[`date > 42 > birthdate > with year mode and refDate 1`] = `1964-03-22T08:05:48.849Z`;
+exports[`date > 42 > birthdate > with year 1`] = `2000-05-16T22:59:36.655Z`;
-exports[`date > 42 > birthdate > with year range and refDate 1`] = `0057-12-20T11:59:38.353Z`;
+exports[`date > 42 > birthdate > with year range 1`] = `1937-10-30T15:52:21.843Z`;
exports[`date > 42 > future > with only Date refDate 1`] = `2021-07-08T10:07:33.524Z`;
@@ -195,17 +191,13 @@ exports[`date > 1211 > betweens > with string dates and count 1`] = `
exports[`date > 1211 > birthdate > with age and refDate 1`] = `1981-01-26T13:16:31.426Z`;
-exports[`date > 1211 > birthdate > with age mode and refDate 1`] = `1998-08-21T21:24:31.101Z`;
-
exports[`date > 1211 > birthdate > with age range and refDate 1`] = `1996-10-13T01:44:07.954Z`;
-exports[`date > 1211 > birthdate > with only refDate 1`] = `1998-07-25T13:16:47.251Z`;
+exports[`date > 1211 > birthdate > with only refDate 1`] = `1998-08-21T21:24:31.101Z`;
-exports[`date > 1211 > birthdate > with year and refDate 1`] = `0021-01-26T13:16:31.426Z`;
+exports[`date > 1211 > birthdate > with year 1`] = `2000-12-04T01:16:03.291Z`;
-exports[`date > 1211 > birthdate > with year mode and refDate 1`] = `1998-07-25T13:16:47.251Z`;
-
-exports[`date > 1211 > birthdate > with year range and refDate 1`] = `0113-12-03T19:45:28.165Z`;
+exports[`date > 1211 > birthdate > with year range 1`] = `1993-10-11T07:45:00.030Z`;
exports[`date > 1211 > future > with only Date refDate 1`] = `2022-01-26T14:59:27.356Z`;
@@ -321,17 +313,13 @@ exports[`date > 1337 > betweens > with string dates and count 1`] = `
exports[`date > 1337 > birthdate > with age and refDate 1`] = `1980-05-27T14:46:44.794Z`;
-exports[`date > 1337 > birthdate > with age mode and refDate 1`] = `1956-08-25T03:56:58.153Z`;
-
exports[`date > 1337 > birthdate > with age range and refDate 1`] = `1956-02-15T21:16:37.850Z`;
-exports[`date > 1337 > birthdate > with only refDate 1`] = `1957-03-31T18:18:16.563Z`;
-
-exports[`date > 1337 > birthdate > with year and refDate 1`] = `0020-05-27T14:46:44.794Z`;
+exports[`date > 1337 > birthdate > with only refDate 1`] = `1956-08-25T03:56:58.153Z`;
-exports[`date > 1337 > birthdate > with year mode and refDate 1`] = `1957-03-31T18:18:16.563Z`;
+exports[`date > 1337 > birthdate > with year 1`] = `2000-04-06T02:45:32.287Z`;
-exports[`date > 1337 > birthdate > with year range and refDate 1`] = `0046-08-09T19:19:14.289Z`;
+exports[`date > 1337 > birthdate > with year range 1`] = `1926-06-20T07:18:01.782Z`;
exports[`date > 1337 > future > with only Date refDate 1`] = `2021-05-28T08:29:26.600Z`;
diff --git a/test/modules/date.spec.ts b/test/modules/date.spec.ts
index 432573c7..d459db33 100644
--- a/test/modules/date.spec.ts
+++ b/test/modules/date.spec.ts
@@ -106,37 +106,27 @@ describe('date', () => {
t.describe('birthdate', (t) => {
t.it('with only refDate', { refDate })
- .it('with age mode and refDate', {
- mode: 'age',
- refDate,
- })
.it('with age and refDate', {
+ mode: 'age',
min: 40,
max: 40,
- mode: 'age',
refDate,
})
.it('with age range and refDate', {
+ mode: 'age',
min: 20,
max: 80,
- mode: 'age',
refDate,
})
- .it('with year mode and refDate', {
+ .it('with year', {
mode: 'year',
- refDate,
- })
- .it('with year and refDate', {
min: 2000,
max: 2000,
- mode: 'age',
- refDate,
})
- .it('with year range and refDate', {
+ .it('with year range', {
+ mode: 'year',
min: 1900,
max: 2000,
- mode: 'age',
- refDate,
});
});
});
@@ -543,9 +533,9 @@ describe('date', () => {
expect(birthdate).toBeInstanceOf(Date);
});
- it('returns a random birthdate between two years', () => {
+ it('returns a random birthdate in one year', () => {
const min = 1990;
- const max = 2000;
+ const max = 1990;
const birthdate = faker.date.birthdate({ min, max, mode: 'year' });
@@ -557,53 +547,72 @@ describe('date', () => {
expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(max);
});
- it('returns a random birthdate that is 18+ by default', () => {
- // Generate the latest possible value => youngest
- faker.seed(2855577693);
-
- const refDate = new Date();
- const birthdate = faker.date.birthdate({ refDate });
- expect(birthdate).toBeInstanceOf(Date);
- const value = birthdate.valueOf();
- const refDateValue = refDate.valueOf();
- expect(value).toBeLessThanOrEqual(refDateValue);
- const deltaDate = new Date(refDateValue - value);
- expect(deltaDate.getUTCFullYear() - 1970).toBeGreaterThanOrEqual(18);
- });
-
- it('returns a random birthdate in one year', () => {
+ it('returns a random birthdate between two years', () => {
const min = 1990;
- const max = 1990;
+ const max = 2000;
const birthdate = faker.date.birthdate({ min, max, mode: 'year' });
// birthdate is a date object
expect(birthdate).toBeInstanceOf(Date);
- expect(birthdate.toISOString()).not.toMatch(/T00:00:00.000Z/);
// Generated date is between min and max
expect(birthdate.getUTCFullYear()).toBeGreaterThanOrEqual(min);
expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(max);
});
+ it('returns a random birthdate for specific age', () => {
+ const min = 21;
+ const max = 21;
+ const refDate = new Date();
+
+ const birthdate = faker.date.birthdate({
+ min,
+ max,
+ refDate,
+ mode: 'age',
+ });
+
+ expect(birthdate).toBeInstanceOf(Date);
+ const value = birthdate.valueOf();
+ const refDateValue = refDate.valueOf();
+ expect(value).toBeLessThanOrEqual(refDateValue);
+ const deltaDate = new Date(refDateValue - value);
+ expect(deltaDate.getUTCFullYear() - 1970).toBe(21);
+ });
+
it('returns a random birthdate between two ages', () => {
- const min = 4;
- const max = 5;
+ const min = 21;
+ const max = 22;
+ const refDate = new Date();
const birthdate = faker.date.birthdate({ min, max, mode: 'age' });
- // birthdate is a date object
expect(birthdate).toBeInstanceOf(Date);
-
- // Generated date is between min and max
- expect(birthdate.getUTCFullYear()).toBeGreaterThanOrEqual(
- new Date().getUTCFullYear() - max - 1
- );
- expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(
- new Date().getUTCFullYear() - min
- );
+ const value = birthdate.valueOf();
+ const refDateValue = refDate.valueOf();
+ expect(value).toBeLessThanOrEqual(refDateValue);
+ const deltaDate = new Date(refDateValue - value);
+ expect(deltaDate.getUTCFullYear() - 1970).toBeGreaterThanOrEqual(21);
+ expect(deltaDate.getUTCFullYear() - 1970).toBeLessThanOrEqual(22);
});
+ it.each(['min', 'max', 'mode'] as const)(
+ "should throw an error when '%s' is not provided",
+ (key) => {
+ const options = { min: 18, max: 80, mode: 'age' } as const;
+
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete options[key];
+
+ expect(() => faker.date.birthdate(options)).toThrow(
+ new FakerError(
+ `The 'min', 'max', and 'mode' options must be set together.`
+ )
+ );
+ }
+ );
+
it('should throw an error when the min > max year', () => {
const min = 2000;
const max = 1990;
@@ -612,7 +621,21 @@ describe('date', () => {
faker.date.birthdate({ min, max, mode: 'year' })
).toThrow(
new FakerError(
- `Max 1990 should be larger than or equal to min 2000.`
+ `Max year 1990 should be greater than or equal to min year 2000.`
+ )
+ );
+ });
+
+ it('should throw an error when the min > max age', () => {
+ const min = 31;
+ const max = 25;
+ const refDate = Date.UTC(2020, 0, 1);
+
+ expect(() =>
+ faker.date.birthdate({ min, max, refDate, mode: 'age' })
+ ).toThrow(
+ new FakerError(
+ `Max age 25 should be greater than or equal to min age 31.`
)
);
});