Formatting Date-time fields with IntlDateFormatter

Displaying date and time strings with the PHP IntlDateFormatter class.

By Bob Ray  |  July 28, 2026  |  7 min read
Formatting Date-time fields with IntlDateFormatter

Background

In my previous article, we used PHP's date() function to display a formatted string for the date-time fields. Unfortunately the date() function produces all output in English, and the strftime() function, which honors the locale to produce output in any language is now deprecated.

In this article, we'll look at PHP's IntlDateFormatter Class. In the previous article, we were dealing with a number of resource fields. In this one and the following article in this series, we'll focus on just date-time fields. We'll use the createdon field ,because unlike the editedon and publishedon fields, it almost always has a value. (Some Resources that are created in code can have a createdon field set to 0 or 1, but all Resources created in the Manager will have that field set to a timestamp for the date and time they were first saved.)

Keep in mind that the information here, and in the following articles applies to displaying a formatted version of any date-time code, which can be either a timestamp or a string describing the date and time. Because using a string like (Friday, December 12th 4:07 PM) is not always reliable, it's strongly recommended that you always use a timestamp when possible, so that's what we'll do here.

Getting a Timestamp the Fast Way

The MODX Resource date-time fields (e.g., createdon) are actually stored as a timestamps in the database. When you retrieve them with get(), MODX converts them to human-readable strings. They can then be converted to timestamps with PHP's strtotime() function, but it's quite a bit faster and more efficient to get the timestamps directly from the database. Here's a generic method for getting the raw value of any MODX object field directly, given the object's ID:

$object = 'modResource';
$fieldName = 'createdon';
$id = 12;

  /* Make it run in either MODX 2 or MODX 3 */
  $prefix = $modx->getVersionData()['version'] >= 3
    ? 'MODX\Revolution\\'
    : '';

$query = $modx->newQuery($prefix . $object, $id);
$query->select($field);
$timeStamp = $modx->getValue($query->prepare());

Overview

I read a number of articles and manual pages on the IntlDateFormatter class, and they all seemed to make it look much more difficult than it really is. I thought I'd try to explain it in a way that in a way that makes it a little more approachable. Before going any deeper, I should mention that the IntlDateFormatter comes in two flavors. One is a "procedural" form that looks like it would have before PHP classes became standardized. The second is an "Object-oriented" form in which the IntlDateFormatter is a class. Because most people are working with classes (MODX Revolution is almost entirely class-based), I'll use that version in all the discussions below. Both versions work essentially the same way. They take the same arguments and handle them the same way, so if you'd prefer to use procedural code, you can search Google for IntlDateFormatter procedural code php manual for some examples.

To use the class version you do something like this:

$dateFormatter = new IntDateFormatter([args]);
$output = $dateFormatter->format($timeStamp);

For the procedural version, you would do something like this:

$format = datefmt_create([args]);
$output =   datefmt_format($format, $timestamp);

The initial arguments are the same for each version. Note that with the "procedural" version, the first line returns the IntlDateFormatter object, which is used as the first argument in the second line, so it appears to be class-based under the hood.

The Arguments

The IntlDateFormatter class takes six arguments, but only the first three are required. The other three are optional.

In the code just above, I used [args] to represent the arguments. Here are the real arguments (must be in this order):

$dateFormatter = new IntlDateFormatter(
    /* Required */
    $locale, // Standard language code
    $dateFormat, // Constant that specifies what the date should look like
    $timeFormat, // Constant that specifies what the date should look like

    /* Optional */
    $timezone, // standard location description
    $calendar, // default is Gregorian
    $customPattern, // roll-your-own format for date and time
);

Arguments Explained

The locale argument is the language code. Two-letter codes usually refer to the language itself (e.g., en for English, fr for French, de for German, es for Spanish, pt for Portuguese, etc.). Longer codes usually refer to a version of the language for a particular country (e.g., pt-BR for Brazilian Portuguese, en-UK for the United Kingdom, en-US for the USA. etc.).

There's a good list of the possible values at LocalePlanet. Note that although LocalePlanet lists the longer codes with an underscore, the recommended separator is a hyphen, which is what MODX uses.

The dateFormat argument lets you specify which one of the built-in date formats you want to use. The value must be one of the IntlDateFormatter constants (or their numeric values if you want to make life difficult for yourself and anyone who sees your code). It will make your life easier if you can live with one of these options (more on the date options in a bit). If not you can use the sixth argument to create a custom date display (more on this in my next article).

The timeFormat argument specifies which one of the built-in time formats you want to use (more on the options below). Like the dateFormat argument, the value must be one of the IntlDateFormat constants, or its numeric value (which no one should use). You can also create a custom time format, as we'll see in my next article.

The built-in formats are sometimes overridden for specific cultures. For example the MEDIUM date format will typically display the month name. I spent some time trying to figure out why this didn't work for German. Eventually I learned that German-speakers prefer not to see the month name spelled out in medium-length versions of dates.

The optional timezone argument takes the standard PHP date/timezone names like (America/Chicago, or Europe/Amsterdam). If, present the time will be presented for the specified timezone.

The calendar argument allows for non-Gregorian calendars (Gregorian is the default).

The final argument, customPattern allows you to create a custom display for the date, and/or time. More on this in my next article.

Built-in Date Options

Here are some examples of what is displayed by the IntlDateFormatter constants (with the date set to FULL) for 12/25/2025 4:03:22 am in English (en-US):

IntlDateFormatter::FULL -- Thursday, December 25, 2025
IntlDateFormatter::LONG -- December 25, 2025
<IntlDateFormatter></IntlDateFormatter>::MEDIUM -- Dec 25, 2025
IntlDateFormatter::SHORT -- 12/25/25

Here they are for German (de):

IntlDateFormatter::FULL -- Donnerstag, 25. Dezember 2025
IntlDateFormatter::LONG -- 25. Dezember 2025
IntlDateFormatter::MEDIUM -- 25.12.2025
IntlDateFormatter::SHORT -- 25.12.25

Relative Dates

You can prefix any of the date constants with RELATIVE_. If the date is yesterday, today, or tomorrow, the entire date section will be replaced with the appropriate one of those words. If the date is not for yesterday, today, or tomorrow, the RELATIVE_ prefix will be ignored.

Built-in Time Options

These are the same options as for dates, but without the RELATIVE_ option.

Here they are for English (Date:SHORT, timezone:America/Chicago) at 4:03:22 am in English (en-US):

IntlDateFormatter::FULL -- 12/25/25, 4:03:22 AM Central Standard Time
IntlDateFormatter::LONG -- 12/25/25, 4:03:22 AM CST
IntlDateFormatter::MEDIUM -- 12/25/25, 4:03:22 AM
IntlDateFormatter::SHORT -- 12/25/25, 4:03 AM

(If the date is FULL, or LONG you will also get the word " at " in English ahead of the time.)

And in German:

IntlDateFormatter::FULL -- 25.12.25, 04:03:22 Nordamerikanische Inland-Normalzeit
IntlDateFormatter::LONG -- 25.12.25, 04:03:22 GMT-6
IntlDateFormatter::MEDIUM -- 25.12.25, 04:03:22
IntlDateFormatter::SHORT -- 25.12.25, 04:03

Remember that with the longer options the date will be a different length in different languages. The length will also vary for different dates in the same language, depending on the month and day names.

/* German (FULL,FULL) */
/Donnerstag, 25. Dezember 2025 um 11:03:22 Mitteleuropäische Normalzeit

/* English (FULL,FULL) */
Thursday, December 25, 2025 at 4:03:22 AM Central Standard Time

IntlDateFormatter::NONE

This is also a possible value for both the date and time, in case you want to show one without the other. It is only an option for those two arguments. If you want to leave out any of the optional arguments, you can't leave them empty, but you can send null for the value. If, for example, you want to send a custom pattern, but don't care about the timezone and calendar, you can set the timezone and calendar arguments to null. The default Gregorian calendar will be used. See my next article before trying to use a pattern.

Coming Up

If none of the formats above works for you, you can create a custom date and/or time display using a custom pattern in the sixth argument. We'll see how to do that in my next article. In a later article, we'll circle around to using the IntlDateFormatter to show hidden Resource fields.


About Bob Ray

Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.

Learn more about Bob Ray.