After all these years, I still struggle whenever I have to play with dates in WordPress. Between wp_date()
, date_i18n
(), get_the_date()
, and of course the different date options of PHP, I always find it hard to start. Sprinkle a bit of timezone on top of that, and I find myself having to check the docs every time!
In today’s example, I wanted to generate a localized date and time when a post was published. Something like June 11, 2025, at 8:24 am
in English or le 11 juin 2025 à 08h
24. Seems simple enough.
Timezones
Let’s start with getting the timezone out of the way. My site is set to the timezone of Paris (UTC +2 right now). Luckily, in WordPress the timezone info is easily accessible via wp_timezone()
or wp_timezone_string()
:
wp_timezone_string()
gives meEurope/Paris
.wp_timezone()
gives me aDateTimeZone
object with the timezone:
DateTimeZone {#8386
timezone: Europe/Paris (+02:00),
}
I think I can work with that!
Using DateTimeImmutable
(get_post_datetime) and wp_date
I would need to get the post’s full publishing date and time. I could then turn it into a DateTimeImmutable
object that would take that timezone into account. Luckily, WordPress gives us post_date
in the WP_Post
object: 2025-06-11 08:24:
00
My first idea was to turn this into a DateTimeImmutable
object, passing it wp_timezone()
:
$post = get_post( $post_id );
$publishing_date = new \DateTimeImmutable(
$post->post_date,
wp_timezone()
);
DateTimeImmutable @1749623040 {#8416
date: 2025-06-11 08:24:00.0 Europe/Paris (+02:00),
}
But it turns out WordPress can do this for us! get_post_datetime()
to the rescue:
$post = get_post( $post_id );
$post_datetime = get_post_datetime( $post_id );
DateTimeImmutable @1749623040 {#8399
date: 2025-06-11 08:24:00.0 Europe/Paris (+02:00),
}
At this point, all what’s left is to format and localize that date, so the result can be used regardless of the site language and date / time format. This is where wp_date()
comes in. We’ll provide it with a localized format, and the Unix timestamp from our DateTimeImmutable
object.
Let’s build our localized format first. This is the part that I find clunky, and prone to issues because of the translatable string:
$datetime_format = sprintf(
/* Translators: %1$s is a formatted date, e.g. June 11, 2025. %2$s is a formatted time, e.g. 8:24 am. All other words/letters need to be escaped. */
__( '%1$s \a\t %2$s', 'my-textdomain' ),
get_option( 'date_format' ),
get_option( 'time_format' )
);
On a French site, this may give us \l\e j F Y \à H:i
.
Feed that to wp_date()
, together with the Unix timestamp from our DateTimeImmutable
object:
$formatted_publishing_date = wp_date(
$datetime_format,
get_post_datetime( $post_id )->getTimestamp()
);
And you end up with le 11 juin 2025 à 08:24
.
This works, but it all seems unnecessarily complicated. 🙂