Weird Looking: human-readable date differences

human-readable date differences

December 21, 2005 5:29am (4 years, 7 months and 1 week ago)
I had an idea today.  I wanted to include human-readable ages beside some of the dates on my blog (e.g. “1 day, 8 hours ago”).  I searched around a bit, and all of the solutions I found just divide the difference in seconds by 31536000 to get years and so on.  It doesn’t take leap years or different-sized months into account.

Today is December 21, 2005.  To me, 2 years, 2 months ago was October 21, 2003.

It took a while to write.  The first version used brute force to get the difference out of mktime.  That worked perfectly but was slow.  I used it to generate a bunch of test cases, though.

What I eventually figured out is that you can just subtract the beginning years, months, hours, etc. from the current ones.  After that, use borrowing similar to long subtraction to get rid of any negative numbers.  Seconds borrow 60 from a minute, hours borrow 24 from a day, days borrow from an array of month lengths.  You get the idea.  Anyway, here’s the code, which I’ll post on the SmartyPlugins wiki as soon as I figure out how.

<?php

/**
* Smarty plugin
* -----------------------------------------
* File:    modifier.human_age.php
* Type:    modifier
* Name:    human_age
* Params:  from_time    unix timestamp (required)
*          to_time      unix timestamp (default=false (means now))
*          strip_zeroes remove units with zero values ("0 days") (default=true)
*          limit        limit the number of units returned (default=no limit)
* Author:  Michael Barton (http://www.weirdlooking.com/email)
* Purpose: Returns a human-readable difference between two times
*          e.g. "1 day, 8 hours, 48 minutes, 57 seconds"
* Remarks: Correct for leap years and different-sized months.
*          Well, my idea of correct.  Turns out it's slightly subjective.
*
* @link http://www.weirdlooking.com/blog/45
* -----------------------------------------
*/

 require_once $smarty->_get_plugin_filepath('shared','make_timestamp');

 function smarty_modifier_human_age($from_time, $to_time = false, $strip_zeroes = true, $limit = false)
 {
   if ($to_time === false)
     $to_time = time();
   $order = array('year', 'mon', 'mday', 'hours', 'minutes', 'seconds');
   $names = array('year', 'month', 'week', 'day', 'hour', 'minute', 'second');
   $sizes = array(12, 2=>24, 60, 60);
   $month_lengths = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30);
   $from = getdate($from_time);
   $to = getdate($to_time);
   $delta = array();
   foreach ($order as $unit)
     $delta[] = $to[$unit] - $from[$unit];
   for ($i = 5; $i >= 0; $i--)
     if ($delta[$i] < 0)
     {
       $delta[$i-1]--;
       if ($i == 2 && ($to['mon'] == 3 && !($to['year'] % 4)))
         $delta[$i] += 29; // fix this before the year 2100!!!
       else if ($i == 2)
         $delta[$i] +=  $month_lengths[$to['mon']-1];
       else
         $delta[$i] += $sizes[$i-1];
     }
   $delta[2] -= ($week = floor($delta[2]/7)) * 7;
   $delta = array($delta[0], $delta[1], $week, $delta[2], $delta[3], $delta[4], $delta[5]);
   $ret = array();
   foreach ($delta as $i => $val)
     if (($delta[$i] || !$strip_zeroes) && ($limit === false || sizeof($ret) < $limit))
       $ret[] = $delta[$i].' '.$names[$i].($delta[$i] == 1 ? '' : 's');
   return join(', ', $ret);
 }
?>

Comments

Apr 13, 2006 1:08am
Very nice! Is it free? What license are you using for this code? Public Domain? BSD? LGPL? :)

Thanks
Apr 13, 2006 1:56am
I really need to fix code formatting on IE…

Anywho, consider it public domain.
Apr 13, 2006 3:22pm
Thanks Mike, I am using it on the new music website I am working on (very beta yet) http://cchits.org  =)
Oct 25, 2006 10:38am
Is there a detection for leap years all 4 years the february has 29 days ?
Oct 25, 2006 11:47am
Most of the leap year handling is inherited from the getdate API.

If the start date is in like February 2100 and the end date is in March, the calculations will be off by a day.  I didn’t worry about that too much for my blog, but it’d be fairly easy to fix.  It needs to account for the fact that years which are divisible by 100 and not divisible by 400 shouldn’t get a leap year.
Apr 5, 2008 4:52am
Hi,
    Let me know the format of date that what we have to submit this modifier like that YYYY-mm-dd or dd/mm/YYYY.

Thanks

Leave a comment


Accepts BBCode with a few enhancements.