I wrote about localising dates (and other data) in a recent blog post, but unfortunately there were some shortcomings where time zones were concerned. As I alluded to in that post there is a way around this via the Intl extension that exposes a simple API to format DateTime instances.

Thankfully this follow up post will be quite short as the setup is very simple for those of you on Ubuntu/Debian you can use the repositories.

sudo apt-get install php5-intl

For other distributions you can use PECL to install Intl after building the dependencies - icu (on Redhat this is as simple as yum install libicu libicu-devel.x86_64).

pecl install intl
echo "extension=intl.so" >> /etc/php.ini

Don’t forget to restart your webserver (eg. Apache) to make the new extension available to PHP.

Now it is installed you can use the extension to format dates in code.

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES',
    IntlDateFormatter::FULL,
    IntlDateFormatter::FULL
);
echo $IntlDateFormatter->format($DateTime);
// martes, 21 de julio de 2015, 14:11:11 (Hora de verano británica)

In this example I am telling the IntlDateFormatter that I want to use the Spanish locale (es_ES) to print out the full date and time. By changing the constants you can alter the format of the date that will be printed.

So if you just want the time you could create a formatter in the following manner.

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES',
    IntlDateFormatter::NONE,
    IntlDateFormatter::FULL
);
echo $IntlDateFormatter->format($DateTime);
// 14:15:04 (Hora de verano británica)

There are a few constants that can be passed into both the second (date) and third (time) parameters of the IntlDateFormatter constructor to change output format.

  • IntlDateFormatter::NONE - exclude this element from display
  • IntlDateFormatter::SHORT - shortest format (22/07/2007)
  • IntlDateFormatter::MEDIUM - abbreviated format (Jul 22, 2007)
  • IntlDateFormatter::LONG - unabbreviated format (July 22, 2007)
  • IntlDateFormatter::FULL - full date information

Another example using the long format with the Spanish locale:

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES',
    IntlDateFormatter::LONG,
    IntlDateFormatter::LONG
);
echo $IntlDateFormatter->format($DateTime);
// 21 de julio de 2015, 14:25:41 GMT+1

So now you have seen how to change the format of the date we can look at the timezone aspect of the equation. Normally it uses the default PHP timezone, but this can be specified as the fourth parameter of the IntlDateFormatter constructor. You can pass in an instance of DateTimeZone or IntlTimeZone or a timezone string such as Europe/London or GMT+10.

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES',
    IntlDateFormatter::FULL,
    IntlDateFormatter::FULL,
    'Australia/Yancowinna'
);
echo $IntlDateFormatter->format($DateTime);
// martes, 21 de julio de 2015, 23:03:30 (Hora estándar de Australia central)

Incidentally, you can also pass other values into the format() method too and not just an instance of DateTime. You can also pass an IntlCalendar instance, the number of seconds since the Unix epoch (01/01/1970 00:00:00) or a an array compatible with localtime().

By default Intl uses the Gregorian calendar but it can also make use of other calendars by specifying a fifth parameter in the calls to IntlDateFormatter constructor. So by default the previous example would include a calendar specification like the following.

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES',
    IntlDateFormatter::FULL,
    IntlDateFormatter::FULL,
    'Australia/Yancowinna',
    IntlDateFormatter::GREGORIAN
);
echo $IntlDateFormatter->format($DateTime);
// martes, 21 de julio de 2015, 23:12:43 (Hora estándar de Australia central)

Should you wish to use another calendar it can be specified as part of the locale. In the following example I have decided to use the Buddhist calendar.

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES@calendar=buddhist',
    IntlDateFormatter::FULL,
    IntlDateFormatter::FULL,
    'Australia/Yancowinna',
    IntlDateFormatter::TRADITIONAL
);
echo $IntlDateFormatter->format($DateTime);
// martes, 21 de julio de 2558 BE, 23:16:08 (Hora estándar de Australia central)

Notice that I have changed the code for the locale (first parameter) to es_ES@calendar=buddhist and the calendar (fifth parameter) to IntlDateFormatter::TRADITIONAL. You could also use the Islamic calendar with:

$DateTime = new DateTime();
$IntlDateFormatter = new IntlDateFormatter(
    'es_ES@calendar=islamic',
    IntlDateFormatter::FULL,
    IntlDateFormatter::FULL,
    'Australia/Yancowinna',
    IntlDateFormatter::TRADITIONAL
);
echo $IntlDateFormatter->format($DateTime);
// martes, 5 de Shawwal de 1436 AH, 23:23:10 (Hora estándar de Australia central)

The calendars ICU allows you to play with include:

  • Japanese (@calendar=japanese)
  • Buddhist (@calendar=buddhist)
  • Chinese (@calendar=chinese)
  • Persian (@calendar=persian)
  • Indian (@calendar=indian)
  • Islamic (@calendar=islamic)
  • Hebrew (@calendar=hebrew)
  • Coptic (@calendar=coptic)
  • Ethiopic (@calendar=ethiopic)

So there you have it; localised time zone aware dates with PHP on multiple calendar types. If the provided formats suit your application then this is a simple way to ensure your date and time information is readable in various locations and languages.