Dates in JavaScript

Aims

  • Understand dates
    • Timestamps
    • Timezones & UTC
  • Current Date
  • Other dates (parsing)
  • Get/Set Parts of Dates
  • Format Dates
  • Using dates
  • 3rd party libraries

Understanding Dates

  • JavaScript has an ability to programatically log time
  • The programmer creates a 'date object'
    • which has methods that will return day, month, etc.
  • NOTE: Date objects record points in time, not just dates
  • Underneath it all, dates are basically a number (called a 'timestamp'). They are the number of milliseconds since the unix epoch (1st January 1970 00:00:000 (UTC)) (N.B. Normal UNIX timestamps are seconds, not milliseconds!)
    • That's amazing because:
      • if you want to see if one date is before another, you can just subtract and see if -ve/+ve
      • if you want to add a minute, you just add 1000 * 60.
  • The whole process then revolves around turning that number into human-readable format

Issues with time

  • Time is complex because:
    1. The value you get (unless you pass a timestamp from your server) is going to be taken from that users computer clock. If that clock is wrong, then...
    2. Timezones:
      • Obvs. 1pm here is not 1pm in France, for example
      • Time on the ground for a user is called 'local time'
      • In some places local time is 'unusual' and comes from political reasons, not geospatial ones...
      • Solution: UTC

UTC

  • UTC (Universal Time Code) is a way of standardising time between places by specifying a primary timezone
    • (in this case GMT aka Greenwich Mean Time)
  • Differences can then be accounted for with an offset
    • e.g. BST (British Summer Time) (+1hr)
    • You should therefore always record dates in UTC

Current date

const now = new Date();

// e.g. Mon Oct 14 2019 14:48:26 GMT+0100 (British Summer Time)
console.log(now);

// The timestamp: 1571060906179
console.log(now.getTime());
  • You create a date using the Date constructor
  • If you pass it no arguments it will create now
  • The date object it creates has many properties (see next slides)
  • If you console log it, its string will print out the date in ISO FORMAT (aka ISO-8601 extended format) - see upcoming slide
  • If you call its getTime method, you get the timestamp
    • ​If you just want the timestamp for now you can call Date.now();

Creating 'now'...

// Date(year, monthIndex, date, hour, min, seconds, milliseconds)
const date = new Date(2016, 6, 27, 13, 30, 0, 0);

// OR with timestamp (e.g. from date.getTime() of another?)
const date2 = new Date(1571065751456);

// OR  With UTC date string (z or 'zulu' is the timezone)
const date = new Date("2016-07-27T13:30:00Z");
const date3 = new Date("Wed, 27 July 2016 13:30:00");

Creating another date in time

  • There are several ways to create a date. See the code below:
    1. The arguments method
    2. With a timestamp
    3. With a dateString (BE CAREFUL!!)

Creation with Arguments

The 'arguments' method

To create a date we pass the Date constructor:

  • year (required)

    • Integer value representing the year. Values from 0 to 99 map to the years 1900 to 1999; all other values are the actual year.

  • monthIndex (required)

    • Integer value representing the month, beginning with 0 for January to 11 for December.

  • day (optional)

    • Integer value representing the day of the month. If not specified, the default value of 1 is used.

  • hours (optional)

    • Integer value representing the hour of the day. The default is 0 (midnight).

  • minutes (optional)

    • Integer value representing the minute segment of a time. The default is 0 minutes past the hour.

  • seconds (optional)

    • Integer value representing the second segment of a time. The default is zero seconds past the minute.

  • milliseconds (optional)

    • Integer value representing the millisecond segment of a time. The default is 0 milliseconds past the second.

Creation with a timestamp

Shown earlier...

Creation with dateString

(aka 'parsing')

ISO Format (aka ISO-8601 extended format)

credits toptal.com

Dates provided with different strings may be difficult to compare!!

Non-standard Strings

  • Whilst the day can be passed in the UTC string it is not required.
  • MUST BE UTC STRING (because cross-browser)
    • For consistent creation with strings, see moment.js later...

Testing if something is we are given is a [valid] date

function isDate(d) {
  return d instanceof Date ; 
}

function isValidDate(d) {
  // Note: we use the old window.isNaN() here
  return d instanceof Date && !isNaN(d); 
}

Using dates

Date Operations

const date1 = new Date(2016, 6, 25);
const date2 = new Date(2016, 6, 26);

// Are 2 dates the same
date1.getTime() === date2.getTime()
// For same date with different time you have to do this manually

// Earlier or later (greater === later)
date1 > date2

// adding/subtracting
const originalDate = new Date(2016, 6, 20, 15);
console.log('oldDate', originalDate.toLocaleString());
const nextDate = new Date(originalDate.getTime()); // How to clone a date
const newDateNumber = originalDate.getDate() + 20; // + 20 days
nextDate.setDate(newDateNumber);
const newDateSring = originalDate.toLocaleString();
console.log('newDate', newDateSring);

// Diffing between two times
const date1 = new Date();// some time in the past
const date2 = new Date(); // now

const milliSecondsDiff = Math.abs(date1.getTime() - date2.getTime());
// Number of milliseconds per day =
//   24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 msecs/second
const daysDiff = Math.floor(milliSecondsDiff/(1000 * 60 * 60  * 24));

console.log(daysDiff);

Getters & Setters

const dateTime = new Date();

// Local Time
const date = dateTime.getDate(); // 1 - 31
const day = dateTime.getDay(); // 0 - 6 (For name, use either an array or Intl.format)
const month = dateTime.getMonth(); // 0 - 11

// USE INSTEAD OF getYear
const year = dateTime.getFullYear(); // Gives 4 digits, if created that way


const hour = dateTime.getHours(); // 0 - 23
const minute = dateTime.getMinutes(); // 0 - 59
const seconds = dateTime.getSeconds(); // 0 - 59
const milliseconds = dateTime.getMilliseconds(); // 0 - 999

// Offset from UTC in minutes (so +1 or BST is -60)
const UTC_Offset = dateTime.getTimezoneOffset(); 

const UTC_Month = dateTime.getUTCMonth(); // Gets the UTC value for the month
  • These get you parts of the date
  • DON'T use getYear, use getFullYear
  • These are for local values
    • If you want the UTC underlying, you can use the same methods but with 'UTC' after get (see last as an example)
  • There are set equivalents for all of these
    • ​The setters will adjust the whole date if you pass in more than the limit

Formatting

Combining to string

export function getDatetime(timestamp) {
  const d = new Date(timestamp);
  const dateTimeString = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;

  return dateTimeString; // 2011-11-18 14:54:39.929
}
const userLocale =
    navigator.languages && navigator.languages.length
      ? navigator.languages[0]
      : navigator.language;
  • This will get you the user's locale
  • You should have options to switch that locale so you don't lock them in.

Getting user locale

const today = new Date().toLocaleDateString(
  'en-GB',  // locale
  {  // options
	day : 'numeric',
	month : 'short',
	year : 'numeric'
})
  • toLocaleDateString is awesome!
  • It allows you to output in a format appropriate for that locale
    • If you're doing a lot with dates then this is more performant
  • You can specify a locale (or undefined, to use your current locale)
  • and [optionally] specify options
  • JS lacks some of the helpers that other languages have, so you often have to create them yourself
  • There is an equivalent for times: toLocaleTimeString
  • And both combined: toLocaleString (I believe this may be sketchy cross browser)

Formatting (toLocaleString)

const date = new Date(Date.UTC(2022, 11, 28, 3, 23, 16, 738));
const format = new Intl.DateTimeFormat('en-GB', { dateStyle: 'full', timeStyle: 'long', timeZone: 'Australia/Sydney' });

console.log(format(date));

// With fallbacks
console.log(new Intl.DateTimeFormat(['en-US', 'en-GB'], { dateStyle: 'full', timeStyle: 'long', timeZone: 'Australia/Sydney' }).format(date))
  • The Intl object has a method you can use:

Formatting (Intl Module)

Resources

Third-Party Libraries

What do they give us?

  • Parsing - creating date objects from [sometimes badly formatted] strings
  • Formatting - outputting the date in specific formats
  • Arithmetic - adding/subtracting n days/months, etc.
  • Helpers for timezones
  • i18n/l10n (Internationalization/localization) - Ways to show the dates in different languages

Light Work: day.js

date-fns

  • Like lodash for dates (140+ functions)
  • Has options for the 'functional programming' style
  • works in browser and node
  • best feature set
  • docs (formats)
  • DEMO

 

(Timezone Addon Package: date-fns-tz)

Moment.js (inc. Timezone)

The Future?

Temporal API

What is it?

  • Designed to replace Date
    • Goes down to Nanoseconds
    • Easier API
    • Built-in Duration type
    • Built-in Timezones and I18N
  • spec (article)
  • currently stage 3 (so close to finished)
  • browser support zero
  • BUT polyfill
  • Article
  • Don't use in production yet, but learn...
  • demo