Built-in :date output modifier versus custom PHP output modifier for MODX Revolution timestamps
0 reputation · 20 Apr 2023, 08:08 UTC
0 reputation · 20 Apr 2023, 08:08 UTC
Goal: format a resource timestamp such as publishedon directly inside a MODX Revolution template tag.
The built-in :date output modifier calls PHP strftime with no custom code, but its output depends on server locale, timezone, and strftime behavior. A custom output modifier snippet allows arbitrary formatting and localization, yet it must be written in PHP, installed, and invoked for each placeholder. Both run at parse time after resource fields resolve, and an empty timestamp can still trigger the modifier depending on whether the tag is cached or uncached.
Assumption: MODX Revolution. The deciding constraint is whether deterministic formatting across environments matters more than avoiding custom PHP.
Does the built-in :date modifier produce consistent output across servers with different PHP timezone settings? When should a custom output modifier be preferred over :date if the only requirement is a fixed date format? Does tag caching change whether an empty timestamp value reaches the modifier?
35275 reputation · 20 Apr 2023, 18:45 UTC
No. The built-in :date output modifier in MODX Revolution does not produce consistent output across servers with different PHP timezone settings. It calls PHP's strftime(), whose behavior depends on the server's locale, timezone, and operating-system implementation. Even when the MODX system setting date_timezone is set, strftime() remains sensitive to platform differences—historically between Windows and Unix-like systems—and to locale configuration.
If your only requirement is a fixed date format and you need that format to be identical across multiple servers or containers, a custom output modifier is preferred. A snippet using DateTime or DateTimeImmutable with an explicit timezone and a format string such as 'Y-m-d H:i:s' is portable, because DateTime::format() uses a documented, consistent set of format characters rather than strftime()'s platform-dependent specifiers. The built-in :date is reasonable only for a single-server deployment where the PHP timezone, locale, and OS are fully controlled.
Tag caching does not change whether an empty timestamp reaches the modifier. Both cached tags ([[*publishedon:date=%Y-%m-%d]]) and uncached tags ([[!*publishedon:date=%Y-%m-%d]]) resolve the resource field and pass the value to the output modifier at parse time. The only difference is when the modifier runs: once when the cache is written, or on every request for uncached tags. If publishedon is zero or null, strftime() may still receive that value and return an empty string or a fallback such as the Unix epoch, depending on the format string and PHP version.
formatDate.[[*publishedon:formatDate]].<?php
if (empty($input)) {
return '';
}
$dt = new DateTime('@' . $input);
$dt->setTimezone(new DateTimeZone('UTC'));
return $dt->format('Y-m-d H:i:s');
?>
Because strftime() is deprecated as of PHP 8.1, MODX 3.x may handle the :date modifier differently than MODX 2.x—potentially through a polyfill or an internal switch to DateTime. That behavior is version-dependent and should be verified in your specific stack before relying on it. To confirm locally, create a resource with a known publishedon value, render both [[*publishedon:date=%Y-%m-%d %H:%M:%S]] and your custom modifier, then change the PHP timezone and reload.
Missing diagnostic detail: Which MODX major version and PHP version are you running? The answer determines whether the built-in :date modifier is deprecated or internally rewritten, which changes the recommendation.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 20 Apr 2023, 08:52 UTC
The built-in :date modifier passes its format string to PHP strftime(), so %x and %c are locale-aware and are not PHP date() tokens such as Y-m-d. On PHP 8.1+, strftime() behavior changed, so identical tags can render differently across servers even with similar locale settings. For an empty publishedon, cached tags may skip modifier processing at cache time, while uncached tags typically still invoke it. Verify by comparing [[*publishedon:date=`%x`]] against a custom snippet using DateTime::format('Y-m-d') with an explicit timezone on each target environment.