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

refactor(date)!: birthdate improvements #2756

Merged
merged 24 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8bde608
test(date): additional birthday tests
ST-DDT Nov 9, 2023
09b1740
Merge branch 'next' into test/date/birthday
ST-DDT Mar 16, 2024
dc22120
Merge branch 'next' into test/date/birthday
ST-DDT Mar 16, 2024
7d7a654
chore: apply suggestions
ST-DDT Mar 16, 2024
92a7958
refactor(date)!: change birthdate default mode to age
ST-DDT Mar 16, 2024
d573935
chore: add error attempting to prevent migration errors
ST-DDT Mar 17, 2024
b8bc1ca
Merge branch 'next' into test/date/birthday
ST-DDT Mar 17, 2024
fb4fbf1
refactor: signature improvements
ST-DDT Mar 17, 2024
d1956ad
docs: remove defaults
ST-DDT Mar 17, 2024
e4747e3
adjust backwards compatibility and migration guide
ST-DDT Mar 17, 2024
bcef417
remove default comment
ST-DDT Mar 17, 2024
6870d15
chore add remove in v10 comment
ST-DDT Mar 17, 2024
ffc431a
Merge branch 'next' into test/date/birthday
ST-DDT Mar 24, 2024
79277ec
refactor: throw errors and add distinct signatures
ST-DDT Mar 24, 2024
e637e13
Merge branch 'next' into test/date/birthday
ST-DDT Mar 27, 2024
59426d5
chore: apply review suggestions
ST-DDT Mar 27, 2024
bc9eb71
Merge branch 'next' into test/date/birthday
ST-DDT Mar 28, 2024
01aeb37
Merge branch 'next' into test/date/birthday
ST-DDT Mar 28, 2024
3fd134d
docs: fix jsdocs
ST-DDT Mar 28, 2024
fb023aa
chore: cleanup and simplifications
ST-DDT Mar 29, 2024
5a8d023
Merge branch 'next' into test/date/birthday
ST-DDT Mar 29, 2024
737e2d7
test: fix seeded tests
ST-DDT Mar 29, 2024
083a059
chore: apply suggestions
ST-DDT Mar 30, 2024
63b60eb
Merge branch 'next' into test/date/birthday
ST-DDT Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/guide/upgrading_v9/2756.md
Original file line number Diff line number Diff line change
@@ -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.
237 changes: 173 additions & 64 deletions src/modules/date/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
*
* @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) {
Shinigami92 marked this conversation as resolved.
Show resolved Hide resolved
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 });
}
}
}
}

Expand Down
30 changes: 9 additions & 21 deletions test/modules/__snapshots__/date.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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`;

Expand Down Expand Up @@ -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`;

Expand Down Expand Up @@ -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`;

Expand Down
Loading