Using amp-timeago to display relative dates on your Native-AMP site

In my post about how I renewed my website with Gutenberg and native AMP support I mentioned that I’d be sharing some implementation details. A couple days ago I posted about how I built a reusable section block with Gutenberg. Today we’re going to look at an AMP-specific feature and how I made use of it for my site. While the AMP plugin for WordPress does a great job in ensuring your WordPress site becomes AMP-compatible, there are still tons of additional AMP features to explore, some of which are too specific to generally add support in the plugin. An example of this is the amp-timeago component, which allows displaying relative time periods, pretty much in realtime. In other words, instead of showing a concrete date and time, it will show a string such as “x seconds/minutes/hours/days/weeks/months/… ago” – you get the gist. You can see a live-example of this when looking at when this post was published, just above, below the headline. And this is precisely what we’re going to focus on in this post, how I implemented this feature and how you can implement it for your Native-AMP WordPress site.

Why use amp-timeago?

The amp-timeago component of AMP allows to display relative time periods between now and another given date and time. This has become a common practice in a lot of platforms such as Facebook or Twitter, so why not leverage it on your blog as well?

In WordPress, there is a human_time_diff() function that allows you to do something similar. However, since that function operates server-side, it is practically unusable for real-world sites, since it only shows accurate time periods if you don’t use any kind of page caching and let WordPress handle every request directly – which I don’t recommend for performance reasons.

amp-timeago is a client-side solution. On the server, you only provide the date and time in a machine-readable format, and also a regular formatted date/time value that is human-readable. The latter will not be visible on the page because a relative time period will be displayed instead. amp-timeago will however display it if you hover over the time period, which is another benefit over the simple human_time_diff(). If a user wants to quickly find out the exact date, they still can.

Displaying relative post dates

Themes typically handle output of the post dates through either a function (such as mytheme_posted_on()) or a dedicated template (such as template-parts/entry-date.php). I personally prefer using templates for generating markup, so that’s what we’ll look it in this post. The code here is applicable to the function approach as well though.

Post dates are commonly displayed inside time elements – so the most important part here is that we need to use amp-timeago elements instead. However, since AMP support is typically enabled through the plugin, we wanna make sure we fall back gracefully, in case the plugin is not activated. This allows using the theme also for sites that do not support native AMP. Let’s have a look at the code I used to render post dates.

$post_date_gmt       = get_post_time( 'c', true, null, false );
$post_date_formatted = get_date_from_gmt( $post_date_gmt, get_option( 'date_format' ) );

if ( felix_arntz_v6_is_amp() ) {
	/* translators: %s: post time period */
	$posted      = _x( 'Posted %s', 'post date', 'felix-arntz-v6' );
	$locale      = substr( get_locale(), 0, 2 );
	$time_string = '<amp-timeago height="24" layout="fixed-height" datetime="%1$s" locale="' . esc_attr( $locale ) . '">%2$s</amp-timeago>';
} else {
	/* translators: %s: post date */
	$posted      = _x( 'Posted on %s', 'post date', 'felix-arntz-v6' );
	$time_string = '<time class="entry-date" datetime="%1$s">%2$s</time>';
}

$time_string = sprintf(
	$time_string,
	esc_attr( $post_date_gmt ),
	esc_html( $post_date_formatted )
);

?>
<span class="posted-on">
	<?php
	printf(
		esc_html( $posted ),
		'<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	);
	?>
</span>
<?php

First of all, as you can see, I use the GMT time the post was published at. This doesn’t have to do anything with AMP, I just strongly recommend following this pattern for any moment you are rendering dates. When a site changes their timezone, all dates from old posts will become invalid then – the GMT dates do not though. So relying on GMT will ensure your dates are always accurate, and you can still display them in the local time (which is what get_date_from_gmt() is used for). If you are interested in more background on this, please watch the amazing WordCamp session by Rarst about broken time.

Then, there is a function felix_arntz_v6_is_amp(). This is a simple wrapper function for checking whether we should serve AMP content for the current request. I recommend you to add a similar function to your theme. Here is its simple code:

/**
 * Checks whether the current context is an AMP endpoint.
 *
 * @since 1.0.0
 *
 * @return bool True if an AMP endpoint, false otherwise.
 */
function felix_arntz_v6_is_amp() {
	return function_exists( 'is_amp_endpoint' ) && is_amp_endpoint();
}

Depending on whether AMP should be served or not, we then decide on which tag to render, and also which text to use in combination with it. “Posted on August 4th, 2018” sounds correct, but “Posted on 2 months ago” doesn’t, right? Therefore, when rendering a relative time period via amp-timeago, we need to adjust the related string to get the proper “Posted 2 months ago” output. The only additional tweaks we need for amp-timeago is providing our site’s language to it, which we can detect via get_locale(), and provide a height attribute to it. I chose 24 because that fits well with the font size and line height on my site.

Note that the datetime attribute of both the time and amp-timeago tags requires a date in ISO 8601 format, with timezone information. To get this in a way that we can a 100% rely on, we need to use the GMT date – as indicated before, for other timezones things can get clunky here for multiple reasons.

And that is already it – you can now render your post dates as related time periods via the amp-timeago component.

Displaying relative comment dates

Just as an extra addition, I’ll share the code to accomplish this for comment dates as well. You might have a dedicated template for a single comment in your theme where you could use that. Here is the code responsible for rendering the current comment date with amp-timeago support:

$comment_date_gmt       = get_comment_time( 'c', true, false );
$comment_date_formatted = sprintf(
	/* translators: 1: comment date, 2: comment time */
	__( '%1$s at %2$s', 'felix-arntz-v6' ),
	get_date_from_gmt( $comment_date_gmt, get_option( 'date_format' ) ),
	get_date_from_gmt( $comment_date_gmt, get_option( 'time_format' ) )
);

if ( felix_arntz_v6_is_amp() ) {
	$locale      = substr( get_locale(), 0, 2 );
	$time_string = '<amp-timeago height="24" layout="fixed-height" datetime="%1$s" locale="' . esc_attr( $locale ) . '">%2$s</amp-timeago>';
} else {
	$time_string = '<time datetime="%1$s">%2$s</time>';
}

$time_string = sprintf(
	$time_string,
	esc_attr( $comment_date_gmt ),
	esc_html( $comment_date_formatted )
);

?>
<div class="comment-date">
	<a href="<?php echo esc_url( get_comment_link() ); ?>">
		<?php echo $time_string; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ ?>
	</a>
</div>
<?php

Note that the markup is a bit different, but that is just because I personally render comment dates differently than post dates. They also don’t have the “Posted on…” / “Posted…” prefix. Otherwise, the code is fairly similar, with the exception that comment templating functions are used instead of the post templating functions.

Leave a Reply

Your email address will not be published. Required fields are marked *