Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Wissam Abirached
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Wissam Abirached

Painless Date / Time Formatting With formatDate() In Angular 10.2.3

By
Published in Comments (2)

For reasons that I've never fully understand, many people within the JavaScript community simply ignore the native Date object. Which is a shame because it's a well-documented and powerful API, especially when it comes to the use of relative date calculations. I see many developers reach for libraries like Moment and DateFns without even giving Date a try. That said, the other day, I stumbled upon the fact that Angular actually ships with a built-in formatDate() function! This could obviate the need for libraries like Moment and DateFns in the vast majority of cases. As such, I wanted to sit down and try formatDate() in Angular 10.2.3.

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

Just as with almost all date-formatting approaches, the Angular formatDate() function uses a "mask". A mask is nothing more than a static definition of a dynamic value. You can think of a Domain Name, such as bennadel.com, as being a "mask" for an underlying IP-address. When it comes to date-formatting, we use masks to represent parts of a date/time value. For example, the mask, yyyy is often used to represent a 4-digit Year.

The formatDate() function takes 3-4 arguments:

formatDate( value, mask, localID [, timezone] )

From what I've read, Angular ships with the localID of en-US. However, you can include other locales by importing the localization package. I've never worked with the localization package before, so I won't speak any further on that topic.

To see the formatDate() function in action, I've put together a simple demo that loops over a bunch of mask variations and uses them to format a single date. The masks and results are then output to the browser:

// Import the core angular services.
import { Component } from "@angular/core";
import { formatDate } from "@angular/common";
import { Inject } from "@angular/core";
import { LOCALE_ID } from "@angular/core";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

interface Example {
	mask: string;
	result: string;
}

@Component({
	selector: "app-root",
	styleUrls: [ "./app.component.less" ],
	template:
	`
		<div class="example">
			<div class="example__mask">
				LOCALE_ID
			</div>
			<div class="example__result">
				{{ localID }}
			</div>
		</div>

		<div *ngFor="let example of examples" class="example">
			<div class="example__mask">
				{{ example.mask }}
			</div>
			<div class="example__result">
				{{ example.result }}
			</div>
		</div>
	`
})
export class AppComponent {

	public examples: Example[];
	public localID: string;
	public now: Date;

	// I initialize the app component.
	// --
	// NOTE: Out of the box, Angular ships with the "en-US" local for formatting. If you
	// want to use other locales, you have to add the localize package.
	constructor( @Inject( LOCALE_ID ) localID: string ) {

		this.localID = localID;
		this.now = new Date();
		this.examples = [];

		var masks = [
			// Built-in mask aliases.
			"short",
			"medium",
			"long",
			"full",
			"shortDate",
			"mediumDate",
			"longDate",
			"fullDate",
			"shortTime",
			"mediumTime",
			"longTime",
			"fullTime",
			// Years.
			"yy",
			"yyyy",
			// Months.
			// --
			// NOTE: Month mask are UPPER CASE so as not to conflict with minutes.
			"M",
			"MM",
			"MMM",
			"MMMM",
			// Days.
			"d",
			"dd",
			// Weekdays.
			"E",
			"EEEE",
			// AM / PM.
			"aa",
			// Hours.
			"h",
			"hh",
			// 24-Hours version.
			"H",
			"HH",
			// Minutes.
			// --
			// NOTE: Minute mask are LOWER CASE so as not to conflict with months.
			"m",
			"mm",
			// Seconds.
			"s",
			"ss",
			// They can, of course, be used in combination.
			"yyyy-dd-MM",
			"E, MMM d",
			// You can ESCAPE parts of the mask by wrapping them in quotes.
			"MMM d 'somewhere around' H:mm aa"
		];

		for ( var mask of masks ) {

			this.examples.push({
				mask: mask,
				result: formatDate( this.now, mask, this.localID )
			});

		}

	}

}

As you can see, there are a number of built-in compound masks like, fullDate; but you can also create your own compound masks by combining different mask tokens. You can even include non-mask token in your mask by escaping portions of the string by wrapping the escaped portion in single-quotes. With that said, when we run the above Angular code, we get the following output:

Dates and times being formatted with formatDate() in Angular 10.2.3

How cool is that?! Using masks to format dates is super powerful. I am sure that most server-side languages include some form of date/time formatting. And, I've been using masks to format JavaScript dates for years. But, it's super exciting to see that Angular comes with this functionality baked right in.

Epilogue on the Date Pipe in Angular

In addition to the formatDate() function, which allows us to explicitly compile date outputs, you can also use it implicitly with the Date Pipe. The Date Pipe allows you to format dates and times directly within your Angular templates:

{{ dateObj | date:'mm:ss' }}

Between the formatDate() function and the Date Pipe, you may never have to reach for Moment or DateFns again.

Want to use code from this post? Check out the license.

Reader Comments

15,848 Comments

@All,

After this post, I got to thinking about Moment.js and how we use it at work. And like 95% of our uses-cases are Moment's .fromNow() function which takes dates and returns human-friendly strings like, a few seconds ago. I wanted to see if I could implement a .fromNow() function in Angular:

www.bennadel.com/blog/3923-building-a-moment-inspired-fromnow-date-formatting-method-in-angular-10-2-3.htm

If you squint hard-enough to see the world in "milliseconds", then it's actually fairly straightforward to build.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel