How to save date and day into separate array without sundays? - php

I am trying to save a series of five dates into an array to be called. The date has no range since it is taking the current date to start with.
I would like to save without any Sundays as delivery is not an option for Sunday in my case. I am saving both dates and day in separate arrays.
$date = new DateTime("+ 1 day", new DateTimeZone('Asia/Thailand') );
for ($i=1; $i<=5; $i++) {
$date->modify("+1 weekday");
$delivery_dates[] = $date->format("m/d/Y");
$delivery_days[] = $date->format("l, d F Y");
}
At the moment, I am getting the following -
Sunday, Monday, Tuesday, Wednesday, Thursday (inclusive of the dates for each day in d F Y format)
Is there a way I can get the following -
Monday, Tuesday, Wednesday, Thursday, Friday (inclusive of the dates for each day in d F Y format)?
For every Sunday I would like to +1 day so it makes an available day for delivery, on Monday.
I have used the following -
for ($i=1; $i<=5; $i++) {
$date->modify("+1 weekday");
if ($date->format("N") !== 7 {
$delivery_dates[] = $date->format("m/d/Y");
$delivery_days[] = $date->format("l, d F Y");
}
}
The codes above still display Sunday.

$from = '2018-10-10';
$to = '2018-12-10';
$start = new \DateTime($from);
$end = new \DateTime($to);
$interval = \DateInterval::createFromDateString('1 day'); // 1 day === P1D, 5 month and 5 years === P5M5Y
$period = new \DatePeriod($start, $interval, $end); // A date period allows iteration over a set of dates and times, recurring at regular intervals, over a given period.
// new \DatePeriod(10-10-2010, 5, 30-10-2010) ===> [10-10-2010, 15-10-2010, 20-10-2010, 25-10-2010, 30-10-2010]
$result = [];
foreach ($period as $day) {
if (! in_array($day->format('w'), [0, 6])) { // check every date in period, ->format('w'): w - number of day; Monday = 1 or 7 (depends on wat day declared first)
$result['date'][] = $day->format("m/d/Y");
$result['day'][] = $day->format("l, d F Y");
}
}

Assuming you can use Carbon, one way to implement is to generate the range with the help of a while-loop, and only add a date if it is not a Sunday, while ensuring that you'll get the required amount of days, in your case, 5.
/**
* #param int $amount
* #return Carbon[]
*/
public function getDeliveryDates($amount = 5): array
{
$days = [];
$current = Carbon::today();
while (\count($days) < $amount) {
$day = clone $current; // addDay works on instance, cloning it
$day->addDay();
$current = $day; // setting current to current day, so that it will iterate
if ($current->isSunday()) {
continue;
}
$days[] = $current;
}
return $days;
}
Then you can just format your date. If you still want two arrays with the info, you just need to iterate over the generated array:
$formattedDates = [];
$formattedDays = [];
foreach (getDeliveryDates() as $date) {
$formattedDates[] = $date->format('m/d/Y');
$formattedDays[] = $date->format('l, d F Y');
}

Related

PHP DateTime Textual Representation

I wonder if it is possible to get this as an ouput using PHP DateTime:
'first thursday of February 2021'
The other way round were
$date = new DateTime('first thursday of February 2021');
What I have to accomplish: I have a given date and want to know, if it is the first thursday or not without building the date and comparing dates after this like:
$date = new DateTime('first thursday of February 2021');
$given_date = new DateTime('2021-02-04');
return ($given_date == $date);
Anyone?
I forgot: my given date can be the second saturday in march or the third friday in june and so on.
I will explain a little further for those who doesn't understand my question. Imaging a time table which says: all thursdays office is open from 8am to 5pm but first thursday in march office will open at 7am and closes at 6pm. Now a customer wants to schedule a meeting and we have only a date but must know: is it the first thursday or not. My database holds such information like day, month and possibly position in month. My solution now is a loop to determine the position in month.
private function getPositionInMonth(DateTime $date)
{
$positions = ['first', 'second', 'third', 'fourth', 'fifth'];
$day_name = $date->format('l');
foreach ($positions as $i => $position) {
$str = $position . ' ' . $day_name . ' ' . $date->format('F') . ' ' . $date->format('Y');
$test_date = new DateTime($str);
if ($test_date->format('d.m.Y') == $date->format('d.m.Y')) {
return ($i + 1);
}
}
}
All other information can be fetched in the sql query.
A DateTime object should be formatted as a string like "first thursday of February 2021".
function formatNoWeekday(Datetime $date){
$no = ['first','second','third','fourth','fifth'];
$day = $date->format('j');
$frm = $date->format(' l \o\f F Y');
foreach($no as $n){
$strDate = $n.$frm;
if(date_create($strDate)->format('j') == $day){
return $strDate;
}
}
return false;
}
$result = formatNoWeekday(date_create('2021-02-16'));
//"third Tuesday of February 2021"
Solved it with a tiny bit of math like CBroe suggested:
private function getPositionInMonth(DateTime $date)
{
$test_date = new DateTime($date->format('Y-m-') . '01');
$diff = round(($date->getTimestamp() - $test_date->getTimestamp()) / 86400, 0);
if($diff < 7) {
return 1;
}
return (int)($diff / 7) + 1;
}
That function will give the position in month.
(Diff calculation fixed, DateTime gives d=0 and m=1 for the 29th of march, now it works. If you find any date which fails please mention it)
DateTime object supports relative formats. For this you can call method modify
<?php
$date = new DateTime('now');
$date->modify('first thursday of February 2021');
$given_date = new DateTime('2021-02-04');
return ($given_date == $date);
DateTime::modify
Relative Formats

Ignoring holidays in week working days in PHP

I want to get all the working days of current week by ignoring weekends (Saturday and Sunday) also I want to ignore holidays as well (holiday list are in database)
Here is my code to get all the working days of current weeks (excluding Saturday and Sunday)
$holiday_date_list = []; // from database
$dt = new DateTime();
$dates = [];
for ($d = 1; $d <= 5; $d++)
{
$dt->setISODate($dt->format('o'), $dt->format('W'), $d);
$dates[$dt->format('D')] = $dt->format('Y-m-d');
}
foreach ($dates as $key => $value)
{
echo $value . "<br>";
}
How can exclude holiday list from the above script?

Get Mondays of every second month

I am trying to get the date of every monday skipping a month each time but I keep getting only the monday of the first week. This is my code:
$begin = new \DateTime("2017-04-01");
$end = new \DateTime("2017-08-31");
$interval = \DateInterval::createFromDateString("next monday of +2 months");
$period = new \DatePeriod($begin, $interval, $end);
foreach ( $period as $dt )
{
// echo the date for each monday found in the second month
}
Any help would be greatly appreciated.
You seem to have mixed the $begin and $end values. Also, the DateInterval will only have one Monday every single month. I was not able to find a interval expression to get every Monday in every other month, so I did the filtering manually. In this example we use the month from the begin date, and includes the Mondays from that date, skip two months and so on.
<?php
$end = new \DateTime("2017-04-01");
$begin = new \DateTime("2007-08-31");
$month_even = ((int) $begin->format('m')) % 2 === 0;
$interval = \DateInterval::createFromDateString("next monday");
$period = new \DatePeriod($begin, $interval, $end);
foreach ($period as $dt) {
// Check if we want to show even months, make sure that the current month is even
// or, if we want to show odd months, the month should be odd.
if ((((int) $dt->format('m')) % 2 === 0) === $month_even) {
echo $dt->format('d-m-Y') . PHP_EOL;
}
}
Outputs:
31-08-2007
01-10-2007
[...]
06-02-2017
13-02-2017
20-02-2017
27-02-2017

Get last three 15-day periods in PHP

I'm having trouble coming up with a solution for this and Google doesn't seem to be yielding anything (I'm also unsure what to search for) but basically, what I want is to get the last three 15-day periods at any given date.
A period being the 1st to the 15th of the month and 16th to the last day of the month.
Let's say today, we're on Sep 22, 2016. The date ranges I want to get are:
Sep 1 - Sep 15
Aug 16 - Aug 31
Aug 1 - Aug 15
Similarly if we were on Sep 12, 2016, the dates I'd want to get are:
Aug 16 - Aug 31
Aug 1 - Aug 15
Jul 16 - July 31
Right now I have some pseudo code that goes like this:
Get day (number)
check if greater than 15
Get last 15-day period date ranges
Continue to get 2 more date ranges before that
I guess what I'm having trouble with here is translating 3 and 4 into PHP code.
Can someone shed some light here?
We can simplify these requirements into two distinct things.
Define a given month into 2 periods. The first, being the 1st day of the month to the 15th day of the month. The second, being the 16th day of the month to the last day of the month.
Given a date, determine which are the last 3 periods subsequent the current period from that date, exclusive.
Step 1
The first part is easy.
$date = new DateTimeImmutable("Sep 22, 2016");
// Period 1 (from 1st day of month to 15th)
$p1Start = $date->modify("first day of this month");
$p1End = $p1Start->add(new DateInterval("P14D"));
// Period 2 (from 16th day of month to last day of month)
$p2Start = $p1End->add(new DateInterval("P1D"));
$p2End = $p2Start->modify("last day of this month");
echo "Period 1 Starts On: {$p1Start->format('M j, Y')}\n";
echo "Period 1 Ends On: {$p1End->format('M j, Y')}\n\n";
echo "Period 2 Starts On: {$p2Start->format('M j, Y')}\n";
echo "Period 2 Ends On: {$p2End->format('M j, Y')}\n";
Output you get from this is as expected:
Period 1 Starts On: Sep 1, 2016
Period 1 Ends On: Sep 15, 2016
Period 2 Starts On: Sep 16, 2016
Period 2 Ends On: Sep 30, 2016
Now you just need to put all of this in a function and reuse the same solution to get to step 2.
function getPeriods($dateString) {
$date = new DateTimeImmutable($dateString);
// Period 1 (from 1st day of month to 15th)
$p1Start = $date->modify("first day of this month");
$p1End = $p1Start->add(new DateInterval("P14D"));
// Period 2 (from 16th day of month to last day of month)
$p2Start = $p1End->add(new DateInterval("P1D"));
$p2End = $p2Start->modify("last day of this month");
return [
"period1" => ["start" => $p1Start, "end" => $p1End],
"period2" => ["start" => $p2Start, "end" => $p2End],
];
}
So now let's say you start with a date like Sep 12, 2016. You can figure out which period this date falls in by checking the return value from this function like so.
$date = "Sep 12, 2016";
$periods = getPeriods($date);
if ($date >= $periods["period1"]["start"] && $date <= $periods["period1"]["end"]) {
// It's in Period1
} else {
// It's in Period2
}
Step 2
Now let's just modify this function a little to simplify so that the solution can be expanded upon. So we'll name it getPeriod instead of getPeriods and have it only return one period from a given date.
function getPeriod($dateString) {
$periods = [];
$date = new DateTimeImmutable($dateString);
// Period 1 (from 1st day of month to 15th)
$p1Start = $date->modify("first day of this month");
$p1End = $p1Start->add(new DateInterval("P14D"));
// Period 2 (from 16th day of month to last day of month)
$p2Start = $p1End->add(new DateInterval("P1D"));
$p2End = $p2Start->modify("last day of this month");
// Figure out which period the given date belongs in
if ($date >= $p1Start && $date <= $p1End) {
$period = ["start" => $p1Start, "end" => $p1End];
} else {
$period = ["start" => $p2Start, "end" => $p2End];
}
return $period;
}
So to get the previous period from this date we just simply take the start date of the current period, returned by this function, subtract 1 day from that, and send the new date back to the function again to get the previous period.
Similarly, to get the next period just take the end date, add 1 day, and send that back to the function.
Final Result
Here's an example.
// We want up to 3 periods previous to this given date's period.
$date = "Sep 12, 2016";
$periods = [];
$currentPeriod = getPeriod($date);
for ($i = 0; $i < 3; $i++) {
$currentPeriod = $periods[] = getPeriod($currentPeriod["start"]->sub(new DateInterval("P1D"))->format("c"));
}
// Print out the period dates
$i = 1;
foreach($periods as $period) {
echo "Period $i: {$period['start']->format('M j, Y')} - {$period['end']->format('M j, Y')}\n";
$i++;
}
Output is what you'd expect...
Period 1: Aug 16, 2016 - Aug 31, 2016
Period 2: Aug 1, 2016 - Aug 15, 2016
Period 3: Jul 16, 2016 - Jul 31, 2016
function getDateRangesStartingFrom(\Carbon\Carbon $date, $numberOfMonths = 3)
{
$ranges = [];
for ($i = 0; $i < $numberOfMonths; $i++)
{
$month = [];
/**
* Generates the first 01-15 range.
*/
$rangeOne = \Carbon\Carbon::createFromFormat('Y-m-d', "{$date->year}-{$date->month}-01")->addMonths($i);
$rangeTwo = (clone $rangeOne)->addDays(14);
$month[] = [$rangeOne, $rangeTwo];
/**
* Generates the second 16-X (depending on the month) range.
*/
$rangeThree = (clone $rangeTwo)->addDays(1);
$rangeFour = (clone $rangeTwo)->addDays($rangeOne->daysInMonth - 15);
$month[] = [$rangeThree, $rangeFour];
/**
* We push all the ranges into the global array so we can return it later.
*/
$ranges[$rangeOne->format('F')] = $month;
}
return $ranges;
}
$ranges = getDateRangesStartingFrom(\Carbon\Carbon::createFromFormat('Y-m-d', '2016-09-22'));
var_dump($ranges);
This is how I would do it.
I use Carbon so that dealing with dates is much easier, go check it out!
I have created a function named getDateRangesStartingFrom(\Carbon\Carbon $date) that takes a parameter that represents from which date (or the month, essentially, since that's what we care about) the ranges should begin from to generate the ranges.
I then make a loop of $numberOfMonths iterations to begin the generation.
First, we proceed by creating the first part of the range, by using the year and month of the starting date. I then add the iterator index that represents what month we are in. Second, we create a second date that represents the second half of the first range (1-15).
After, we create the other two parts, by subtracting the days in the month by the days we've added before, so that we get the remaining days of the month.
We push all these ranges into a function scoped variable, and return the results.
Sherif's answer is good as well but take this as an alternative if you want to try Carbon.
Tried to find a solution written as simple code as possible.
I have some comments in code. I think that make clear how code is working.
Check the working code here
<?php
// use one of the two examples.
// $now = DateTime::createFromFormat('Y-m-d', '2016-09-12');
$now = new DateTime();
print_r(getPeriods($now));
function getPeriods(DateTime $now)
{
$currentDay = $now->format('d');
$dates = []; // Hold all date ranges
$start = clone $now;
$end = clone $now;
if ($currentDay > 15) { // For dates after 15th day of month
// Starting period is the first day of previous month
$start->modify('first day of previous month');
//Ending date is the 15th day of this month
$end->modify('first day of this month')->modify('14 days');
} else { // For dates before 15th day of month
//Starting period is the 16th day of the previous of previous month.
$start->modify('first day of previous month')
->modify('first day of previous month')
->modify('15 days');
//Ending period is the last day of previous month
$end->modify('last day of previous month');
}
$dates[] = $start;
$i = 2;
// $c will hold all intermediate dates of periods between
// $start and $end
$c = clone $start;
while ($c < $end) {
if ($c->format('d') > 15) {
// if we are in the 16th of the month,
// give me the last day of the month
$c->modify('last day of this month');
} else {
// give me the 15th day of month
$c->modify('14 days');
}
$dates[] = clone $c; // always clone $c so can not change other references
if ($i > 0) {
$c->modify('1 day');
$dates[] = clone $c;
}
$i--;
}
return array_chunk($dates, 2);
}

How can I get specific dates from a number of weeks?

Using PHP, I'd like to get the dates for specific weekdays within a given number of weeks. For example, I want to get the dates for Monday, Wednesday and Friday from 10 weeks.
The pseudo code for what I want is like this:
function (monday, wednesday, friday, 10) {
// 10 is week numbers
week1 5,7,9 oct 2015
week2 12,14,16 oct 2015
...
week10
}
i write a solution for this. Thanks for all answers.
<?
$dates = array();
$i = 0;
while(true) {
$i++;
$time = strtotime("+$i days");
$dayOfWeek = date('w', $time);
if(
/*
0-sun
1-mon***
2-tue
3-wed***
4-thu
5-fri***
6-sat
*/
($dayOfWeek == 0) or
($dayOfWeek == 2) or
($dayOfWeek == 4) or
($dayOfWeek == 6)
) {
continue;
}
$dates[] = date('Y-m-d', $time);
if( count($dates) > 30 ) {
break;
}
echo json_encode($dates );
}
?>
Sounds like you want to work with schedules. I suggest you to have a look at library called When, it's "Date / Calendar recursion library for PHP 5.3+".
What you want to do is MWF schedule for next 10 weeks:
$now = new DateTime('NOW');
$after10Weeks = clone $now;
$after10Weeks->modify('+10 week');
$r = new When();
$r->startDate($now)
->freq("weekly")
->until($after10Weeks)
->byday(array('MO', 'WE', 'FR'))
->generateOccurrences();
$occurrences = $r->occurrences;
Use PHP DateTime ISO date:
$date = new DateTime(); //The object
$year=2015; // Desired year
$days = array('Mon', 'Wed', 'Fri'); // Desired days of week
for ($yearDay=1; $yearDay<=366; $yearDay++) {
$date->setISODate($year,null, $yearDay); // call ISO Date
$week=$date->format('W'); // Get the week resulted
$day=$date->format('D'); // Get the day name resulted
if($week==10 && in_array($day, $days)) // return only dates of desired days of week 10
echo $date->format('M d Y')."<br>";
}
More information about ISO Date here
You can try this code with PHPTester

Categories