Saturday, July 24, 2021

Parsing Dates and Times Using Luxon

One of the most challenging tasks for Date and time libraries is the parsing of International Dates and Times. They come in such a wide array of formats that it’s all but impossible to accommodate all of them. Different Date libraries approach this problem using a variety of solutions, some better than others. The Moment.js and Luxon Date libraries utilize a similar approach to date parsing using Date format strings. In today’s blog, we’ll learn how to create Luxon Date objects from Datetime strings.

ISO 8601 Date Parsing Methods

The makers of Luxon are the first to admit that it is not a Natural language processing (NLP) tool. Having said that, it does provide direct support for several well-known formats, including ISO 8601 formats, as well as an ad-hoc parser for parsing specific formats.

DateTime.fromISO()

ISO 8601 is an interchange format for date and time-related data. It’s maintained by the Geneva-based International Organization for Standardization (ISO) and was first published in 1988 with updates in 1991, 2000, 2004 and 2019. It covers many different formats and is very widely supported, so I would look at them before trying anything else. Here are a few examples of ISO 8601 dates and times:

  • Date: 2021-05-16
  • Date and time in UTC: 2021-05-16T16:03:44+00:00, 2021-05-16T16:03:44Z, 20210516T160344Z
  • Week: 2021-W19
  • Week with weekday: 2021-W19-7
  • Date without year: –05-16
  • Ordinal date: 2021-136

When in doubt, you can always try passing your datetime string to DateTime.fromISO() and see what happens:

// valid datetime
var luxonDate = DateTime.fromISO("2021-05-16T16:03:44+00:00");

//invalid
console.log(luxon.DateTime.fromISO('Wed, 02 Jun 2021'));

If the string cannot be parsed, you’ll be able to see it in the invalid attribute:

Luxon Unparsable Date and Time

HTTP and RFC2822 Formats

HTTP headers and emails follow their own formatting standards: RFC 850 and 1123 for HTTP header specs and RFC 2822 for emails. Luxon’s parsers for these formats are fromHTTP() and fromRFC2822() respectively. Here are a few examples:

DateTime.fromHTTP("Sunday, 06-Nov-94 08:49:37 GMT");
DateTime.fromHTTP("Sun, 06 Nov 1994 08:49:37 GMT");
DateTime.fromRFC2822("Tue, 01 Nov 2016 13:23:12 +0630");

SQL Dates

Luxon accepts standard SQL dates, times, and datetimes, via the fromSQL() method. You can also pass it an options parameter that specifies the time zone, locale, and other details.

Here are some examples:

DateTime.fromSQL("2017-05-15");
DateTime.fromSQL("2017-05-15 09:24:15");
DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles');
DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' });

Timestamps

As you probably know, JS Dates are stored as a timestamp known as Unix time, Epoch time, and POSIX time representing the number of seconds that have elapsed since the Unix epoch, 00:00:00 UTC on 1 January 1970, minus leap seconds. Timestamps may or may not contain milliseconds, so there are separate methods for each:

DateTime.fromMillis(1542674993410);
DateTime.fromSeconds(1542674993);
// from a JS Date object
DateTime.fromSeconds(new Date().getTime());

Creating a Luxon DateTime Object from a JS Date

An even easier way to create a Luxon DateTime Object from a JS Date than we saw in the previous example is to use the fromJSDate() method. No getTime() invocation required!

var jsDate = new Date();
DateTime.fromJSDate(jsDate);

Ad-hoc Parsing with DateTime.fromFormat()

DateTime.fromFormat() comes in quite handy for working with strings from some legacy systems or for parsing dates in a web page that don’t follow any of the aforementioned formats. Case in point, I created a demo that parses the dates of up-coming conferences in London, UK. These are expressed in either a “Wed, 02 Jun 2021,/” format for one day events and “Tue, 01 – Wed, 02 Jun 2021” for multi-day events.

Breaking Multi-day Events into Start and End Dates

Multi-day events are easy to spot by the hyphen (-), which denotes a date range. I used jQuery to iterate over eventTimes and then split their contents into a two-element array. Then, a replace() is performed that takes the day and month portion of the end date and replaces it with those of the first date to yield the start date:

var inputFormat = "ccc, dd LLL yyyy",
    eventTimes  = $('.eventTime'),
    startDates  = new Array(eventTimes.length), 
    endDates    = new Array(eventTimes.length);
    
eventTimes.each(function(index) {
                                // remove extra newlines and spaces in source
  var eventDates = $(this).text().replace(/\n\s+/,' ').split(' - ');
  
  // check if date range
  if (eventDates.length === 2) {
    var startDate = eventDates[1].replace(/[a-zA-Z]{3}, \d{2}/, eventDates[0]);
    startDates[index] = 
      luxon.DateTime.fromFormat(startDate, inputFormat, {zone: 'utc'});
    endDates[index] = 
      luxon.DateTime.fromFormat(eventDates[1], inputFormat, {zone: 'utc'});
  }
});

Parsing Dates

The fromFormat() method accepts a long list of tokens. For our purposes, the full format string is “ccc, dd LLL yyyy”.

var inputFormat = "ccc, dd LLL yyyy";

eventTimes.each(function(index) {
                                // remove extra newlines and spaces in source
  var eventDates = $(this).text().replace(/\n\s+/,' ').split(' - ');
  
  if (eventDates.length === 1) {
    startDates[index] = 
      luxon.DateTime.fromFormat(eventDates[0], inputFormat, {zone: 'utc'});
  } else if (eventDates.length === 2) {
    //...
  }
});

Here’s the demo in which the above code resides:

See the Pen Luxon Date Parsing Demo by Rob Gravelle (@blackjacques) on CodePen.

There’s nothing exciting happening because we haven’t done anything with the Luxon Date objects (yet), but you can always edit the code in Codepen by adding console.log() statements that show what the startDates and endDates arrays contain. The next tutorial will cover output formats by modifying the above demo to transform the input date strings into various output formats.

Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Popular Articles

Featured