Skip to content

Commit

Permalink
feat: add date.birthdate (#962)
Browse files Browse the repository at this point in the history
Co-authored-by: Priyansh <bobbyskhs@gmail.com>
Co-authored-by: Shinigami <chrissi92@hotmail.de>
  • Loading branch information
3 people committed May 17, 2022
1 parent f9a1415 commit 5e66d96
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 0 deletions.
60 changes: 60 additions & 0 deletions src/modules/date/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Faker } from '../..';
import type { DateEntryDefinition } from '../../definitions';
import { FakerError } from '../../errors/faker-error';

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

return this.faker.helpers.arrayElement(source[type]);
}

/**
* Returns a random 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'` .
*
* 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`).
*
* Defaults to `year`.
*
* @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
*/
birthdate(
options: {
min?: number;
max?: number;
mode?: 'age' | 'year';
refDate?: string | Date | number;
} = {}
): Date {
const mode = options.mode === 'age' ? 'age' : 'year';
const refDate = toDate(options.refDate);
const refYear = refDate.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(refDate).setUTCFullYear(refYear - (options.max ?? 80) - 1);
max = new Date(refDate).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 - 18
);
}

if (max < min) {
throw new FakerError(`Max ${max} should be larger then min ${min}.`);
}

return new Date(this.faker.datatype.number({ min, max }));
}
}
152 changes: 152 additions & 0 deletions test/date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ const seededRuns = [
context: 'Tuesday',
abbr_context: 'Tue',
},
birthdate: {
noArgs: new Date('1943-08-06T10:03:17.283Z'),
ageMode: new Date('1942-09-15T09:55:20.478Z'),
ageRange: new Date('1941-12-15T14:59:26.122Z'),
age: new Date('1959-06-26T13:52:19.442Z'),
yearMode: new Date('1943-08-06T10:03:17.283Z'),
yearRange: new Date('1937-10-30T15:52:07.381Z'),
year: new Date('2000-05-16T22:59:36.513Z'),
},
},
},
{
Expand Down Expand Up @@ -66,6 +75,15 @@ const seededRuns = [
context: 'Monday',
abbr_context: 'Mon',
},
birthdate: {
noArgs: new Date('1936-07-04T15:55:47.989Z'),
ageMode: new Date('1935-08-14T07:41:47.183Z'),
ageRange: new Date('1935-02-03T18:44:07.874Z'),
age: new Date('1959-05-16T12:14:12.585Z'),
yearMode: new Date('1936-07-04T15:55:47.989Z'),
yearRange: new Date('1926-06-20T07:18:05.539Z'),
year: new Date('2000-04-06T02:45:32.324Z'),
},
},
},
{
Expand Down Expand Up @@ -99,6 +117,15 @@ const seededRuns = [
context: 'Saturday',
abbr_context: 'Sat',
},
birthdate: {
noArgs: new Date('1978-06-29T09:24:02.647Z'),
ageMode: new Date('1977-08-10T01:09:17.468Z'),
ageRange: new Date('1975-10-01T07:11:50.190Z'),
age: new Date('1960-01-14T18:44:13.966Z'),
yearMode: new Date('1978-06-29T09:24:02.647Z'),
yearRange: new Date('1993-10-11T07:44:59.519Z'),
year: new Date('2000-12-04T01:16:03.286Z'),
},
},
},
];
Expand Down Expand Up @@ -361,6 +388,92 @@ describe('date', () => {
expect(actual).toEqual(expectations.weekday.abbr_context);
});
});

describe('birthdate()', () => {
it('should return deterministic value birthdate by default', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.noArgs);
});

it('should return deterministic value birthdate by age mode ', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
mode: 'age',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.ageMode);
});

it('should return deterministic value birthdate by age range', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
min: 20,
max: 80,
mode: 'age',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.ageRange);
});

it('should return deterministic value birthdate by age', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
min: 40,
max: 40,
mode: 'age',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.age);
});

it('should return deterministic value birthdate by year mode', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
mode: 'year',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.yearMode);
});

it('should return deterministic value birthdate by year range', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
min: 1900,
max: 2000,
mode: 'year',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.yearRange);
});

it('should return deterministic value birthdate by year', () => {
faker.seed(seed);

const actual = faker.date.birthdate({
min: 2000,
max: 2000,
mode: 'year',
refDate: '2000-02-09T20:54:02.397Z',
});

expect(actual).toEqual(expectations.birthdate.year);
});
});
});
}

Expand Down Expand Up @@ -612,6 +725,45 @@ describe('date', () => {
faker.definitions.date.weekday.abbr_context = backup_abbr_context;
});
});

describe('birthdate', () => {
it('returns a random birthdate', () => {
const birthdate = faker.date.birthdate();
expect(birthdate).toBeInstanceOf(Date);
});

it('returns a random birthdate between two years', () => {
const min = 1990;
const max = 2000;

const birthdate = faker.date.birthdate({ min, max, mode: 'year' });

// birthdate is a date object
expect(birthdate).toBeInstanceOf(Date);

// Generated date is between min and max
expect(birthdate.getUTCFullYear()).toBeGreaterThanOrEqual(min);
expect(birthdate.getUTCFullYear()).toBeLessThanOrEqual(max);
});

it('returns a random birthdate between two ages', () => {
const min = 4;
const max = 5;

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
);
});
});
}
});
});

0 comments on commit 5e66d96

Please sign in to comment.