One of the many great features of Magento is that it is able to manage several locales. This means it can deliver localized content and adapt how dates, times, currencies, figures, ... are displayed based on its inner configuration. But using this in our own modules requires a bit of unravelling. And some of waow effect when combined with Moment.js.

TL;DR

Get the full example extension on Github: https://github.com/herveguetin/Herve_DateTimeExample_M2

Good to know

The strategy when working with localized data, especially dates / times, is to:

  1. Save them as UTC in the database with correct formatting
  2. Convert them from UTC to the proper locale when displaying them

Getting the current UTC DateTime

In order to properly work with UTC DateTimes and save them in the database following Magento best practices, they must be formatted like Y-m-d H:i:s.

The following snippet uses both native \DateTime from PHP and some help from Magento core code to format it correctly.

$currentDateTimeUTC = (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
// 2017-01-18 22:03:49

Formatting a UTC DateTime based on Magento locale

This is now beginning to be interesting... The goal is to make a full use of Magento core framework to convert a UTC DateTime to its value for the timezone configured for the current store. This store-scoped timezone is taken from the system configuration in the administration console: Stores > Configuration > General > Locale Options > Timezone.

The following snippet uses the \Magento\Framework\Stdlib\DateTime\TimezoneInterface implemented by \Magento\Framework\Stdlib\DateTime\Timezone to do this job. It formats our $currentDateTimeUTC into an ISO DateTime value.

/** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone */
$localizedDateTimeISO = $timezone->date(new \DateTime($currentDateTimeUTC))
    ->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
// 2017-01-18 23:03:49

We can also format this DateTime into a more user friendly and verbose format that will also take the locale configuration from Stores > Configuration > General > Locale Options > Locale.

/** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone */
$localizedDateTimeFormatted = $timezone->formatDateTime($currentDateTimeUTC, \IntlDateFormatter::MEDIUM);
// Jan 18, 2017, 11:03 PM

As you can see, the result here is something like "Jan 18, 2017, 11:03 PM" because my Magento locale config is "English (United States)". If it was "Français (France)", the result would have been "18 janv. 2017 23:03".

So far so good. Now let's spice things up a little by using Moment.js for some nice frontend rendering.

Using Moment.js

It turns out that Magento 2 is shipped with Moment.js so we can use it in some JS module of our own.

So we can easily display things like "a few seconds ago" by feeding Moment.js with our $localizedDateTimeISO value.

As the purpose of this blog is not to speak about the Javascript implementation in Magento 2, I will provide the very basic info to make things happen. Please see further in this post to grab a full working example extension.

The template

This is what a phtml template from our module may look like:

<div data-momentjs-container></div>  
<script type="text/x-magento-init">{"[data-momentjs-container]": {"momentJsExample": {"iso": "<?php echo $localizedDateTimeISO ?>"}}}</script>  

Basically, we are telling to the Magento Javascript layer to send the JSON data {"iso": "<?php echo $localizedDateTimeISO ?>"} to the div[data-momentjs-container] element thru the momentJsExample require.js module. If this sentence sounds like #%?$#& for you... no worries, you just have to dig a little into how Magento 2 works with Javascript ;)

The requirejs-config.js

This is an example content of what the requirejs-config.js file of our module may look like.

var config = {  
    map: {
        '*': {
            momentJsExample: 'Namespace_Module/js/momentjs-example'
        }
    }
};

The actual Javascript

And, finally here is how the Namespace_Module/js/momentjs-example.js file uses the fromNow() method of Moment.js.

define([  
    "jquery",
    "moment",
    "jquery/ui"
], function($, moment) {
    "use strict";

    $.widget('namespaceModule.momentJsExample', {
        _create: function () {
            var jsonConfigFromPhtml = this.options;

            // Using the fromNow() method of Moment.js to display "a few seconds ago", "4 hours ago", ...
            var momentResult = moment(jsonConfigFromPhtml.iso).fromNow();

            this.element.html(momentResult);
        }
    });

    return $.namespaceModule.momentJsExample;
});

Wrap up and Github example extension

So, by combining all of the above, it is possible to deliver this kind of content to the user on the frontend:

Github example extension

Please find it here: https://github.com/herveguetin/Herve_DateTimeExample_M2