Skip to content

Commit 5e66d96

Browse files
ST-DDTluciferreevesShinigami92
authored
feat: add date.birthdate (#962)
Co-authored-by: Priyansh <[email protected]> Co-authored-by: Shinigami <[email protected]>
1 parent f9a1415 commit 5e66d96

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

src/modules/date/index.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Faker } from '../..';
22
import type { DateEntryDefinition } from '../../definitions';
3+
import { FakerError } from '../../errors/faker-error';
34

45
/**
56
* Converts date passed as a string, number or Date to a Date object.
@@ -253,4 +254,63 @@ export class _Date {
253254

254255
return this.faker.helpers.arrayElement(source[type]);
255256
}
257+
258+
/**
259+
* Returns a random birthdate.
260+
*
261+
* @param options The options to use to generate the birthdate. If no options are set, an age between 18 and 80 (inclusive) is generated.
262+
* @param options.min The minimum age or year to generate a birthdate.
263+
* @param options.max The maximum age or year to generate a birthdate.
264+
* @param options.refDate The date to use as reference point for the newly generated date. Defaults to `now`.
265+
* @param options.mode The mode to generate the birthdate. Supported modes are `'age'` and `'year'` .
266+
*
267+
* There are two modes available `'age'` and `'year'`:
268+
* - `'age'`: The min and max options define the age of the person (e.g. `18` - `42`).
269+
* - `'year'`: The min and max options define the range the birthdate may be in (e.g. `1900` - `2000`).
270+
*
271+
* Defaults to `year`.
272+
*
273+
* @example
274+
* faker.date.birthdate() // 1977-07-10T01:37:30.719Z
275+
* faker.date.birthdate({ min: 18, max: 65, mode: 'age' }) // 2003-11-02T20:03:20.116Z
276+
* faker.date.birthdate({ min: 1900, max: 2000, mode: 'year' }) // 1940-08-20T08:53:07.538Z
277+
*/
278+
birthdate(
279+
options: {
280+
min?: number;
281+
max?: number;
282+
mode?: 'age' | 'year';
283+
refDate?: string | Date | number;
284+
} = {}
285+
): Date {
286+
const mode = options.mode === 'age' ? 'age' : 'year';
287+
const refDate = toDate(options.refDate);
288+
const refYear = refDate.getUTCFullYear();
289+
290+
// If no min or max is specified, generate a random date between (now - 80) years and (now - 18) years respectively
291+
// So that people can still be considered as adults in most cases
292+
293+
// Convert to epoch timestamps
294+
let min: number;
295+
let max: number;
296+
if (mode === 'age') {
297+
min = new Date(refDate).setUTCFullYear(refYear - (options.max ?? 80) - 1);
298+
max = new Date(refDate).setUTCFullYear(refYear - (options.min ?? 18));
299+
} else {
300+
// Avoid generating dates the first and last date of the year
301+
// to avoid running into other years depending on the timezone.
302+
min = new Date(Date.UTC(0, 0, 2)).setUTCFullYear(
303+
options.min ?? refYear - 80
304+
);
305+
max = new Date(Date.UTC(0, 11, 30)).setUTCFullYear(
306+
options.max ?? refYear - 18
307+
);
308+
}
309+
310+
if (max < min) {
311+
throw new FakerError(`Max ${max} should be larger then min ${min}.`);
312+
}
313+
314+
return new Date(this.faker.datatype.number({ min, max }));
315+
}
256316
}

test/date.spec.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ const seededRuns = [
3333
context: 'Tuesday',
3434
abbr_context: 'Tue',
3535
},
36+
birthdate: {
37+
noArgs: new Date('1943-08-06T10:03:17.283Z'),
38+
ageMode: new Date('1942-09-15T09:55:20.478Z'),
39+
ageRange: new Date('1941-12-15T14:59:26.122Z'),
40+
age: new Date('1959-06-26T13:52:19.442Z'),
41+
yearMode: new Date('1943-08-06T10:03:17.283Z'),
42+
yearRange: new Date('1937-10-30T15:52:07.381Z'),
43+
year: new Date('2000-05-16T22:59:36.513Z'),
44+
},
3645
},
3746
},
3847
{
@@ -66,6 +75,15 @@ const seededRuns = [
6675
context: 'Monday',
6776
abbr_context: 'Mon',
6877
},
78+
birthdate: {
79+
noArgs: new Date('1936-07-04T15:55:47.989Z'),
80+
ageMode: new Date('1935-08-14T07:41:47.183Z'),
81+
ageRange: new Date('1935-02-03T18:44:07.874Z'),
82+
age: new Date('1959-05-16T12:14:12.585Z'),
83+
yearMode: new Date('1936-07-04T15:55:47.989Z'),
84+
yearRange: new Date('1926-06-20T07:18:05.539Z'),
85+
year: new Date('2000-04-06T02:45:32.324Z'),
86+
},
6987
},
7088
},
7189
{
@@ -99,6 +117,15 @@ const seededRuns = [
99117
context: 'Saturday',
100118
abbr_context: 'Sat',
101119
},
120+
birthdate: {
121+
noArgs: new Date('1978-06-29T09:24:02.647Z'),
122+
ageMode: new Date('1977-08-10T01:09:17.468Z'),
123+
ageRange: new Date('1975-10-01T07:11:50.190Z'),
124+
age: new Date('1960-01-14T18:44:13.966Z'),
125+
yearMode: new Date('1978-06-29T09:24:02.647Z'),
126+
yearRange: new Date('1993-10-11T07:44:59.519Z'),
127+
year: new Date('2000-12-04T01:16:03.286Z'),
128+
},
102129
},
103130
},
104131
];
@@ -361,6 +388,92 @@ describe('date', () => {
361388
expect(actual).toEqual(expectations.weekday.abbr_context);
362389
});
363390
});
391+
392+
describe('birthdate()', () => {
393+
it('should return deterministic value birthdate by default', () => {
394+
faker.seed(seed);
395+
396+
const actual = faker.date.birthdate({
397+
refDate: '2000-02-09T20:54:02.397Z',
398+
});
399+
400+
expect(actual).toEqual(expectations.birthdate.noArgs);
401+
});
402+
403+
it('should return deterministic value birthdate by age mode ', () => {
404+
faker.seed(seed);
405+
406+
const actual = faker.date.birthdate({
407+
mode: 'age',
408+
refDate: '2000-02-09T20:54:02.397Z',
409+
});
410+
411+
expect(actual).toEqual(expectations.birthdate.ageMode);
412+
});
413+
414+
it('should return deterministic value birthdate by age range', () => {
415+
faker.seed(seed);
416+
417+
const actual = faker.date.birthdate({
418+
min: 20,
419+
max: 80,
420+
mode: 'age',
421+
refDate: '2000-02-09T20:54:02.397Z',
422+
});
423+
424+
expect(actual).toEqual(expectations.birthdate.ageRange);
425+
});
426+
427+
it('should return deterministic value birthdate by age', () => {
428+
faker.seed(seed);
429+
430+
const actual = faker.date.birthdate({
431+
min: 40,
432+
max: 40,
433+
mode: 'age',
434+
refDate: '2000-02-09T20:54:02.397Z',
435+
});
436+
437+
expect(actual).toEqual(expectations.birthdate.age);
438+
});
439+
440+
it('should return deterministic value birthdate by year mode', () => {
441+
faker.seed(seed);
442+
443+
const actual = faker.date.birthdate({
444+
mode: 'year',
445+
refDate: '2000-02-09T20:54:02.397Z',
446+
});
447+
448+
expect(actual).toEqual(expectations.birthdate.yearMode);
449+
});
450+
451+
it('should return deterministic value birthdate by year range', () => {
452+
faker.seed(seed);
453+
454+
const actual = faker.date.birthdate({
455+
min: 1900,
456+
max: 2000,
457+
mode: 'year',
458+
refDate: '2000-02-09T20:54:02.397Z',
459+
});
460+
461+
expect(actual).toEqual(expectations.birthdate.yearRange);
462+
});
463+
464+
it('should return deterministic value birthdate by year', () => {
465+
faker.seed(seed);
466+
467+
const actual = faker.date.birthdate({
468+
min: 2000,
469+
max: 2000,
470+
mode: 'year',
471+
refDate: '2000-02-09T20:54:02.397Z',
472+
});
473+
474+
expect(actual).toEqual(expectations.birthdate.year);
475+
});
476+
});
364477
});
365478
}
366479

@@ -612,6 +725,45 @@ describe('date', () => {
612725
faker.definitions.date.weekday.abbr_context = backup_abbr_context;
613726
});
614727
});
728+
729+
describe('birthdate', () => {
730+
it('returns a random birthdate', () => {
731+
const birthdate = faker.date.birthdate();
732+
expect(birthdate).toBeInstanceOf(Date);
733+
});
734+
735+
it('returns a random birthdate between two years', () => {
736+
const min = 1990;
737+
const max = 2000;
738+
739+
const birthdate = faker.date.birthdate({ min, max, mode: 'year' });
740+
741+
// birthdate is a date object
742+
expect(birthdate).toBeInstanceOf(Date);
743+
744+
// Generated date is between min and max
745+
expect(birthdate.getUTCFullYear()).toBeGreaterThanOrEqual(min);
746+
expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(max);
747+
});
748+
749+
it('returns a random birthdate between two ages', () => {
750+
const min = 4;
751+
const max = 5;
752+
753+
const birthdate = faker.date.birthdate({ min, max, mode: 'age' });
754+
755+
// birthdate is a date object
756+
expect(birthdate).toBeInstanceOf(Date);
757+
758+
// Generated date is between min and max
759+
expect(birthdate.getUTCFullYear()).toBeGreaterThanOrEqual(
760+
new Date().getUTCFullYear() - max - 1
761+
);
762+
expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(
763+
new Date().getUTCFullYear() - min
764+
);
765+
});
766+
});
615767
}
616768
});
617769
});

0 commit comments

Comments
 (0)