Notes

Formatting and validating dates in JavaScript

Edit on GitHub


JavaScript
4 minutes

Date Formats

Dates come in multiple formats:

  • a Unix timestamp (1481884441)
  • an ISO 8601 string (2016-11-04T17:39:59.910Z)
  • a locale string (Thursday, December 20, 2012, GMT)

Let’s get started by defining some variables to use in our examples

1const UNIX = 1481884441
2const ISO8601 = '2016-11-04T17:39:59.910Z'
3const invalidISO = '2016-31-31T17:39:59.910Z'
4const invalidDate = '2016/31/31'
5
6let date = Date.now() // Date.now() is faster than new Date()
Date.now() vs. new Date()

Note that while Date.now() is faster in performance than new Date(), it gives a number value (epoch/unix time) instead of a Date object. So, you won’t be able to do something like Date.now().toLocaleTimeString() because .toLocaleDateString() only works on a Date object.

1typeof new Date() // "object"
2typeof Date.now() // "number"
3
4new Date().toLocaleDateString() // "28/03/2017"
5Date.now().toLocaleDateString() // Uncaught TypeError: Date.now(...).toLocaleDateString is not a function

Check if value is a valid date

Moment.js

You can use moment.js and it’s isValid() to see if the value is a valid date

1import moment from 'moment'
2
3moment.unix(UNIX).isValid() // check unix timestamps
4moment(ISO8601).isValid() // check date strings

Validating dates with vanilla JS is a bit tricky because of the multiple date fromats.

Validate Date Strings

You can use Date.parse() to check if the value is a valid date string. It gives the Unix timestamp if valid date string and NaN if the string is unrecognized or contains illegal date values (e.g. February 31?)

To check is the output is Nan, you can use the isNaN function (Date.prase(x) === NaN doesn’t work for some reason)

1function isDateString (x) {
2  return isNaN(Date.parse(x)) ? false : true
3}
4
5isDateString(UNIX) ? console.info('hell yeah') : console.info('neh') // "neh"
6isDateString(ISO8601) ? console.info('hell yeah') : console.info('neh') // "hell yeah"

Date.parse() is no good for validating Unix timestamps.

Validate Unix Timestamps

The trick is to convert the timestamp to date object first and validate that. Since JS works in milliseconds, you need to convert the timestamp (seconds) to milliseconds first (by multiplying it with 1000)

 1function isUnixTimestamp (x) {
 2  let timestamp = x * 1000
 3  let time = new Date(timestamp)
 4
 5  return isNaN(Date.parse(time)) ? false : true
 6}
 7
 8isUnixTimestamp(UNIX) ? console.info('timestamp') : console.info('neh') // "timestamp"
 9isUnixTimestamp(ISO8601) ? console.info('timestamp') : console.info('neh') // "neh"
10isUnixTimestamp(invalidISO) ? console.info('timestamp') : console.info('neh') // "neh"

We can combine both of the above function to make a isValidDate function.

1function isValidDate (x) {
2  return isDateString(x) || isUnixTimestamp(x) ? true : false
3}
4
5sValidDate(UNIX) ? console.info('valid date') : console.info('nope') // "valid date"
6isValidDate(ISO8601) ? console.info('valid date') : console.info('nope') // "valid date"
7isValidDate(invalidISO) ? console.info('valid date') : console.info('nope') // "nope"
8isValidDate(invalidDate) ? console.info('valid date') : console.info('nope') // "nope"
9isValidDate('abc') ? console.info('valid date') : console.info('nope') // "nope"

Note that there are going to be some edge cases with this:

1// 123 is a valid date
2isValidDate('123') ? console.info('valid date') : console.info('nope') // "valid date" 123 is a valid timestamp!
3
4// Checking for 31st of Feb (this will give different values based on locale date format)
5isValidDate('2016/02/31') ? console.info('valid date') : console.info('nope') // "valid date"
6isValidDate('2016/31/02') ? console.info('valid date') : console.info('nope') // "nope"

Formatting dates with Locale strings

toLocaleString() let’s you format a date using the locale settings. Locales let you specify the language whose formatting conventipons should be used. Different locales have different conventions for writing dates, for example en-US uses 12-hour time with AM/PM while en-GB uses 24-hour format. en-US has month first and then date, while en-gb has date first and then month. You can customize the format for the locale string.

1console.info('en-us', date.toLocaleString('en-us')) // "1/10/2017, 3:29:15 PM"
2console.info('en-GB', date.toLocaleString('en-GB')) // "10/01/2017, 15:29:15"
3console.info('ar-EG', date.toLocaleString('ar-EG')) // "١٠‏/١‏/٢٠١٧ ٣:٣٢:٣٣ م"
4console.info('de-DE', date.toLocaleString('de-DE')) // "10.1.2017, 15:32:33"

You can further customize the locale string by passing it an options argument (object).

 1let localeStringOptions = {
 2  weekday: 'long',
 3  month: 'long',
 4  day: 'numeric',
 5  year: 'numeric'
 6}
 7
 8console.info('en-us', date.toLocaleString('en-us', localeStringOptions)) // "Tuesday, January 10, 2017"
 9console.info('en-GB', date.toLocaleString('en-GB', localeStringOptions)) // "Tuesday, 10 January 2017"
10console.info('ar-EG', date.toLocaleString('ar-EG', localeStringOptions)) // "الثلاثاء، ١٠ يناير، ٢٠١٧"
11console.info('de-DE', date.toLocaleString('de-DE', localeStringOptions)) // "Dienstag, 10. Januar 2017"

Furthermore, you can detect the locale of the browser with navigator.language and navigator.userLanguage, like this:

1let locale = navigator.language || navigator.userLanguage
2
3console.info('month', date.toLocaleString(locale, {month: 'long'})) // "January"

For example, get the month name (e.g. Jan) using toLocaleString

1let time = new Date('2016-11-04T17:39:59.910Z')
2
3let localeString = time.toLocaleString('en-US', {month: 'short'})
4
5console.info(localeString) // Nov

Notes

  • Date.now() is faster than new Date() since you don’t have to instantiate a new Date object. stackoverflow jsperf

Related