I'm making an automatic countdown to an event that starts 2-3 times a day on diffrent times.
The event starts every 7 hours, so one day there are 4 events and the other 3.
Example:
http://i.stack.imgur.com/IvYbh.png
$monday = array( '02:00', '09:00', '16:00', '23:00' );
$tuesday = array( '06:00', '13:00', '20:00' );
$wednesday = array( '03:00', '10:00', '17:00' );
$thursday = array( '00:00', '07:00', '14:00', '21:00' );
$friday = array( '04:00', '11:00', '18:00' );
$saturday = array( '01:00', '08:00', '15:00', '22:00' );
$sunday = array( '05:00', '12:00', '19:00' );
How to make the countdown run to the next event?
Example: if it is Monday, 01:30, it should say 30min left
I already made the countdown part:
$hours = floor($this->sec / 3600);
$minutes = floor(($this->sec / 60) % 60);
$seconds = $this->sec % 60;
return "$hours" . ' hours ' . "$minutes" . ' minutes ' . "$seconds" . ' seconds';
Update: I know that the PHP won´t update just on itself. I will refresh the page manually.
function days_hours_minutes_from_now($date)
{
$now = time();
$your_date = strtotime($date);
$datediff = $your_date - $now;
echo "days: " .floor($datediff/(60*60*24));
echo " hours: " .floor($datediff/(60*60)) % 24; //hours
echo " minutes: " .floor($datediff/(60)) % 60; //minutes
echo " seconds: " .$datediff % 60; //seconds
}
days_hours_minutes_from_now("2015-08-19 13:52:28");
days: 1 hours: 0 minutes: 14 seconds: 56
Hasn't been tested thoroughly, but should be close to what you're looking for.
You need to compare the time against when the event starts to get a countdown.
However, as RiggsFolly rightly pointed out, unless you do this in javascript, your countdown isn't going to update in the user's browser unless they refresh the page.
Edit:
Now, if you wanted to ensure you always returned the time remaining until the next event, then you can loop through a series of dates, sorted in ascending order, and return the first result where the day is a non-negative value. If any date exceeds the current date/time, day will equal -1
Related
I am making a ticketing system for my company. In my database I record the timestamp of when a ticket is first raised and a timestamp of when the ticket is marked as completed.
I have written a function which returns the average time (hrs) a ticket takes to complete:
public function calculateAvgResolveTime()
{
$timeQuery = $this->database->query('SELECT ticketCreated, ticketCompletedOn FROM employeeTickets');
$cumulativeTicketTime = $cumulativeTimes = 0;
while($time = $timeQuery->fetch_assoc()) {
$timeCreated = strtotime($time['ticketCreated']);
$timeCompleted = strtotime($time['ticketCompletedOn']);
if($timeCompleted > $timeCreated) {
$cumulativeTimes++;
$cumulativeTicketTime = $cumulativeTicketTime + ($timeCompleted - $timeCreated);
}
}
$time = ($cumulativeTicketTime / 60 / 60);
$time = sprintf('%02d:%02d', (int) $time, round(fmod($time, 1) * 60));
return $time;
}
Is there a way I could exclude certain hours? For example our office is open from 09:00-17:00 Monday to Friday.
At the moment if a ticket is raised at 16:30 on a Friday and is completed 09:15 on Monday the average time would be quite high when actually the ticket only took 45 minutes of working time.
Result of var_export():
array(
array ( 'ticketCreated' => '2020-02-03 15:59:30','ticketCompletedOn' => '2020-02-04 09:53:35'),
array ( 'ticketCreated' => '2020-02-04 14:00:00', 'ticketCompletedOn' => '2020-02-04 14:36:00')
)
You will have to loop over the dates between ticketCreated and ticketCompletedOn day by day.
There seems to be no mathy way(or at least not in readable format) to solve this as you have time constraints of excluding Saturdays and Sundays as well as the working period being from 09:00:00 to 17:00:00.
Snippet:
<?php
$data =array(
array ( 'ticketCreated' => '2020-02-03 15:59:30','ticketCompletedOn' => '2020-02-04 09:53:35'),
array ( 'ticketCreated' => '2020-02-04 14:00:00', 'ticketCompletedOn' => '2020-02-04 14:36:00')
);
$sum_time = 0;
foreach($data as $details){
$start_time = new DateTime($details['ticketCreated']);
$end_time = new DateTime($details['ticketCompletedOn']);
$end_of_day = new DateTime($start_time->format('Y-m-d') . ' 17:00:00'); // since a day ends at 17:00
do{
$diff = $end_time->diff($start_time);
$diff2 = $end_of_day->diff($start_time);
if($end_time->format('Y-m-d') === $start_time->format('Y-m-d')){ // meaning finished on the same day
$sum_time += ($diff->h * 60) + ($diff->i) + ($diff->s / 60);
}else if(!in_array($end_of_day->format('N'),[6,7])){ // skipping Saturdays and Sundays
$sum_time += ($diff2->h * 60) + ($diff2->i) + ($diff2->s / 60); // add the day's offset(480 minutes)
}
$end_of_day->add(new DateInterval('P1D'));
$start_time = new DateTime($end_of_day->format('Y-m-d') . ' 09:00:00'); // start time for next day which is 09:00
}while($start_time <= $end_time);
}
$avg = $sum_time / count($data);
echo "$avg minutes",PHP_EOL;
Demo: https://3v4l.org/gpFt4
Explanation:
We first create DateTime instances of the dates.
We now have a do while loop inside.
If the end time and start time fall on the same day, we just take differences in terms of hours, minutes and seconds.
If the end time and start time doesn't fall on the same day, then we subtract the times from start_time from end_of_day which will be 480 minutes for a proper start or remaining offset of that day till 17:00:00.
If we come across a day which is Saturday or Sunday, we just skip it.
In the end, we just print the average by dividing sum by total number of tickets.
I have a function like this:
function time_elapsed_string($ptime)
{
$date_time = strtotime("1348-10-10 04:30:01") + $ptime;
$year = date("Y",$date_time);
$month = date("m",$date_time);
$day = date("d",$date_time);
$time = date("H:i:s",$date_time);
$etime = time() - $ptime + 1;
$a = array( 31536000 => 'year',
2592000 => 'month',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
foreach ($a as $secs => $str)
{
$d = $etime / $secs;
if ($d >= 1)
{
$r = round($d);
// EX:
return array('date' => $day.'-'.$month.'-'.$year, // 2016-02-20
'time' => $time, // 03:30:04
'difference' => $r . ' ' . $str . ' ago' // 2 month ago
);
}
}
}
And I use it like this:
$ptime = 1470692661;
$html = '<span title="date: '.time_elapsed_string($ptime)['date'].' time: '.time_elapsed_string($ptime)['time'].'">in '.time_elapsed_string($ptime)['difference'].'<span>';
As you see, I'm using of that function's result like this:
time_elapsed_string($ptime)['date']
ime_elapsed_string($ptime)['time']
time_elapsed_string($ptime)['difference']
In fact I'm calling that function every time I need one of its results. Is that right? Or should I call it once and store it into an array?
Note: My code works as well.
Counting time elapsed since some date/time like this is mauvais ton.
DateTime has been available since PHP 5.2.0 and tonns of people underestimate it. Why don't you use this instead of loops and ifs?
$create_time = "2016-08-02 12:35:04";
$current_time="2016-08-02 16:16:02";
$dtCurrent = DateTime::createFromFormat('Y-m-d H:i:s', $current_time);
// to use current timestamp, use the following:
//$dtCurrent = new DateTime();
$dtCreate = DateTime::createFromFormat('Y-m-d H:i:s', $create_time);
$diff = $dtCurrent->diff($dtCreate);
Now, you can format the result however you want:
$interval = $diff->format("%h hours %i minutes %s seconds");
This will give a clean 3 hours 40 minutes 58 seconds without any arrays, which is better.
UPDATE
There is a general solution to get hours / minutes / seconds via regex:
$interval = $diff->format("%y years %m months %d days %h hours %i minutes %s seconds");
// now remove zero values
$interval = preg_replace('/(^0| 0) (years|months|days|hours|minutes|seconds)/', '', $interval);
UPDATE 2
As of your comment:
Look, I want to use your approach .. but I really cannot implement it .. Actually I need three things: time, date, difference ..! But your approach doesn't give me them..
Well, we already know how to get the difference, it's the $interval variable described above.
To get time and date, you can get it from the $dtCreate variable by, again, using format:
$time = $dtCreate->format('H:i:s');
$date = $dtCreate->format('d-m-Y');
This is a no brainer.
Yes - store the function call result of time_elapsed_string($ptime) in an array, then use that to access your results. You're wasting CPU cycles otherwise!
// call it once
$result = time_elapsed_string($ptime);
// then use:
$result['date'];
$result['time'];
$result['difference'];
I am trying to transform a unix timestamp into a human readable string so i can show how long ago a user signed up.
Here is my data:
mysql> select createdate as unixtimestamp,date_format(from_unixtime(createdate),'%e %b %Y') as dateformatted from users where userid=40645;
+---------------+---------------+
| unixtimestamp | dateformatted |
+---------------+---------------+
| 1162642968 | 4 Nov 2006 |
+---------------+---------------+
1 row in set (0.00 sec)
mysql>
Ok so here is where the problem resides. I found 3 different functions on the internet that return a human readable string from a unix timestamp. All 3 failed to work.
I'd like someone to look at these functions and help me figure out how to fix one of them to return the correct human readable string.
On with the show!
Here is function #1:
function getElapstedTimeHumanReadable($time)
{
$names = array("seconds", "minutes", "hours", "days", "months", "years");
$values = array(1, 60, 3600, 24 * 3600, 30 * 24 * 3600, 365 * 24 * 3600);
$time = time()-$time;
for($i = count($values) - 1; $i > 0 && $time < $values[$i]; $i--);
if($i == 0) {
$timestamp = intval($time / $values[$i]) . " " . $names[$i];
} else {
$t1 = intval($time / $values[$i]);
$t2 = intval(($time - $t1 * $values[$i]) / $values[$i - 1]);
$timestamp= "$t1 " . $names[$i] . ", $t2 " . $names[$i - 1];
}
return $timestamp;
}
My return value for this function is "Joined 1 days, 17 hours ago"
Clearly this isn't correct.
Here is function #2:
function getElapsedTimeHumanReadable($time)
{
$time = time() - $time;
$points = array(
'year' => 31556926,
'month' => 2629743,
'week' => 604800,
'day' => 86400,
'hour' => 3600,
'minute' => 60,
'second' => 1
);
foreach($points as $point => $value)
{
if($elapsed = floor($time/$value) > 0)
{
$s = $elapsed>1?'s':'';
$timestamp = "$elapsed $point$s";
break;
}
}
return $timestamp;
}
My return value for this function is "Joined 1 day ago
And finally, here is function #3:
function getElapsedTimeHumanReadable($time)
{
$etime=time()-$time;
if ($etime < 1)
{
return '0 seconds';
}
$a = array( 365 * 24 * 60 * 60 => 'year',
30 * 24 * 60 * 60 => 'month',
24 * 60 * 60 => 'day',
60 * 60 => 'hour',
60 => 'minute',
1 => 'second'
);
$a_plural = array( 'year' => 'years',
'month' => 'months',
'day' => 'days',
'hour' => 'hours',
'minute' => 'minutes',
'second' => 'seconds'
);
foreach ($a as $secs => $str)
{
$d = $etime / $secs;
if ($d >= 1)
{
$r = round($d);
return $r . ' ' . ($r > 1 ? $a_plural[$str] : $str) . ' ago';
}
}
}
So theres my code and my data. Not quite sure why none seem to work. I tried looking at the code but I cannot figure out how to solve it.
Whats interesting is they all say 2 days, but my timestamp appears to show 2006.
Thanks for the help.
$time = 1162642968 ;
$date = new DateTime( );
$date->setTimestamp( $time );
$today = new DateTime( 'now', new DateTimeZone( "Europe/Rome" ) );
$diff = $today->diff( $date);
echo "Year: " . $diff->y . " - Month: " . $diff->m . " - Days: " . $diff->d . " - Hours: " . $diff->h;
EXAMPLE
As suggested I'll add explanation, even if I think it is really self explain.
$date = new DateTime() create the object and $date->setTimestamp( $time ) is used to put that date at a value from the mysql timestamp.
$today is created pointing at the actual date.
$date->diff() create a DateInterval Object ( http://php.net/manual/en/class.dateinterval.php ) that contains all the necessary datas.
If you want to solve this yourself, you should calculate the difference and base it on those values. I haven't tested RiccardoC's Answer, but this seems as a nice way to go.
As I see in your posting, you calculate a year always as 365 days, so if you don't want to go in a deep detail with time zones, extra hours, extra days, different month lengths etc, you could use something simple as that:
function getElapsedTimeHumanReadable($timestamp) {
$diff = time() - $timestamp;
$years = intval($diff/31536000); //seconds in a year 60*60*24*365
$diff -= ($years*31536000);
$months = intval($diff/2592000); //seconds in a month 60*60*24*30
$diff -= ($months*2592000);
$days = intval($diff/86400); //seconds in a day 60*60*24
return $years." years, ".$months." months, ".$days." days ago";
}
echo getElapsedTimeHumanReadable(1162642968); // November 4th, 2006
Echos 9 years, 0 months, 17 days ago
Say I have a date function that produces
the output: 2011-10-03
PHP:
$todayDt = date('Y-m-d');
Anyway to get this date to instead show 2 days 1 hour ago
This function might be of some use. You might want to refine the check for months a bit, but this is just a quick example:
function RelativeTime($iTimestamp, $iLevel = 2)
{
!ctype_digit($iTimestamp)
&& $iTimestamp = strtotime($iTimestamp);
$iSecondsInADay = 86400;
$aDisplay = array();
// Start at the largest denominator
$iDiff = time() - $iTimestamp;
$aPeriods = array(
array('Period' => $iSecondsInADay * 356, 'Label' => 'year'),
array('Period' => $iSecondsInADay * 31, 'Label' => 'month'),
array('Period' => $iSecondsInADay, 'Label' => 'day'),
array('Period' => 3600, 'Label' => 'hour'),
array('Period' => 60, 'Label' => 'minute'),
array('Period' => 1, 'Label' => 'second'),
);
foreach ($aPeriods as $aPeriod)
{
$iCount = floor($iDiff / $aPeriod['Period']);
if ($iCount > 0)
{
$aDisplay[] = $iCount . ' ' . $aPeriod['Label'] . ($iCount > 1 ? 's' : '');
$iDiff -= $iCount * $aPeriod['Period'];
}
}
$iRange = count($aDisplay) > $iLevel
? $iLevel
: count($aDisplay);
return implode(' ', array_slice($aDisplay, 0, $iRange)) . ' ago';
}
And some examples of usage:
echo RelativeTime(time() - 102, 1);
// Will output: 1 minute ago
echo RelativeTime(time() - 2002);
// Will output: 33 minutes 22 seconds ago
echo RelativeTime(time() - 100002002, 6);
// Will output: 3 years 2 months 27 days 10 hours 20 minutes 2 seconds ago
echo RelativeTime('2011-09-05');
// Will output: 30 days 22 hours ago
This is post is just for a solution that does not use the DateTime::diff method. It also uses inputs with greater precision, so be aware of that.
$now = date('Y-m-d H:i:s');
$then = '2011-10-03 00:00:00'; // This will calculate the difference
// between now and midnight October 3rd
$nowTime = strtotime($now);
$thenTime = strtotime($then);
$diff = $nowTime - $thenTime;
$secs = $diff % 60;
$diff = intval($diff / 60);
$minutes = $diff % 60;
$diff = intval($diff / 60);
$hours = $diff % 24;
$diff = intval($diff / 24);
$days = $diff;
echo($days . ' days ' . $hours . ' hours ' . $minutes . ' minutes ' . $secs . ' seconds ago');
At the moment I tested it, the output was:
2 days 16 hours 6 minutes 2 seconds ago
If all you want are the days and hours, then you can just choose to echo those two out:
echo($days . ' days ' . $hours . ' hours ago');
2 days 16 hours ago
I'm am trying to calculate the hours for someone based on the number of hours worked and the time period in which they worked.
For example:
The shift patterns are 07:00 to 15:00 is 'morning', 15:00 to 23:00 is 'afternoons', and 23:00 to 07:00 is 'nights'.
Therefore if I started at 08:00 and finished at 17:30, this would mean that I get paid 7 'morning' hours and 2.5 'afternoon' hours.
I adapted a piece of code from a project I developed some time ago.
The function 'intersection' calculates the amount of time that overlaps between the two ranges s1-e1 and s2-e2.
Note that all the times are in seconds, and we add 3600*24 seconds when the time is in the next day.
<?php
function intersection($s1, $e1, $s2, $e2)
{
if ($e1 < $s2)
return 0;
if ($s1 > $e2)
return 0;
if ($s1 < $s2)
$s1 = $s2;
if ($e1 > $e2)
$e1 = $e2;
return $e1 - $s1;
}
$start = strtotime("07:00");
$end = strtotime("17:30");
// $end = strtotime("05:30") + 3600*24; // the work ended at 05:30 morning of the next day
$morning_start = strtotime("07:00");
$morning_end = strtotime("15:00");
$afternoon_start = strtotime("15:00");
$afternoon_end = strtotime("23:00");
$night_start = strtotime("23:00");
$night_end = strtotime("07:00") + 3600*24; // 07:00 of next day, add 3600*24 seconds
echo "morning: " . intersection( $start, $end, $morning_start, $morning_end ) / 3600 . " hours\n";
echo "afternoon: " . intersection( $start, $end, $afternoon_start, $afternoon_end ) / 3600 . " hours\n";
echo "night: " . intersection( $start, $end, $night_start, $night_end ) / 3600 . " hours\n";
You could have 3 counters, one for each shift. Then you'd need a way to increment by hour and for each hour you increment. You check if it is within each shift, and if it is within a certain one, then you increment that counter.
In the end, the values of each counter should be the amount you worked in each shift.
This is what I just threw together for fun.
There is much room for improvement and it can be easily extended to provide even more calculations, your imagination is the limit.
The array of shifts can be given named keys for readability, but I chose to remove them since 'night1' and 'night2' make no sense to me :-)
<?php
$shift_data = array(
array(
'rate' => 12.5,
'start' => strtotime('00:00:00'),
'end' => strtotime('07:00:00'),
),
array(
'rate' => 7.55,
'start' => strtotime('07:00:00'),
'end' => strtotime('15:00:00'),
),
array(
'rate' => 10,
'start' => strtotime('15:00:00'),
'end' => strtotime('23:00:00'),
),
array(
'rate' => 12.5,
'start' => strtotime('23:00:00'),
'end' => strtotime('07:00:00') + 86400, // next morning
),
);
function calculateWage($start, $end, $rate) {
$result = array();
$result['time']['seconds'] = $end - $start;
$result['time']['minutes'] = $result['time']['seconds'] / 60;
$result['time']['hours'] = $result['time']['minutes'] / 60;
$result['wages'] = $result['time']['hours'] * $rate;
//print_r($result);
return $result;
}
$shift_start = strtotime('08:00');
$shift_end = strtotime('17:30');
$shift_wages = 0;
foreach ($shift_data as $shift) {
if ($shift['start'] <= $shift_end) {
$start = ($shift_start <= $shift['start']) ? $shift['start'] : $shift_start;
$end = ($shift_end <= $shift['end']) ? $shift_end : $shift['end'];
$shift_wage = calculateWage($start, $end, $shift['rate']);
$shift_wages = $shift_wages + $shift_wage['wages'];
}
}
echo "\nTotal wages for today: $" . number_format($shift_wages, 2, '.', ',') . "\n\n";
?>
Here is another way: (I am assuming the start and end hours in unix timestamp)
Get the starting and ending day of employee
Get the work hours by a simple formula: $ending_hour - $starting_hour
If work hours is more than 8 hours, the employee should get paid for extra.
To calculate extra hours of next shift: $ending_hour - $next_shifts_starting_hour
My shift times are simplier: 22:00 -> 07:00 = nightwork, 07:00 -> 22:00 = daywork
But I got wrong answer with Javi R version if shifts are like 22:30 -> 08:00 (Result: day = 0 hours, night = 8:30 hours) and 20:00 -> 10:00 (Result: day = 2 hours, night = 9 hours) but right answer with shift time like 23:00 -> 07:00 (Result: day = 0 hours, night = 7 hours).
Sorry about that answer. I don't have enough rep but I need to get it work.