-1

I found this really nice PHP script that converts any datetime into a relative string, for example:

'2013-05-01 00:22:35'  ->  '3 months ago'

It's really cool, but I would like to "trick" the function so that even if the date is, let's say, 20 minutes before, the function returns 1 hour ago instead of 20 minutes ago. Thus, I want to enforce a minimum difference of 1 hour, even when the difference is less than that.

For reference, here is the function.

function time_elapsed_string($datetime, $full = false) {
  $now = new DateTime;
  $ago = new DateTime($datetime);
  $diff = $now->diff($ago);

  $diff->w = floor($diff->d / 7);
  $diff->d -= $diff->w * 7;

  $string = array(
    'y' => 'year',
    'm' => 'month',
    'w' => 'week',
    'd' => 'day',
    'h' => 'hour',
    'i' => 'minute',
    's' => 'second',
  );
  foreach ($string as $k => &$v) {
    if ($diff->$k) {
      $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
    } else {
      unset($string[$k]);
    }
  }

  if (!$full) $string = array_slice($string, 0, 1);
  return $string ? implode(', ', $string) . ' ago' : 'just now';
}

I tried a lot of different things, but nothing really worked.

How can I enforce a minimum difference of 1 hour?

Just a student
  • 10,560
  • 2
  • 41
  • 69
jgvk
  • 37
  • 4
  • 1
    So you want the minimum to be 1 hour ago? – Qirel Jun 10 '17 at 12:15
  • Exactly. I want function to return '1 hour ago' instead of (for example) '25 minutes ago' – jgvk Jun 10 '17 at 12:17
  • 1
    Why you using `foreach`. you can just use `$diff->d` or `$diff->h` for get particular value. and `if($diff->h < 0){ $hours = 1;} echo $hours`. – urfusion Jun 10 '17 at 12:19
  • @urfusion Your approach is flawed in multiple ways. Most importantly, the difference can be exactly 1 day, in which case `$diff->h` will be `0`. You wouldn't want to suddenly output `1 hour ago` in that case. – Just a student Jun 10 '17 at 12:26
  • @Justastudent So how can I do it then? – jgvk Jun 10 '17 at 12:27
  • @jgvk you have not modified the hours or minutes value, so why would it print anything else? – inarilo Jun 10 '17 at 12:27
  • @Justastudent 1 is always greater then 0. So I think it will be 1 in that conditions. – urfusion Jun 10 '17 at 12:28
  • Basically I tried a lot of different stuffs, nothing really worked, so I wanted to put a clear working code here on SO and start from there – jgvk Jun 10 '17 at 12:28
  • 4
    I don't understand, the question states "this is what I've tried", but it's seemingly verbatim to this answer? https://stackoverflow.com/a/18602474 – Jared Farrish Jun 10 '17 at 12:44
  • I agree with @JaredFarrish and @ jgvk: The question is unclear for a few reasons and another being "why" would you want to "trick" it? – Funk Forty Niner Jun 10 '17 at 12:55
  • *"How can I enforce a minimum difference of 1 hour?"* - Add an hour? *"I tried a lot of different things, but nothing really worked."* - Edit your question to show what you tried then. TBH, I'm on the fence here how to vote to close this; unclear/too broad. Edit: "unclear". – Funk Forty Niner Jun 10 '17 at 12:57
  • @Fred-ii- The question is, as far as I can tell, how you can apply a maximum onto a DateTimeInterval. That is, given an interval `$interval`, OP wants `max($interval, '1 hour')`, in pseudo code. That is not the same as adding 1 hour. I (heavily) edited the question to get it into shape a bit, and incorporated a comment of OP that said that they tried different things, but not what. See the [post history](https://stackoverflow.com/posts/44473175/revisions). I agree that the question was not formulated as well as it could have been, but I do think that it is a potentially useful question. – Just a student Jun 10 '17 at 13:02
  • @Justastudent I voted to close as unclear because of the same code used from Jared's find, what they tried but didn't include in the question. They can accept one of the answers below or comment under it as to why it didn't work for them, should that be the case. – Funk Forty Niner Jun 10 '17 at 13:05

3 Answers3

1

days : If the DateInterval object was created by DateTime::diff(), then this is the total number of days between the start and end dates. Otherwise, days will be FALSE. http://php.net/manual/en/class.dateinterval.php

Since we know it won't be false, it will evaluate to false only when 0.

if($diff->days) {
    $diff->w = floor($diff->d / 7);
    $diff->d -= $diff->w * 7;
} else /* less than a day's difference */ if(!$diff->h && ($diff->i || $diff->s)) /* less than an hour's difference */ {
    $diff->h = 1;
    $diff->i = 0;
    $diff->s = 0;
}
inarilo
  • 804
  • 1
  • 9
  • 14
  • What about a difference of only 10 seconds? Or a difference of 1 day and 2 minutes? I don't think OP wants to change that to 1 day and 1 hour. – Just a student Jun 10 '17 at 12:59
  • guess i misunderstood the question – inarilo Jun 10 '17 at 13:03
  • 1
    Makes sense, the first version of the question was not so clear. Good update, but this still won't work for a difference of 1 year, 1 second or 1 month, 1 second. Your answer would also benefit from a little bit of explanation :-) – Just a student Jun 10 '17 at 13:11
  • thanks for pointing out the errors :) i was thinking about days but used d... i hope this time i've got it covered :) – inarilo Jun 10 '17 at 13:23
1

After you have figured out the difference, simply check if the largest difference is only minutes or seconds. If so, return the minimum of 1 hour that you require.

// here is your foreach loop

// check if less than 1 hour, if so, return that
$keys = array_keys($string);
if ($keys[0] === 'i' || $keys[0] === 's') {
  return 'less than 1 hour ago';
}

Try it online!
I put the time of posting the question in this example. When you look at it more than 1 hour after that, you'll have to modify the test case to a DateTime that is less than 1 hour ago in UTC time.

Test cases. It is now 2017-06-10 12:45:00 in UTC.

time_elapsed_string("2017-06-10 12:11:19"); // less than 1 hour ago
time_elapsed_string("2017-06-10 11:44:00"); // 1 hour ago
time_elapsed_string("2017-06-10 11:44:00", true); // 1 hour, 1 minute ago
time_elapsed_string("2017-06-09 11:45:00", true); // 1 day, 1 hour ago
time_elapsed_string("2017-06-09 11:45:00"); // 1 day ago
Just a student
  • 10,560
  • 2
  • 41
  • 69
0

I've added some refinements to your original code. I've also removed the full option from your function as I don't think you intend to use it for your case. If my inline comments fail to explain my process, just ask.

Code: (Demo)

function time_elapsed_string($datetime){
    $now=new DateTime;  // current datetime
    $ago=new DateTime($datetime);  // user datetime
    if($now<$ago){return "Unexpected future datetime value";}
    $diff=$now->diff($ago);  // datetime difference
    $diff->w=intval(floor($diff->d/7));  // add weeks to diff output
    $diff->d-=$diff->w*7;  // reduce days based on weeks calculation
    $units=[
        'y'=>'year',
        'm'=>'month',
        'w'=>'week',
        'd'=>'day',
        'h'=>'hour'
    ];
    $kept_diff=array_intersect_key((array)$diff,$units);  // omit unwanted elements of diff()'s output
    if(!max($kept_diff)){  // if y, m, w, d, & h all = 0, show default
        return 'default: 1 hour ago';  // inserted "default: " for demo.  Remove in production.
    }else{
        $diffs=array_filter(array_merge($units,$kept_diff)); // sort by $units order, then omit elements with 0 value
        return current($diffs).' '.$units[key($diffs)].(current($diffs)>1?'s':'').' ago'; // return highest unit data in plain English
    }
}
echo time_elapsed_string('2017-06-30 02:22:35');  // output: 3 weeks ago
mickmackusa
  • 43,625
  • 12
  • 83
  • 136