Time Ago: How to Display Publish Dates as Time Since Posted

By Osvaldas Valutis
We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now

It’s common to present dates on the Web in a format such as Published on September 12th, 2015, or 09/12/2015, 09:41:23 and 2015-09-12.

Each of these examples tells the full date and/or time of some kind of activity – be it a published article, or a reader comment, or perhaps an uploaded video.

Date formats like this might seem perfectly reasonable. After all, they’re informative and human-readable! Well yes, but “human-readable” doesn’t necessary mean users will readily be able to understand how recently the activity has occurred. The Web is a fast-moving place, and giving your content a sense of freshness could be the key to engaging with your audience! So, let’s look at how we could improve on these common date formats.

A Little Trick

Once again, let’s say you have stumbled on a post that was actually published just a couple of minutes ago, but the subheading of the post states this:

Published on September 12th, 2016

… or this:

Published on 2016-09-12, 09:41:23

The problem with these messages is that they don’t communicate the feeling that your website has just been updated or that you do that very often. So, surely it be more inviting – and much clearer – to present the time this way:

Published 2 minutes ago

Seen this many times before? But how many of you have built this into your projects? Take Facebook for example: what would it look like if they presented the dates for the latest content as in my first example? Compare the left and right columns in the following image:

timestamp formats compared

The left column’s dates aren’t that attractive, are they? I’ll wager the right hand examples are more appealing to you. Knowing that the content is fresh is very important – especially on the social networks, where people are more likely to ignore content that’s old or not clearly timestamped.

Printing Better Dates

In order to present better dates, you’ll need some server-side scripting, and I’ll use PHP for this demo. I created a tiny function called time_ago(), as shown here:


    define( TIMEBEFORE_NOW,         'now' );
    define( TIMEBEFORE_MINUTE,      '{num} minute ago' );
    define( TIMEBEFORE_MINUTES,     '{num} minutes ago' );
    define( TIMEBEFORE_HOUR,        '{num} hour ago' );
    define( TIMEBEFORE_HOURS,       '{num} hours ago' );
    define( TIMEBEFORE_YESTERDAY,   'yesterday' );
    define( TIMEBEFORE_FORMAT,      '%e %b' );
    define( TIMEBEFORE_FORMAT_YEAR, '%e %b, %Y' );

    function time_ago( $time )
        $out    = ''; // what we will print out
        $now    = time(); // current time
        $diff   = $now - $time; // difference between the current and the provided dates

        if( $diff < 60 ) // it happened now
            return TIMEBEFORE_NOW;

        elseif( $diff < 3600 ) // it happened X minutes ago
            return str_replace( '{num}', ( $out = round( $diff / 60 ) ), $out == 1 ? TIMEBEFORE_MINUTE : TIMEBEFORE_MINUTES );

        elseif( $diff < 3600 * 24 ) // it happened X hours ago
            return str_replace( '{num}', ( $out = round( $diff / 3600 ) ), $out == 1 ? TIMEBEFORE_HOUR : TIMEBEFORE_HOURS );

        elseif( $diff < 3600 * 24 * 2 ) // it happened yesterday
            return TIMEBEFORE_YESTERDAY;

        else // falling back on a usual date format as it happened later than yesterday
            return strftime( date( 'Y', $time ) == date( 'Y' ) ? TIMEBEFORE_FORMAT : TIMEBEFORE_FORMAT_YEAR, $time );


Let’s look at some details of this code.

  • The only argument you must provide is $time, and it’s a date in Unix Timestamp – such as time_ago( 1442082961 ).
  • Examples of what the function will return in respect of the $time passed:
    • now – if it happened less than 60 seconds ago (TIMEBEFORE_NOW)
    • 3 minutes ago – if less than 60 minutes ago (TIMEBEFORE_MINUTE(S))
    • 8 hours ago – if less than 24 hours ago (TIMEBEFORE_HOUR(S))
    • yesterday – if less than 48 hours ago (TIMEBEFORE_YESTERDAY)
    • 12 Sep – if more than 48 hours and happened this year (TIMEBEFORE_FORMAT)
    • 12 Sep, 2015 – if happened not this year (TIMEBEFORE_FORMAT_YEAR).
  • The PHP definitions are for separating the config-like data from the function code (it would be a good practice to put all of the define() occurrences into a config file and the function in helpers file).
  • {num} in definitions are replaced with actual numbers (minutes or hours).
  • I use strftime() instead of date() to avoid language/locale issues.

So, for example, if you want to get this onto your WordPress site, you’d simply write this:

<?=time_ago( get_the_time( 'U' ) )?>

Or if it was some other hypothetical CMS:

<?=time_ago( $post->date_created )?>

Or the static way:

<?=time_ago( 1447571705 )?>

Accessibility & Usability

There’s a specific HTML element that you should use for presenting dates: <time>. In our case, when using the time_ago function, the value of the time element is not always in a valid date format (like yesterday or 3 minutes ago). Therefore, you should also provide a fallback value by using [datetime] attribute:

Published <time 
    datetime="<?=date( 'Y-m-d', $time )?>" 
    title="<?=strftime( date( 'Y', $time ) == 
        date( 'Y' ) ? TIMEBEFORE_FORMAT : TIMEBEFORE_FORMAT_YEAR, $time )?>">
    <?=time_ago( $time )?>

This would result in better accessibility, for example:

Published <time datetime="2015-09-12" title="September 12">3 minutes ago</time>

Did you spot the [title] attribute? It’s a tiny usability improvement: putting the cursor over date text shows a message presented in the title attribute. That’s for the users who, for some reason, were looking for the “real” dates. Here’s a little CSS trick to beef up the feeling that there is something more:

    cursor: help;

CSS help cursor

JavaScript Enhancement

There’s one more thing we can do! Have you noticed that Facebook also increments the dates in real time? Just watch at 3 mins for a minute and it will turn into 4 mins and so on. So, there are types of websites were this works out as a really nice enhancement. It wouldn’t be useful on an article post like this, but It’s perfect on a site like Readerrr:

<time data-time="<?=$time?>" ...

Finally, we need to convert the PHP code into a JavaScript equivalent. I’ve done this for you in vanilla JS (though a jQuery version is available as well). The script walks through each time[data-time] element once every minute (setTimeout( updateDates, 1000 * 60 )) and updates the values:

// ...
var elements    = document.querySelectorAll( 'time[data-time]' ),
    updateDates = function()
        Array.prototype.forEach.call( elements, function( entry )
            var out = '';
            // ...
            entry.textContent = out;
        setTimeout( updateDates, 1000 * 60 );
setTimeout( updateDates, 1000 * 60 );
// ...

A JavaScript demo

Online Demo and Code Download

You can check out an online demo of the above code or download the full demo code.

One more thing

In the examples above, the full date is presented if the activity has occurred three or more days ago. But it’s quite easy to extend the script for presenting the time in the ways like 5 days ago, 2 weeks ago and 1 month ago, and so on:

// ...

define( TIMEBEFORE_DAYS,    '{num} days ago' );
define( TIMEBEFORE_WEEK,    '{num} week ago' );
define( TIMEBEFORE_WEEKS,   '{num} weeks ago' );
define( TIMEBEFORE_MONTH,   '{num} month ago' );
define( TIMEBEFORE_MONTHS,  '{num} months ago' );

function time_before( $time )
    // ...

    elseif( $diff < 3600 * 24 * 7 )
        return str_replace( '{num}', round( $diff / ( 3600 * 24 ) ), TIMEBEFORE_DAYS );

    elseif( $diff < 3600 * 24 * 7 * 4 )
        return str_replace( '{num}', ( $out = round( $diff / ( 3600 * 24 * 7 ) ) ), $out == 1 ? TIMEBEFORE_WEEK : TIMEBEFORE_WEEKS );

    elseif( $diff < 3600 * 24 * 7 * 4 * 12 )
        return str_replace( '{num}', ( $out = round( $diff / ( 3600 * 24 * 7 * 4 ) ) ), $out == 1 ? TIMEBEFORE_MONTH : TIMEBEFORE_MONTHS );

        // ...

Wrap Up

User experience and satisfaction is in details. Sometimes a simple detail – such as a dynamic date format – is enough to make our websites a little bit better.

So, what do you think of this solution? Would you consider using it on your next project? Do you have any questions about it? Please let me know in the comments.

By the way, I hope somebody can tell Instagram people that 122w isn’t cool, and that 2.4yrs would be much easier to understand. That would be better, wouldn’t it?

We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now
  • OphelieLechat

    “By the way, I hope somebody can tell Instagram people that 122w isn’t cool, and that 2.4yrswould be much easier to understand. That would be better, wouldn’t it?”

    Yes, a million times better!

    Thanks for this post Osvaldas. At SitePoint, we’ve opted to keep the publication date (using the PST time zone) for clarity, but if we were a more news-based site, we’d be looking at a different date format. I’m curious to know whether readers have a preference.

    • Sure but something like “2 years, 5 months” would be even better. It’s weird to use decimal numbers when you do not have a base of 10. Use the same units as you normally use. You never say “1,4 years ago” in normal speech, you say “one year and 5 months ago”, or just round it to “one and a half years”. You could use quarters, but sticking to months is more consistent.

  • Lasse Rafn

    “date_created )?&gt”

    You have some formatting issues =D &gt should probably me: > or simply >

    • Ralph Mason

      Ugh, thanks for that! We’ve been messing with code formatting a bit, and it’s hard to spot all the consequences. :-)

  • KrisG

    Maybe Sitepoint could read this article and get something from it. I states the published date as ‘January 20, 2016’, ‘6 days ago’ would be cooler.

    • Ralph Mason

      Yeah, see Ophelie’s comment on that above. :-)

  • Ralph Mason

    Thanks for pointing out that option. :-)

  • Great article!

    These dates are in fact better on pages where we want to see that the website is often updated. However, I don’t think they should be displayed everywhere.

    If I take SitePoint as an example, I think that displaying the exact date on each article page is a great idea that should be kept, but displaying relative dates could be a good idea to test on the articles lists.

    Anyway, thanks for thinking about telling that we should display the absolute date on a toolbox, as it’s often forgotten…

  • Terrell

    I used this and my time is saying 47 years ago on all post

    • John zenith

      Yes, that should be expected if you’re not converting that date time to a unix timstamp. For example, if you pass in a date time to the function like so, date(‘Y-m-d H:i:s’), it’ll return 47 years ago. So, to get around this, wrap your date time in the strtotime() function, strtotime( date(‘Y-m-d H:i:s’) ), or just make it look simpler, strtotime( ‘+5 days’ );