In a previous post, I outlined my strategy of using the ng-bootstrap datepicker with a formatter for US dates (mm/dd/yyyy).
In this post, I will provide an updated version. Check the specs below for specifics on improvements. Among these are:
- Allows basically any separator (including none at all!)
- Works with or without zero padding months or days
- Allows entry of double digit years, which will then assume 19XX or 20XX depending on the breakpoint that is set (40 is used below - check tests for details)
us-date-parser-formatter.ts
import * as _ from 'lodash-es';
import { NgbDateStruct, NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';
import { Injectable } from '@angular/core';
@Injectable()
export class DateParserFormatter extends NgbDateParserFormatter {
private shortYearBreakpoint = 40;
parse(value: string): NgbDateStruct {
if (!value) { return null; }
let dateParts: string[];
if (/^(?:\d{6}|\d{8})$/.test(value)) {
dateParts = /(\d{2})(\d{2})(\d{2}|\d{4})$/.exec(value).slice(1, 4);
} else if (/^\d{1,2}[-\/]\d{1,2}[-\/](?:\d{2}|\d{4})$/.test(value)) {
dateParts = value.split(/[-\/]/);
} else {
return null;
}
let year = _.toInteger(dateParts[2]);
if (year < 100) {
year += year <= this.shortYearBreakpoint ? 2000 : 1900;
}
return { year, month: _.toInteger(dateParts[0]), day: _.toInteger(dateParts[1]) } as NgbDateStruct;
}
format(date: NgbDateStruct): string {
return date ? `${_.padStart(date.month.toString(), 2, '0')}/${_.padStart(date.day.toString(), 2, '0')}/${date.year}` : '';
}
}
us-date-parser-formatter.spec.ts
import { DateParserFormatter } from './date-parser-formatter';
describe('DateParserFormatter', () => {
let parser: DateParserFormatter;
beforeEach(() => {
parser = new DateParserFormatter();
});
describe('When a string of the format m/d/yy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('1/2/34')).toEqual({ year: 2034, month: 1, day: 2 });
});
});
describe('When a string of the format mm/dd/yyyy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('01/02/1934')).toEqual({ year: 1934, month: 1, day: 2 });
});
});
describe('When a string of the format m/d/yyyy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('1/2/1934')).toEqual({ year: 1934, month: 1, day: 2 });
});
});
describe('When a string of the format m-d-yy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('1-2-34')).toEqual({ year: 2034, month: 1, day: 2 });
});
});
describe('When a string of the format mm-dd-yyyy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('01-02-1934')).toEqual({ year: 1934, month: 1, day: 2 });
});
});
describe('When a string of the format m-d-yyyy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('1-2-1934')).toEqual({ year: 1934, month: 1, day: 2 });
});
});
describe('When a string of the format mmddyy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('010234')).toEqual({ year: 2034, month: 1, day: 2 });
});
});
describe('When a string of the format mmdd19yy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('01021934')).toEqual({ year: 1934, month: 1, day: 2 });
});
});
describe('When a string of the format mmdd20yy is parsed', () => {
it('it returns the correct date object.', () => {
expect(parser.parse('01022034')).toEqual({ year: 2034, month: 1, day: 2 });
});
});
describe('When an invalid string with alpha characters', () => {
it('it returns null.', () => {
expect(parser.parse('abc')).toBeNull();
});
});
describe('When a valid string with additional invalid chars', () => {
it('it returns null.', () => {
expect(parser.parse('123456abc')).toBeNull();
});
});
describe('When an invalid string with numeric characters', () => {
it('it returns null.', () => {
expect(parser.parse('12345')).toBeNull();
});
});
describe('When a struct is formatted', () => {
it('it returns a string in the format mm/dd/yyyy', () => {
expect(parser.format({ year: 1934, month: 1, day: 2 })).toBe('01/02/1934');
});
});
describe('When a falsy value is formatted', () => {
it('it returns an empty string.', () => {
expect(parser.format(undefined)).toBe('');
});
});
describe('When a short year is greater than 40', () => {
it('it assumes 1900s.', () => {
expect(parser.parse('1/2/41')).toEqual({ year: 1941, month: 1, day: 2 });
});
});
describe('When a short year is less than or equal to 40', () => {
it('it assumes 2000s.', () => {
expect(parser.parse('1/2/40')).toEqual({ year: 2040, month: 1, day: 2 });
});
});
});