4-digit years for localized dates in Twig templates

Building a Twig extension to display localized dates with 4-digit years

The localizeddate filter in Twig (in the Intl Extension) formats dates according to the current or given locale.

While you can display dates with the dd/mm/yy / mm/dd/yy / yy/mm/dd formats depending on the locale, it’s not possible to display a 4-digit year like in 31/12/2014 (French style) or 12/31/2014 (US style) or any other format depending on the locale.

This post shows how to implement a Twig extension to display such dates, keeping the localization benefit.

You probably know the Twig Intl Extension to localize dates like this:

{{ post.published_at|localizeddate('short', 'none') }}

It uses the current locale along with the PHP IntlDateFormatter to format dates depending on the current locale.

The short format will display a date like dd/mm/yy for the french (fr_FR) locale, while it will display a date like mm/dd/yy for the en_US locale. The medium format displays a date like Jan 12, 1952.

It’s therefore impossible to display a localized date in the dd/mm/yyyy (fr_FR) or mm/dd/yyyy (en_US) formats.

Here is a solution to display localized dates with a 4-digit year, whatever the year, whatever the locale. The solution is to extend the Twig Intl extension in a new extension, so as to customize the date format generated by the PHP IntlDateFormatter class, transforming the “yy” (2-digit years) pattern to “y” (4-digit years), according to the ICU date format syntax.

namespace Acme\DemoBundle\Twig;

class AcmeIntlExtension extends \Twig_Extensions_Extension_Intl
    public function getFilters()
        return array(
            new \Twig_SimpleFilter('localizeddate', array($this, 'twigLocalizedDateFilter'), array('needs_environment' => true)),

    public function twigLocalizedDateFilter($env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null)
        $date = twig_date_converter($env, $date, $timezone);

        $formatValues = array(
            'none'   => \IntlDateFormatter::NONE,
            'short'  => \IntlDateFormatter::SHORT,
            'medium' => \IntlDateFormatter::MEDIUM,
            'long'   => \IntlDateFormatter::LONG,
            'full'   => \IntlDateFormatter::FULL,

        $formatter = \IntlDateFormatter::create(

        if ($format === null) {
            // Replace yy to y (but yyy should be kept yyy, and yyyy kept as yyyy)
            // This way, years are NEVER shown as "yy" (eg. 14), but always like "yyyy" (eg. 2014)
            $pattern = preg_replace(':(^|[^y])y{2,2}([^y]|$):', '$1y$2', $formatter->getPattern());


        return $formatter->format($date->getTimestamp());

Declare the extension in the app/config.yml file:

        class: Acme\DemoBundle\Twig\AcmeIntlExtension
            - { name: twig.extension }

There was no other easy way to change the pattern of the date, so I had to use the regular expression to change “yy” to “y”, while preserving “yyy” or “yyyy”.