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

feat: add date.birthdate #962

Merged
merged 6 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
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
);
});
});
}
});
});