Categories
JavaScript JavaScript Basics

Great New Features in ES2018 You May Have Missed

Ever since ES2015 was released, which was a great leap forward in itself, JavaScript has been improving at a fast pace. Every year since then, new features have been added to JavaScript specifications.

Features like new syntax and new methods for building in JavaScript have been added consistently. In ES2016 and 2017, The Object object had methods like Object.values and Object.entries added.

String methods like padStart and padEnd were added in ES2017. async and await, which is a shorthand syntax for chaining promises, were also added in ES2017.

The includes methods for arrays were added in ES2016. ES2018 was another release with lots of new features. With ES2018, the spread syntax is now available for object literals. Rest parameters were also added.

The for await...of loop, which is a loop syntax iterating through promises sequentially, was also added. The SharedArrayBuffer object was added for representing raw binary data that cannot become detached.

A finally function was also added to the Promise object.


Spread Operator in Objects

The spread syntax works by copying the values of the original array, and then inserting them into another array or putting them in the order they appeared in the array as the list of arguments in a function in the same order.

When the spread operator is used with objects, the key-value pairs appear in the same order they appeared in the original object.

With ES2018, the spread operator works with object literals. Then key-value pairs of an object can be inserted into another object with the spread operator.

If there are two objects with the same key that the spread operator is applied to in the same object, the one that’s inserted later will overwrite the one that’s inserted earlier.

For example, if we have the following:

let obj1 = {foo: 'bar', a: 1};  
let obj2 = {foo: 'baz', b: 1};  
let obj3 = {...obj1, ...obj2 }

Then we get {foo: “baz”, a: 1, b: 1} as the value of obj3 because obj1 is spread before obj2. They both have foo as a key in the object.

First foo: 'bar' is inserted by the spread operator to obj3. Then foo: 'baz' overwrites the value of foo after obj2 is merged in since it has the same key, foo, but was inserted later.

This is great for merging objects as we don’t have to loop through the keys and put in the values, which is much more than one line of code.

One thing to note is that we can’t mix the spread operator between regular objects and iterable objects. For example, we will get TypeError if we write the following:

let obj = {foo: 'bar'};  
let array = [...obj];

Rest Operator

The rest operator is a JavaScript operator where we can store an indefinite number of arguments in a function as an array. It looks exactly like the spread operator, which lets us spread entries of an array or a list of key-value pairs of an object into another object.

For example, if we have a function that has a long list of arguments, we can use the rest operator to shorten the list of parameters in a function.

The rest operator is used with the following syntax:

const fn = (a,b,..restOfTheArgs) => {...}

Where restOfTheArgs is an array with the list of arguments other than the first two.

For example, if we want to write a sum function that takes an indefinite list of numbers as arguments and sum up the numbers, we can write:

const sum = (a,b,...otherNums) => {  
  return a + b + otherNums.reduce((x,y)=>x+y, 0);  
}

As we can see, this is very handy for functions that have any list of arguments. Before we had this, we had to use the arguments object available in functions to get a list of arguments. This is not ideal as we allow them to pass in anything into the arguments.

With the rest operator, we have the best of both worlds. We can have some fixed parameters, while the rest stay flexible. This makes functions more flexible than functions with a fixed number of parameters, while having some flexibility of functions that take an indefinite number of arguments.

The arguments object has all the arguments of the function. Also, it’s not a real array, so array functions aren’t available to them. It’s just an object with indexes and keys to denote the argument’s positions.

Methods like sort, map, forEach, or pop cannot be run on the argument’s object. It also has other properties. This creates confusion for programmers. The arguments that are converted to an array with the rest operator do not have these issues, as they are real arrays.

To call the sum function we wrote, we can write:

const result = sum(1,2,3,4,5,6,7,8,9,10);

The result will be 55, since we summed up all the arguments together. otherNums is an array with all the numbers other than 1 and 2.

We can also use the rest operator to destructure a list of arguments into a list of variables. This means that we can convert a list of parameters into an array with the spread operator, and then decompose the array of arguments into a list of variables.

This is very useful as we can get the entries of the array that’s operated on by the rest operator and convert them to named variables. For example, we can write:

const sum = (a,b,...[c,d,e])=> a+b+c+d+e;

This way, we can use the rest operator, but limit the number of arguments that your function accepts. We take the function parameters a and b, and we also take c, d, and e as parameters.

However, it’s probably clearer without using the rest operator since all the parameters are fixed, we can just list the parameters directly.


for await...of

The for await...of loop allows us to create a loop that iterates over a list of promises as well as over the entries of a normal iterables.

It works with iterable objects like arrays, string, argument object, NodeList object, TypedArray, Map, Set, and user-defined synchronous and asynchronous iterable objects.

To use it, we write:

for await (let variable of iterable) {  
  // run code on variable
}

The variable may be declared with const, let, or var, and the iterable is the iterable objects that we are iterating over. We can iterate over asynchronous iterable objects like the following:

const asynNums = {  
  [Symbol.asyncIterator]() {  
    return {  
      i: 6,  
      next() {  
        if (this.i < 20) {  
          return Promise.resolve({  
            value: this.i++,  
            done: false  
          });  
        }
        return Promise.resolve({  
          done: true  
        });  
      }  
    };  
  }  
};

(async () => {  
  for await (let num of asynNums) {  
    console.log(num);  
  }  
})();

We should see 6 to 19 logged. It also works with async generators:

async function* asynNumGenerator() {  
  var i = 6;  
  while (i < 20) {  
    yield i++;  
  }  
}

(async () => {  
  for await (let num of asynNumGenerator()) {  
    console.log(num);  
  }  
})();

We should see the same thing logged. It also works great with promises:

const arr = Array.from({  
  length: 20  
}, (v, i) => i)let promises = [];

for (let num of arr) {  
  const promise = new Promise((resolve, reject) => {  
    setTimeout(() => {  
      resolve(num);  
    }, 100)  
  })  
  promises.push(promise);  
}

(async () => {  
  for await (let num of promises) {  
    console.log(num);  
  }  
})();

As we can see, if we run the code above, we should see 0 to 19 logged sequentially, which means that promises were iterated through in sequence. This is very handy as we never have anything that can iterate through asynchronous code before this loop syntax was introduced.


Promise.finally()

The finally function is added to the Promise object which runs when the promise is settled. That means it runs whenever the promise is fulfilled or rejected.

It takes a callback function that runs whenever the promise is settled. This lets us run code regardless of how a promise ends. Also, this means that we no longer have to duplicate code in the callbacks for then and catch functions.

To use the finally function, we use it as the following:

promise  
.finally(()=>{  
  // run code that when promise is settled  
})

The finally is useful is we want to run code whenever a promise ends like cleanup code or processing after the code ends.

It’s very similar to calling .then(onFinally, onFinally). However, we do not have to declare it twice to make a variable for it and pass it in twice. The callback for the finally function doesn’t take any argument, as there’s no way to determine the status of the promise beforehand.

It’s used for cases when we don’t know what will happen to the promise, so there’s no need to provide any argument. If a promise is fulfilled then the resolved value will be intact after calling the finally function.

For example, if we have:

Promise.resolve(3).then(() => {}, () => {})

This will resolve to undefined, but if we have:

Promise.resolve(3).finally(() => {})

The code above will resolve to 3. Similarly, if the promise if rejected, we get:

Promise.reject(5).finally(() => {})

This will be rejected with the value 5.


SharedArrayBuffer

The SharedArrayBuffer is an object that’s used to represent a fixed-length raw binary data buffer.

It’s similar to the ArrayBuffer object, but we can use it to create views on shared memory. It can’t become detached, unlike an ArrayBuffer. The constructor of it takes the length of the buffer as an argument.

The length is the size in byte for the array buffer to create. It returns a SharedArrayBuffer of the specified size with contents initialized to 0.

Shared memory can be created and updated by multiple workers on the main thread. However, this may take a while until the changes are propagated to all contexts.


Conclusion

With ES2018, we have more handy features to help developers develop JavaScript apps.

The highlights include the spread operator for objects, which let us copy key-value pairs to other objects and the rest operator to let us pass optional arguments.

The for await...of loop lets us iterate through collections of asynchronous code which could never easily be done before.

Categories
JavaScript JavaScript Basics

How to Get the Length of An Object

There are 2 ways to get the length of the list of keys of an object.

Object.keys

Object.keys gets the top level list of keys of an object and returns an array of them. For example:

const a = {foo: 1, bar: 2};  
const length = Object.keys(a).length // 2

Object.getPropertyNames

Object.getPropertyNames also gets a list of all top level of keys of an object and return them as an array. For example:

const a = {foo: 1, bar: 2};  
const length = Object.getOwnPropertyNames(a).length // 2

for…in Loop

There is a special loop for looping through the keys of an object. You can do the following:

const a = {foo: 1, bar: 2};  
let keysCount = 0;  
for (let key in a) {  
    keysCount++;  
}  
console.log(keysCount) // 2
Categories
JavaScript Vue

How to Add Geolocation to a Vue.js App

Many apps want provide an experience based on the user’s location. This is where the HTML Geolocation API comes in. You can use it easily to get the location of the current device.

To get the location of the device in the browser using plain JavaScript, we write:

if (navigator.geolocation) {  
  navigator.geolocation.getCurrentPosition(getPosition);  
}

function getPosition(position) {  
  console.log(position.coords.latitude, position.coords.longitude);  
}

As you can see, getting the latitude and longitude is very easy.

We can also easily add geolocation to any Vue.js app. The vue-browser-geolocation package is a great add-on for adding geolocation capabilities to your app. It provides a promise-based API for getting the location of the device, so we can easily use async and await to get the location with this package.

Getting Started

In this article, we will build an app to get the list of Canadian politicians in your local area if you are in Canada from the Represent Civic Information API.

We will use the vue-browser-geolocation package to get the location, and then use the API to get the list of items from the API. We will get the list of politicians from the local area, the constituent boundaries from the local area, and the list of representatives from the given legislative body selected from a list.

To start, we run the Vue CLI to create the project. Run npx @vue/cli create politicians-app to create the app. In the wizard, we choose ‘Manually select features’, then choose to include Babel, and Vue Router.

Next we install some packages we need to build the app. We will use Axios for making HTTP requests, BootstrapVue for styling, Vue Avatar for showing the avatar picture of the politician, and vue-browser-geolocation to get the location of the device. Run npm i axios bootstrap-vue vue-avatar vue-browser-geolocation to install all the packages.

Making API Requests

Now we can start writing the app. We start by creating a mixin for making HTTP requests that we use in our components. Create a mixins folder in the src folder and create requestsMixin.js in the mixins folder. Then we add the following to the file:

const axios = require("axios");  
const APIURL = "https://represent.opennorth.ca";

export const requestsMixin = {  
  methods: {  
    getRepresentatives(lat, lng) {  
      return axios.get(`${APIURL}/representatives/?point=${lat},${lng}`);  
    }, 

    getBoundaries(lat, lng) {  
      return axios.get(`${APIURL}/boundaries/?contains=${lat},${lng}`);  
    }, 

    getRepresentativeSetsRepresentatives(set, lat, lng) {  
      return axios.get(  
        `${APIURL}/representatives/${set}/?point=${lat},${lng}`  
      );  
    }, 

    getRepresentativeSets() {  
      return axios.get(`${APIURL}/representative-sets/?offset=0&limit=200`);  
    }  
  }  
};

We use the Represent Civic Information API to get the representatives and boundaries by location, and also get the list of legislative bodies in Canada. The full list of endpoints is at https://represent.opennorth.ca/api/#representativeset.

Getting Geolocation

Next create the pages for display the data. In the views folder, create Boundaries.vue and add:

<template>  
  <div class="page">  
    <h1 class="text-center">Your Constituent Boundary</h1>  
    <template v-if="noLocation">  
      <h2 class="text-center">Enable geolocation to see list of boundaries</h2>  
    </template>  
    <template v-if="!noLocation">  
      <b-card v-for="(b, i) in boundaries" :key="i" class="card">  
        <b-card-title>  
          <h2>{{b.name}}</h2>  
        </b-card-title> <b-card-text>  
          <p>  
            <b>Boundary Set Name:</b>  
            {{b.boundary_set_name}}  
          </p>  
        </b-card-text>  
      </b-card>  
    </template>  
  </div>  
</template>

<script>  
import { requestsMixin } from "@/mixins/requestsMixin";

export default {  
  name: "boundaries",  
  mixins: [requestsMixin],  
  data() {  
    return {  
      boundaries: [],  
      noLocation: true  
    };  
  },  
  beforeMount() {  
    this.getBounds();  
  },  
  methods: {  
    async getBounds() {  
      try {  
        const coordinates = await this.$getLocation({  
          enableHighAccuracy: true  
        });  
        const { lat, lng } = coordinates;  
        const response = await this.getBoundaries(lat, lng);  
        this.boundaries = response.data.objects;  
        this.noLocation = false;  
      } catch (error) {  
        this.noLocation = true;  
      }  
    }  
  }  
};  
</script>

In this page, we get the constituency boundaries for the location that the user’s device is currently in with the vue-browser-geolocation package. this.$getLocation in the getBounds function is provided by the package. The promise resolves to an object with the latitude and longitude when geolocation is enabled by the user. We wrap the code with a try...catch in case that it is disabled. If geolocation is disabled, we show a message to let the user know that they have to enable geolocation to see data.

If geolocation is enabled, then this.getBoundaries will be called. The function is provided by the requestsMixin that we created earlier. We included it with the mixin property of this component. The items are displayed in BootstrapVue cards.

Next we replace the code in Home.vue with the following:

<template>  
  <div class="page">  
    <h1 class="text-center">Your Local Representatives</h1>  
    <template v-if="noLocation">  
      <h2 class="text-center">Enable geolocation to see list of representatives</h2>  
    </template>  
    <template v-if="!noLocation">  
      <b-card v-for="(r, i) in reps" :key="i" class="card">  
        <b-card-title>  
          <h2>  
            <avatar :src="r.photo_url" :inline="true"></avatar>  
            <span class="title">{{r.name}}</span>  
          </h2>  
        </b-card-title> <b-card-text>  
          <h5>Offices:</h5>  
          <div v-for="(o,i) in r.offices" :key="i">  
            <p>  
              <b>Address:</b>  
              {{o.postal}}  
            </p>  
            <p>  
              <b>Telephone:</b>  
              {{o.tel}}  
            </p>  
            <p>  
              <b>Type:</b>  
              {{o.type}}  
            </p>  
          </div>  
          <p>  
            <b>Party:</b>  
            {{r.party_name}}  
          </p>  
        </b-card-text> <b-button :href="r.url" variant="primary" target="_blank">Go to Source</b-button>  
      </b-card>  
    </template>  
  </div>  
</template>

<script>  
import { requestsMixin } from "@/mixins/requestsMixin";  
import Avatar from "vue-avatar";

export default {  
  name: "home",  
  mixins: [requestsMixin],  
  data() {  
    return {  
      reps: [],  
      noLocation: true  
    };  
  },  
  components: {  
    Avatar  
  },

  beforeMount() {  
    this.getAllRepresentatives();  
  },  
  methods: {  
    async getAllRepresentatives() {  
      try {  
        const coordinates = await this.$getLocation({  
          enableHighAccuracy: true  
        });  
        const { lat, lng } = coordinates;  
        const response = await this.getRepresentatives(lat, lng);  
        this.reps = response.data.objects;  
        this.noLocation = false;  
      } catch (error) {  
        this.noLocation = true;  
      }  
    }  
  }  
};  
</script>

We use the same this.$geoLocation function as Boundaries.vue. In this page, we get the list of representatives for the location that the user’s device is currently in. The geolocation feature is the same as Boundaries.vue. We run the getAllRepresentatives function in beforeMount so that it loads when the page loads.

The items from the API are displayed in the BootstrapVue cards. We display the picture in an avatar with the vue-avatar package.

Next we create Representatives.vue in the views folder. We add the following code to the file:

<template>  
  <div class="page">  
    <h1 class="text-center">Representative Sets</h1>  
    <template v-if="noLocation">  
      <h2 class="text-center">Enable geolocation to see list of representatives</h2>  
    </template>  
    <template v-if="!noLocation">  
      <b-form>  
        <b-form-group label="Representative Set" label-for="repSet">  
          <b-form-select  
            name="repSet"  
            v-model="form.repSet"  
            :options="repSets"  
            required  
            @change="getRepSetReps()"  
          ></b-form-select>  
        </b-form-group>  
      </b-form>  
      <b-card v-for="(r, i) in reps" :key="i" class="card">  
        <b-card-title>  
          <h2>  
            <avatar :src="r.photo_url" :inline="true"></avatar>  
            <span class="title">{{r.name}}</span>  
          </h2>  
        </b-card-title> <b-card-text>  
          <h5>Info:</h5>  
          <p>  
            <b>Elected Office:</b>  
            {{r.office}}  
          </p>  
          <p>  
            <b>District Name:</b>  
            {{r.district_name}}  
          </p>  
          <p>  
            <b>Party:</b>  
            {{r.party_name || 'None'}}  
          </p>  
          <h5>Offices:</h5>  
          <div v-for="(o,i) in r.offices" :key="i">  
            <p>  
              <b>Address:</b>  
              {{o.postal}}  
            </p>  
            <p>  
              <b>Telephone:</b>  
              {{o.tel}}  
            </p>  
            <p>  
              <b>Type:</b>  
              {{o.type}}  
            </p>  
          </div>  
        </b-card-text>  
      </b-card>  
    </template>  
  </div>  
</template>

<script>  
import { requestsMixin } from "@/mixins/requestsMixin";  
import Avatar from "vue-avatar";

export default {  
  name: "boundaries",  
  mixins: [requestsMixin],  
  data() {  
    return {  
      repSets: [],  
      reps: [],  
      form: {  
        repSet: "strathcona-county-council"  
      },  
      noLocation: true,  
      coordinates: {}  
    };  
  },  
  components: {  
    Avatar  
  },  
  beforeMount() {  
    this.getRepSets();  
    this.getLocation();  
  },  
  methods: {  
    async getRepSets() {  
      const response = await this.getRepresentativeSets();  
      this.repSets = response.data.objects.map(s => {  
        const [part1, part2, value] = s.url.split("/");  
        return {  
          text: s.name,  
          value  
        };  
      });  
    }, 

    async getLocation() {  
      try {  
        const coordinates = await this.$getLocation({  
          enableHighAccuracy: true  
        });  
        this.coordinates = coordinates;  
        this.noLocation = false;  
      } catch (error) {  
        this.noLocation = true;  
      }  
    }, 

    async getRepSetReps() {  
      const { lat, lng } = this.coordinates;  
      const response = await this.getRepresentativeSetsRepresentatives(  
        this.form.repSet,  
        lat,  
        lng  
      );  
      this.reps = response.data.objects;  
    }  
  }  
};  
</script>

When the page loads, we call this.getRepSets and this.getLocation to populate the dropdown with the legislative bodies in Canada and get the location respectively. We get the items in the this.getRepSetReps function with the coordinates provided when the dropdown selection is changed and when geolocation API enabled. We only display the dropdown when geolocation is enabled so that we won’t let the user select anything without it being enabled.

The items are also displayed in BootstrapVue cards in this page.

Then we change the existing code in App.vue to:

<template>  
  <div>  
    <b-navbar toggleable="lg" type="dark" variant="info">  
      <b-navbar-brand href="#">Canadian Politicians App</b-navbar-brand><b-navbar-toggle target="nav-collapse"></b-navbar-toggle><b-collapse id="nav-collapse" is-nav>  
        <b-navbar-nav>  
          <b-nav-item to="/" :active="path == '/'">Home</b-nav-item>  
          <b-nav-item to="/boundaries" :active="path == '/boundaries'">Boundaries</b-nav-item>  
          <b-nav-item to="/representatives" :active="path == '/representatives'">Representatives</b-nav-item>  
        </b-navbar-nav>  
      </b-collapse>  
    </b-navbar>  
    <router-view />  
  </div>  
</template><script>  
export default {  
  data() {  
    return {  
      path: this.$route && this.$route.path  
    };  
  },  
  watch: {  
    $route(route) {  
      this.path = route.path;  
    }  
  }  
};  
</script>

<style>  
.page {  
  padding: 20px;  
  margin: 0 auto;  
}

.card,  
form {  
  max-width: 800px;  
  margin: 0 auto;  
}

.title {  
  position: relative;  
  top: -13px;  
  left: 10px;  
}  
</style>

We add the BootstrapVue toolbar and the links to each page. In the top bar, we set the active prop for the links so that we highlight the link of the current page that is displayed. In the scripts section, we watch the $route object provided by Vue Router for the current path of the app and assign it to this.path so that we can use it to set the active prop.

In the style block, we add padding and margin to the pages with the page class. We set the form and card widths so that they won’t be too wide, and we add the title class so the titles in the cards align with the avatars.

In main.js, we replace the existing code with:

import Vue from 'vue'  
import App from './App.vue'  
import router from './router'  
import store from './store'  
import VueGeolocation from 'vue-browser-geolocation';  
import BootstrapVue from 'bootstrap-vue'  
import 'bootstrap/dist/css/bootstrap.css'  
import 'bootstrap-vue/dist/bootstrap-vue.css'Vue.use(BootstrapVue)  
Vue.use(VueGeolocation);Vue.config.productionTip = falsenew Vue({  
  router,  
  store,  
  render: h => h(App)  
}).$mount('#app')

This adds BootstrapVue library and styles to our app, along with the vue-browser-geolocation package.

In router.js we replace the existing code with:

import Vue from "vue";  
import Router from "vue-router";  
import Home from "./views/Home.vue";  
import Boundaries from "./views/Boundaries.vue";  
import Representatives from "./views/Representatives.vue";

Vue.use(Router);export default new Router({  
  mode: "history",  
  base: process.env.BASE_URL,  
  routes: [  
    {  
      path: "/",  
      name: "home",  
      component: Home  
    },  
    {  
      path: "/boundaries",  
      name: "boundaries",  
      component: Boundaries  
    },  
    {  
      path: "/representatives",  
      name: "representatives",  
      component: Representatives  
    }  
  ]  
});

Now users can go the packages we linked to in the top bar, and also by entering the URLs directly.

Now we run the app by running npm run serve .

Categories
JavaScript JavaScript Basics

Formatting Dates With the DateTimeFormat Object

Different parts of the world have different date formats. To deal with this, JavaScript has the Intl.DateTimeFormat constructor to let us format dates into different formats according to different locales.

This means that we can format dates for different places without manipulating date strings ourselves, making our lives much easier. The Intl.DateTimeFormat constructor takes a locale string as the argument.

With the DateTimeFormat constructed with the constructor, we can use the format instance method, which takes in a Date object to return a date string with the date formatted for the locale you specified in the Intl.DateTimeFormat constructor.


The Constructor and the Format Method

To use the Intl.DateTimeFormat constructor, we can use it like in the following example:

const date = new Date(2019, 0, 1, 0, 0, 0);  
console.log(new Intl.DateTimeFormat('en-US').format(date));  
console.log(new Intl.DateTimeFormat('fr-ca').format(date));  
console.log(new Intl.DateTimeFormat(['ban', 'de']).format(date));

In the above example, we created the Date object, and then we used the Intl.DateTimeFormat constructor with one or more locales passed in as a string.

Then, we called format by passing in the Date object. The first example would log 1/1/2019 since the United States use the MM/DD/YYYY date format.

The second example would log 2019–01–01 since French Canadian dates are in YYYY-MM-DD format. The third example takes an array where there are multiple locales, the ones further to the right are the fallback locales for the ones for the left.

In our example, since we have an invalid locale string ban in the first entry of the array, we use the German date format de instead, to format the Date object, so we get 1.1.2019 since Germany uses the DD.MM.YYYY date format.

As we can see from the examples above, the Intl.DateTimeFormat constructor takes a locale string or an array of locale strings. It also accepts Unicode extension keys for locale strings, so that we can append them to our language tag.

The extension keys nu for setting the numbering system, ca for the calendar to format the date, and hc for the hour cycle are supported.

nu’s possible values can be arab, arabext, bali, beng, deva, fullwide, gujr, guru, hanidec, khmr, knda, laoo, latn, limb, mlym, mong, mymr, orya, tamldec, telu, thai, tibt.

ca’s possible values include buddhist, chinese, coptic, ethiopia, ethiopic, gregory, hebrew, indian, islamic, iso8601, japanese, persian, roc.

hc’s possible values include h11, h12, h23, h24. For example, to format dates according to the Buddhist calendar, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0);  
console.log(new Intl.DateTimeFormat('en-US-u-ca-buddhist').format(date));

Then we get 1/1/2562 in the console.log since the Buddhist calendar’s year 0 is at 545 BC.

The second argument takes an object that lets us set various options in its properties to format the date.

The localeMatcher option specifies the locale-matching algorithm to use. The possible values are lookup and best fit. The lookup algorithm searches for the locale until it finds the one that fits the character set of the strings that are being compared.

best fit finds the locale that is at least, but possibly more, suited than the lookup algorithm. The timeZone option lets us set the time zone which formats the date.

The most basic implementation only recognizes UTC, but others may recognize IANA time zone names in the IANA time zone data database, like Asia/Shanghai, Asia/Kolkata, America/New_York.

The hour12 option specifies whether the format is for 12-hour time or 24-hour time. The default is locale dependent. It overrides the hc language tag in the first argument.

It’s a true or false value where true means format the date time with 12-hour time. The hourCycle option has possible values of h11, h12, h23, h24 and overrides the hc language tag. hour12 takes precedence over this option if it’s specified.

The formatMatcher property specifies the matching algorithm to use. Possible values are basic and best fit. best fit is the default value.

The following subsets of the date time are required to match the date object to the correct format:

  • weekday, year, month, day, hour, minute, second
  • weekday, year, month, day
  • year, month, day
  • year, month
  • month, day
  • hour, minute, second
  • hour, minute

weekday represents the weekday value, possible values are:

  • long (e.g., Friday)
  • short (e.g., Fri)
  • “narrow" (e.g., T). Note that two weekdays may have the same narrow style for some locales. For example, Tuesday‘s narrow style is also T.

era represents the era, possible values are:

  • long (e.g., Anno Domini)
  • short (e.g., AD)
  • narrow (e.g., A)

year represents the year. The possible values are:

  • numeric (e.g., 2019)
  • 2-digit (e.g., 19)

month is the representation of the month. The possible values are:

  • numeric (e.g., 2)
  • 2-digit (e.g., 02)
  • long (e.g., February)
  • short (e.g., Feb)
  • narrow (e.g., M). Note that two months may have the same narrow style for some locales (e.g. May‘s narrow style is also M).

day represents the day. Possible values are:

  • numeric (e.g., 2)
  • 2-digit (e.g., 02)

hour is the representation of the hour. The possible values are numeric, 2-digit.

minute is the representation of the minute. The possible values are numeric, 2-digit.

second is the representation of the second. The possible values are numeric, 2-digit.

timeZoneName is the representation of the time zone name. The possible values are:

  • long (e.g., Pacific Standard Time)
  • short (e.g., GMT-8)

The default value for each component property above is undefined, but if all components are undefined then the year, month, and day are assumed to be numeric.

An example for using the options is below:

const date = new Date(2019, 0, 1, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric'  
};  
console.log(new Intl.DateTimeFormat('en-ca', options).format(date));

In the code above, we set the locale to Canadian English, and in the options object we set weekday to long, year to numeric, month to long, and day to numeric, so that we get the full weekday name in English, year as a number, month with the full name, and day as a number.

The console.log will log “Tuesday, January 1, 2019”. The example above can have a time zone added to it, as in the following example:

const date = new Date(2019, 0, 1, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
console.log(new Intl.DateTimeFormat('en-ca', options).format(date));

In the example above, we added the timeZone and timeZoneLong so that we get the time zone displayed, so that we get “Tuesday, January 1, 2019, Pacific Standard Time”.

The Intl.DateTimeFormat constructor handles non-English format equally well. For example, if we want to format a date into Chinese, we can change the locale and keep the options the same as above like in the following code:

const date = new Date(2019, 0, 1, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
console.log(new Intl.DateTimeFormat('zh-Hant', options).format(date));

Then, we get “2019年1月1日 星期二 太平洋標準時間”, which is the same as “Tuesday, January 1, 2019, Pacific Standard Time” in traditional Chinese.


Other Instance Methods

Instances of the Intl.DateTimeFormat constructor object also have a few instance methods in addition to the format() method.

It also has the formatToParts() method to return an array of objects representing different parts of the date string. The resolvedOptions() method returns a new object with properties that reflect the locale and formatting options that we computed during the initialization of the object.

The formatRange() method accepts two Date objects as arguments and formats the date range in the most concise way, based on the locale and options provided when we instantiate the DateTimeFormat object.

Finally, it has the formatRangeToParts() method which accepts two Date objects as arguments and formats the date range in the most concise way based on the locale and options provided when we instantiate the DateTimeFormat object and return the date time parts in an array of objects.

For example, if we have the following code that calls the formatRangeToParts() method:

const startDate = new Date(2019, 0, 1, 0, 0, 0);  
const endDate = new Date(2019, 0, 2, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
const dateRange = new Intl.DateTimeFormat('zh-Hant', options).formatRangeToParts(startDate, endDate)  
console.log(dateRange);

Then we get the following date and time parts logged:

[  
  {  
    "type": "year",  
    "value": "2019",  
    "source": "startRange"  
  },  
  {  
    "type": "literal",  
    "value": "年",  
    "source": "startRange"  
  },  
  {  
    "type": "month",  
    "value": "1",  
    "source": "startRange"  
  },  
  {  
    "type": "literal",  
    "value": "月",  
    "source": "startRange"  
  },  
  {  
    "type": "day",  
    "value": "1",  
    "source": "startRange"  
  },  
  {  
    "type": "literal",  
    "value": "日 ",  
    "source": "startRange"  
  },  
  {  
    "type": "weekday",  
    "value": "星期二",  
    "source": "startRange"  
  },  
  {  
    "type": "literal",  
    "value": " ",  
    "source": "startRange"  
  },  
  {  
    "type": "timeZoneName",  
    "value": "太平洋標準時間",  
    "source": "startRange"  
  },  
  {  
    "type": "literal",  
    "value": " – ",  
    "source": "shared"  
  },  
  {  
    "type": "year",  
    "value": "2019",  
    "source": "endRange"  
  },  
  {  
    "type": "literal",  
    "value": "年",  
    "source": "endRange"  
  },  
  {  
    "type": "month",  
    "value": "1",  
    "source": "endRange"  
  },  
  {  
    "type": "literal",  
    "value": "月",  
    "source": "endRange"  
  },  
  {  
    "type": "day",  
    "value": "2",  
    "source": "endRange"  
  },  
  {  
    "type": "literal",  
    "value": "日 ",  
    "source": "endRange"  
  },  
  {  
    "type": "weekday",  
    "value": "星期三",  
    "source": "endRange"  
  },  
  {  
    "type": "literal",  
    "value": " ",  
    "source": "endRange"  
  },  
  {  
    "type": "timeZoneName",  
    "value": "太平洋標準時間",  
    "source": "endRange"  
  }  
]

As we can see from the console output above, we get the date and time parts in each entry of the array, with the parts of the startDate coming first and the endDate parts being in the latter parts of the array.

If we call the formatRange() method, like in the code below:

const startDate = new Date(2019, 0, 1, 0, 0, 0);  
const endDate = new Date(2019, 0, 2, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
const dateRange = new Intl.DateTimeFormat('en', options).formatRange(startDate, endDate)  
console.log(dateRange);

Then we get:

"Tuesday, January 1, 2019, Pacific Standard Time – Wednesday, January 2, 2019, Pacific Standard Time"

From the console.log.

To call the resolvedOptions method, we can write the code below:

const date = new Date(2019, 0, 1, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
const resolvedOptions = new Intl.DateTimeFormat('en', options).resolvedOptions(date)  
console.log(resolvedOptions);

Then we get the options that we passed in for formatting the date back:

{  
  "locale": "en",  
  "calendar": "gregory",  
  "numberingSystem": "latn",  
  "timeZone": "America/Vancouver",  
  "weekday": "long",  
  "year": "numeric",  
  "month": "long",  
  "day": "numeric",  
  "timeZoneName": "long"  
}

In the console.log.

To use the formatToParts() method, we can use it like the format() method, like in the following code:

const date = new Date(2019, 0, 1, 0, 0, 0);  
const options = {  
  weekday: 'long',  
  year: 'numeric',  
  month: 'long',  
  day: 'numeric',  
  timeZone: 'America/Vancouver',  
  timeZoneName: 'long'  
};  
const dateParts = new Intl.DateTimeFormat('en', options).formatToParts(date)  
console.log(dateParts);

Then we get the parts of the formatted date in the console.log like in the output below:

[  
  {  
    "type": "weekday",  
    "value": "Tuesday"  
  },  
  {  
    "type": "literal",  
    "value": ", "  
  },  
  {  
    "type": "month",  
    "value": "January"  
  },  
  {  
    "type": "literal",  
    "value": " "  
  },  
  {  
    "type": "day",  
    "value": "1"  
  },  
  {  
    "type": "literal",  
    "value": ", "  
  },  
  {  
    "type": "year",  
    "value": "2019"  
  },  
  {  
    "type": "literal",  
    "value": ", "  
  },  
  {  
    "type": "timeZoneName",  
    "value": "Pacific Standard Time"  
  }  
]

As we can see, the Intl.DateTimeFormat constructor is very useful for formatting dates for different locales. It eliminates a lot of hassle when formatting dates.

The constructor takes one or more locales as the first argument and a variety of options as the second argument.

It can format dates into one string and also format them and break them up into parts. This saves a lot of hassle when formatting dates for different regions since we don’t have to do any date string manipulation.

Categories
JavaScript JavaScript Basics

Handling Dates in JavaScript

Handling date and time are also important in many JavaScript programs. This is why JavaScript has a built-in Date object to handle manipulating dates. Dates objects contain a number that represents the milliseconds since January 1970 in UTC (the Linux epoch time).

We can do a lot of things with the Date object to manipulate dates. However, it has many quirks and missing functionality. Therefore, third-party libraries are better with handling dates than using the built-in Date object in many cases. Moment.js is one of the more popular ones, so it’s naturally it is the first choice when we need to manipulate dates in JavaScript.

Built in Date Object

To work with dates in JavaScript, we have to create a new Date object. The constructor for the date object takes zero or more arguments. When there are no arguments passed in, then Date object created will have the current date and time in your local time zone, as of the time of instantiation.

The parameters it can accept include a date string, another date object, or the (years, month index, day, hour, minutes, seconds, and milliseconds) together. We can pass in a date string as follows to the date constructor:

new Date('2019-01-01');

If we log the result, we get ‘Mon Dec 31 2018 16:00:00 GMT-0800 (Pacific Standard Time)’ since the date is stored in UTC and the browser is running on a computer in the Pacific Time Zone, which is 8 hours behind UTC at January 1, 2019 is when Pacific Standard Time.

As we can see, passing a date string to the Date constructor is confusing. Different browsers will parse date strings differently. Likewise, the Date.parse method also shouldn’t be used to parse date strings. We can see that date-only strings like the one we passed in are treated as UTC and not local time.

If we want to pass in a Date object to the constructor we can write:

new Date(new Date());

We get something like ‘Sun Oct 20 2019 10:57:58 GMT-0700 (Pacific Daylight Time)’ when we log the result. This is shown in your local time.

We can pass in the year, month index, day, hour, minutes, seconds, and milliseconds into the constructor. Only year and month are required. So we can write something like:

new Date(2019,0,1,0,0,0,0)

This will return ‘Tue Jan 01 2019 00:00:00 GMT-0800 (Pacific Standard Time)’ if we log it. Note that we have 0 in the second argument of the constructor. The second argument is the month index, which is 0 for January, 1 for February, and so on. So December would be 11. The rest of the arguments are what we expect. The first is the year, the third is the day, the fourth is hours, the fifth is minutes, 6th is seconds, and the last one is milliseconds.

We can convert a Date object to the UNIX timestamp, which is the number of milliseconds since January 1, 1970 UTC with the unary + operator, so if we write:

+new Date(2019,0,1,0,0,0,0)

We get 1546329600000 when we log the output because it coerces the date to a number.

Another way to get the timestamp is by using the .getTime() method.

To alter the individual components of a Date object like setting the hours or day or getting components of a Date like the day of the week or day of the month, there are methods to do that in a Date object.

Methods in the Date Object

There are methods in the Date object to get and set parts of dates, and convert time to UTC, or set time zone offset. Some important Date object methods include the following:

Date.now()

Returns the current time in the form of a UNIX timestamp with leap seconds ignored. This method is static.

Date.UTC()

Returns the current time in the form of a UNIX timestamp with leap seconds ignored. This method is static. It accepts the same arguments as the longest list of arguments for the Date constructor which is year, month index, day, hour, minutes, seconds, and milliseconds, but only year and month are required. We can use it as in the following code:

Date.UTC(2019,0,1,1,0,0,0)

Which we return something like 1546304400000.

Getter Functions

Date.getDate()

Returns the day of the month according to local time. For example, if we have:

const date = new Date(2019,0,1,0,0,0,0);  
console.log(date.getDate());

We would get 1 since we specified it as the day of the month in the constructor’s arguments.

Date.getDay()

Returns the day of the week (0–6) for the specified date according to local time, where 0 represents Sunday and 6 represents Saturday and 1 to 5 represents the days in between. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
console.log(date.getDay());

Then we get 2, which is Tuesday.

Date.getFullYear()

Returns the 4 digit year of the specified date according to local time. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
console.log(date.getFullYear());

Then we get 2019, which is the 4 digit year of date.

Date.getHours()

Return the hour (0–23) of the specified date according to local time. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
console.log(date.getHours());

Then we get 0 which is what we specified in the hours argument in the constructor.

Date. getMilliseconds()

Returns the milliseconds of the specified date from 0 to 999 according to local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getMilliseconds());

This gets the milliseconds specified in the date object, which should return 0 as we specified in the constructor.

Date.getMinutes()

Returns the minute (0–59) of the specified date in local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getMinutes());

Then we get 0 minutes as we specified in the constructor.

Date.getMonth()

Returns the month (From 0 for January to 11 for December) of the specified date according to local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getMonth());

Then we get month 0 for January as we specified in the constructor.

Date.getSeconds()

Returns the seconds (0–59) in the specified date in local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getSeconds());

Then we get 0 seconds as we specified in the constructor.

Date.getTime()

Returns the UNIX timestamp for the specified date, which is the number of milliseconds since January 1, 1970 00:00:00 UTC. For prior times, it will be negative. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getTime());

Then we get 1546329600000 returned from the getTime() function. A shorthand for this is to use the unary + operator, like in the following code:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(+date);

If we run the code above, we get the same result logged.

Date.getTimezoneOffset()

Return time zone offset in minutes for the current location set on your host device. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getTimezoneOffset());

Then we get 480 if your host device is set to the Pacific Time Zone, since on January 1, 2019, the Pacific Time Zone is 8 hours behind UTC, so we get that the time zone offset is 480 minutes, which is 8 hours.

Date.getUTCDate()

Returns the day of the month, from 1 to 31, in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCDate());

We get 1 since we specified January 1, 2019 as the date.

Date.getUTCDay()

Return the day of the week from 0 to 6 according to the specified date in UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCDay());

Then we get 2 because January 1, 2019 is Tuesday.

Date.getUTCFullYear()

Return the 4 digit year in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCFullYear());

We get 2019 since this is what we specified in the date constructor.

Date.getUTCHours()

Return the hours from 0 to 23 in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCHours());

If the locale setting is set to the Pacific Time Zone, we get 8 since we specified that the hour is 0 in local time which is the same as 8 in UTC on January 1, 2019.

Date.getUTCMilliseconds()

Returns the milliseconds of the specified date from 0 to 999 according to UTC. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCMilliseconds());

This gets the milliseconds specified in the date object, which should return 0 as we specified in the constructor.

Date.getUTCMonth()

Return the month from 0 for January to 11 for December in the specified time according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCMonth());

This gets the month specified in the date object, which should return 0 like we specified in the constructor.

Date.getUTCSeconds()

Returns the seconds (0–59) in the specified date in UTC. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCSeconds());

Then we get 0 seconds as we specified in the constructor.

Date.getYear()

Returns the year, which is usually 2 to 3 digits in the specified date according to the local time zone.

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
console.log(date.getUTCSeconds());

Then we get 119 for 2019.

If we instead have:

const date = new Date(1900, 0, 1, 0, 0, 0, 0);  
console.log(date.getYear());

We get 0 for 1900. And if we have:

const date = new Date(2000, 0, 1, 0, 0, 0, 0);  
console.log(date.getYear());

Then we get 100 for 2000. So 1900 is year 0 according to the getYear() method. It’s much better to use the getFullYear() method instead of getting the correct year from a Date object.

Setter Functions

There are also corresponding setter functions for each of the getter functions we mentioned above. We have the following setter functions for the Date object:

Date.setDate()

Sets the day of the month according to local time. For example, if we have:

const date = new Date(2019,0,1,0,0,0,0);  
date.setDate(2)  
console.log(date);

We would get ‘Wed Jan 02 2019 00:00:00 GMT-0800 (Pacific Standard Time)’ when the host device is set to the Pacific Time Zone since we specified 2 in the setDate function.

Date.setFullYear()

Sets the 4 digit year of the specified date according to local time. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
date.setFullYear(2020)  
console.log(date.getFullYear());

Then we get 2020, which is the 4 digit year of date.

Date.setHours()

Sets the hour (0–23) of the specified date according to local time. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
date.setHours(3)  
console.log(date.getHours());

Then we get 3 when we call getHours which is what we specified in the hours argument in the setHours function.

Date. setMilliseconds()

Sets the milliseconds of the specified date from 0 to 999 according to local time. For example, we can write:

const date = new Date(2019,0,1,0,0,0,0);  
date.setMilliseconds(3)  
console.log(date.getMilliseconds());

This sets the milliseconds specified in the date object, which should return 3 as we specified in the setMilliseconds function when we call the getMilliseconds function.

Date.setMinutes()

Sets the minute (0–59) of the specified date in local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setMinutes(20)  
console.log(date.getMinutes());

Then we get 20 minutes returned from getMinutes() like we specified in the setMinutes function.

Date.setMonth()

Sets the month (From 0 for January to 11 for December) of the specified date according to local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setMonth(3)  
console.log(date.getMonth());

Then we get month 3 returned from getMonth() after we set it by setMonth()

Date.setSeconds()

Sets the seconds (0–59) in the specified date in local time. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setSeconds(10);  
console.log(date.getSeconds());

Then we get 10 seconds from getSeconds() like we specified in the setSeconds function.

Date.setTime()

Sets the UNIX timestamp for the specified date, which is the number of milliseconds since January 1, 1970 00:00:00 UTC. For prior times, it will be negative. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setTime(100)  
console.log(date.getTime());

Then we get 100 returned from the getTime() function since we set it to 100 with setTime .

Date.setUTCDate()

Sets the day of the month, from 1 to 31, in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCDate(2)  
console.log(date.getUTCDate(2));

We get 2 since we specified 2 in setUTCDate

Date.setUTCFullYear()

Sets the 4 digit year in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCFullYear(2020)  
console.log(date.getUTCFullYear());

We get 2020 with getUTCFullYear() since this is what we specified in the date setUTCFullYear() function.

Date.setUTCHours()

Sets the hours from 0 to 23 in the specified date according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCHours(2)  
console.log(date.getUTCHours());

We get 2 with getUTCHours() since we specified it when we call setUTCHours().

Date.setUTCMilliseconds()

Sets the milliseconds of the specified date from 0 to 999 according to UTC. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCMilliseconds(10)  
console.log(date.getUTCMilliseconds());

This gets the milliseconds with getUTCMilliseconds(), which should return 10 as we specified in the setUTCMilliseconds().

Date.setUTCMonth()

Return the month from 0 for January to 11 for December in the specified time according to UTC. For example, if we write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCMonth(3);  
console.log(date.getUTCMonth());

This gets the month specified in the date object, which should return 3 from getUTCMonth() like we specified in the setUTCMonth() call.

Date.setUTCSeconds()

Sets the seconds (0–59) in the specified date in UTC. For example, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0, 0);  
date.setUTCSeconds(10);  
console.log(date.getUTCSeconds());

Then we get 10 seconds when we called getUTCSeconds() since we specified in 10 when we called setUTCSeconds().

The setYear() method set the ye. It’s much better to use the setFullYear() method instead for getting the correct year from a Date object.

The Date object has a few limitations. For example, it doesn’t support time zones. This creates problems when we need time zones data in our dates. JavaScript dates only supports the local time zone, UTC, or time zone offsets relative to UTC. Only some of the conversions between these time zones are allowed when manipulating date objects. For example, when we convert Date objects to a date string, we can only choose between local time and UTC. The JavaScript Date object stores dates in UTC internally.

Moment.js

Manipulating time and date in JavaScript is painful because of the limitations that we discussed above. Even with the functions available with Date objects, there are plenty of chances for bugs and errors. Also, there are no functions for formatting times and dates which is a big problem. To solve this, we use moment.js. This is the best library available for dealing with time.

The native JavaScript Date objects have issues with time zones with YYYY-MM-DD dates being parsed into UTC time as opposed to local time. This creates bugs for developers who aren’t aware of this problem. See https://stackoverflow.com/questions/29174810/javascript-date-timezone-issue

Also there are differences in support for parts of Dates in different browsers. (See https://stackoverflow.com/questions/11253351/javascript-date-object-issue-in-safari-and-ie)

It is also hard to add and subtract timestamps with the built-in Date functions. There is no way to do this without writing a lot of code and doing a lot of checks. Also, there is no way to compare 2 times.

Formatting dates is also not available without writing your own code for using third party libraries.

Moment.js solves all of these issues by providing built-in functions to do all these common operations. It provides functions for parsing and formatting dates.

The moment constructor is where you can pass in a date string, and a moment object will be created. For example, you can pass in:

moment('2019-08-04')

You will get back a moment which you can compare with other moment objects, and add or subtract by different time spans.

If you do not pass in anything to the moment constructor, you get the current date and time.

It also takes a second argument. If you want to make sure a date is parsed as a YYYY-MM-DD date, then write moment(‘2019–08–04’, 'YYYY-MM-DD'). If you don’t know the format of your date or time, then you can pass in an array of possible formats and Moment will pick the right one:

moment('2019–08–04', ['YYYY-MM-DD', 'DD-MM-YYYY']);

After you create a Moment object, then you can do many things like formatting dates:

const a = moment('2019–08–04', 'YYYY-MM-DD').format('MMMM Do YYYY, h:mm:ss a');  
console.log(a);// August 4th 2019, 12:00:00 am

const b = moment('2019–08–04', 'YYYY-MM-DD').format('dddd');  
console.log(b);// Sunday

const c = moment('2019–08–04', 'YYYY-MM-DD').format("MMM Do YY");  
console.log(c);// Aug 4th 19

const d = moment('2019–08–04', 'YYYY-MM-DD').format('YYYY [escaped] YYYY');      
console.log(d);// 2019 escaped 2019

const e = moment('2019–08–04', 'YYYY-MM-DD').format();  
console.log(e);// 2019-08-04T00:00:00-07:00

From the above examples, we see that we can format dates in pretty much any way we want.

We can also tell what time span a date is relative to another date by writing:

const augDate = moment('2019–08–04', 'YYYY-MM-DD');  
const sepDate = moment('2019–09–04', 'YYYY-MM-DD');  
console.log(augDate.from(sepDate)); // a month ago

We can also add or subtract Moment dates:

const augDate = moment('2019–08–04', 'YYYY-MM-DD');  
const sepDate = moment('2019–09–04', 'YYYY-MM-DD');  
console.log(augDate.add(10, 'days').calendar()); // 08/14/2019  
console.log(augDate.subtract(10, 'days').calendar()); // 07/25/2019

It is easy to compare 2 dates:

moment('2010-01-01').isSame('2010-01-01', 'month'); // true  
moment('2010-01-01').isSame('2010-05-01', 'day');   // false, different month  
moment('2008-01-01').isSame('2011-01-01', 'month'); // false, different year

You can also check if a date has Daylight Saving Time in effect or not:

const augDate = moment('2019–08–04', 'YYYY-MM-DD');  
const decDate = moment('2019–12–04', 'YYYY-MM-DD');  
console.log(augDate.isDST()) // true  
console.log(decDate.isDST()) // false

And you can convert back to JavaScript date any time by calling the toDate() function on a Moment object.

As we can see, JavaScript’s Date object has limited capabilities when it comes to manipulating dates. It also doesn’t parse dates very well and has no ability for formatting dates easily. This is why we need to use libraries like Moment.js to fill in the gaps so we can create bug-free code that manipulates dates much more easily.