I know about the unwanted behaviour of PHP's function
strtotime
For example, when adding a month (+1 month) to dates like: 31.01.2011 -> 03.03.2011
I know it's not officially a PHP bug, and that this solution has some arguments behind it, but at least for me, this behavior has caused a lot waste of time (in the past and present) and I personally hate it.
What I found even stranger is that for example in:
MySQL: DATE_ADD('2011-01-31', INTERVAL 1 MONTH) returns 2011-02-28
or
C# where new DateTime(2011, 01, 31).AddMonths(1); will return 28.02.2011
wolframalpha.com giving 31.01.2013 + 1 month as input; will return Thursday, February 28, 2013
It sees to me that others have found a more decent solution to the stupid question that I saw alot in PHP bug reports "what day will it be, if I say we meet in a month from now" or something like that. The answer is: if 31 does not exists in next month, get me the last day of that month, but please stick to next month.
So MY QUESTION IS: is there a PHP function (written by somebody) that resolves this not officially recognized bug? As I don't think I am the only one who wants another behavior when adding / subtracting months.
I am particulary interested in solutions what also work not just for the end of the month, but a complete replacement of strtotime. Also the case strotime +n months should be also dealt with.
Happy coding!
what you need is to tell PHP to be smarter
$the_date = strtotime('31.01.2011');
echo date('r', strtotime('last day of next month', $the_date));
$the_date = strtotime('31.03.2011');
echo date('r', strtotime('last day of next month', $the_date));
assuming you are only interesting on the last day of next month
reference - http://www.php.net/manual/en/datetime.formats.relative.php
PHP devs surely don't consider this as bug. But in strtotime's docs there are few comments with solutions for your problem (look for 28th Feb examples ;)), i.e. this one extending DateTime class:
<?php
// this will give us 2010-02-28 ()
echo PHPDateTime::DateNextMonth(strftime('%F', strtotime("2010-01-31 00:00:00")), 31);
?>
Class PHPDateTime:
<?php
/**
* IA FrameWork
* #package: Classes & Object Oriented Programming
* #subpackage: Date & Time Manipulation
* #author: ItsAsh <ash at itsash dot co dot uk>
*/
final class PHPDateTime extends DateTime {
// Public Methods
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Calculate time difference between two dates
* ...
*/
public static function TimeDifference($date1, $date2)
$date1 = is_int($date1) ? $date1 : strtotime($date1);
$date2 = is_int($date2) ? $date2 : strtotime($date2);
if (($date1 !== false) && ($date2 !== false)) {
if ($date2 >= $date1) {
$diff = ($date2 - $date1);
if ($days = intval((floor($diff / 86400))))
$diff %= 86400;
if ($hours = intval((floor($diff / 3600))))
$diff %= 3600;
if ($minutes = intval((floor($diff / 60))))
$diff %= 60;
return array($days, $hours, $minutes, intval($diff));
}
}
return false;
}
/**
* Formatted time difference between two dates
*
* ...
*/
public static function StringTimeDifference($date1, $date2) {
$i = array();
list($d, $h, $m, $s) = (array) self::TimeDifference($date1, $date2);
if ($d > 0)
$i[] = sprintf('%d Days', $d);
if ($h > 0)
$i[] = sprintf('%d Hours', $h);
if (($d == 0) && ($m > 0))
$i[] = sprintf('%d Minutes', $m);
if (($h == 0) && ($s > 0))
$i[] = sprintf('%d Seconds', $s);
return count($i) ? implode(' ', $i) : 'Just Now';
}
/**
* Calculate the date next month
*
* ...
*/
public static function DateNextMonth($now, $date = 0) {
$mdate = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
list($y, $m, $d) = explode('-', (is_int($now) ? strftime('%F', $now) : $now));
if ($date)
$d = $date;
if (++$m == 2)
$d = (($y % 4) === 0) ? (($d <= 29) ? $d : 29) : (($d <= 28) ? $d : 28);
else
$d = ($d <= $mdate[$m]) ? $d : $mdate[$m];
return strftime('%F', mktime(0, 0, 0, $m, $d, $y));
}
}
?>
Here's the algorithm you can use. It should be simple enough to implement yourself.
Have the original date and the +1 month date in variables
Extract the month part of both variables
If the difference is greater than 1 month (or if the original is December and the other is not January) change the latter variable to the last day of the next month. You can use for example t in date() to get the last day: date( 't.m.Y' )
Had the same issue recently and ended up writing a class that handles adding/subtracting various time intervals to DateTime objects.
Here's the code:
https://gist.github.com/pavlepredic/6220041#file-gistfile1-php
I've been using this class for a while and it seems to work fine, but I'm really interested in some peer review. What you do is create a TimeInterval object (in your case, you would specify 1 month as the interval) and then call addToDate() method, making sure you set $preventMonthOverflow argument to true. The code will make sure that the resulting date does not overflow into next month.
Sample usage:
$int = new TimeInterval(1, TimeInterval::MONTH);
$date = date_create('2013-01-31');
$future = $int->addToDate($date, true);
echo $future->format('Y-m-d');
Resulting date is:
2013-02-28
Here is an implementation of an improved version of Juhana's answer above:
<?php
function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
$addMon = clone $currentDate;
$addMon->add(new DateInterval("P1M"));
$nextMon = clone $currentDate;
$nextMon->modify("last day of next month");
if ($addMon->format("n") == $nextMon->format("n")) {
$recurDay = $createdDate->format("j");
$daysInMon = $addMon->format("t");
$currentDay = $currentDate->format("j");
if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
$addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
}
return $addMon;
} else {
return $nextMon;
}
}
This version takes $createdDate under the presumption that you are dealing with a recurring monthly period, such as a subscription, that started on a specific date, such as the 31st. It always takes $createdDate so late "recurs on" dates won't shift to lower values as they are pushed forward thru lesser-valued months (e.g., so all 29th, 30th or 31st recur dates won't eventually get stuck on the 28th after passing thru a non-leap-year February).
Here is some driver code to test the algorithm:
$createdDate = new DateTime("2015-03-31");
echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;
$next = sameDateNextMonth($createdDate, $createdDate);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
foreach(range(1, 12) as $i) {
$next = sameDateNextMonth($createdDate, $next);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
}
Which outputs:
created date = 2015-03-31
next date = 2015-04-30
next date = 2015-05-31
next date = 2015-06-30
next date = 2015-07-31
next date = 2015-08-31
next date = 2015-09-30
next date = 2015-10-31
next date = 2015-11-30
next date = 2015-12-31
next date = 2016-01-31
next date = 2016-02-29
next date = 2016-03-31
next date = 2016-04-30
I have solved it by this way:
$startDate = date("Y-m-d");
$month = date("m",strtotime($startDate));
$nextmonth = date("m",strtotime("$startDate +1 month"));
if((($nextmonth-$month) > 1) || ($month == 12 && $nextmonth != 1))
{
$nextDate = date( 't.m.Y',strtotime("$initialDate +1 week"));
}else
{
$nextDate = date("Y-m-d",strtotime("$initialDate +1 month"));
}
echo $nextDate;
Somewhat similar to the Juhana's answer but more intuitive and less complications expected. Idea is like this:
Store original date and the +n month(s) date in variables
Extract the day part of both variables
If days do not match, subtract number of days from the future date
Plus side of this solution is that works for any date (not just the border dates) and it also works for subtracting months (by putting - instead of +).
Here is an example implementation:
$start = mktime(0,0,0,1,31,2015);
for ($contract = 0; $contract < 12; $contract++) {
$end = strtotime('+ ' . $contract . ' months', $start);
if (date('d', $start) != date('d', $end)) {
$end = strtotime('- ' . date('d', $end) . ' days', $end);
}
echo date('d-m-Y', $end) . '|';
}
And the output is following:
31-01-2015|28-02-2015|31-03-2015|30-04-2015|31-05-2015|30-06-2015|31-07-2015|31-08-2015|30-09-2015|31-10-2015|30-11-2015|31-12-2015|
function ldom($m,$y){
//return tha last date of a given month based on the month and the year
//(factors in leap years)
$first_day= strtotime (date($m.'/1/'.$y));
$next_month = date('m',strtotime ( '+32 day' , $first_day)) ;
$last_day= strtotime ( '-1 day' , strtotime (date($next_month.'/1/'.$y)) ) ;
return $last_day;
}
I'm not sure how to go about this one. I'm building a calendar in PHP and need users to be able to add a repeating event that follows the following rule:
Last [DOW] of the month (so Last [Mon/Tues/Wed/etc] of the week)
I've got the rule itself stored, I'm just not sure how best to extrapolate the last Mon/Tue/Wed of the month in PHP? I fell like i'm making this more complicated than it needs to be.
Assuming you have variables for $month=4, $dow=3 and $year=2011 how would I best do this?
For everything date-related that you can express in proper English but have a hard time expressing using numbers, strtotime is your best friend.
echo strtotime("last Monday of June 2011");
This returns a timestamp that you can use as the second parameter to date and the likes to get a proper, human-readable date. Since it's a built-in function written in C, this solution is also much faster than almost anything else you could come up with written in PHP (though I'm quite sure it wouldn't matter much in a real-world scenario).
So assuming you have $month=4, $dow=3 and $year=2011, you'll need an array mapping $month values to their English textual representations and another array mapping $dow values to their textual representations.
Here's an alternative:
<?
function lastDayOfMonth($month, $year) {
switch ($month) {
case 2:
# if year is divisible by 4 and not divisible by 100
if (($year % 4 == 0) && ($year % 100) > 0)
return 29;
# or if year is divisible by 400
if ($year % 400 == 0)
return 29;
return 28;
case 4:
case 6:
case 9:
case 11:
return 30;
default:
return 31;
}
}
function lastDayOfWeek($month, $year, $dow) {
$d = new DateTime();
#Finding the last day of month
$d = $d->setDate($year, $month, lastDayOfMonth($month, $year));
#Getting the day of week of last day of month
$date_parts = getdate($d->getTimestamp());
$diff = 0;
#if we can't find the $dow in this week... (cause it would lie on next month)
if ($dow > $date_parts['wday']) {
# ...we go back a week.
$diff -= 7;
}
return $date_parts['mday'] + $diff + ($dow - $date_parts['wday']);
}
# checking the algorithm for this month...
for ($i=0; $i < 7; $i++) {
echo lastDayOfWeek(6,2011,$i) . "<br>";
}
?>
I have a generic function for you to calculate the nth day of a month. Hope this could help you to resolve your issue.
function get_Nth_dow($dow, $occurence, $m, $y)
{
$numdays = date('t', mktime(0, 0, 0, $m, 1, $y));
$add = 7 * ($occurence - 1);
$firstdow = date('w', mktime(0, 0, 0, $m, 1, $y));
$diff = $firstdow - $dow;
$day_of_month = 1;
if ($diff > 0)
{
$day_of_month += ($add - $diff);
}
elseif ($diff < $numdays)
{
$day_of_month -= ($diff - $add);
}
return $day_of_month;
}
$DOW = day of the week (0 = Sunday, 6 = Saturday).
$X = occurence (1 = first, 2 = third, etc..). If the given month does
not have the occurrence, then it will return the last. For example, if
you ask for the 7th occurrence of Friday, it will return the last
Friday of the month.
$M = month
$Y = year
Example, get_Nth_DOW(2,3,7,2009) will return the third Tuesday of 7th 2009.
How do I go about getting all the work days (mon-fri) in a given time period (let's say, today till the end of the next month) ?
If you're using PHP 5.2+ you can use the library I wrote in order to handle date recursion in PHP called When.
With the library, the code would be something like:
$r = new When();
$r->recur(<start date here>, 'weekly')
->until(<end date here>)
->wkst('SU')
->byday(array('MO', 'TU', 'WE', 'TH', 'FR'));
while($result = $r->next())
{
echo $result->format('c') . '<br />';
}
This sample does exactly what you need, in an quick and efficient way.
It doesn't do nested loops and uses the totally awesome DateTime object.
$oDateTime = new DateTime();
$oDayIncrease = new DateInterval("P1D");
$aWeekDays = array();
$sStart = $oDateTime->format("m-Y");
while($oDateTime->format("m-Y") == $sStart) {
$iDayInWeek = $oDateTime->format("w");
if ($iDayInWeek > 0 && $iDayInWeek < 6) {
$aWeekDays[] = clone $oDateTime;
}
$oDateTime->add($oDayIncrease);
}
Try it here: http://codepad.org/wuAyAqnF
To use it, simply pass a timestamp to get_weekdays. You'll get back an array of all the weekdays, as timestamps, for the rest of the current month. Optionally, you can pass a $to argument - you will get all weekdays between $from and $to.
function get_weekdays ($from, $to=false) {
if ($to == false)
$to = last_day_of_month($from);
$days = array();
for ($x = $from; $x < $to; $x+=86400 ) {
if (date('w', $x) > 0 && date('w', $x) < 6)
$days[] = $x;
}
return $days;
}
function last_day_of_month($ts=false) {
$m = date('m', $ts);
$y = date('y', $ts);
return mktime(23, 59, 59, ($m+1), 0, $y);
}
I suppose you could loop through the dates and check the day for each one, and increment a counter.
Can't think of anything else off the top of my head.
Pseudocode coming your way:
Calculate the number of days between now and the last day of the month
Get the current day of the week (i.e. Wednesday)
Based on the current day of the week, and the number of days left in the month, it's simple calculation to figure out how many weekend days are left in the month - it's going to be the number of days remaining in the month, minus the number of Sundays/Saturdays left in the month.
I would write a function, something like:
daysLeftInMonth(daysLeftInMonth, startingDayOfWeek, dayOfWeekToCalculate)
where:
daysLeftInMonth is last day of the month (30), minus the current date (15)
startingDayOfWeek is the day of the week you want to start on (for today it would be Wednesday)
dayOfWeekToCalculate is the day of the week you want to count, e.g. Saturday or Sunday. June 2011 currently has 2 Sunday, and 2 Saturdays left 'til the end of the month
So, your algorithm becomes something like:
getWeekdaysLeft(todaysDate)
...getWeekdaysLeft is something like:
sundaysLeft = daysLeftInMonth(lastDayOfMonth - todaysDate, "Wednesday", "Sunday");
saturdaysLeft = daysLeftInMonth(lastDayOfMonth - todaysDate, "Wednesday", "Saturday");
return ((lastDayOfMonth - todaysDate) - (sundaysLeft + saturdaysLeft));
This code does at least one part you ask for. Instead of "end of next month" it simply works with a given number of days.
$dfrom = time();
$fourweeks = 7 * 4;
for ($i = 0; $i < $fourweeks; $i ++) {
$stamp = $dfrom + ($i * 24 * 60 * 60);
$weekday = date("D", $stamp);
if (in_array($weekday, array("Mon", "Tue", "Wed", "Thu", "Fri"))) {
print date(DATE_RSS, $stamp) . "\n";
}
}
// Find today's day of the month (i.e. 15)
$today = intval(date('d'));
// Define the array that will hold the work days.
$work_days = array()
// Find this month's last day. (i.e. 30)
$last = intval(date('d', strtotime('last day of this month')));
// Loop through all of the days between today and the last day of the month (i.e. 15 through 30)
for ( $i = $today; $i <= $last; $i++ )
{
// Create a timestamp.
$timestamp = mktime(null, null, null, null, $i);
// If the day of the week is greater than Sunday (0) but less than Saturday (6), add the timestamp to an array.
if ( intval(date('w', $timestamp)) > 0 && intval(date('w', $timestamp)) < 6 )
$work_days[] = mktime($timestamp);
}
The $work_days array will contain timestamps which you could use this way:
echo date('Y-m-d', $work_days[0]);
The code above with work in PHP 4 as well as PHP 5. It does not rely on the functionality of the DateTime class which was not available until PHP 5.2 and does not require the use of "libraries" created by other people.
So I have a script that returns the number of weeks in a particular month and year. How can I take a specific day from that month and determine if it is part of week 1,2,3,4 or 5 of that month?
The most frustrating thing I have ever tried to get working - but here it is!
<?php
/**
* Returns the amount of weeks into the month a date is
* #param $date a YYYY-MM-DD formatted date
* #param $rollover The day on which the week rolls over
*/
function getWeeks($date, $rollover)
{
$cut = substr($date, 0, 8);
$daylen = 86400;
$timestamp = strtotime($date);
$first = strtotime($cut . "00");
$elapsed = ($timestamp - $first) / $daylen;
$weeks = 1;
for ($i = 1; $i <= $elapsed; $i++)
{
$dayfind = $cut . (strlen($i) < 2 ? '0' . $i : $i);
$daytimestamp = strtotime($dayfind);
$day = strtolower(date("l", $daytimestamp));
if($day == strtolower($rollover)) $weeks ++;
}
return $weeks;
}
//
echo getWeeks("2011-06-11", "sunday"); //outputs 2, for the second week of the month
?>
Edit: so much for "single line" - needed variables to avoid recomputation with the conditional. Tossed in a default argument while I was at it.
function weekOfMonth($when = null) {
if ($when === null) $when = time();
$week = date('W', $when); // note that ISO weeks start on Monday
$firstWeekOfMonth = date('W', strtotime(date('Y-m-01', $when)));
return 1 + ($week < $firstWeekOfMonth ? $week : $week - $firstWeekOfMonth);
}
Please note that weekOfMonth(strtotime('Oct 31, 2011')); will return 6; some rare months have 6 weeks in them, contrary to OP's expectation. January 2017 is another month with 6 ISO weeks - Sunday the 1st falls in the last year's week, since ISO weeks start on Monday.
For starshine531, to return a 0 indexed week of the month, change the return 1 + to return 0 + or return (int).
For Justin Stayton, for weeks starting on Sunday instead of Monday I would use strftime('%U' instead of date('W', as follows:
function weekOfMonth($when = null) {
if ($when === null) $when = time();
$week = strftime('%U', $when); // weeks start on Sunday
$firstWeekOfMonth = strftime('%U', strtotime(date('Y-m-01', $when)));
return 1 + ($week < $firstWeekOfMonth ? $week : $week - $firstWeekOfMonth);
}
For this version, 2017-04-30 is now in week 6 of April, while 2017-01-31 is now in week 5.
public function getWeeks($timestamp)
{
$maxday = date("t",$timestamp);
$thismonth = getdate($timestamp);
$timeStamp = mktime(0,0,0,$thismonth['mon'],1,$thismonth['year']); //Create time stamp of the first day from the give date.
$startday = date('w',$timeStamp); //get first day of the given month
$day = $thismonth['mday'];
$weeks = 0;
$week_num = 0;
for ($i=0; $i<($maxday+$startday); $i++) {
if(($i % 7) == 0){
$weeks++;
}
if($day == ($i - $startday + 1)){
$week_num = $weeks;
}
}
return $week_num;
}
Hello all i have been struggling for the whole day trying to figure this code out, i finally figured it out so i thought i would share it with you all.
all you need to do is put a time stamp into the function and it will return the week number back to you.
thanks
there is a problem with this method. if the passing date (Lets say 2012/01/01 which is a Sunday) and "$rollover" day is "Sunday", then this function will return 2. where its actually is 1'st week. i think i have fixed it in following function.
please add comments to make it better.
function getWeeks($date, $rollover)
{
$cut = substr($date, 0, 8);
$daylen = 86400;
$timestamp = strtotime($date);
$first = strtotime($cut . "01");
$elapsed = (($timestamp - $first) / $daylen)+1;
$i = 1;
$weeks = 0;
for($i==1; $i<=$elapsed; $i++)
{
$dayfind = $cut . (strlen($i) < 2 ? '0' . $i : $i);
$daytimestamp = strtotime($dayfind);
$day = strtolower(date("l", $daytimestamp));
if($day == strtolower($rollover))
{
$weeks++;
}
}
if($weeks==0)
{
$weeks++;
}
return $weeks;
}
This is a solution based on sberry's mathematical solution but using the PHP DateTime class instead.
function week_of_month($date) {
$first_of_month = new DateObject($date->format('Y/m/1'));
$day_of_first = $first_of_month->format('N');
$day_of_month = $date->format('j');
return floor(($day_of_first + $day_of_month - 1) / 7) + 1;
}
Just Copy and Past the code and pass month and year.
e.g month=04 year=2013.
That's exactly what You Need.
$mm= $_REQUEST['month'];
$yy= $_REQUEST['year'];
$startdate=date($yy."-".$mm."-01") ;
$current_date=date('Y-m-t');
$ld= cal_days_in_month(CAL_GREGORIAN, $mm, $yy);
$lastday=$yy.'-'.$mm.'-'.$ld;
$start_date = date('Y-m-d', strtotime($startdate));
$end_date = date('Y-m-d', strtotime($lastday));
$end_date1 = date('Y-m-d', strtotime($lastday." + 6 days"));
$count_week=0;
$week_array = array();
for($date = $start_date; $date <= $end_date1; $date = date('Y-m-d', strtotime($date. ' + 7 days')))
{
$getarray=getWeekDates($date, $start_date, $end_date);
echo "<br>";
$week_array[]=$getarray;
echo "\n";
$count_week++;
}
// its give the number of week for the given month and year
echo $count_week;
//print_r($week_array);
function getWeekDates($date, $start_date, $end_date)
{
$week = date('W', strtotime($date));
$year = date('Y', strtotime($date));
$from = date("Y-m-d", strtotime("{$year}-W{$week}+1"));
if($from < $start_date) $from = $start_date;
$to = date("Y-m-d", strtotime("{$year}-W{$week}-6"));
if($to > $end_date) $to = $end_date;
$array1 = array(
"ssdate" => $from,
"eedate" => $to,
);
return $array1;
// echo "Start Date-->".$from."End Date -->".$to;
}
for($i=0;$i<$count_week;$i++)
{
$start= $week_array[$i]['ssdate'];
echo "--";
$week_array[$i]['eedate'];
echo "<br>";
}
OUTPUT:
week( 0 )=>2013-03-01---2013-03-02
week( 1 )=>2013-03-03---2013-03-09
week( 2 )=>2013-03-10---2013-03-16
week( 3 )=>2013-03-17---2013-03-23
week( 4 )=>2013-03-24---2013-03-30
week( 5 )=>2013-03-31---2013-03-31
I think I found an elegant solution
$time = time(); // or whenever
$week_of_the_month = ceil(date('d', $time)/7);
For a Monday-Sunday (ISO 8601) week (or, if you simply don't care), you can do this in one line:
function get_week_of_month($date) {
return date('W', $date) - date('W', strtotime(date("Y-m-01", $date))) + 1;
}
(Source)
For anything else, (e.g. a Sunday-Saturday week), you just need to tweak $date inside the function:
function get_week_of_month($date) {
$date += 86400; //For weeks starting on Sunday
return date('W', $date) - date('W', strtotime(date("Y-m-01", $date))) + 1;
}
(Thanks to these guys/gals)
NOTE: You may run into some issues at the end of the year (e.g. around 12/31, 1/1, etc.). Read more here.
This is the snippet that I made to fulfill my requirements for the same. Hope this will help you.
function getWeek($timestamp) {
$week_year = date('W',$timestamp);
$week = 0;//date('d',$timestamp)/7;
$year = date('Y',$timestamp);
$month = date('m',$timestamp);
$day = date('d',$timestamp);
$prev_month = date('m',$timestamp) -1;
if($month != 1 ){
$last_day_prev = $year."-".$prev_month."-1";
$last_day_prev = date('t',strtotime($last_day_prev));
$week_year_last_mon = date('W',strtotime($year."-".$prev_month."-".$last_day_prev));
$week_year_first_this = date('W',strtotime($year."-".$month."-1"));
if($week_year_first_this == $week_year_last_mon){
$week_diff = 0;
}
else{
$week_diff = 1;
}
if($week_year ==1 && $month == 12 ){
// to handle December's last two days coming in first week of January
$week_year = 53;
}
$week = $week_year-$week_year_last_mon + 1 +$week_diff;
}
else{
// to handle first three days January coming in last week of December.
$week_year_first_this = date('W',strtotime($year."-01-1"));
if($week_year_first_this ==52 || $week_year_first_this ==53){
if($week_year == 52 || $week_year == 53){
$week =1;
}
else{
$week = $week_year + 1;
}
}
else{
$week = $week_year;
}
}
return $week;
}
This is probably not a good way to do this but it's my first thought and I'm really tired.
Put all your dates into an array. The date object must have a day name (Monday). Create a method that searches the array and when ever you hit a Sunday you add 1 to a week counter. Once you find the date you're looking for return the week counter. That is the week the day falls in of the year. For the week in the month you have to reset the week counter every time you get to the last day in each month.
Here comes two liner:
function getWeekOfMonth(DateTime $date) {
$firstDayOfMonth = new DateTime($date->format('Y-m-1'));
return ceil(($firstDayOfMonth->format('N') + $date->format('j') - 1) / 7);
}
And Wtower's solutions doesn't work 100% properly.
Thought I'd share my function as well. This returns an array of weeks. Every week is an array with weeks day (0..6) as key and months day (1..31) as value.
Function assumes that week starts with Sunday.
Enjoy!
function get_weeks($year, $month){
$days_in_month = date("t", mktime(0, 0, 0, $month, 1, $year));
$weeks_in_month = 1;
$weeks = array();
//loop through month
for ($day=1; $day<=$days_in_month; $day++) {
$week_day = date("w", mktime(0, 0, 0, $month, $day, $year));//0..6 starting sunday
$weeks[$weeks_in_month][$week_day] = $day;
if ($week_day == 6) {
$weeks_in_month++;
}
}
return $weeks;
}
My 5 cents:
/**
* calculate number of weeks in a particular month
*/
function weeksInMonth($month=null,$year=null){
if( null==($year) ) {
$year = date("Y",time());
}
if(null==($month)) {
$month = date("m",time());
}
// find number of days in this month
$daysInMonths = date('t',strtotime($year.'-'.$month.'-01'));
$numOfweeks = ($daysInMonths%7==0?0:1) + intval($daysInMonths/7);
$monthEndingDay= date('N',strtotime($year.'-'.$month.'-'.$daysInMonths));
$monthStartDay = date('N',strtotime($year.'-'.$month.'-01'));
if($monthEndingDay<$monthStartDay){
$numOfweeks++;
}
return $numOfweeks;
}
I create this function, from brazil :) I hope it is useful
function weekofmonth($time) {
$firstday = 1;
$lastday = date('j',$time);
$lastdayweek = 6; //Saturday
$week = 1;
for ($day=1;$day<=$lastday;$day++) {
$timetmp = mktime(0, 0, 0, date('n',$time), $day, date('Y',$time));
if (date('N',$timetmp) == $lastdayweek) {
$week++;
}
}
if (date('N',$time)==$lastdayweek) {
$week--;
}
return $week;
}
$time = mktime(0, 0, 0, 9, 30, 2014);
echo weekofmonth($time);
I found a easy way to determine what week of the month today is in, and it would be a small change to have it work on any other date. I'm adding my two cents in here as I think my way is much more compact then the methods listed.
$monthstart = date("N",strtotime(date("n/1/Y")));
$date =( date("j")+$monthstart ) /7;
$ddate= floor( $date );
if($ddate != date) {$ddate++;}
and $ddate contains the week number you could modify it like so
function findweek($indate)
{
$monthstart = date("N",strtotime(date("n/1/Y",strtotime($indate))));
$date =( date("j",strtotime($indate))+$monthstart ) /7;
$ddate= floor( $date );
if($ddate != $date) {$ddate++;}
return $ddate;
}
and it would return what week of the month any date you give it is.
what it does is first find the number of days from the start of the week to the first of the month. then adds that on to the current date then divides the new date by 7 and that will give you how many weeks have passed since the start of the month, including a decimal place for the part of the the current week that has passed. so what I do next is round down that number, then compare the rounded down version to the original if the two match your at the end of the week so it's already in the number. if they don't then just add one to the rounded down number and voila you have the current week number.
Srahul07's solution works perfectly... If you abide by the Monday-Sunday week system! Here in 'murica, non-business folk tend to go by Sunday-Saturday being a week, so May 1, 2011 is week 1 and May 2, 2011 is still week 1.
Adding the following logic to the bottom of his function, right before it returns $week will convert this to a Sunday -> Monday system:
if (!date('w',strtotime("$year-$month-01")) && date('w',$timestamp))
$week--;
elseif (date('w',strtotime("$year-$month-01")) && !date('w',$timestamp))
$week++;
After alot of efoort i found the solution
<?php
function getWeeks($month,$year)
{
$month = intval($month); //force month to single integer if '0x'
$suff = array('st','nd','rd','th','th','th'); //week suffixes
$end = date('t',mktime(0,0,0,$month,1,$year)); //last date day of month: 28 - 31
$start = date('w',mktime(0,0,0,$month,1,$year)); //1st day of month: 0 - 6 (Sun - Sat)
$last = 7 - $start; //get last day date (Sat) of first week
$noweeks = ceil((($end - ($last + 1))/7) + 1); //total no. weeks in month
$output = ""; //initialize string
$monthlabel = str_pad($month, 2, '0', STR_PAD_LEFT);
for($x=1;$x<$noweeks+1;$x++)
{
if($x == 1)
{
$startdate = "$year-$monthlabel-01";
$day = $last - 6;
}
else
{
$day = $last + 1 + (($x-2)*7);
$day = str_pad($day, 2, '0', STR_PAD_LEFT);
$startdate = "$year-$monthlabel-$day";
}
if($x == $noweeks)
{
$enddate = "$year-$monthlabel-$end";
}
else
{
$dayend = $day + 6;
$dayend = str_pad($dayend, 2, '0', STR_PAD_LEFT);
$enddate = "$year-$monthlabel-$dayend";
}
$j=1;
if($j--)
{
$k=getTotalDate($startdate,$enddate);
$j=1;
}
$output .= "Week ".$xyz." week -> Start date=$startdate End date=$enddate <br />";
}
return $output;
}
if(isset($_POST) && !empty($_POST)){
$month = $_POST['m'];
$year = $_POST['y'];
echo getWeeks($month,$year);
}
?>
<form method="post">
M:
<input name="m" value="" />
Y:
<input name="y" value="" />
<input type="submit" value="go" />
</form>
I really liked #michaelc's answer. However, I got stuck on a few points. It seemed that every time Sunday rolled around, there was an offset of one. I think it has to do with what day of the week is the start of the week. In any case, here is my slight alteration to it, expanded a bit for readability:
function wom(\DateTime $date) {
// The week of the year of the current month
$cw = date('W', $date->getTimestamp());
// The week of the year of the first of the given month
$fw = date('W',strtotime(date('Y-m-01',$date->getTimeStamp())));
// Offset
$o = 1;
// If it is a Saturday, offset by two.
if( date('N',$date->getTimestamp()) == 7 ) {
$o = 2;
}
return $cw -$fw + $o;
}
So if the date is Nov. 9, 2013...
$cw = 45
$fw = 44
and with the offset of 1, it correctly returns 2.
If the date is Nov. 10, 2013, $cw and $fw are the same as before, but the offset is 2, and it correctly returns 3.
function get_week_of_month( $timestamp )
{
$week_of_month = 0;
$month = date( 'j', $timestamp );
$test_month = $month;
while( $test_month == $month )
{
$week_of_month++;
$timestamp = strtotime( '-1 week', $timestamp );
$test_month = date( 'j', $timestamp );
}
return $week_of_month;
}
I found this online:
http://kcwebprogrammers.blogspot.de/2009/03/current-week-in-month-php.html
He has a very simple solution which seems to work fine for me.
$currentWeek = ceiling((date("d") - date("w") - 1) / 7) + 1;
So for example:
$now = strtotime("today");
$weekOfMonth = ceil((date("d", $now) - date("w", $now) - 1) / 7) + 1;
you can use W in newer php versions. http://php.net/manual/en/function.date.php
i have used it like so:
function getWeek($date) {
$month_start=strtotime("1 ".date('F Y',$date));
$current_date=strtotime(date('j F Y',$date));
$month_week=date("W",$month_start);
$current_week=date("W",$current_date);
return ($current_week-$month_week);
}//0 is the week of the first.
Short and foolproof:
// Function accepts $date as a string,
// Returns the week number in which the given date falls.
// Assumed week starts on Sunday.
function wom($date) {
$date = strtotime($date);
$weeknoofday = date('w', $date);
$day = date('j', $date);
$weekofmonth = ceil(($day + (7-($weeknoofday+1))) / 7);
return $weekofmonth;
}
// Test
foreach (range(1, 31) as $day) {
$test_date = "2015-01-" . str_pad($day, 2, '0', STR_PAD_LEFT);
echo "$test_date - ";
echo wom($test_date) . "\n";
}
I use this simple function:
function weekNumberInMonth($timestampDate)
{
$firstDayOfMonth = strtotime(date('01-M-Y 00:00:00', $timestampDate));
$firstWeekdayOfMonth = date( 'w', $firstDayOfMonth);
$dayNumberInMonth = date('d', $timestampDate);
$weekNumberInMonth = ceil(($dayNumberInMonth + $firstWeekdayOfMonth) / 7);
return $weekNumberInMonth;
}
if I understand correct, the question is how to identify what number of week within a month of a specific day... I was looking for similar solution. I used some ideas of above answers to develop my own solution. Hope it can be helpful for somebody. If Yes, then UpVote my answer.
function week_number_within_month($datenew){
$year = date("Y",strtotime($datenew));
$month = date("m",strtotime($datenew));
// find number of days in this month
$daysInMonths = date('t',strtotime($year.'-'.$month.'-01'));
$numOfweeks = ($daysInMonths%7==0?0:1) + intval($daysInMonths/7);
$monthEndingDay= date('N',strtotime($year.'-'.$month.'-'.$daysInMonths));
$monthStartDay = date('N',strtotime($year.'-'.$month.'-01'));
if($monthEndingDay<$monthStartDay){
$numOfweeks++;
}
$date=date('Y/m/d', strtotime($year.'-'. $month.'-01'));
$week_array=Array();
for ($i=1; $i<=$numOfweeks; $i++){ /// create an Array of all days of month separated by weeks as a keys
$max = 7;
if ($i ==1){ $max = 8 - $monthStartDay;}
if ($i == $numOfweeks){ $max = $monthEndingDay;}
for ($r=1; $r<=$max; $r++){
$week_array[$i][]=$date;
$date = date('Y/m/d',strtotime($date . "+1 days"));
}
}
$new_datenew = date('Y/m/d', strtotime($datenew));
$week_result='';
foreach ($week_array as $key => $val){ /// finding what week number of my date from week_array
foreach ($val as $kr => $value){
if ($new_datenew == $value){
$week_result = $key;
}
}
}
return $week_result;
}
print week_number_within_month('2016-09-15');
function getWeekOfMonth(\DateTime $date)
{
$firstWeekdayOfMonth = new DateTime("first weekday 0 {$date->format('M')} {$date->format('Y')}");
$offset = $firstWeekdayOfMonth->format('N')-1;
return intval(($date->format('j') + $offset)/7)+1;
}
/**
* In case of Week we can get the week of year. So whenever we will get the week of the month then we have to
* subtract the until last month weeks from it will give us the current month week.
*/
$dateComponents = getdate();
if($dateComponents['mon'] == 1)
$weekOfMonth = date('W', strtotime($dateComponents['year'].'-'.$dateComponents['mon'].'-'.$dateComponents['mday']))-1; // We subtract -1 to map it to the array
else
$weekOfMonth = date('W', strtotime($dateComponents['year'].'-'.$dateComponents['mon'].'-'.$dateComponents['mday']))-date('W', strtotime($dateComponents['year'].'-'.$dateComponents['mon'].'-01'));
Using Carbon:
$date = Carbon::now();
$d1 = $date->startOfMonth();
$d2 = $date->endOfMonth();
$weeks = $d1->diffInWeeks($d2);
If you clearly want to separate a month into 4 Weeks, you can use this function.
This is helpful, if you want
"the first monday of month"
"the third thursday of month" etc.
Here we go
/**
* This Calculates (and returns) the week number within a month, based on date('j') day of month.
* This is useful, if you want to have (for instance) the first Thu in month, regardless of date
* #param $Timestamp
* #return float|int
*/
function getWeekOfMonth($Timestamp)
{
$DayOfMonth=date('j', $Timestamp); // Day of the month without leading zeros 0-31
if($DayOfMonth>21) return 4;
if($DayOfMonth>14) return 3;
if($DayOfMonth>7) return 2;
return 1;
}
From carbon:
return (int) ceil((new Datetime())->format('d') / 7);
As simple as possible :)
Python: Number of the Week in a Month
This is a worked example in Python - should be simple to convert.