PHP: find dates every week for different days - php

I am trying to solve a problem I am looking for hours into.
My students have special courses and I am trying to find the dates.
For example:
Student 1 in Class A: every Monday from Jan 1st till April 1st
Student 2 in Class B: every Wednesday from April 1st until June...
So I programmed a function in which I can pass info like begin, end, weekday to show me the dates:
function tkcheck ($beginnfunc,$endfunc,$daycheck)
{
$begin = new DateTime($beginnfunc);
$end = new DateTime($endfunc);
$interval = new DateInterval('P1W');
$period = new DatePeriod($begin, $interval, $end);
foreach ($period as $date) {
$dayw = $date->modify($daycheck);
if ($dayw < $end) {
$daystring = $dayw->format ('d-m-Y');
$q1day1[] = $daystring;
}
}
}
tkcheck ('2022-02-20','2022-04-01','next Wednesday');
print_r($q1day1);
But print_r does not show me any information when I try to use my function tkcheck...
Maybe some here might help me, thank you!

$q1day1 is scoped within the function. It is not accessible out of the function.
To fix this return the variable.
function tkcheck ($beginnfunc,$endfunc,$daycheck)
{
$begin = new DateTime($beginnfunc);
$end = new DateTime($endfunc);
$interval = new DateInterval('P1W');
$period = new DatePeriod($begin, $interval, $end);
foreach ($period as $date) {
$dayw = $date->modify($daycheck);
if ($dayw < $end) {
$daystring = $dayw->format ('d-m-Y');
$q1day1[] = $daystring;
}
}
return $q1day1;
}
$result = tkcheck ('2022-02-20','2022-04-01','next wednesday');
print_r($result);
See Variable Scope for more info.

The special feature of this solution is simply to create a Date::Interval from 'next Monday'.
$start = date_create('Jan 1st 2022')->modify('-1 Day');
$end = date_create('Apr 1st 2022');
$interval = DateInterval::createFromDateString('next Monday');
$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);
$dateArray = iterator_to_array ($period);
echo '<pre>'.var_export($dateArray,true).'</pre>';
The modification with "-1 Day" is necessary to record a Monday that falls exactly on the start date.

Related

Counting days "forgets" last day

i'm using this code and it works fine so far:
<?php
$start = (new DateTime('2019-02-11'));
$end = (new DateTime('2019-04-23'));
$months = new DatePeriod($start, DateInterval::createFromDateString('1 month'), $end);
$days = new DatePeriod($start, DateInterval::createFromDateString('1 day'), $end);
echo "<table border=1><tr>";
foreach ($months as $month){
echo "<td>".$month->format("M");
foreach ($days as $day)
{
if($month->format("M")==$day->format("M")){$d++;}
}
echo " (".$d." days)</td>";
unset($d);
}
echo "</tr></table>";
?>
The output is:
Feb (18 days) Mar (31 days) Apr (22 days)
Unfortunately it "forgots" the last day of April in some way (must be 23 days like stated in $end).
Why does the code not count/include the last day?
Do i need to add something like "plus 1 day" to the $end date?
Thank you guys!
Your end date has timestamp 00:00:00 which means, according to the DateTime interval object, the day hasn't actually started yet. If you change your $end to $end = (new DateTime('2019-04-23 01:00:00')); it will include that day as well.
Also you should probably add $d = 0; in your foreach loop for months. Right now you don't define it. (Which defaults it to 0, so it still works. But it's better to define it yourself.)
I have to disagree with Dirks answer. Using \DateTime a day starts at zero (00:00:00).
Testable with:
<?php
$start = new \DateTimeImmutable('2019-02-10 23:59:57');
for ($i = 0; $i < 7; ++$i) {
echo $start->format('c'), "\n";
$start = $start->modify('+1 second');
}
Which shows (for my timezone):
2019-02-10T23:59:57+01:00
2019-02-10T23:59:58+01:00
2019-02-10T23:59:59+01:00
2019-02-11T00:00:00+01:00
2019-02-11T00:00:01+01:00
2019-02-11T00:00:02+01:00
2019-02-11T00:00:03+01:00
https://3v4l.org/8Q3kb
The problem with your code, is that \DatePeriod excludes the end date. And as such the inner loops runs a day short.
<?php
$start = new \DateTimeImmutable('2019-02-11');
$end = $start->modify('+3 days');
$period = new \DatePeriod($start, new \DateInterval('P1D'), $end);
foreach ($period as $date) {
echo $date->format('c'), "\n";
}
echo 'actual end date: ', $end->format('c');
# 2019-02-11T00:00:00+01:00
# 2019-02-12T00:00:00+01:00
# 2019-02-13T00:00:00+01:00
# actual end date: 2019-02-14T00:00:00+01:00
But overall the approach is a bit excessive. For example, the following gives the same results with (imo) better semantics and only a minimum of looping:
<?php
/**
* #param DateTimeImmutable $start
* #param DateTimeImmutable $end
*
* #return Generator
*/
function remainingDaysPerMonthBetween(\DateTimeImmutable $start, \DateTimeImmutable $end): \Generator {
while ($start < $end) {
$diff = $start->diff(min(
$start->modify('last day of this month'),
$end
));
yield [$start, $diff->days];
$start = $start->modify('first day of next month');
}
}
$start = new \DateTimeImmutable('2019-02-11');
$end = new \DateTimeImmutable('2019-04-23');
foreach (remainingDaysPerMonthBetween($start, $end) as [$date, $remainingDays]) {
printf("%s: %d\n", $date->format('M'), $remainingDays + 1);
}
https://3v4l.org/BCAYq

How to section a date period monthly

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);

Count month between two dates and insert result into mysql database

The following code working well on browser:
<?php
$start = (new DateTime('2010-12-02'))->modify('first day of this month');
$end = (new DateTime('2012-05-06'))->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt)
{
echo $dt->format("Y-m"). "<br>\n";
}
?>
and give the following result on browser:
2010-12 2011-01 2011-02 2011-03 2011-04 2011-05 2011-06 2011-07
2011-08 2011-09 2011-10 2011-11 2011-12 2012-01 2012-02 2012-03
2012-04 2012-05
In phrunner i tried to insert the above data (which is multiple data) into one row in mysql but only the first data inserted (2010-12):
...
foreach ($period as $dt)
{
$sql = "INSERT INTO table2 (payment) values ('".$data->format("Y-m")."')";
CustomQuery($sql);
}
?>

PHP DateInterval

I have this function witch return an array of date. I need to jump on every seven days from now until last year.
$date[] = $lastDate = (new \DateTIme('NOW'))->format('Y-m-d');
for ($i = 1; $i < 54; ++$i) { // 54 -> number of weeks in a year
$date[] = $lastDate = date('Y-m-d', strtotime('-7 day', strtotime($lastDate)));
}
return array_reverse($date);
It works but I can do better.
I would like to change it because using 54 for the number of weeks in a year is not very good. (it can change)
So I want to use the DateInterval php class.
I can have the date of the last year with :
$lastYear = date('Y-m-d', strtotime('-1 year', strtotime($lastDate)));
But I don't know how I can have my array with all my dates with the DateInterval class.
Can someone help me? I'm very bad with date manipulation :( ...
Here is an example array about what I need:
["2015-07-06", "2015-07-13", "2015-07-20", "2015-07-27", "2015-08-03", "2015-08-10", "2015-08-17", "2015-08-24", "2015-08-31", "2015-09-07", "2015-09-14", "2015-09-21", "2015-09-28", "2015-10-05", "2015-10-12", "2015-10-19", "2015-10-26", "2015-11-02", "2015-11-09", "2015-11-16", "2015-11-23", "2015-11-30", "2015-12-07", "2015-12-14", "2015-12-21", "2015-12-28", "2016-01-04", "2016-01-11", "2016-01-18", "2016-01-25", "2016-02-01", "2016-02-08", "2016-02-15", "2016-02-22", "2016-02-29", "2016-03-07", "2016-03-14", "2016-03-21", "2016-03-28", "2016-04-04", "2016-04-11", "2016-04-18", "2016-04-25", "2016-05-02", "2016-05-09", "2016-05-16", "2016-05-23", "2016-05-30", "2016-06-06", "2016-06-13", "2016-06-20", "2016-06-27", "2016-07-04"]
PHP got it 's own native DateInterval object. Here 's a short example how to use it.
$oPeriodStart = new DateTime();
$oPeriodEnd = new DateTime('+12 months');
$oPeriod = new DatePeriod(
$oPeriodStart,
DateInterval::createFromDateString('7 days'),
$oPeriodEnd
);
foreach ($oPeriod as $oInterval) {
var_dump($oInterval->format('Y-m-d));
}
So what we 've done here? For a period of dates you need a start date, an end date and the interval. Just test it for yourself. Have fun.
Try this:
$timestamp = strtotime("last Sunday");
$sundays = array();
$last_year_timestamp = strtotime("-1 year ",$timestamp);
while($timestamp >= $last_year_timestamp) {
if (date("w", $timestamp) == 0) {
$sundays[] = date("Y-m-d", $timestamp);
$timestamp -= 86400*7;
continue;
}
$timestamp -= 86400;
}

calculate sundays between two dates

I want to calculate all Sunday's between given two dates. I tried following code. It works fine if days are less but if i enter more days. It keeps processing and Maximum execution time exceeds i changed the time but it even keeps processing even execution time is 200sec.
code is
<?php
$one="2013-01-01";
$two="2013-02-30";
$no=0;
for($i=$one;$i<=$two;$i++)
{
$day=date("N",strtotime($i));
if($day==7)
{
$no++;
}
}
echo $no;
?>
please help.
John Conde's answer is correct, but here is a more efficient and mathy solution:
$start = new DateTime('2013-01-06');
$end = new DateTime('2013-01-20');
$days = $start->diff($end, true)->days;
$sundays = intval($days / 7) + ($start->format('N') + $days % 7 >= 7);
echo $sundays;
Let me break it down for you.
$start = new DateTime('2013-01-06');
$end = new DateTime('2013-01-20');
First, create some DateTime objects, which are powerful built-in PHP objects meant for exactly this kind of problem.
$days = $start->diff($end, true)->days;
Next, use DateTime::diff to find the difference from $start to $end (passing true here as the second parameter ensures that this value is always positive), and get the number of days between them.
$sundays = intval($days / 7) + ($start->format('N') + $days % 7 >= 7);
Here comes the big one - but it's not so complicated, really. First, we know there is one Sunday for every week, so we have at least $days / 7 Sundays to begin with, rounded down to the nearest int with intval.
On top of that, there could be a Sunday in a span of time less than a week; for example, Friday to Monday of the next week contains 4 days; one of them is a Sunday. So, depending on when we start and end, there could be another. This is easy to account for:
$start->format('N') (see DateTime::format) gives us the ISO-8601 day of the week for the start date, which is a number from 1 to 7 (1 is Monday, 7 is Sunday).
$days % 7 gives us the number of leftover days that don't divide evenly into weeks.
If our starting day and the number of leftover days add up to 7 or more, then we reached a Sunday. Knowing that, we just have to add that expression, which will give us 1 if it's true or 0 if it's false, since we're adding it to an int value.
And there you have it! The advantage of this method is that it doesn't require iterating over every day between the given times and checking to see if it's a Sunday, which will save you a lot computation, and also it will make you look really clever. Hope that helps!
<?php
$no = 0;
$start = new DateTime('2013-01-01');
$end = new DateTime('2013-04-30');
$interval = DateInterval::createFromDateString('1 day');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt)
{
if ($dt->format('N') == 7)
{
$no++;
}
}
echo $no;
See it in action
Here is a solution if you want the Sundays in a specific date range.
function dateRange($begin, $end, $interval = null)
{
$begin = new DateTime($begin);
$end = new DateTime($end);
$end = $end->modify('+1 day');
$interval = new DateInterval($interval ? $interval : 'P1D');
return iterator_to_array(new DatePeriod($begin, $interval, $end));
}
/* define date range */
$dates = dateRange('2018-03-01', '2018-03-31');
/* define weekdays */
$weekends = array_filter($dates, function ($date) {
$day = $date->format("N");
return $day === '6' || $day === '7';
});
/* weekdays output */
foreach ($weekends as $date) {
echo $date->format("D Y-m-d") . "</br>";
}
/* define sundays */
$sundays = array_filter($dates, function ($date) {
return $date->format("N") === '7';
});
/* sundays output */
foreach ($sundays as $date) {
echo $date->format("D Y-m-d") . "</br>";
}
/* define mondays */
$mondays = array_filter($dates, function ($date) {
return $date->format("N") === '1';
});
/* mondays output */
foreach ($mondays as $date) {
echo $date->format("D Y-m-d") . "</br>";
}
Just change the number for any days you want in your output:
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
Sunday = 7

Categories