Correct way to make a schedule - php

I've been debating whether to use Date functions or just whether there is a cleaner way to make an array of a given day's minutes at the moment my code reads like this;
function emptySchedule()
{
$emptySchedule = [];
for ($h = 0; $h < 24; ++$h) {
for ($m = 0; $m < 60; ++$m) {
$emptySchedule[str_pad($h, 2, '0', STR_PAD_LEFT).':'.str_pad($m, 2, '0', STR_PAD_LEFT)] = [];
}
}
return $emptySchedule;
}
But it just feels dirty, is there a better way to achieve the same result or maybe a better way to handle the minutes in the day with a date object or even better a globally accepted correct way? I'd prefer not to use a package, but if there is a good one I'm open to it.
EDIT; for some context, I will be putting arrays into each minute of the day.
EDIT2: for the purpose of what this will be used for there is no need for any input, my final function was;
public function emptySchedule()
{
$startDate = date_create("today"); //Any start date works
$endDate = date_create("tomorrow"); //Any end date works
$step = new \DateInterval("PT1M"); //1 minute, can change
$date = $startDate;
$times = [];
while ($date <= $endDate) {
$times[$date->format("H:i")] = [];
$date = $date->add($step);
}
return $times;
}

This is what I'd do:
$startDate = date_create("today"); //Any start date works
$endDate = date_create("tomorrow"); //Any end date works
$step = new DateInterval("PT1M"); //1 minute, can change
$date = $startDate;
$times = [];
while ($date <= $endDate) {
$times[$date->format("H:i")] = [];
$date = $date->add($step);
}
print_r($times);
http://sandbox.onlinephpfunctions.com/code/dd91a8f3f1707ca20aae93f70969275d5fb8dedd

Well, a more elegant approach of your code is:
<?php
function emptySchedule($hoursInit = date('G'), $hoursActive = 24)
{
$emptySchedule = [];
for ($h = $hoursInit; $h < $hoursActive; ++$h) {
for ($m = 0; $m < 60; ++$m) {
$emptySchedule[sprintf('%02d:%02d', $h, $m)] = [];
}
}
return $emptySchedule;
}
You can use interval too, working with lunch hours in your schedule and stuff:
<?php
function emptySchedule($hoursInit = date('G'), $hoursActive = 24, $interval = false)
{
$emptySchedule = [];
for ($h = $hoursInit; $h < $hoursActive; ++$h) {
if ($interval && ($h < $interval['start'] || $h > $interval['end'])) {
for ($m = 0; $m < 60; ++$m) {
$emptySchedule[sprintf('%02d:%02d', $h, $m)] = [];
}
}
}
return $emptySchedule;
}
But, like #deceze said, using date functions is a must, it's not simple to do right now, I'll try to figure out a better way;

Related

how to exclude an arbitrary period of time (1-2 hours) from the PHP array

There is an array of working hours, how to exclude lunch time from it?
$start = strtotime('8:00');
$end = strtotime('18:00');
$step = 1800; // 30 min
for($i = $start; $i <= $end; $i += $step)
{
$arTime[] = date('H:i', $i);
}
print_r($arTime);
A class that allows easy creation / manipulation / comparison of date and time, you can use DateTime to create the desired array.
Set a time-interval that is to be excluded from your results array using $lunchStart and $lunchStop.
$start = new DateTime('8:00');
$end = new DateTime('18:00');
$lunchStart = new DateTime('13:00');
$lunchStop = new DateTime('14:00');
while ($start <= $end) {
$slot = $start->format('H:i');
if ($start <= $lunchStart || $start >= $lunchStop) {
$arTime[] = $slot;
}
$start->add(new DateInterval('PT30M')); // 30 min interval
}
working demo

php carbon check if now is between two times (10pm-8am)

$start = '22:00:00';
$end = '08:00:00';
$now = Carbon::now('UTC');
How can I check if the time of $now is within the timerange?
There are several ways to achieve that by using Carbon. One of the easiest ways is using createFromTimeString and between methods:
$now = Carbon::now();
$start = Carbon::createFromTimeString('22:00');
$end = Carbon::createFromTimeString('08:00')->addDay();
if ($now->between($start, $end)) {
// ¯\_(ツ)_/¯
}
Try this:
$time = Carbon::now();
$morning = Carbon::create($time->year, $time->month, $time->day, 8, 0, 0); //set time to 08:00
$evening = Carbon::create($time->year, $time->month, $time->day, 18, 0, 0); //set time to 18:00
if($time->between($morning, $evening, true)) {
//current time is between morning and evening
} else {
//current time is earlier than morning or later than evening
}
The true in $time->between($morning, $evening, true) checks whether the $time is between and including $morning and $evening. If you write false instead it checks just if it is between the two times but not including.
Actually, you could leave true away because it is set by default and not needed.
Check here for more information on how to compare dates and times with Carbon.
$start = '22:00:00';
$end = '08:00:00';
$now = Carbon::now('UTC');
$time = $now->format('H:i:s');
if ($time >= $start && $time <= $end) {
...
}
Should do it, but doesn't take date into consideration
You can reverse check algorithm.
<?php
$pushChannel = "general";
$now = Carbon::now();
$start = Carbon::createFromTime(8, 0);
$end = Carbon::createFromTime(22, 0);
if (!$now->between($start, $end)) {
$pushChannel = "silent";
$restrictStartTime = Carbon::createFromTime(22, 0, 0); //carbon inbuild function which will create todays date with the given time
$restrictEndTime = Carbon::createFromTime(8, 0, 0)->addDays(1); //this will create tomorrows date with the given time
$now = Carbon::now();
if($now->gt($restrictStartTime) && $now->lt($restrictEndTime)) {
.....
}
Please Try below code,
$start = '22:00:00';
$end = '08:00:00';
$now = Carbon::now('UTC');
$nowTime = $now->hour.':'.$now->minute.':'.$now->second;
if(strtotime($nowTime) > strtotime($start) && strtotime($nowTime) < strtotime($end) ) {
echo 'YES';
} else {
echo 'NO';
}
What Chris is trying to point out is if the endtime crosses over midnight then you must account for that.
This is not the cleanest way to do it but here is a method that seems to work.
private function isNowBetweenTimes($timezone, $startDateTime, $endDateTime) {
$curTimeLocal = Carbon::now($timezone);
$startTime = $curTimeLocal->copy();
$startTime->hour = $startDateTime->hour;
$startTime->minute = $startDateTime->minute;
$endTime = $curTimeLocal->copy();
$endTime->hour = $endDateTime->hour;
$endTime->minute = $endDateTime->minute;
if ($endTime->lessThan($startTime))
$endTime->addDay();
return ($curTimeLocal->isBetween($startTime, $endTime));
}
This example only cares about the hour and minutes and not the seconds but you can easily copy that as well. The key to this is comparing start and end time before comparing them to the current time and add a day to end time if end time is less than start time.
For complete solution which supports all start and end time range you can use bitwise XOR.
/*
* must using hours in 24 hours format e.g. set 0 for 12 pm, 6 for 6 am and 13 for 1 pm
*/
private $startTime = '0';
private $endTime = '6';
$currentHour = \Carbon\Carbon::now()->hour;
$start = $this->startTime > $this->endTime ? !($this->startTime <= $currentHour) : $this->startTime <= $currentHour;
$end = $currentHour < $this->endTime;
if (!($start ^ $end)) {
//Do stuff here if you want exactly between start and end time
}
an updated version of #AliN11's answer taking into account ranges accross two days or in the same day
$now = now();
$start = Carbon::createFromTimeString('22:00');
$end = Carbon::createFromTimeString('08:00');
if ($start > $end) {
$end = $end->addDay();
}
if ($now->between($start, $end)||$now->addDay()->between($start, $end)) {
//add statements
}
<?php
$now = date("H");
if ($now < "20") {
echo "Have a good day!";
}
Try this :
$start = 22; //Eg. start hour
$end = 08; //Eg. end hour
$now = Carbon::now('UTC');
if( $start < $now->hour && $now->hour < $end){
// Do something
}
#AliN11's (currently top) answer is good, but doesn't work as one would immediately expect, after midnight it just breaks, as raised in the comments by #Sasha
The solution is to reverse the logic, and check if the time is not between the inverse hours.
Here is an alternative that works as one would expect:
$now = Carbon::now();
$start = Carbon::createFromTimeString('08:00');
$end = Carbon::createFromTimeString('22:00');
if (! $now->between($start, $end)) {
// We're all good
}
Yes, the midnight plays a vital role in time duration. We can find now() being the given time range as follows:
$now = Carbon::now();
$start = Carbon::createFromTime('22', '00');
$end = Carbon::createFromTime('08', '00');
if ($start->gt($end)) {
if ($now->gte($start)) {
$end->addDay();
} elseif ($now->lte($end)) {
$start->subDay();
} else {
return false;
}
}
return $now->between($start, $end);

How can I create arrays of suitable candidates based on certain criteria?

So to brief you on the question:
I have a number of employees field in a form.
Employees have a salary and the day rate is calculated using (salary / 260)
Assuming employees work a fixed standard day
A task has a certain budget and a date range (start to end - e.g. 29/03/2017 to 31/03/2017)
A task also requires a fixed amount of employees (i.e. 3)
Now with those details in mind, I want to get a list of x employees (the fixed amount, i.e. 3) for which those employee's salaries match the date range of the project, and also slot into the budget.
I started doing this as follows, but have no idea how to continue further:
$employees = array(
0 => array(
'id' => 1,
'salary' => 22000
);
);
$startDate = some_date_here;
$endDate = some_date_here;
foreach($employees as &$user) {
$dayRate = round($user['salary'] / 260, 2);
}
EDIT:
Using http://php.net/manual/en/function.date-diff.php#117691, I managed to find the days between the dates of a project as follows (code update):
function days_diff($d1, $d2) {
$x1 = days($d1);
$x2 = days($d2);
if ($x1 && $x2) {
return abs($x1 - $x2);
}
}
function days($x) {
if (get_class($x) != 'DateTime') {
return false;
}
$y = $x->format('Y') - 1;
$days = $y * 365;
$z = (int)($y / 4);
$days += $z;
$z = (int)($y / 100);
$days -= $z;
$z = (int)($y / 400);
$days += $z;
$days += $x->format('z');
return $days;
}
$startDate = DateTime::createFromFormat('Y-m-d', $startDate);
$endDate = DateTime::createFromFormat('Y-m-d', $endDate);
$projectDays = days_diff($startDate, $endDate);
I think you can do this by the following code.
$startDate = some_date_here;
$endDate = some_date_here;
$noOfDays = calculate_using_end-start;
$employeeRate = [];
$noOfEmployeesRequired = 3;
foreach($employees as $index=>$user) {
$dayRate = round($user['salary'] / 260, 2);
$totalRate = $dayRate*$noOfDays;
$employeeRate[$user['id']] = $totalRate;
}
Now I have all employees Cost hence i sort it and find the cheapest solution
sort($employeeRate);
$output = array();
$counter = 0;
foreach($employeeRate as $id=>$emp) {
array_push($output, $id);
$counter++;
if($counter == $noOfEmployeesRequired) {
break;
}
}

unexpected while-loop behavior

I've tried building and placing the while loop in different ways and different locations and looking at $val as the right/wrong variable to be placing in the while loop but I'm just not getting it. I expected output to be: 815 830 845 900....... until reaching 1900.
Instead I got 815 then 1915 1930 1945 up through 0000 then it cycles over starting at 1915. Someone please tell me what i have placed in the wrong location or what variable I've used wrongly.
date_default_timezone_set('America/New_York');
$st='800';
$et='1900';
$frac = 900;
$x = $et;
$val=0;
if (strlen($st) < 4) {$st = "0".$st;}
$current_time = strtotime(date($st));
$r = $current_time % $frac;
$new_time = $current_time + ($frac-$r);
$val = date('Gi', $new_time);
echo $val."<br>";
while ($val !== $et){
if (strlen($val) < 4) {$st = "0".$val;}
$current_time = strtotime('+ 15 minutes',date($val));
$r = $current_time % $frac;
$new_time = $current_time + ($frac-$r);
$val = date('Gi', $new_time);
echo $val."<br>";
}
NOTE TO THE - MARKDOWN PLAYERS -- Edited POST ANSWER: You can bash the question all you want, but the fact is it was a clear question, clear objective trying to be reached and it was a clear attempt (one of several) to code it properly that failed, so bad code, not bad effort. So eventually I asked for help. Your using the markdown system the wrong way and for the wrong reason, but whatever I have little expectation that you care. Thanks to those that made the effort actually be constructive and teach/help.
With unelegant for loop:
for ($i = 800, $t= 1900; $i <= $t; $i += 15) {
printf('%1$3d <br>', $i);
if ($i%100 >= 45) $i += 40;
}
With DatePeriod:
$timezone = new DateTimeZone("America/New_York");
$begin = new DateTime("now", $timezone);
$begin->setTime(8, 0, 0);
$end = new DateTime("now", $timezone);
$end->setTime(19, 0, 0);
$interval = new DateInterval('PT15M');
$end->add($interval);
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date){
echo $date->format("Gi") . "<br>";
}
With generators:
function xmins($start, $end, $step = 15) {
if ($start < $end) {
if (($step <= 0) || ($step >= 40)) {
throw new LogicException('Step must be in range 1-40');
}
for ($i = $start; $i <= $end; $i += $step) {
if (60 <= $temp = $i % 100) {
$i += 40;
}
yield $i;
if ((60-$step) === $temp = $i % 100) {
$i += 100 - $temp - $step;
}
}
}
}
foreach (xmins(800,1900,15) as $i) {
printf('%1$3d<br>', $i);
}
Using DateInterval:
$date = new DateTime('2015-01-01 08:00:00');
$mins = new DateInterval('PT15M');
do {
$date->add($mins);
echo $date->format('Hi')."\n";
} while($date->format('Hi') < 1900);

Get Difference Between Two Times (Unix Epoch)

You know when it's late in the night and your brain is fried? I'm having one of those nights right now, and my function so far is not working as it should, so please take a look at it:
(I should note that I'm using the PHP 5.2.9, and the function / method DateTime:Diff() is not available until PHP 5.3.0.
<?php
function time_diff($ts1, $ts2) {
# Find The Bigger Number
if ($ts1 == $ts2) {
return '0 Seconds';
} else if ($ts1 > $ts2) {
$large = $ts1;
$small = $ts2;
} else {
$small = $ts1;
$large = $ts2;
}
# Get the Diffrence
$diff = $large - $small;
# Setup The Scope of Time
$s = 1; $ss = 0;
$m = $s * 60; $ms = 0;
$h = $m * 60; $hs = 0;
$d = $h * 24; $ds = 0;
$n = $d * 31; $ns = 0;
$y = $n * 365; $ys = 0;
# Find the Scope
while (($diff - $y) > 0) { $ys++; $diff -= $y; }
while (($diff - $n) > 0) { $ms++; $diff -= $n; }
while (($diff - $d) > 0) { $ds++; $diff -= $d; }
while (($diff - $h) > 0) { $hs++; $diff -= $h; }
while (($diff - $m) > 0) { $ms++; $diff -= $m; }
while (($diff - $s) > 0) { $ss++; $diff -= $s; }
# Print the Results
return "$ys Years, $ns Months, $ds Days, $hs Hours, $ms Minutes & $ss Seconds.";
}
// Test the Function:
ediff(strtotime('December 16, 1988'), time());
# Output Should be:
# 20 Years, 11 Months, 8 Days, X Hours, Y Minutes & Z Seconds.
?>
This isn't an answer to your question, but I just wanted to point out...
while (($diff - $y) > 0) { $ys++; $diff -= $y; }
is a very inefficient way of writing
$ys = $diff / $y;
$diff = $diff % $y;
Also, this
else if ($ts1 > $ts2) {
$large = $ts1;
$small = $ts2;
} else {
$small = $ts1;
$large = $ts2;
}
# Get the Diffrence
$diff = $large - $small;
can easily be rewritten as
$diff = abs($ts1 - $ts2);
I have a feeling that the problem in your code would be more apparent if it was less verbose. :)
how about simplifying the first part with a simple
$diff = abs($ts2 - $ts1);
Then, when you do this:
$n = $d * 31; $ns = 0;
$y = $n * 365; $ys = 0;
you are actually saying that a year is composed of 365 31 day long months. which is actually about 36 year long years. Probably not what you want.
Finally, we are all grown ups here. Please use grown up variable names i.e. $YEAR_IN_SECONDS instead of $ys. As you can clearly see, you may write code once, but 20 other schmucks are going to have to read it a lot of times.
In the case of needed all months during the given times-stamp then we have use of the following coding in php :
function MonthsBetweenTimeStamp($t1, $t2) {
$monthsYear = array();
$lastYearMonth = strtotime(gmdate('F-Y', $t2));
$startYearMonth = strtotime(gmdate('F-Y', $t1));
while ($startYearMonth < $lastYearMonth) {
$monthsYear[] = gmdate("F-Y", $startYearMonth);
//Increment of one month directly
$startYearMonth = strtotime(gmdate("F-Y", $startYearMonth) . ' + 1 month');
}
if (empty($monthsYear)) {
$monthsYear = array($startYearMonth));
}
return $monthsYear;
How about this:
function time_diff($t1, $t2)
{
$totalSeconds = abs($t1-$t2);
$date = getdate($totalSeconds);
$firstYear = getdate(0);
$years = $date['year']-$firstYear['year'];
$months = $date['mon'];
$days = $date['mday'];
$hours = $date['hour'];
$minutes = $date['minutes'];
$seconds = $date['seconds'];
return "$years Years, $months Months, $days Days, $hours Hours, $minutes Minutes & $seconds Seconds.";
}
This uses the difference of the given times as a date. Then you can let the "getdate" do all the work for you. The only challenge is the number years - which is simply the getdate year (of the difference) minus the Unix epoch year (1970).
If you don't like using an actual month, you could also divide the "year" day by the number of days in 12 equal months
$months = $date['yday'] / (365/12);
Similarly days could be figured out the remaining days with modulus
$days = $date['yday'] % (365/12);

Categories