I have a timestamp which I need to return as:
When it's under 1 hour I need it to say "30 minutes ago".
When it's under 24 hours I need it to say "6 hours ago".
When it gets to 24 hours I need it to say "1 day ago".
When it gets to 48 hours I need it to say "2 days ago".
Would this be done with a conditional statement?
So far I can return the number of days:
$post_timestamp = strtotime($post_timestamp);
$current_date = strtotime(time());
$datediff = $current_date - $post_timestamp;
$days = floor($datediff/(60*60*24));
I guess you can use DateTime, DateInterval and DatePeriod:
$date1 = new DateTime();
$date2 = DateTime::createFromFormat('U', $post_timestamp); # I assume a unix timestamp here
//determine what interval should be used - 1 minute
$interval = new \DateInterval('PT1M');
//create periods every minute between the two dates
$periods = new \DatePeriod($date2, $interval, $date1);
//count the number of objects within the periods
$mins = iterator_count($periods);
if ($mins < 60)
{
$say = "30 minutes ago";
} elseif ( $mins >= 60 and $mins < 60 * 24)
{
$say = "6 hours ago";
} elseif ( $mins >= 60 * 24 and $mins < 60 * 48)
{
$say = "1 day ago";
} elseif ( $mins >= 60 * 48)
{
$say = "2 days ago";
}
print $say;
You can use code like following :
date_default_timezone_set('Asia/Calcutta');
$post_timestamp="2017-05-21 5:00 pm";
$post_timestamp = strtotime($post_timestamp);
$current_date = strtotime(date('Y-m-d h:i a'));
$datediff = $current_date - $post_timestamp;
$mins = ($datediff) / 60;
$hours = $datediff / ( 60 * 60 );
You will get minutes and hours using this, put your condition accordingly
So, I've got this working code which converts a MySQL date to UNIX Timestamp and subtracts it from the current date() to show a "time elapsed since X"-like timer. (the part which takes the date from database is missing since it's in another script)
<?php
function time_elapsed($secs){
$bit = array(
' year' => $secs / 31556926 % 12,
' week' => $secs / 604800 % 52,
' day' => $secs / 86400 % 7,
' hour' => $secs / 3600 % 24,
' minute' => $secs / 60 % 60,
' second' => $secs % 60
);
foreach($bit as $k => $v){
if($v > 1)$ret[] = $v . $k . 's';
if($v == 1)$ret[] = $v . $k;
}
array_splice($ret, count($ret)-1, 0, 'and');
$ret[] = 'ago.';
return join(' ', $ret);
}
$nowtime = time() + 10; //add 10s to avoid error
$oldtime = strtotime($mysqltime2);
$time_elapsed = time_elapsed($nowtime-$oldtime)."\n";
echo wordwrap($time_elapsed,35,"<br />\n"); //split long line
?>
I managed to fix an error reporting missing array or something if the script was executed at the same time of the MySQL date by adding 10 seconds to the current timestamp.
Another issue I have is that the script shows "and X seconds" even if there aren't minutes/hours/days/etc before it.
E.g.
"and X seconds"
"Y minutes and X seconds"
"Z hours Y minutes and X seconds"
"N days Z hours Y minutes and X seconds"
I want to remove the "and" before seconds ONLY if there's no minutes/hours/etc before it.
Any tip on how to fix this?
You could only splice when needed:
if( count($ret) > 1 ) {
array_splice($ret, count($ret)-1, 0, 'and');
}
Not sure if this will help, but why not do most of the date maths in mysql?
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html
Here is something I did before to list posts in a comments section of a site, it outputs "post made 2 hours ago" etc
SQL:
UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(made_on) as madeon
PHP (pass madeon from query to here):
function seconds_to_textual_time_ago($seconds)
{
$hour = 60* 60;
$day = 24 * $hour;
$month = 30 * $day;
$year = 365 * $day;
switch(true)
{
case ($seconds < 60) :
$time_ago = "Less than 1 minute ago";
break;
case ($seconds >= 60 && $seconds < $hour):
$minutes = floor($seconds /60);
if ($minutes > 1)
$time_ago = "$minutes minutes ago";
else
$time_ago = "1 minute ago";
break;
case ($seconds >= $hour && $seconds < $day):
$hours = floor($seconds /$hour);
if ($hours > 1)
$time_ago = "$hours hours ago";
else
$time_ago = "1 hour ago";
break;
case ($seconds >= $day && $seconds < $month):
$days = floor($seconds /$day);
if ($days > 1)
$time_ago = "$days days ago";
else
$time_ago = "1 day ago";
break;
case ($seconds >= $month && $seconds < $year):
$months = floor($seconds /$month);
if ($months > 1)
$time_ago = "$months months ago";
else
$time_ago = "1 month ago";
break;
case ($seconds >= $year ):
$years = floor($seconds /$year);
if ($years > 1)
$time_ago = "$years years ago";
else
$time_ago = "1 year ago";
break;
default:
break;
}
return $time_ago;
}
The simplest I can think of is
$time_elapsed = time_elapsed($nowtime-$oldtime))."\n";
if($nowtime-$oldtime < 61){
$time_elapsed=str_replace('and ',' ',$time_elapsed);
}
For example if I have:
$seconds = 3744000; // i want to output: 43 days, 8 hours, 0 minutes
Do I have to create a function to convert this? Or does PHP already have something built in to do this like date()?
function secondsToWords($seconds)
{
$ret = "";
/*** get the days ***/
$days = intval(intval($seconds) / (3600*24));
if($days> 0)
{
$ret .= "$days days ";
}
/*** get the hours ***/
$hours = (intval($seconds) / 3600) % 24;
if($hours > 0)
{
$ret .= "$hours hours ";
}
/*** get the minutes ***/
$minutes = (intval($seconds) / 60) % 60;
if($minutes > 0)
{
$ret .= "$minutes minutes ";
}
/*** get the seconds ***/
$seconds = intval($seconds) % 60;
if ($seconds > 0) {
$ret .= "$seconds seconds";
}
return $ret;
}
print secondsToWords(3744000);
This is very simple and easy to find days , hours, minute and second in core php :
$dbDate = strtotime("".$yourdbtime."");
$endDate = time();
$diff = $endDate - $dbDate;
$days = floor($diff/86400);
$hours = floor(($diff-$days*86400)/(60 * 60));
$min = floor(($diff-($days*86400+$hours*3600))/60);
$second = $diff - ($days*86400+$hours*3600+$min*60);
if($days > 0) echo $days." Days ago";
elseif($hours > 0) echo $hours." Hours ago";
elseif($min > 0) echo $min." Minutes ago";
else echo "Just now";
An easy way to accomplish this nowadays is using DateTimeImmutable, DateInterval and PHP 5.5.0 or higher:
$seconds = 3744000;
$interval = new DateInterval("PT{$seconds}S");
$now = new DateTimeImmutable('now', new DateTimeZone('utc'));
$difference = $now->diff($now->add($interval))->format('%a days, %h hours, %i minutes');
The result will be:
43 days, 8 hours, 0 minutes
The code adds the seconds to a date and calculates the difference to it. Like this, the seconds are transformed into the specified days, hours and minutes.
Warning 1: Working without UTC - Clock changes
You may not specify the DateTimeZone in the constructor of the DateTimeImmutable object to UTC.
$now = new DateTimeImmutable();
There are regions in this world, where the clock changes on specific days of the year. Most countries in the EU change between a summer- and winter-time for example.
If your date interval overlaps the day on that a clock change occurs and your server is set to the related region for that clock change, the result might change as well. This is best shown with the following example:
$twentyFourHours = new DateInterval('PT24H');
$twentyFiveHours = new DateInterval('PT25H');
//Pacific time changed from summer- to winter-time on that day
$summerToWinter = new DateTimeImmutable('2018-11-04');
If you add 24 hours to the $summerToWinter date, you will get the following result:
$extra24Hours = $summerToWinter->add($twentyFourHours);
echo $summerToWinter->format('y-m-d H:i');
echo $extra24Hours->format('y-m-d H:i');
echo $summerToWinter->diff($extra24Hours)->format('%a days, %h hours, %i minutes');
18-11-04 00:00
18-11-04 23:00
0 days, 24 hours, 0 minutes
As you can see, between 00:00 and 23:00 on that day lay 24 hours, which is technically correct. Because of the clock change the timelap between 02:00 and 03:00 occured twice on that day.
Adding 25 hours will result in this:
$extra25Hours = $summerToWinter->add($twentyFiveHours);
echo $summerToWinter->format('y-m-d H:i');
echo $extra25Hours->format('y-m-d H:i');
echo $summerToWinter->diff($extra25Hours)->format('%a days, %h hours, %i minutes');
18-11-04 00:00
18-11-05 00:00
1 days, 0 hours, 0 minutes
As we can see, 1 day elapsed, that has had 25 hours. If this is applied for the 3744000 seconds from the original question, the result would show:
43 days, 7 hours, 0 minutes
The information, that an elapsed day has had 25 hours, is not shown though.
Also, I was not able to recreate the same effect for a day that changes the clock from winter to summer time, that should only elapse 23 hours.
Warning 2: Working with the raw DateInterval object
Using this code without DateTimeImmutable will cause the wrong output:
$seconds = 3744000;
$interval = new DateInterval("PT{$seconds}S");
$difference = $interval->format('%a days, %h hours, %i minutes, %s seconds');
Now, only the seconds are set in the DateInterval object. $difference would be:
(unknown) days, 0 hours, 0 minutes, 3744000 seconds
I like Ian Gregory's answer the most and upvoted it but thought i'd just simplify it a little bit :
function secondsToWords($seconds)
{
$days = intval(intval($seconds) / (3600*24));
$hours = (intval($seconds) / 3600) % 24;
$minutes = (intval($seconds) / 60) % 60;
$seconds = intval($seconds) % 60;
$days = $days ? $days . ' days' : '';
$hours = $hours ? $hours . ' hours' : '';
$minutes = $minutes ? $minutes . ' minutes' : '';
$seconds = $seconds ? $seconds . ' seconds' : '';
return $days . $hours . $minutes . $seconds;
}
Here's what I've got so far:
/**
* Parse a duration between 2 date/times in seconds
* and to convert that duration into a formatted string
*
* #param integer $time_start start time in seconds
* #param integer $time_end end time in seconds
* #param string $format like the php strftime formatting uses %y %m %w %d %h or %i.
* #param boolean $chop chop off sections that have 0 values
*/
public static function FormatDateDiff($time_start = 0, $time_end = 0, $format = "%s", $chop = false) {
if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
list($year_start,$month_start,$day_start) = explode('-',date('Y-m-d',$time_start));
list($year_end,$month_end,$day_end) = explode('-',date('Y-m-d',$time_end));
$years = $year_end - $year_start;
$months = $month_end - $month_start;
$days = $day_start - $day_end;
$weeks = 0;
$hours = 0;
$mins = 0;
$secs = 0;
if(mktime(0,0,0,$month_end,$day_end) < mktime(0,0,0,$month_start,$day_start)) {
$years -= 1;
}
if($days < 0) {
$months -= 1;
$days += 30; // this is an approximation...not sure how to figure this out
}
if($months < 0) $months += 12;
if(strpos($format, '%y')===false) {
$months += $years * 12;
}
if(strpos($format, '%w')!==false) {
$weeks = floor($days/7);
$days %= 7;
}
echo date('Y-m-d',$time_start).' to '.date('Y-m-d',$time_end).": {$years}y {$months}m {$weeks}w {$days}d<br/>";
}
(It's incomplete and inaccurate)
I can't seem to get the math right. Naively dividing it out won't work because of leap years and differing lengths of months.
The logic also needs to change depending on the format string. For example, passing 04-Feb-2010 to 28-Jun-2011 (as unix timestamps) with format string %y year %m month %d day should output 1 year 4 month 24 day but if %y year is omitted then it needs to add 12 months to the month, i.e., output should be 16 month 24 day.
Should handle times too...but I haven't got to that yet.
None of these date_diff solutions handle weeks. And I don't know how I could hack it into date_diff, so that's not really a solution for me.
Furthermore, $diff->format doesn't do what I asked...to give the total months and days if "bigger units" are omitted. Example:
>>> $start = new DateTime('04-Feb-2010')
>>> $end = new DateTime('28-Jun-2011')
>>> $diff = $start->diff($end)
>>> $diff->format('%m months, %d days')
'4 months, 24 days'
Should be 16 months, 24 days, as I stated earlier. Please stop being so quick to close my question as a dupe before you understand it fully. If the solutions to other questions can be tweaked to solve this, fine, but please explain how, because I don't get it.
To be clear,
if %y is omitted, years should be rolled in the months
if %m is omitted, months should be rolled into the days
if %w is omitted, weeks should be rolled into the days
if %h is omitted, hours should be rolled into minutes
if %m is omitted, minutes should be rolled into seconds
If "smaller units" are omitted, the next biggest unit can be rounded or floored where it makes sense.
I don't expect an accepted answer, but here is how to get date_diff to do weeks.
<?php
$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
$interval = $february->diff($january);
$parts = $interval->format('%y %m %d %h %i %s %a');
$weeks = 0;
list($years, $months, $days, $hours, $minutes, $seconds, $total_days) = explode(' ', $parts);
if ($days >= 7) {
$weeks = (int)($days / 7);
$days %= 7;
}
echo "$years years, $months months, $weeks weeks, $days days, $hours hours, $minutes minutes $seconds seconds";
// 1 years, 1 months, 2 weeks, 5 days, 3 hours, 35 minutes 28 seconds
Maybe with that you can integrate it into your function to do the rolling over and handling the user given format.
If the bigger units aren't given, you can start from the largest unit and apply them back to the next smaller unit. (i.e. 1 year 1 month with no years should add 12 back to months). If "month" isn't included in the format, then you can use the total days to handle the fact that months have different numbers of days.
I would use DateTime::diff() for this:
$start = new DateTime('2012-01-01 12:00:00');
$end = new DateTime('2012-01-20 06:59:59');
$diff = $start->diff($end);
echo $diff->format('%d days, %h hours, %m minutes, %s seconds');
See DateInterval::format() for more info about the formats.
$absolute = false; //default, returns years, months, days, etc. True would return seconds
$difference = date_diff($dateTimeStart, $dateTimeEnd, $absolute);
echo $difference->format("%y Year(s) %m Month(s) ...");
I would try this first, if it does not work for your purposes then you could try to use the Calendar functions to fetch the correct number of days in each month.
I think you would need to decide upon your own logic for calculating weeks. Personally, I would say $weeks = $difference->d / 7; but there is no strictly correct way to count elapsed weeks in a portion of a month. Think of a calendar, we often speak of first, second, third week, but unless the month started on a Sunday (or Monday, or Saturday, depending on company, religion, etc.) then really we are not being absolute in these descriptions. You can say absolutely that there have been 3 Sundays (for instance) since the beginning of the month, but not three weeks. On the 28th, have four weeks passed? What if the month started on Wed, then is it five?
Also, if you want a custom format (35 Months) you can always access the members directly and concatenate a format string.
Here's my attempt at an interesting problem. It's not quite working 100%, but it's very close.
The function begins by calculating the total number of seconds and months that the date difference requires. Then, using the $tokens array that is sorted in ascending order, it loops through those tokens and tries to match it with the input $format. If the $format is found, the appropriate value is subtracted from the total number of months or seconds.
However, there is a possibility of having a value greater than zero for months or years, but the $format string didn't specify either of those parameters. If so, the function rolls over the appropriate number of days in order to come up with the correct calculations.
I've briefly tested it, and it works for all of the examples in this thread. You can test it out on codepad to see if you can break it! :) I'd be interested in improvements upon this.
<?php
function calc( $start, $end, $format = '%s', $chop = false)
{
$tokens = array( '%y', '%m', '%w', '%d', '%h', '%i', '%s');
if( !is_a( $start, 'DateTime') || !is_a( $end, 'DateTime'))
{
return;
}
$diff = $start->diff( $end);
$months = ($diff->y * 12) + $diff->m;
$secs = ($diff->d * 24 * 3600) +
($diff->h * 3600) +
($diff->i * 60) +
($diff->s);
$output = array();
while( $token = array_shift( $tokens))
{
$token_present = !(strpos( $format, $token) === false);
switch( $token)
{
case '%y':
if( ($months / 12) > 0 && $token_present)
{
$output[$token] = floor( $months / 12);
$months -= $output[$token] * 12;
}
break;
case '%m':
if( $months > 0 && $token_present)
{
$output[$token] = $months;
$months = 0;
}
break;
case '%w':
// Rollover between (months or years) and seconds
if( (!isset( $output['%y']) || !isset( $output['%m'])) && $months > 0)
{
$days = $diff->format( '%a');
// Need a fix for leap year probably.
$days -= (isset( $output['%y'])) ? ($output['%y'] * 365) : 0;
$days -= $diff->d;
$secs += ($days * 24 * 60 * 60);
}
$val = (7 * 24 * 60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%d':
$val = (24 * 60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%h':
$val = (60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%i':
$val = (60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%s':
if( $secs > 0 && $token_present)
{
$output[$token] = $secs;
}
break;
}
}
// Filter out blank keys and replace their tokens in the $format string
$filtered = $chop ? array_filter( $output) : $output;
$format = str_replace( array_diff( array_keys($output), array_keys($filtered)), '', $format);
return str_replace( array_keys( $filtered), array_values( $filtered), $format);
}
$start = new DateTime('04-Feb-2010');
$end = new DateTime('28-Jun-2011');
echo calc( $start, $end, "%m months %d days\n"); // 16 months 24 days
$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
echo calc( $january, $february, '%y years %m months %w weeks %d days %h hours %i minutes %s seconds'); // 1 years 1 months 2 weeks 5 days 3 hours 35 minutes 28 seconds
I think I've got it:
if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
$start_dt = new DateTime();
$end_dt = new DateTime();
$start_dt->setTimestamp($time_start);
$end_dt->setTimestamp($time_end);
$has_time = preg_match('`%[his]`',$format) > 0;
if(!$has_time) {
$start_dt->setTime(0,0,0);
$end_dt->setTime(0,0,0);
}
$interval = $end_dt->diff($start_dt);
$parts = $interval->format('%y %m %d %h %i %s %a');
$weeks = 0;
list($years, $months, $days, $hours, $mins, $secs, $total_days) = explode(' ',$parts);
if(strpos($format,'%y')===false) {
$months += $years * 12;
}
if(strpos($format,'%m')===false) {
$days = $total_days;
if(strpos($format,'%y')!==false) {
$start_dt->add(new DateInterval('P'.$years.'Y'));
$interval = $end_dt->diff($start_dt);
$days = $interval->days;
}
}
if(strpos($format,'%w')!==false) {
$weeks = (int)($days/7);
$days %= 7;
}
if(strpos($format,'%d')===false) {
$hours += $days * 24;
}
if(strpos($format,'%h')===false) {
$mins += $hours * 60;
}
if(strpos($format,'%i')===false) {
$secs += $mins * 60;
}
(FYI, I just do some basic str_replacing at the end to throw it into my custom format string)
Edit: There's still 1 more scenario I don't know how to handle... when years and days are requested but not months, the output will be wrong. I'm wondering if I should just mod the total days with 365.... it's kind of a rare scenario.
Edit2: Solved it! We'll just add that many years to the start date and then recalculate the total days.
I have a table with a datetime field, and I want to pull that time stamp and use PHP to convert it to the nearest largest unit of time. For example, if the entry was made 2 minutes and 36 seconds ago, I want to echo 2 minutes in PHP. If it was 3 hours and 5 minutes ago, I want it to say 3 hours. If it was 6 days and 4 hours and 40 minutes ago, it should say 6 days. You get my drift. And if it's under a minute, just the number of seconds.
I am not familiar at all about any of PHP's date or time functions, so please don't assume I already know anything. Thanks.
Here's another function for you:
function relativeTime($timestamp, $format = "Y-m-d")
{
$difference = time() - $timestamp;
if ($difference >= 604800) { // 7 days
return date($format, $timestamp);
}
$periods = array("Second", "Minute", "Hour", "Day");
$lengths = array("60","60","24","7","4.35","12","10");
$ending = "Ago";
for($j = 0; $difference >= $lengths[$j]; $j++)
$difference /= $lengths[$j];
$difference = round($difference);
if($difference != 1) $periods[$j].= "s";
$text = "$difference $periods[$j] $ending";
return $text;
}
It returns values like "5 Minutes Ago", "3 Days Ago", and "34 Seconds Ago". However if the date is over 7 days old, it just returns the full date, i.e. "2010-04-19".
If your field is date or datetime use
SELECT UNIX_TIMESTAMP(field) FROM...
if your field is stored as an int (seconds since 1970) just use
SELECT field FROM...
Get that integer value from the database into a PHP variable $secs, this is the number of seconds since 1970. We assume the date is in the past, then
$diff = time() - $secs;
if ($diff < 60) echo $diff . ' seconds';
else {
$diff = intval($diff / 60);
if ($diff < 60) echo $diff . ' minutes';
else {
$diff = intval($diff / 60);
if ($diff < 24) echo $diff . ' hours';
else {
$diff = intval($diff / 24);
echo $diff . ' days';
}
}
}