NgBootstrap Datepicker - US Date Formatter mm/ff/yyyy

If you have used Angular and Bootstrap then you are probably familiar with ng-bootstrap.

The default formatter that the datepicker uses is the ISO8601 format (yyyy-mm-dd). The docs have a sample that show you how to format the date as dd/mm/yyyy. I have adapted that sample to use the format that is the most widely used in the US (mm/dd/yyyy). Hope you find it useful!

us-date-parser-formatter.spec.ts

import { UnitedStatesDateParserFormatter } from './us-date-parser-formatter';

describe('UnitedStatesDateParserFormatter', () => {
  let sut: UnitedStatesDateParserFormatter;

  beforeEach(() => { sut = new UnitedStatesDateParserFormatter(); });

  describe('Parsing', () => {
    it('should parse null undefined and empty string as null', () => {
      expect(sut.parse(null as any)).toBeNull();
      expect(sut.parse(undefined as any)).toBeNull();
      expect(sut.parse('')).toBeNull();
      expect(sut.parse('   ')).toBeNull();
    });

    it('should parse valid date', () => {
      expect(sut.parse('05/12/2016')).toEqual({ year: 2016, month: 5, day: 12 });
    });

    it('should parse non-date as null', () => {
      expect(sut.parse('foo/bar/baz')).toBeNull();
      expect(sut.parse('2014/bar')).toBeNull();
      expect(sut.parse('2014/11/12/15')).toBeNull();
      expect(sut.parse('5/7')).toBeNull();
    });
  });

  describe('Formatting', () => {
    it('should format null and undefined as an empty string', () => {
      expect(sut.format(null)).toBe('');
      expect(sut.format(undefined as any)).toBe('');
    });

    it('should format a valid date', () => {
      expect(sut.format({ year: 2016, month: 10, day: 15 })).toBe('10/15/2016');
    });

    it('should format a valid date with padding', () => {
      expect(sut.format({ year: 2016, month: 10, day: 5 })).toBe('10/05/2016');
    });

    it('should return empty string for invalid dates', () => {
      expect(sut.format({ year: 2016, month: NaN, day: undefined } as any)).toBe('');
      expect(sut.format({ year: 2016, month: null, day: 0 } as any)).toBe('');
      expect(sut.format({ year: null, month: null, day: 1 } as any)).toBe('');
    });
  });
});

us-date-parser-formatter.ts

import { Injectable } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash-es';

@Injectable()
export class UnitedStatesDateParserFormatter extends NgbDateParserFormatter {

  parse(value: string): NgbDateStruct | null {
    if (value != null) {
      const parts = value.split('/');
      if (parts.length === 3 && this.isNumber(parts[0]) && this.isNumber(parts[1]) && this.isNumber(parts[2])) {
        return { month: _.parseInt(parts[0]), day: _.parseInt(parts[1]), year: _.parseInt(parts[2]) };
      }
    }
    return null;
  }

  format(date: NgbDateStruct | null): string {
    return date && this.isNumber(date.day) && this.isNumber(date.month) && this.isNumber(date.year)
      ? `${this.padNumber(date.month)}/${this.padNumber(date.day)}/${date.year}`
      : '';
  }

  private isNumber(value: any): value is number {
    return !isNaN(_.parseInt(value));
  }

  private padNumber(value: number) {
    if (this.isNumber(value)) {
      return `0${value}`.slice(-2);
    } else {
      return '';
    }
  }
}

2023 Update

Note the usage of the lodash parseInt function in the above code. This can be replaced by the ES5 version of parseInt in modern browsers.

Replaced old Github gist link with the actual code to save you a click.

Also see this followup post - NgBootstrap Datepicker - US Date Formatter (Improved edition) where I improved upon this formatter by allowing more separators beyond / and making the zero padding of months/days optional.

Did this post help you? Buy me a coffee!
Buy Me A Coffee
comments powered by Disqus