I'm using this code to get number of days in last month, but if the current month is January (01) it's going to use 00 month instead of 12.
$prev_month_days = cal_days_in_month(CAL_GREGORIAN, date('m')-1, date('Y'));
Searched Google for 20 minutes and could not find any reasonable and shorthand solution.
Please help. Thanks.
<?php
echo date("t", mktime(0,0,0, date("n") - 1));
?>
The example provided by Dan is not correct.
You have to check the result of date("n") and correct for January return
And if this is part of a time function, which seems logical since you are looking for the previous number of days in a month. You will also have to compensate for the year shifting back. Otherwise you would shoot to December of the current year.
What works better is..
$NowYear = date("Y");
$NowMonth = date("n");
if ($NowMonth == 1) {
$ThenMonth = 12;
$ThenYear = $NowYear - 1;
}
else {
$ThenMonth = $NowMonth - 1;
$ThenYear = $NowYear;
}
$ThenNumberOfDays = date("t", mktime(0, 0, 0, $ThenMonth, 1, $ThenYear));
$ThenNumberOfDays would have the correct number of days...
Related
Basically, I have a date set to today. From that date, I want to get the next and previous 20th day of the month. Basically, starting with 07/10/2020 (date of the post in d/m/Y format), I should end up with a date/timestamp to 20/09/2020 (previous 20th) and 20/10/2020 (next 20th).
I tried a few things and ended up with:
$ld_next_str = date("d/m/Y", strtotime("20 day this month"));
$ld_next_dt = DateTime::createFromFormat("d/m/Y", $ld_next_str);
$ld_prev_str = date("d/m/Y", strtotime("20 day last month"));
$ld_prev_dt = DateTime::createFromFormat("d/m/Y", $ld_prev_str);
But turns out this : "20 day this month" just adds 20 days to the current day so right now (on the seventh) it returns the 27th.
Knowing this, I figured I could probably just do something like subtracting the current day to that date (27 - 7 = 20) but I feel like there's probably a simpler way to do it, using strtotime without any extra steps.
Thanks in advance!
PS : I know you can get some days using "first" / "second" / "third" etc... but (I think) it doesn't go any higher than "twelfth", because I tried "twentieth" and that didn't work...
The formula for the previous 20th is:
mktime(0, 0, 0, $month - (1 - floor($day / 20)), 20, $year))
The formula for the next 20th is:
mktime(0, 0, 0, $month + floor($day / 20), 20, $year))
As a demo:
for ($month = 1; $month <= 12; $month++) {
for ($day = 1; $day <= 30; $day += 5) {
$prev = mktime(0, 0, 0, $month - (1 - floor($day / 20)), 20, 2020);
$next = mktime(0, 0, 0, $month + floor($day / 20), 20, 2020);
echo "$day/$month/2020: ", date('d/m/Y', $prev), ', ', date('d/m/Y', $next), PHP_EOL;
}
}
Outputs:
1/1/2020: 20/12/2019, 20/01/2020
6/1/2020: 20/12/2019, 20/01/2020
11/1/2020: 20/12/2019, 20/01/2020
16/1/2020: 20/12/2019, 20/01/2020
21/1/2020: 20/01/2020, 20/02/2020
26/1/2020: 20/01/2020, 20/02/2020
1/2/2020: 20/01/2020, 20/02/2020
6/2/2020: 20/01/2020, 20/02/2020
11/2/2020: 20/01/2020, 20/02/2020
16/2/2020: 20/01/2020, 20/02/2020
21/2/2020: 20/02/2020, 20/03/2020
...
Solution with an external class (a DateTime extension) that supports the cron syntax.
The string "0 0 20 * *" specifies the 20th of every month and every weekday at 00:00.
The nextCron() and previousCron() methods are used to determine the previous/next point in time based on a specific date.
$cronStr = "0 0 20 * *"; //every 20th 00:00
//source on https://github.com/jspit-de/dt
$next20 = dt::create('now')->nextCron($cronStr);
echo $next20; //2020-10-20 00:00:00
$prev20 = dt::create('now')->previousCron($cronStr);
echo $prev20; //2020-09-20 00:00:00
Outputs for the execution on October 8, 2020.
Here a better solution by using DateTime
$lastMonth = (new DateTime("last month"))->format("Y-m-20");
$thisMonth = (new DateTime("this month"))->format("Y-m-20");
$nextMonth = (new DateTime("next month"))->format("Y-m-20");
echo "$lastMonth - $thisMonth - $nextMonth";
I've just discovered a problem with the usually used method to sum months to a PHP Date. If you search on google or this forum, you usually find somethings like these:
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 month");
or
$months = DateInterval::createFromDateString('1 month');
$dateInDateTime->add($months);
Both approach are not correct, in my opinion.
For example in my code I have to increment 3 times the month of a starting date beginning with last day of April and return the last day of that months.
So my code generates this results:
2017-04-30
2017-05-31
2017-07-31
The second time the script add +1 month to date, goes from 2017-05-31 to 2017-07-01 because 31-05 + 30 days is over the last day of JUNE.
What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me.
This is a common error that explode when you manage February or December (due to change of year).
Im expecting a script that increment month. So if I have 2017-03-23 and sum +1 month, I get 2017-04-23 and if I sum +1 month to 2017-03-31 I got 2017-04-30.
So. Pay attention when using this functions.
I think you are trying something dangerous.
What s going on for february? if you want all the time to change only month number it will break for latest days of this month, same for months with 30 days instead of 31...
You have to think about your approach in another way, because changing the month alone won't make an existing date sometimes.
+30 days seems to be the best thing to do
What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me.
PHP corrects it, indeed. It never returns 31st of June as such a date doesn't exist. It corrects it to 1st of July.
What you apparently expect is that when you add 1 month to the last day of a month to get the last day of the next month. But this doesn't make any sense.
What should strtotime('2017-06-30 +1 month') return?
2017-07-31, because you are adding 1 month to the last day of June or 2017-07-30 because you are adding 1 month to the 30th day of June?
The times runs forward, counting the days from the end of the month is not natural. Sometimes it's useful but not that many times. And there always is a better solution: subtract 1 day from the first day of the next month. This way you don't have to do any correction or care about months with different number of days or even about leap years.
This is the function I wrote:
//it accept negative month value
public static function realAddMonthsToDate($month,$dateToModify,
$dateFormatInput = DEFAULT_SQL_DATE_FORMAT, $dateFormatOutput = DEFAULT_SQL_DATE_FORMAT)
{
$currentDate = DateTime::createFromFormat($dateFormatInput, $dateToModify);
$cDay = $currentDate->format('d');
$cMonth = $currentDate->format('m');
$cYear = $currentDate->format('Y');
$monthRest = $month;
$yearOffset = 0;
if ($month > 12)
{
$yearOffset = floor($month / 12);
$monthRest = $month - ($yearOffset * 12);
}
$cMonth += $monthRest;
if ($cMonth > 12) {
$cMonth = $cMonth - 12;
$cYear += 1;
}
if ($cMonth <= 0)
{
$cMonth = 12 + $cMonth;
$cYear -= 1;
}
$cYear += $yearOffset;
$arrivalMonthDays = cal_days_in_month(CAL_GREGORIAN, $cMonth, $cYear);
if ($cDay >= $arrivalMonthDays) $cDay = $arrivalMonthDays;
$newDate = new DateTime($cYear.'-'.$cMonth.'-'.$cDay);
return $newDate->format($dateFormatOutput);
}
I have a calendar that I want to allow events to be repeated on a week day of the month. Some examples would be:
Repeat every 4th Tuesday of the month
Repeat every 2nd Friday of the month
And so on...
What I need is the ability to find out how many week days (for example Tuesday's) have passed this month so far.
I found some code that returns how many Monday's have passed.
$now=time() + 86400;
if (($dow = date('w', $now)) == 0) $dow = 7;
$begin = $now - (86400 * ($dow-1));
echo "Mondays: ".ceil(date('d', $begin) / 7)."<br/>";
This works well but how do I make it so that I can determine any week day? I cannot seem to get my head around the code to make this work.
strtotime is really useful for this kind of thing. Here are lists of the supported syntax. Using your example of repeat every 2nd Friday of the month I wrote the following simple snippet for you:
<?php
$noOfMonthsFromNow=12;
$dayCondition="Second Friday of";
$months = array();
$years = array();
$currentMonth = (int)date('m');
for($i = $currentMonth; $i < $currentMonth+$noOfMonthsFromNow; $i++) {
$months[] = date('F', mktime(0, 0, 0, $i, 1));
$years[] = date('Y', mktime(0, 0, 0, $i, 1));
}
for ($i=0;$i<count($months);$i++){
$d = date_create($dayCondition.' '.$months[$i].' '.$years[$i]);
if($d instanceof DateTime) echo $d->format('l F d Y H:i:s').'<br>';
}
?>
This can be tested at: http://www.phpfiddle.org/lite/
$beginningOfMonth = strtotime(date('Y-m-01')); // this will give you the timestamp of the beginning of the month
$numTuesdaysPassed = 0;
for ($i = 0; $i <= date('d'); $i ++) { // 'd' == current day of month might need to change to = from <= depending on your needs
if (date('w', $beginningOfMonth + 3600 * $i) == 2) $numTuesdaysPassed ++; // 3600 being seconds in a day, 2 being tuesday from the 'w' (sunday == 0)
}
Not sure if this will work, and there's probably a better way to do it; don't have the means to test it right now but hopefully this puts you on the right track! (I get tripped up on date math a bit too, especially with timezones)
I am trying to get stripe to set a end_trial date on the next occurrence of whatever day of the month the user chooses. i.e. If today is the 16th and the user chooses the 15th I need the unix timestamp for the 15th of the next month. However if today was the 14th I need the timestamp for tomorrow.
I tried the solution found on this SO question Find the date for next 15th using php .
When i ran the code suggested in that question and substituted 15 for 31
$nextnth = mktime(0, 0, 0, date('n') + (date('j') >= 31), 31);
echo date('Y-m-d', $nextnth);
The result is 2013-03-03
I also tried this one Get the date of the next occurrence of the 18th .
The second one would actually give me 2013-03-31 when i ran it one 2013-1-31.
Both had unexpected results. Is february the problem? Any guidance will be much appreciated.
Here is a way to do it.
function nextDate($userDay){
$today = date('d'); // today
$target = date('Y-m-'.$userDay); // target day
if($today <= $userDay){
$return = strtotime($target);
}
else{
$thisMonth = date('m') + 1;
$thisYear = date('Y');
if($userDay >= 28 && $thisMonth == 2){
$userDay = 28;
}
while(!checkdate($thisMonth,$userDay,$thisYear)){
$thisMonth++;
if($thisMonth == 13){
$thisMonth = 1;
$thisYear++;
}
}
$return = strtotime($thisYear.'-'.$thisMonth.'-'.$userDay);
}
return $return;
}
// usage
echo date('Y-m-d',nextDate(29));
We get the user's choice and compare it today.
If today is less than or equal to user choice, we return the timestamp for this month.
If today is greater than user choice, we loop through dates, adding a month (or a year if it's $thisMonth hits 13). Once this date does exist again, we have our answer.
We check the dates using php's checkdate function, strtotime and date.
I really don't understand the question completely. You can easily determine the date for next 30 days for example
$next_ts = time() + 30 * 86400; // add 30 days to current timestamp
$next = date('Y-m-d', $next_ts); // format string as Y-m-d
echo $next;
If that is not what you need, please explain the problem.
I need to create next and previous link urls
here's a sample
previous
<? echo $_GET['month'].', '.$_GET['year']; // shows 11, 2009
next
where the 2nd segment is the year and the first segment is the month
I've got the month and the year in the GET array, but any ideas how best to do this?
I was thinking prevmonth = month-1, but then if the previous month was a new year, that would get all messed up.
You can use mktime with out-of-range values to do things like this. See the example #2 on the manual page.
e.g. echo date("M-d-Y", mktime(0, 0, 0, 13, 1, 1997)); will give 1998-01-01.
strtotime() makes it pretty easy.
$year = 2009;
$month = 5;
$nextMonth = strtotime('+1 Month', strtotime($year.'-'.$month.'-01'));
echo date('Y/m', $nextMonth);
Fairly easy to catch that sort of logic...? Just inc/dec the year if your month goes out of bounds...
#sanity check inputs
$month=min(max(intval($_GET['month']), 1),12);
$year=intval($_GET['year']);
$prev=array($month-1, $year);
if ($prev[0]==0)
{
$prev[0]=12;
$prev[1]--;
}
$next=array($month+1, $year);
if ($next[0]==13)
{
$next[0]=1;
$next[1]++;
}
I think the easiest way would just be to check for the "previous month < 1" condition and decrement the year. There might be a more clever way to do this, but this is easy enough to understand:
$prevyear = intval($_GET['year']);
$prevmonth = intval($_GET['month']) - 1;
// Check for year rollover.
if ( $prevmonth < 1 ) {
$prevmonth = 12;
$prevyear = $prevyear - 1;
}