Php Date Returning Unexpected Week Range - php

Php is returning an unexpected set of week dates given a specific day. For example, given the date 2015-05-20, the days of the week that should be returned are:
array:7 [
0 => "2015-05-17"
1 => "2015-05-18"
2 => "2015-05-19"
3 => "2015-05-20"
4 => "2015-05-21"
5 => "2015-05-22"
6 => "2015-05-23"
]
Or in plain text May 17th to 23rd, 2015. From what I can tell this is correct. Now, given the date 2015-05-17, I get the following results:
array:7 [
0 => "2015-05-10"
1 => "2015-05-11"
2 => "2015-05-12"
3 => "2015-05-13"
4 => "2015-05-14"
5 => "2015-05-15"
6 => "2015-05-16"
]
Which you will notice is the week before the 17th, or May 11th to 16th, 2015.
Here is the function that is pulling my weekdays:
$days_of_week = array();
for($i = 0; $i <= 6; $i ++){
$days_of_week[] = date("Y-m-d", strtotime(date("o", strtotime($date))."W".date("W", strtotime($date)).$i));
}
Basically, for the 7 days in a given week number (which is 20 in this case), push the formatted value to an array (as shown in the two examples above.) Pre-formatted, the values being pushed are:
2015W200
2015W201
2015W202
2015W203
2015W204
2015W205
2015W206
// Year: 2015, Week: 20, Day(In week): 0-6
I think the issue I'm facing is that ISO-8601 week numbers start from Monday and end at Sunday, but I'm trying to pull a week that starts with Sunday and ends with Saturday (for display on a calendar.) Is there another way to accomplish this?

In fact it's a expected result.
2015-05-17 is in 20th week.
Thus days and week day number are respectively: (10 11, 12, 13, 14, 15, 16) and (0, 1, 2, 3, 4, 5, 6)
See Localized Notations section on docs, in format compounds:
Notation
ISO year with ISO week and day:
YY "-"? "W" W "-"? [0-7]
Where the last token stays for a week day number, starting with sunday to saturday.
So when you loop over week numbers you're getting:
2015W20[0] -> That is, a date on sunday (2015-05-10)
2015W20[1] -> That is, a date on monday (2015-05-18)
So you must especify the correct week number of year:
$days_of_week = array();
$week_number = 21;
$year = date('Y');
for($day = 0; $day <= 6; $day++)
{
$format = $year. "W" . $week_number.$day;
print $format . "\n";
array_push($days_of_week, date('Y-m-d', strtotime($format)));
}
print_r($days_of_week);

I figured my problem out. Ignoring ISO-8601 week numbers (1-53), I was able to get a range of seven days based on a given date`s week number (0 Sunday - 6 Saturday).
$day = new DateTime($date); // 2015-05-21
$start = $day->modify("- ".$day->format("w")." days"); // - 4 days - 2015-05-17
$end = clone $start;
$end = $start->modify("+ 7 days"); // 2015-05-24 (foreach ignores last day)
$interval = new DateInterval("P1D");
$week = new DatePeriod($start, $interval, $end);
foreach($week as $key => $val) {
$days_of_week[] = date("Y-m-d", strtotime($val->format("Y-m-d")));
}
This method correctly returns an array of seven days, starting on Sunday and ending on Saturday, based on a given date. Thanks to #Rizier123 for the backbone of this answer.

Related

Laravel 5.4 get every day of the current week using Carbon

I've been using the syntax Carbon::now()->startOfWeek() and Carbon::now()->endOfWeek() for awhile now.
It returns the first day of the week which is the date of Monday and the last day of the week which is the date of Sunday. (I don't know why it wasn't Sunday and Saturday)
But now, I want to get every day of the current week. So what's left is the dates of Tuesday, Wednesday, Thursday, Friday, and Saturday.
Here's my exact syntax on getting Monday and Sunday.
$monday = Carbon::now()->startOfWeek();
$sunday = Carbon::now()->endOfWeek();
You can progress through the week with addDay().
$monday = Carbon::now()->startOfWeek();
$tuesday = $monday->copy()->addDay();
$wednesday = $tuesday->copy()->addDay();
You can also check which day of the week you have.
$wednesday === Carbon::WEDNESDAY; // true
If you want to get the current week of a spesific day try with this
1.- create a carbon day with the day that you need
$carbaoDay = Carbon::createFromFormat('Y-m-d', $request->day);
//spesific day format 2000-01-00
2 aftert into a for loop just push a day
$carbaoDay->startOfWeek()->addDay($i)->format('Y-m-d');
$carbaoDay->startOfWeek() /// always monday
->addDay($i)->format('Y-m-d'); //$i =1 push: 2000-01-01 ,;//$i =2 push: 2000-01-02
$carbaoDay = Carbon::createFromFormat('Y-m-d', $request->day); //spesific day
$week = [];
for ($i=0; $i <7 ; $i++) {
$week[] = $carbaoDay->startOfWeek()->addDay($i)->format('Y-m-d');//push the current day and plus the mount of $i
}
output:
array:7 [
0 => "2020-01-06"
1 => "2020-01-07"
2 => "2020-01-08"
3 => "2020-01-09"
4 => "2020-01-10"
5 => "2020-01-11"
6 => "2020-01-12"
]

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

PHP How to extract first days of the weeks from a Month from Unix timestamp

There is this Unix timestamp and it needs to generate the first days of the week as an array.
$time = '1456034400';
// This present Month February 2016
// in calendar the February has the start of the week
// Sunday 7
// Sunday 14
// Sunday 21
// Sunday 28
How do you get an array like this from the Unix Timestamp:
$weekdays = array(
0 => 7,
1 => 14,
2 => 21,
3 => 28
);
And this method needs to work and be accurate for any given month in years not just Feb 2016.
function getSundays($y, $m)
{
return new DatePeriod(
new DateTime("first Sunday of $y-$m"),
DateInterval::createFromDateString('next sunday'),
new DateTime("last day of $y-$m")
);
}
$days="";
foreach (getSundays(2016, 04) as $Sunday) {
$days[] = $Sunday->format("d");
}
var_dump($days);
https://3v4l.org/DVYM5
A bit faster way (since it uses a simple calculation to iterate weeks):
$time = 1456034400;
$firstDay = strtotime('first Sunday of '.date('M',$time).' '.date('Y',$time));
$lastDay = mktime(0,0,0,date('m',$time)+1,1,date('Y', $time));
$weekdays = array();
for ($i = $firstDay; $i < $lastDay; $i += 7*24*3600){
$weekdays[] = date('d',$i);
}
https://3v4l.org/lQkB2/perf#tabs

Repeatable events php

I have array() of week days to repeat my event.
[days_repeat] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
1,2,3 are the events should be repeated every monday, tuesday, wednesday, but I have also
[start_date] => 2014-04-2
[end_date] => 2014-05-30
As you can see start day isn't the first day to repeat, if it was then was easier, just loop and make dates and break loop, when date is bigger than end date, but maybe you can suggest me some good tactics to achieve this
Days repeat array can contain only week day numbers and so!
First, convert start_date to a timestamp.
Then using php date() function as such :
echo date('w', $your_timestamp);
You get to know what day is the day of first_date, 0 (for Sunday) through 6 (for Saturday).
Then just loop and go through your days checking if they match your array.
Try this:
$daysRepeat = array(1,2,3);
$startDate = '2014-04-02';
$endDate = '2014-05-30';
//Convert to timestamps;
$startDateTstamp = strtotime($startDate);
$endDateTstamp = strtotime($endDate);
//Loop through days between start and end date
$tstamp = $startDateTstamp;
while($tstamp < $endDateTstamp) {
//Calculate numerical representation of week day (1-7)
$weekDay = date('N', $tstamp);
if(in_array($weekDay, $daysRepeat)) {
print date('Y-m-d', $tstamp) . "<br />";
}
$tstamp += (60*60*24);
}
Output:
2014-04-02
2014-04-07
2014-04-08
2014-04-09
2014-04-14
2014-04-15
2014-04-16
2014-04-21
2014-04-22
2014-04-23
2014-04-28
2014-04-29
2014-04-30
2014-05-05
2014-05-06
2014-05-07
2014-05-12
2014-05-13
2014-05-14
2014-05-19
2014-05-20
2014-05-21
2014-05-26
2014-05-27
2014-05-28
if your data represents the days within a week (so first day of the week till the seventh day of the week), than you just need to check the current displayed day, if its the same "daynumber"
eg.
echo (new DateTime())->format("w");
will print
1
for monday
see the docs for more informations.
http://www.php.net/manual/en/function.date.php
so youll need only to check, if the current daynumber is in your repeat array!
You can achieve this with this code:
<?php
$days_repeat = array(
'1', // Monday
'2', // Tuesday
'3', // Wednesday
);
// first set timezone
date_default_timezone_set('UTC');
// Start date
$date = '2014-04-02';
// End date
$end_date = '2014-05-30';
while (strtotime($date) <= strtotime($end_date)) {
// first find out which day of the week it is (0=sunday; 1=monday ... 6=saturday)
$dateWeekDay = date("w", $date);
// convert sunday from 0 to 7 if needed
if ($dateWeekDay == 0) { $dateWeekDay = 7; }
// check if $days_repeat has current day
if (in_array($dateWeekDay, $days_repeat)) {
// do action as this day is in $days_repeat
echo date("Y-m-d"). ' is in $days_repeat';
}
// add +1 day and continue loop
$date = date("Y-m-d", strtotime("+1 day", strtotime($date)));
}
?>
Conveniently, you are representing the day of the week according to the ISO-8601 specification. This means you can use PHP's built-in date() function with no additional translation to your format. That's nice.
Next, you need to figure out how to convert your start_date and end_date into a date format PHP can understand. It looks like mktime() is your best bet here - initialize the time elements to 0 if you don't care about them.
You can now iterate by day and if date("N", $currentDay) is in your array of wanted days, repeat the event on that day (whatever that entails). Here, $currentDay is the looped timestamp you're checking. date("N") returns the same ISO-8601 format day-of-week number as specified in the manual page for date().

Return dates of current calendar week as array in PHP5

How would I put together a PHP5 function that would find the current calendar week and return the dates of each day in the week as an array, starting on Monday? For example, if the function were run today (Thu Feb 25 2010), the function would return an array like:
[0] => Mon Feb 22 2010<br />
[1] => Tue Feb 23 2010<br />
[2] => Wed Feb 24 2010<br />
[3] => Thu Feb 25 2010<br />
[4] => Fri Feb 26 2010<br />
[5] => Sat Feb 27 2010<br />
[6] => Sun Feb 28 2010<br />
It doesn't matter what format the dates are stored as in the array, as I assume that'd be very easy to change. Also, it'd be nice to optionally be able to supply a date as a parameter and get the calendar week of that date instead of the current one.
Thanks!
I suppose a solution would be to start by getting the timestamp that correspond to last monday, using strtotime :
$timestampFirstDay = strtotime('last monday');
But if you try with today (thursday), with something like this :
$timestampFirstDay = strtotime('last thursday');
var_dump(date('Y-m-d', $timestampFirstDay));
you'll get :
string '2010-02-18' (length=10)
i.e. last week... For strtotime, "last" means "the one before today".
Which mean you'll have to test if today is "last monday" as returned by strtotime plus one week -- and, if so, add one week...
Here's a possible (there are probably smarter ideas) solution :
$timestampFirstDay = strtotime('last monday');
if (date('Y-m-d', $timestampFirstDay) == date('Y-m-d', time() - 7*24*3600)) {
// we are that day... => add one week
$timestampFirstDay += 7 * 24 * 3600;
}
And now that we have the timestamp of "last monday", we can write a simple for loop that loops 7 times, adding 1 day each time, like this :
$currentDay = $timestampFirstDay;
for ($i = 0 ; $i < 7 ; $i++) {
echo date('Y-m-d', $currentDay) . '<br />';
$currentDay += 24 * 3600;
}
Which will give us this kind of output :
2010-02-22
2010-02-23
2010-02-24
2010-02-25
2010-02-26
2010-02-27
2010-02-28
Now, up to you to :
Modify that for loop so it stores the dates in an array
Decide which format you want to use for the date function
Have fun ;-)
Lessee. . First, to avoid any timezone issues I'd peg to noon on the day you want to compare against, so an hour either way won't cause any problems.
$dateCompare = time(); // replace with a parameter
$dateCompare = mktime(12, 0, 0, date('n', $dateCompare),
date('j', $dateCompare), date('Y', $dateCompare));
Then find the day of week of your date.
$dow = date('N', $dateCompare);
1 is Monday, so figure out how many days from Monday we are.
$days = $dow - 1;
Now subtract days worth of seconds until you get back to Monday.
$dateCompare -= (3600 * 24 * $days);
Now assemble your array of days.
$output = array();
for ($x = 0; $x < 7; $x++) {
$output[$x] = $dateCompare + ($x * 24 * 3600);
}
This is an array of timestamps, but you could store the dates as strings if you prefer.

Categories