How to do advanced date formatting?

This week, we covered basic date formatting with the date pipe and the dateFormat function. While those tools work perfectly well for most basic scenarios, there are times when we want to do more complex date manipulation, such as:

  • Adding/subtracting days to a date
  • Displaying a duration, such as: “Last update: 2 days ago
  • Comparing dates

Most people use Moment.js for such use cases, but Moment.js is now officially done and in maintenance mode.

One of the best alternatives available is date-fns. It uses modern Javascript and relies on the native Date object as much as possible. If you’ve used Moment.js in the past, date-fns is going to look very familiar:

import { format, formatDistance, formatRelative, subDays } from 'date-fns'

format(new Date(), "'Today is a' eeee")
//=> "Today is a Friday"

formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true })
//=> "3 days ago"

formatRelative(subDays(new Date(), 3), new Date())
//=> "last Friday at 7:26 p.m."Code language: JavaScript (javascript)

The library contains some interesting helper functions, such as closestTo to get which date in an array is the closest to a given date, or formatDistanceToNow, which returns meaningful, readable distances such as “less than a minute” or “about one month.”

These features are independent functions, which is excellent for Angular applications as it allows tree-shaking at build time (only the functions we need are included in our build output), compared to Moment, which had one single moment() function that had everything in it, preventing proper tree-shaking.

Formatting functions in Angular

We mentioned the default config options for the date pipe in Angular yesterday.

Did you know we can also format dates in our Typescript code using a formatDate function?

This function has the same signature as the date pipe, which makes perfect sense because… That’s the function used by the pipe itself (source code here):

formatDate(value: string | number | Date, 
           format: string, 
           locale: string, 
           timezone?: string): stringCode language: JavaScript (javascript)

All you need to use that function is to import it from @angular/common:

import {formatDate} from "@angular/common";Code language: JavaScript (javascript)

The only downside of that function is that there is no default format or locale, so we have to pass those two values as parameters, which isn’t the case for the date pipe.

And by the way, similar functions are available for numbers, currencies, and percentages:

For more information on those functions, I have this tutorial on formatting dates and numbers with Angular.

Date pipe default format and timezone

The date pipe is the most convenient way to format dates with Angular. However, very often, we need to use a consistent date format throughout our application, which means that we have to pass that custom format every time we use the date pipe:

<span>Today is {{today | date: 'MM/dd/yy'}}</span>Code language: HTML, XML (xml)

Of course, we could store that format in a constant and reuse that constant every time we use the pipe, but that’s not very convenient.

Luckily for us, since Angular 15, we can now set a default date format (and timezone) by configuring a new injection token called DATE_PIPE_DEFAULT_OPTIONS.

It works by adding the following code to your dependency injection config (array of providers) in app.modules.ts:

providers: [
  {
    provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'MM/dd/yy'}
  }
]Code language: JavaScript (javascript)

With such a config in place, we can use our pipe without any parameters and have our default formatting applied automatically in our entire application:

<span>Today is {{today | date}}</span>Code language: HTML, XML (xml)

The timezone can be customized as well with the timezone property of that same DatePipeConfig object.