I want to generate a list of salary coverage dates using the last salary date of an employee. The employee gets a salary every 15 days. So every month the coverage should be 1st - 15th and 16th - last day of the month.
For example,
$last_salary_date = "2020-12-01";
$date_now = "2021-02-27";
// I should get the following start and end dates:
// 2020-12-01 - 2021-12-15
// 2020-12-16 - 2021-15-31
// 2021-01-01 - 2021-01-15
// 2021-01-16 - 2021-01-31
// 2021-02-01 - 2021-02-15
// 2021-02-16 - 2021-02-28
$last_salary_date = "2020-02-16";
$date_now = "2021-02-27";
// I should get the following start and end dates:
// 2021-02-16 - 2021-02-28
So far I've done something like this:
$start_date = new DateTime("2021-01-16");
$end_date = new DateTime(date("Y-m-d"));
$interval = \DateInterval::createFromDateString('1 month');
$period = new \DatePeriod($start_date, $interval, $end_date);
$salary_dates = [];
foreach ($period as $dt) {
if (date("Y-m-d") > $dt->format("Y-m-01")) {
$salary_dates[] = (object) [
'start_dt' => $dt->format("Y-m-01"),
'end_dt' => $dt->format("Y-m-15")
];
}
if (date("Y-m-d") > $dt->format("Y-m-15")) {
$salary_dates[] = (object) [
'start_dt' => $dt->format("Y-m-16"),
'end_dt' => $dt->format("Y-m-t")
];
}
}
return $salary_dates;
The problem is it still gets the 1-15th of the first month even though the start should be the 16th.I'm thinking of a better way to do this. Please help
Here is the modified implementation. It is sketched for understanding, but can also be written in a shorter form.
<?php
$start_date = new DateTime("2021-02-16");
$end_date = new DateTime("2021-02-27");
$interval = \DateInterval::createFromDateString('1 month');
$period = new \DatePeriod($start_date, $interval, $end_date);
$salary_dates = [];
foreach ($period as $dt) {
$check_date = function ($date) use ($start_date, $end_date) {
// check if salary interval is in correct range
return
$start_date->format("Y-m-d") <= $date->start_dt
and $end_date->format("Y-m-d") >= $date->start_dt; // are you sure it shouldn't be ->end_dt ? now it's on order
};
// check first two weeks in month
$salary_date = (object)[
'start_dt' => $dt->format("Y-m-01"),
'end_dt' => $dt->format("Y-m-15"),
];
if ($check_date($salary_date)) {
$salary_dates[] = $salary_date;
}
// check second two weeks in month
$salary_date = (object)[
'start_dt' => $dt->format("Y-m-16"),
'end_dt' => $dt->format("Y-m-t")
];
if ($check_date($salary_date)) {
$salary_dates[] = $salary_date;
}
}
print_r($salary_dates);
Related
I am new to PHP. I am displaying the monthly events which is starting event from 03/31/2021 on every month, so here is a critical situation come because not every month exists 31 date. So there is some php function available to check this date is exists in next coming months. If exists then display the event on that date otherwise display the last date of the month.
$monthly_counter = 0;
$monthly_counter_offset = 0;
$begin = new DateTime($val->start); // $val->start = '03/31/2021'
$end = new DateTime($val->repeat_end_date); // $val->repeat_end_date= '03/31/2022'
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($begin, $interval, $end);
foreach ($period as $dt)
{
$loop_date = $dt->format('m/d/Y');
$loop_date_formatted = $dt->format('Y-m-d');
$due_dates[] = $from_date_times;
$from_new_date_times = date('Y-m-d H:i', strtotime('+1 month', strtotime($from_date_times)));
$from_date_times = $from_new_date_times;
$deletedevents2=$val->deletedevents;
$arrayofdeleted=explode(",",$deletedevents2);
$originalDate = $from_date_times;
$newDateforcompare = date("m/d/Y", strtotime($originalDate));
if (in_array($loop_date_formatted, $arrayofdeleted))
{
continue;
}
$number_of_days = $val->number_of_days;
$end_new_date_times = date('Y-m-d H:i', strtotime('+'.$number_of_days.' day', strtotime($loop_date)));
if(date('Y-m-d ', strtotime($val->start))<date('Y-m-d', strtotime($val->ends)))
{
$end_new_date_times = date('Y-m-d H:i', strtotime('+1 day', strtotime($end_new_date_times)));
}
$skipcounter = $val->interval_value;
if (!empty($skipcounter) || $skipcounter != 1) {
$monthly_counter++;
$monthly_counter_offset++;
if ($monthly_counter == $skipcounter || $monthly_counter_offset == 1) {
$monthly_counter = 0;
} else {
continue;
}
} else {
}
$rows[] = array(
'id' => $val->id,
'title' => $val->title,
'description' => $val->calendar_comments,
'start' => $loop_date,
'end' => $end_new_date_times,
'borderColor'=>$val->color,
'backgroundColor'=>$val->color_bg,
'className'=>'timegridclass',
'allDay' => $allday,
);
}
Since you're using fullCalendar, you can simply specify an event which uses RRule to specify the recurrence, rather than using complex PHP code to try and generate it.
e.g.
events: [
{
title: "Sales Meeting",
rrule: "FREQ=MONTHLY;BYMONTHDAY=28,29,30,31;BYSETPOS=-1"
}
]
Working demo: https://codepen.io/ADyson82/pen/bGByBaV
This will generate an event which repeats on the last day of every month, regardless whether the month is 28, 29, 30 or 31 days long.
Obviously you can use PHP to generate this event object, and enhance it with a custom title, start/end dates etc as per your database contents. But I have shown you the basic approach.
Credit to this answer for the specific RRule string.
Documentation: https://fullcalendar.io/docs/rrule-plugin and https://github.com/jakubroztocil/rrule
need to obtain all wednesday dates between two dates. For ex
start and end date=
01/07/2019 - 01/25/2019
expected result=
01/09/2019,
01/16/2019,
01/23/2019
can i use if ($startDate->format('w') == 2) {}
condition to filter wednesdays and push into array. any method to get the result?
Use DatePeriod Class. date period allows iteration over a set of dates and times, recurring at regular intervals, over a given period.
$period = new DatePeriod(
new DateTime($date1),
new DateInterval('P1D'),
new DateTime($date2)
);
$cnt = 0;
foreach ($period as $key => $value) {
if($value->format('D') == 'Wed'){
$wed[$cnt] = $value->format('m/d/Y');
$cnt++;
}
}
Output
[0] => 01/09/2019
[1] => 01/16/2019
[2] => 01/23/2019
<?php
$from_date ='01/07/2019';
$to_date ='01/25/2019';
$from_date = new DateTime($from_date);
$to_date = new DateTime($to_date);
$get_date = array();
for ($date = $from_date; $date <= $to_date; $date->modify('+1 day')) {
if($date->format('l') == 'Wednesday'){
$get_date[] = $date->format('m/d/Y');
}
}
print_r($get_date);
Out put
Array ( [0] => 01/09/2019 [1] => 01/16/2019 [2] => 01/23/2019 )
You will get the required output.
<?php
$date1 = date("01/07/2019");
$date2 = date("01/25/2019");
$day1 = date('D', strtotime($date1));
$period = new DatePeriod(New Datetime($date1),New DateInterval('P1D'),New DateTime($date2));
$cnt = 0;
foreach($period as $key => $value ){
if($value->format('D') == 'Wed'){
$wed[$cnt] = $value->format('m/d/Y');
echo $wed[$cnt];
$cnt++;
echo '<BR>';
}
}
?>
Using the base DateTime class and instead of checking every day, just keep adding 7 days to the date and check if it is still less than the end date. The only additional logic is that if the start date is a Wednesday, then use this date, otherwise get the next Wednesday...
$fromDate = new DateTime('01/02/2019');
if ( $fromDate->format('D') != 'Wed') {
$fromDate->modify("next wednesday");
}
$toDate = new DateTime('01/25/2019');
do {
echo $fromDate->format("m/d/Y").PHP_EOL;
}
while ( $fromDate->modify(""+7 day"") < $toDate );
outputs...
01/02/2019
01/09/2019
01/16/2019
01/23/2019
two dates 13-10-2017 and 13-02-2018. I want to separate this period in months like 13-10-2017 to 31-10-2-17, 01-11-2017 to 30-11-2017, 01-12-2017 to 31-12-2017, 01-01-2018 to 31-01-2018 and 01-02-2018 to 13-02-2018. What I did I can get the month names in the date period but not in the format I want.
Here is my code:
$start_date = new DateTime('13-10-2017');
$end_date = new DateTime('13-02-2018');
$date_interval = new DateInterval('P1M');
$date_period = new DatePeriod($start_date, $date_interval, $end_date);
# calculating number of days in the interval
$interval = $start_date->diff( $end_date );
$days = $interval->days;
# getting names of the months in the interval
$month_count = 0;
$month_names = array();
foreach ($date_period as $date) {
$month_names[] = $date->format('F');
$month_count++;
}
$month_name_string = implode(',', $month_names);
echo $start_date->format('d-m-Y').' to '.$end_date->format('d-m-Y'). ' is ' .$days.' days and month names are: '.$month_name_string;
The output I get :
13-10-2017 to 13-02-2018 is 123 days and month names are: October,November,December,January
You can, while iterating, do the following checks:
If the current month is in $start_date, use its day for the start date
If the current month is in $end_date, use its day for the last day
Else, use the 1 and maximum day of each month (using the t format character)
Also, you need to set the time to 00:00:01 in the final day in order to have it considered in the DateInterval:
<?php
$start_date = new DateTime('13-10-2017');
$end_date = new DateTime('13-02-2018');
$end_date->setTime(0, 0, 1); // important, to consider the last day!
$date_interval = new DateInterval('P1M');
$date_period = new DatePeriod($start_date, $date_interval, $end_date);
# calculating number of days in the interval
$interval = $start_date->diff( $end_date );
$days = $interval->days;
# getting names of the months in the interval
$dates = [];
foreach ($date_period as $date) {
$dateArr = [];
if ($date->format("Y-m") === $start_date->format("Y-m")) {
$dateArr["start"] = $start_date->format("d-m-Y");
}
else {
$dateArr["start"] = $date->format("01-m-Y");
}
if ($date->format("Y-m") === $end_date->format("Y-m")) {
$dateArr["end"] = $end_date->format("d-m-Y");
}
else {
$dateArr["end"] = $date->format("t-m-Y"); // last day of the month
}
$dates[] = $dateArr;
}
foreach ($dates as $date) {
echo $date["start"]." to ".$date["end"].PHP_EOL;
}
Demo
You can employ DateTime::modify function. E.g.:
$month_intervals = [];
foreach ($date_period as $date) {
$start = $date == $start_date ? $start_date : $date->modify('first day of this month');
$month_intervals[] = join([
$start->format('d-m-Y'),
$date->modify('last day of this month')->format('d-m-Y')
], ' to ');
}
$month_intervals[] = join([
(clone $end_date)->modify('first day of this month')->format('d-m-Y'),
$end_date->format('d-m-Y')
], ' to ');
echo implode(',', $month_intervals);
I am trying to create a booking form where a user can select a booking time between 2 given times in 5 minute intervals. For example I want time slots between 10am and 12pm which would give me about 20 time slots.
When the user goes to select a slot, the earliest slot should be at least 15 mins ahead of the current time but the user can select a slot and hour or more if desired.
I found some code on SO (can't remember where) and I've edited it for my needs and it works if the current time is within the start and end time but if the current time is an hour before the earliest time, it doesn't create the time slots.
I know why it does it but i don't know how to fix it. It has to do with the while condition.
I would like to be able to book a slot hours before the first available slot if that is possible.
$timenow = time();
$start_time = strtotime('+15 minutes', $timenow);
// round to next 15 minutes (15 * 60 seconds)
$start_time = ceil($start_time / (5 * 60)) * (5 * 60);
//set the start times
$opentime = strtotime('10:00');
$closetime = strtotime('11:55');
// get a list of prebooked slots from database
$time_slots = $this->countStartTimes();
$available_slots = array();
while($start_time <= $closetime && $start_time >= $opentime) {
$key = date('H:i', $start_time);
if(array_key_exists($key, $time_slots)) {
if($time_slots[$key] == SLOTS) {
$available_slots[] = 'FULL';
break;
}
}
$available_slots[] = date('H:i', $start_time);
$start_time = strtotime('+5 minutes', $start_time);
}
I managed to get it working using Datetime()
$timenow = new DateTime(date('H:i'));
$timenow->add(new DateInterval('PT15M'));
$start = new DateTime('11:00');
$end = new DateTime('14:00');
$interval = new DateInterval('PT5M');
$time_slots = $this->countStartTimes();
$available_slots = array();
$period = new DatePeriod($start, $interval, $end);
foreach($period as $time) {
$timeslot = $time->format('H:i');
if ($timenow > $time) {
continue;
}
if(array_key_exists($timeslot, $time_slots)) {
if($time_slots[$timeslot] == SLOTS) {
$available_slots[] = array('key' => $timeslot, 'value' => 'FULL');
continue;
}
}
$available_slots[] = array('key' => $timeslot, 'value' => $timeslot);
}
Carbon has all of the functions inherited from the base DateTime class. This approach allows you to access the base functionality if you see anything missing in Carbon but is there in DateTime.
// Carbon::diffInYears(Carbon $dt = null, $abs = true)
echo Carbon::now('America/Vancouver')->diffInSeconds(Carbon::now('Europe/London')); // 0
$dtOttawa = Carbon::createFromDate(2000, 1, 1, 'America/Toronto');
$dtVancouver = Carbon::createFromDate(2000, 1, 1, 'America/Vancouver');
echo $dtOttawa->diffInHours($dtVancouver); // 3
echo $dtOttawa->diffInHours($dtVancouver, false); // 3
echo $dtVancouver->diffInHours($dtOttawa, false);
Use carbon class for this it really help you
I'm trying to calculate the number of weekend days between dates from the array below:
$dates[] = array ( 'DateFrom' => '2015-07-10', 'DateTo' => '2015-07-10', 'DateFrom' => '2015-07-12', 'DateTo' => '2015-07-12', 'DateFrom'=> '2015-07-17', 'DateTo'=> '2015-07-19') ;
The result must return number of weekend days between these dates
Between these dates are 3 days of weekend (2015-07-12, 2015-07-18, and 2015-07-19).
Anyone have any idea?
You need to loop through from start date to end date and in each iteration need to check for day (sat/sun)
Algo :
$weekends = 0;
$startDate = strtotime($startDate);
$endDate = strtotime($endDate);
while($startDate<$endDate) {
//"N" gives ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
$day = date("N",$startDate);
if($day == 6 || $day == 7) {
$weekends++;
}
$startDate = 24*60*60 ; //1 day
}
Firstly, if you're defining your array exactly as written, you're duplicating keys and four of those items will be overwritten. But assuming we're just looking at the pairs. Pass the FromDate and ToDate from each pair to this function and add up all the return values.
function getWeekends ($fromDate, $toDate) {
$from = strtotime($fromDate);
$to = strtotime($toDate);
$diff = floor(abs($to-$from)/(60*60*24)); // total days betwixt
$num = floor($diff/7) * 2; // number of weeks * 2
$fromNum = date("N", $from);
$toNum = date("N", $to);
if ($toNum < $fromNum)
$toNum += 7;
// get range of day numbers
$dayarr = range($fromNum, $toNum);
// check if there are any weekdays in that range
$num += count(array_intersect($dayarr, array(6, 7, 13)));
return $num;
}
There may be a more elegant solution.
To be used on each pair of dates:
function getWeekendDays($startDate, $endDate)
{
$weekendDays = array(6, 7);
$period = new DatePeriod(
new DateTime($startDate),
new DateInterval('P1D'),
new DateTime($endDate)
);
$weekendDaysCount = 0;
foreach ($period as $day) {
if (in_array($day->format('N'), $weekendDays)) {
$weekendDaysCount++;
}
}
return $weekendDaysCount;
}