Unified date components in React

Hello,
I’ve recently been working on unifying date formats in Foreman and Katello and adding React components for that (Redmine #21312).
You might have noticed that the first attempt got reverted due to adding too large npm dependencies. After that I explored some alternative implementations and would like to know other devels’ opinions prior to sending a new PR.

The goal of the date unification was providing React components for Foreman, Katello and other plugins that could be used either from erb helpers or js (both React and Angular). After initial research of what is currently used across plugins we agreed on sticking to 4 formats (examples for en_US):

      LongDateTime   October 13, 2017, 1:54 PM
      ShortDateTime  Oct 13, 1:54 PM
      Date           9/3/2010
      RelativeTime   2 days ago

See the discussion in the original PR for details.

I created prototypes of solutions using two libraries Format.js and moment.js. Both have its pros and cons which I describe below and would like to know which one you like more or if you have some more ideas.

Format.js

is a collection of js libraries for formatting dates, numbers and translations. It provides bindings for multiple frameworks (react, ember, handlebars… we’d use only the first one). It uses Intl API for formatting dates and numbers - it’s an internacionalization standard that browsers are adopting. We need only subset of the functionality, that is currently supported in: Chrome 24+, Firefox 29+, Internet Explorer 11+, Opera 15+, Safari 10+ and all versions of Edge.
There’s a blogpost describing purpose of Intl API nicely.

The biggest benefit of Intl API is that it provides true localization for dates. You only specify which components of the datetime you want to display and it builds a date in correct order of month, date, punctuation, etc. for the language. E.g.

  en-US  January 3, 2018
  cs-CZ  3. ledna 2018
  fr-FR  3 janvier 2018

The relevant part of Format.js for us is react-intl. Apart from wrapping the standard Intl API functionality it adds relative date formatting and message formatting. Although the API has been adopted by most of the modern browsers, we still need to have a fallback for the older ones (or for Phantom that lacks it and which we use for testing). This is usually solved by requiring a fallback Intl package. Unfortunately it’s quite large so it should be better loaded lazily only when needed.

Additional raw size of the dependencies for this solution is: 98.6 k for react-intl and 486 k for the fallback intl.

My prototype of that solution is at:
https://github.com/theforeman/foreman/compare/develop...tstrachota:dates_intl
Unfortunately I haven’t managed to get the lazy loading working. I tried to use webpack’s import but for some reason it seems not to work with the dev server.

moment.js

is a library for parsing and manipulating dates in js. It requires no additional dependencies. The drawback is that it provides only limited number of truly localized formats (see Multiple Locale Support in docs) and it’s not that flexible. For our case specifically it means that we’re not able to create ShortDateTime with moment.js in the same format as we use it now (moment always adds year - “Oct 13, 1:54 PM” vs. “Oct 13, 2018 1:54 PM”).
To solve that we can either agree on using different format of ShortDateTime or define it ourselves and maintain formats for all transaltions ourselves (I’d like to avoid that).

Additional raw size of this dependency is 203.7 k.

The prototype lives here:
https://github.com/theforeman/foreman/compare/develop...tstrachota:dates_moment
I didn’t get timezones working, but there’s some support for that so it should be doable.

Given all the above, the possible solutions in my mind are:

  1. use react-intl with the intl dependency loaded conditionally for the browsers that need it
  2. use moment.js and select some other format for ShortDateTime
  3. use moment.js and maintain format for ShortDateTime ourselves

I still tend to prefer no 1). What do you think?

T.

Since you’ve already formatted that way, a poll seems appropriate :wink:

  • use react-intl with the intl dependency loaded conditionally for the browsers that need it
  • use moment.js and select some other format for ShortDateTime
  • use moment.js and maintain format for ShortDateTime ourselves

0 voters

Another suggestion:
Why not use just react-intl without a full fallback? If I understand correctly, the only browser (which we care about) that would require the fallback is IE10. How about making our own partial fallback for that that will cause calls on IE10 to just return the un-localized string? It would be a very simple function that returns its input and conforms to the Intl API. I think this minor reduced compatibility for the ancient browser (that even MS doesn’t support any more) is a reasonable compromise without adding too much complexity or build size.

2 Likes

I agree with @tbrisker: it’s a fair compromise to make.

Unfortunately it can’t be a simple function. We’d need to mimic the whole api. The main issue is Intl.DateTimeFormat that is required.
But we could include the fallback intl only with English localization if we want to reduce the size.

If we only use Intl.DateTimeFormat, why can’t we have something along the lines of:

<![if lte IE 10]>
<script>
window.Intl = {
  'DateTimeFormat': function(str){return str;}
};
</script>
<![endif]-->

Am I missing something here?

Something similar would definitely be doable. The object would probably need to be more structured as DateTimeFormat isn’t that simple function and react-intl enforces Intl to be in a specific shape.
I’m concerned about covering all the required functionality, that could theoretically change in future released of react-intl. Using the fallback intl could give us better results with less efforts.

Since the poll has been open for about a week now and so far all (8) participants voted for using react-intl I think we can close it.

I’ll send an updated PR with this solution.

1 Like