Drupals format_date() function uses the PHP date_format() function to generate date and time strings.

date_format() is not locale-aware, which means that it ALWAYS outputs English language data, such as day and month names and ordinal number suffixes. This then needs to be passed through t() to generate a localised date.

PHP provides the strftime() function which is locale-aware. Using this function instead of date_format() would remove the need to translate all day and month names. Additionally it also solves the problem for locales where the current year is not 2012 - see #202891: format_date() doesn't return localized year correctly which has been an open issue since December 2007.

I am aware the format characters for date_format() and strftime() are different, but it should be possible to provide automated conversion of these format strings to what strftime() expects, possibly even via a compatibility layer that can convert a format string if the '%' character isn't present.

Reference: http://www.php.net/manual/en/class.intldateformatter.php

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gábor Hojtsy’s picture

I think, but I'm not sure that the main objection for this earlier was that this requires the server to have the needed locales (properly) set up, which might not be possible to work out on shared hosting and other cheap hosting scenarios, limiting Drupal's ability to be used for foreign language / multilingual sites there. Not sure if this limitation still holds up, I just vaguely remember it. Will try to get KarenS' attention for this issue :)

Damien Tournoud’s picture

strftime() is a no-go. It is implemented differently on different systems, relies on the system having the proper locale data (most of them will not), and relies on LC_* environment variables that are not thread-safe.

What we want is IntlDateFormatter from the intl extension (powered by the cross-platform ICU library).

cafuego’s picture

@Gábor: It's true that strftime() needs to have the locales set up on the server, but if that's not the case the default behaviour would be no different from what date_format() does anyway. The non-thread-safeness would be a bigger problem.

@Damien: IntlDateFormatter looks reasonable enough... but it would add a dependency on the PECL intl library. I assume that means an updated format_date() implementation should work without that library and use it if present (and perhaps suggest to the user that it should be installed?)

amateescu’s picture

I don't see why IntlDateFormatter would add a dependency on the PECL library.. the class is included in PHP 5.3. The dependency will actually be on the intl extension from PHP core :)

cafuego’s picture

FileSize
2.9 KB

@amateescu: Fair enough, it's a separate package on my Ubuntu machine (and thus probably also on Debian).

I've generated a list of format strings for date_format() and IntDateFormatter() and the differences between them, which is attached.

If there is no difference between strings, their lines are not prefixed. If there is a difference, the date_format() string is prefixed with "--" and the IntlDateFormatter() string with "++". I've dropped out the multi-character strings that simply repeat the single-character string output N times (unless there is a difference in output between the two methods of producing a date).

Unless it's OK to break the date formatter params for D8, these will probably need to be translated via a helper function.

Damien Tournoud’s picture

It is obviously ok to break the string format in D8. We might need to provide some (best-effort) upgrade path, but that's it.

Damien Tournoud’s picture

Issue summary: View changes

Fix typo

cosmicdreams’s picture

Hey gang, I'm eager to focus our efforts in getting system_date_* functions modified. I had a great IRC session with cafuego about this issue tonight. I hope to talk with Karen this Thursday, 9/20/2012, about this.

zeta ζ’s picture

Considering PHP5.3 is recommended for D7 I’m hoping this can eventually be back-ported with current solution as a fall-back.

Anyone else think it ironic to see “9/20/2012” in this thread?

cafuego’s picture

FileSize
9.26 KB

To show what direction I'm thinking in, attached is a patch for common.inc that rewrites format_date(). Don't expect a D8 with this patch attached to actually work, it's just a proof of concept. Still, please comment!

I'm aware that cosmicdreams is reworking the date system to use CMI, so until something comes out of that I don't think there'll be a workable patch in this issue.

@zeta ζ: That would be excellent ... and yes, that should've said 20/9/2555 ;-)

For the record, about the updated patterns in format_date():

  $d = new IntlDateFormatter('und', IntlDateFormatter::NONE, IntlDateFormatter::NONE,
    date_default_timezone_get(), IntlDateFormatter::TRADITIONAL);

  $patterns = array('yyyy-MM-dd\'T\'kk:mm:ssZZ', 'yyyy-MM-dd', 'H:mm:ss', 'MM-d', 'Y\'W\'w', 'Y-MM', 'Y');

  foreach ($patterns as $p) {
    $d->setPattern($p);
    printf ("%s:\t %s\n", $p, $d->format(time()));
  }
chx’s picture

Title: Switch to locale-aware PHP function in format_date() » Switch to IntlDateFormatter in format_date()
chx’s picture

Also, some hosting concerns need to be addressed. While intl does ship with 5.3 it's not enabled everywhere.

cosmicdreams’s picture

Ah that appears to be true. References:

https://github.com/fabpot/Twig/issues/208
http://stackoverflow.com/questions/6242378/fatal-error-class-intldatefor...

We'll have to get a survey of web host support. This is a commonly used library so I expect that has wide support.

chx’s picture

What do you mean, . This is a commonly used library when it first shipped with 5.3? We are insanely carefully evaluating every single minor 5.3 version and in another issue people debate themselves purple over cURL which has been included in PHP since the time of Netscape Communicator or something.

cosmicdreams’s picture

While I don't like to use the term insane about anything. Yes I do know that there are a lot of people that discuss the common set of functionality one finds within web hosting providers. If we have another issue on d.o where we'v discussed whether or not the large majority of web hosting providers include support for the intl library, can you link to that issue?

My assertion that the intl library is a commonly used library is an assumption. That assumption is based on the fact that the library supplies localization, formatting, and translation logic on the php-platform level and therefore would present php programmers a common mechanism for handling this problem space. Using such a library would then be an option for many projects and requiring it's use may be a common issue for those projects. Given the assumed frequency that the library would need to be enabled, based on the assumed popularity of the feature, it would also seem likely that web hosters would include the feature by default, so that they would reduce the number of support requests asking about how to turn it on.

Oh, I think I get you're question now. Yes, this library is apart of the core php library. It's apart of the intl library that's apart of the core php platform.

References:
http://php.net/ChangeLog-5.php
http://php.net/manual/en/intl.requirements.php

And if we don't already use this library for our locale / translation / formatting tasks we might be missing out:
http://php.net/manual/en/intro.intl.php - (summary of the library's uses)

So, yea, it would be interesting to know if this is supported widely

cosmicdreams’s picture

ResourceBundle, Locale, Transliteration, and MessageFormatter in particular seems important enough to use this library.

cosmicdreams’s picture

Also of note:
the intl library is an optional requirement of full-stack Symfony 2: http://symfony.com/doc/2.0/reference/requirements.html

I previously looked through Symfony2's documentation and they seem to wrap the core php intl library with one of their own. I think all of that is in the Locale Component. Adding that would is highly likely to be too much work for D8. But it's worth having the conversation.

cosmicdreams’s picture

And if we decide we shouldn't use this library it's important to itemize why we're considering using it:

  1. It provides system defaults for the common use case of short, medium, long and full date formats and can translate them based on a provide locality / regionality / dialect
  2. Other than that, it seems to provide the same functionality that we had before: formatting dates by a provided formatting pattern

If I'm wrong about that assessment, please let me know.

#1 is important because our handling of date formats can be reduced to storing the custom date formats and provide the option to store locale specific variants for that date format. Right now we have to provide the locale specific default date formats ourselves.

cafuego’s picture

A more important reason than either #1 or #2 is that date_format() outputs the wrong information in some places. It's bad enough that the old function calls t() to translate month and day names, it's something else altogether that it can't output the correct century in Thailand.

cosmicdreams’s picture

Ah yes, being able to use non-Gregorian calendars is another benefit to using IntlDateFormatter.

chx’s picture

May I suggest *both* ? Fallback? Pluggable? Reduced functionality if intl is not available? Intl in contrib? Dont make it a hard dependency.

KarenS’s picture

This is relatively new, and my experience with new date classes in PHP is that they are erratically supported and that we will find that there are small but important differences in the way they display dates across systems and versions. The DateTime class was introduced in 5.1 but was not enabled by default and pretty much broken. In 5.2 it was enabled by default and mostly working, but we still found that there were certain point versions of PHP that produced different results. These were categorized as 'bug fixes', they decided the earlier behavior was incorrect, so they fixed it in point releases, like between 5.2.5 and 5.2.6. Those differences bubbled up as bugs that were quite hard to pin down, and then we had to find ways to produce the right results in both versions, a PITA.

I am also thinking about creating a Drupal-specific extension of the class, that we could use. That would give us some room to work around a number of problems. For instance, this class will work OK for long, medium, and short, assuming you like whatever format you get (because you can't control it). But there is then no way to create a custom format type that you want to localize, like a custom format string that looks one way in France and a different way in the United States. As far as I can see, the PHP class provides no way to do that.

I suggest that we consider whether this should go into core in 5.3 or whether we should give it time to mature and become widely-adopted. I like the idea of having it available in contrib, but I really don't know how we can make such a system 'pluggable'. I am trying to think about how we could possibly make this pluggable, which is the only way we can do this if we can't rely on this class being available everywhere.

KarenS’s picture

Also just to emphasize my concern about the changes we have seen in the past over point releases. Let's say we write a test that works as dates are displayed in PHP 5.10. In PHP 5.15 the display is 'fixed', and it now looks a little different. Now tests may or may not work, depending on which version of PHP you are using. Even if that doesn't happen, we don't have any information about whether PHP 5.10 displays exactly the same thing on Ubuntu as it does on MS. Based on things I've seen in the past with localization like this, my guess is that they do NOT display the same.

By contrast, our current system controls the display, so we can test it. I'm not sure how you write tests for a function whose display depends on the environment it is set up in. You'd have to test that the tests work correctly across systems and versions.

I like the idea of the international formatter, I'm just bringing up the kinds of things that are likely to bite us later, based on things that I've been bitten by in the past.

cosmicdreams’s picture

In that case, we should write a proper Interface so that we can vary the implementation over time. Also, I'm guessing this is a good use case for relying on our new dependency injection container.

KarenS’s picture

I ran across these links, https://github.com/symfony/symfony-standard/commit/10b8ddf1ff49238ae2c04... and https://github.com/fabpot/Silex/commit/4491f79dda59373f447b41d298e62788e....

If I'm reading this right, I think it means that Symfony has a way to create a fallback if a site doesn't have the the international classes available. If this is the case, we might want to add the Symfony locale fallback to Drupal. I still think we'll want to extend the class to do Drupal-specific things, but f I'm reading this right, this would provide a way to be sure there is a class to extend.

Does anyone know more about this?

KarenS’s picture

Looks like this would be a good thing to add: http://symfony.com/doc/2.0/components/locale.html. It says "Stub implementation only supports the en locale." Which is still better than a fatal error for a missing class. So worst case is that it wouldn't have translation on environments that don't have PHP configured the way we want. And we don't know how many, if any, sites that have the right version of PHP for D8 would still be missing this.

Hmmm....

KarenS’s picture

OK, this library is available by default but not installed by default. I not only don't have it on my Mac MAMP installation, it looks like it will be quite a project to get it working. See http://szemian.wordpress.com/2011/03/21/compiling-intl-extension-for-mamp/ for a recipe to do it that has a bright red warning at the top that the recipe doesn't work. So at the moment I can't use it myself.

If you use MAMP ports, I found http://us3.php.net/manual/en/intl.installation.php, which says it is easy to do. I don't use that so I don't know.

There are more recipes at http://blog.geertvd.be/2011/05/18/installing-the-intl-extension-on-mamp/ and http://www.eperezdesigns.com/blog/web-development/php/install-php-extens... and http://thoomtech.com/post/15366294744/mamp-intl-lion. It sounds less than straight-forward.

The idea of using Symfony as a fallback was only if there would be rare occasions when it isn't available. If I can't even use it myself, I have to assume it is NOT going to be widely available. Big enterprises that can control their own environments can install it, but anyone using third-party hosting is up a creek if it's not already installed.

I'm not sure this should be used in core yet.

KarenS’s picture

And I found dozens of posts complaining about the fact that Symfony requires intl because they don't have it. I came across this post as well, https://groups.google.com/forum/#!topic/symfony-devs/RBtwBObsLCE, where Fabien Potencier says that the symfony fallback does NOT provide support for intlDateFormatter, so that is not a solution either. The fallback just avoids a fatal error when trying to use a non-existing class.

KarenS’s picture

If we do decide to use it, I was thinking of wrapping it in a Drupal class, like this. This would give us the ability to default to using Drupal's own settings for language and country and add in a handy method for transforming strings from the PHP format to the intlDateFormatter format. And if we find other places where the default Formatter behavior could be tailored better to Drupal, or we want to add in additional exception handling, etc, we can do that here as well.

KarenS’s picture

OK, here's my idea for this. I have proposed adding an extension of the DateTime class to core at #1802278: Add a Date component to core. I added to that class some logic that allows you to use the IntlDateFormatter in the format() function. It's optional, because I don't think it's safe to assume it will be available, but it's available for those who want it. If that gets in, I have a follow-up patch that alters the format_date function to accept an array of the values needed by the IntlDateFormatter, which is then passed on to the date class in the format() method. The class uses the same logic currently used in format_date() as the fallback, so that still works if the Intl class can't be used.

If sometime before core is released we determine that the IntlFormatter is widely enough available that we want to make it the default handling method, we can, otherwise we still have an option that can be exploited in contrib.

cafuego’s picture

OK, here's my idea for this. I have proposed adding an extension of the DateTime class to core at #1802278: Add a Date component to core . I added to that class some logic that allows you to use the IntlDateFormatter in the format() function. It's optional, because I don't think it's safe to assume it will be available, but it's available for those who want it.

That sounds good, but I think that the IntlDateFormatter should be the default option, with a fall-back to date() if it's not available and a map function that translates the other way. I don't really have an articulated technical reason for that, I just think that we should prefer a built-in multilingual way of doing things when such a way is available.

Much like what the gd library currently does (http://drupalcode.org/project/drupal.git/blob/refs/heads/7.x:/modules/im...) , a lack of intl could include a warning in the status report.

As for availability, the intl extension is trivial to install on Debian, Ubuntu, RHEL and CentOS (and OEL too I suppose). It seems that intl is becoming more common as a requirement for PHP based apps (Moodle and Symfony2 at a pinch seem to require intl).

KarenS’s picture

If we make it the default we have to solve the problem of two different date format strings and have a way for core to manage both of them. If it is not the default, core only has to worry about the same date strings we've always used and anyone who wants to use the intl formatter is responsible for passing in the new type of string. The problem with the strings is the biggest problem of handling this, IMO. I'm open for ideas on how both types of strings can work seamlessly.

chx’s picture

#30 your argument about "easy to install" is entirely irrelevant for core. We are targetting shared hosts where you can't install anything. By that measure we would be using PHP 5.4 because it is easy to install...

KarenS’s picture

I've been playing with this in conjunction with my core patch for a Date Object that can use the intlDateFormatter. I think I've found the right way to integrate the two kinds of date strings that we will have if we can't rely on that class always being available. There is work going on in #1571632: Convert regional settings to configuration system to get the date strings into YAML files. If we add another value to the YAML file we can have a way to provide a format of either type. Then if I can get the date object committed, I want to go back to the format_date() function and add a way to provide the intl values to that, and we should have a system that can create a date using either the standard PHP method or the new Intl method.

I'm adding a comment to #1571632: Convert regional settings to configuration system about how I think that could go.

KarenS’s picture

#1811912: Add pluggable calendar backend to core and centralize date translation is my suggestion for how to incorporate this functionality. That patch depends on and extends the patch at #1802278: Add a Date component to core, so you need both.

If that seems like an acceptable solution, we could mark this issue as a duplicate. I made that a separate issue instead of posting here because it includes a wider scope -- adding a calendar system plugin to core.

alexpott’s picture

Status: Active » Closed (duplicate)

So that we're on PHP 5.3.10 as a minimum for Drupal 8 we will always have the IntlDateFormatter class available and the ability to use it was added in #1802278: Add a Date component to core

Gábor Hojtsy’s picture

Yeah, well, those actually trying to use it will see there are several problems on the user interface around intl date formats. If I recall right, you cannot actually edit them for example.... :/

Gábor Hojtsy’s picture

Issue summary: View changes

Updated issue summary. Added reference to php specification

Mojtaba Reyhani’s picture

Issue summary: View changes

how can use this patch manually, or have any tutorial to use this patch.