I have a start date of 20090101 and an end date of 20091130 and I'm trying to build and array of all the months in between, which would look like this:
<?php
/* ... */
$arrDates['Jan'] = 2009;
$arrDates['Feb'] = 2009;
$arrDates['Mar'] = 2009;
/* ... */
?>
How can I do this?
I don't fully understand your array structure.
But maybe this helps: When using PHP 5.3 you can use code like below to get an iterator with all months in the given range:
<?php
$db = new DateTime( '2009-01-01 00:00:00' );
$de = new DateTime( '2009-11-30 23:59:59' );
$di = DateInterval::createFromDateString( 'first day of next month' );
foreach ( $di as $dt )
{
echo $dt->format( "Y-m\n" );
}
?>
The following snippet creates such an array:
$startDate = '20090101';
$endDate = '20091130';
$arrDates = array();
$cur = strtotime($startDate);
$end = strtotime($endDate);
while ($cur < $end) {
$arrDates[date('M', $cur)] = date('Y', $cur);
$cur = mktime(0, 0, 0, date('m', $cur) + 1, 1, date('Y', $cur));
}
// If you want to add the 'end' month too...
$arrDates[date('M', $end)] = date('Y', $end);
However, note that, as danii commented, you are unclear about how you want to handle a timespan that is larger than a year. The code above will simply use the last year in the range you provide.
This code will work with pretty much any version of PHP (PHP 4+). If you want a more elegant solution and are using PHP 5.2+, I recommend the solution offered by GZipp.
I had a similar situation for a website i was building for travelagency. You need timestamps, arrays and looping. Take a look at the date function PHP provides. It gives you some interesting options to play with dates. E.g. the no. of days in a specified month.
You say "the months in between", but since your example includes the starting month, I assume you mean "the months in between plus the starting and ending months".
$dt_start = new DateTime('20090101');
$dt_end = new DateTime('20091130');
$arrDates[] = $dt_start->format('M');
while ($dt_start->modify('first day of next month') <= $dt_end) {
$arrDates[] = $dt_start->format('M'); // Or whatever you want to do with it.
}
(This is essentially johannes' solution with a little manual reading applied to adapt it for PHP 5.2.)
You can't use the month as a key, the key must be unique or if your range spans more than a year it won't work correctly.
This will return an array with Mon-Year as the key
function foo($startdate, $enddate) {
// create a timestamp for start date
if(!preg_match('/^(\d{4})(\d{2})(\d{2})$/', $startdate, $m)) die('Invalid start date format');
$start_time = mktime(0, 0, 0, $m[2], $m[3], $m[1]);
// create a timestamp for end date
if(!preg_match('/^(\d{4})(\d{2})(\d{2})$/', $enddate, $m)) die('Invalid end date format');
$end_time = mktime(23, 59, 59, $m[2], $m[3], $m[1]);
// build the array of months by incrementing $start_time by one month through each iteration
$ret = array();
while($start_time < $end_time) {
$ret[date('M-Y', $start_time)] = date('Y', $start_time);
$start_time = strtotime(date('m/d/Y', $start_time).' +1month');
}
return $ret;
}
$arrDates = foo('20090101', '20111130');
print_r($arrDates);
Array(
[Jan-2009] => 2009
[Feb-2009] => 2009
[Mar-2009] => 2009
[Apr-2009] => 2009
[May-2009] => 2009
[Jun-2009] => 2009
[Jul-2009] => 2009
[Aug-2009] => 2009
....
)
A bit convoluted but works...:
function buildDateRange($strStartDate, $strEndDate)
{
$strStartM = date('M', $strStartDate);
$strStartY = date('Y', $strStartDate);
$strEndM = date('M', $strEndDate);
$strEndY = date('Y', $strEndDate);
$intCurMN = date('m', $strStartDate);
$ii = 0;
$blnFinished = FALSE;
while(!$blnFinished)
{
$strCurM = date('M', mktime(0, 0, 0, $intCurMN , "01", $strStartY));
$strCurY = date('Y' ,mktime(0, 0, 0, $intCurMN , "01", $strStartY));
$arrSearchDates [$strCurM] = $strCurY;
$intCurMN = date('m', mktime(0, 0, 0, $intCurMN+1 , "01", $strStartY));
if($strEndM == $strCurM && $strEndY == $strCurY)
{
$blnFinished = TRUE;
}
}
Return ($arrSearchDates);
}
Related
I've been using the following function fine until the other day when the clocks went forward:
function months($month_format="F") {
$array = array();
for ($i = 1; $i <=12; $i++) {
$array[$i]['string'] = date($month_format, mktime(0,0,0,$i));
$array[$i]['int'] = date('m', mktime(0,0,0,$i));
}
return $array;
}
It outputs an array with:
string[]
int[]
Since the other day (like 2 days ago, when the clocks went forward in the UK), the function seems to be showing me 2 march months and 0 Februarys....
Very very strange...
I'm using rethinkdb with their eachPosTime function... not sure if this is causing it.
I tried using a different function, but still using mktime:
function months(){
$start_month = 1;
$end_month = 12;
$start_year = date("Y");
$array = array();
for($m=$start_month; $m<=12; ++$m){
echo $m.'<br>';
$array[$m]['string'] = date('F', mktime(0,0,0,$m));
$array[$m]['int'] = date('m', mktime(0,0,0,$m));
if($start_month == 12 && $m==12 && $end_month < 12)
{
$m = 0;
$start_year = $start_year+1;
}
//echo date('F Y', mktime(0, 0, 0, $m, 1, $start_year)).'<br>';
if($m == $end_month) break;
}
return $array;
}
Still, I am having no luck.
Check the image here, which shows the output of the months() function:
Output of the months() function
This is not to do with the clocks changing, but the time of the month, and the entirely unhelpful signature of the mktime function.
When you leave out parameters from a "mktime" call, they are filled in with today's date. In your case, you specified month without day or year, so when you ask for mktime(0, 0, 0, 2); it will fill in today's day and year, and look for the 29th February 2021 - a day that doesn't exist. Since the time library "overflows" dates, this becomes the 1st March.
The solution is to pass an explicit day to "mktime", as in mktime(0,0,0,$m,1) or to use a less confusing function.
I have a data like this:
$date = '01-01-2014';
$time = '15:20:00';
$location = 'New Delhi';
$recursive = '1';
.........................
......................... // other data
$recursive = 1 means weekly and 2 means monthly.
Now what i am tring to do is if recursive type is weekly then add 7 days into it till 3 months and if recursive type is monthly then add 1 month into it till 3 months.
Exa:1 $date = '01-01-2014' and $recursive = '1'
Means in above example $recursive is weekly, so get a recursive date for January, February and March.
So the expected results are:
01-01-2014, 08-01-2014, 15-01-2014, 22-01-2014, 29-01-2014 (recursiive date in january)
05-02-2014, 12-02-2014, 19-02-2014, 26-02-2014 (recursiive date in february)
05-03-2014, 12-03-2014, 19-03-2014, 26-03-2014 (recursiive date in march)
Exa 2: $date = 15-04-2014 and $recursive = 1 then get recursive date for April, May and June.
output:
15-04-2014,22-04-2014,29-04-2014 (recursive date in april)
06-05-2014,13-05-2014,20-05-2014,27-05-2014 (recursive date in may)
03-06-2014,10-06-2014,17-06-2014,24-06-2014 (recursive date in june)
Exa 3 : $date = 01-01-2014 and $recursive = 2 then get recursive date for April, May and June.
This is monthly recursive, means add 1 month into it.
output:
01-01-2014
01-02-2014 (recursiive date in february)
01-03-2014 (recursiive date in march)
then i want to insert these dates with other data into database table.
so how to achive above things? should i write logic in php or use mysql query for it?
Thanks in advance.
SIDENOTE: currently i am using this accepted answer. but now i am trying to change that.
so basically what you want to do is something like this.
//you have to have a default time zone set.. so i just did this you should already have it in your .ini file
date_default_timezone_set('America/Los_Angeles');
$date = '01-01-2014';
$time = '15:20:00';
$location = 'New Delhi';
$recursive = '1';
//set your start date and end date
$startdate = date_create($date);
$enddate = date_create($date);
$enddate = date_add($enddate,date_interval_create_from_date_string("3 months"));
//set the interval string
if($recursive == '1'){
$str = "7 days";
} else {
$str = "1 month";
}
function recursivefunc($str, $start, $end){
//if the start is equal or bigger than end pop out.
$s = date_format($start,"Y/m/d");
$e = date_format($end,"Y/m/d");
if(strtotime($s) >= strtotime($e)){
return 1;
}
echo date_format($start,"Y/m/d"), '<br>'; //print out the starting date for each loop
$newDate = date_add($start,date_interval_create_from_date_string($str)); //increment the start
recursiveFunc($str, $newDate, $end); //call the function again
}
recursiveFunc($str, $startdate, $enddate); // initial call to the function
This is how i solve it.
$date = '01-01-2014';
$location = 'New Delhi';
$start = strtotime("+2 months", strtotime($date)); //start date
$end_date = date("Y-m-t", $start); // last day of end month
$end = strtotime($end_date); //last date
/* if recursion type is weekly */
if($json['recurrence'] == "1")
{
for($dd=$start; $dd<=$end;)
{
$new_date = date('Y-m-d', $dd);
$data[] = array(
'date' => $new_date,
'location' => $location
);
$dd = strtotime("+7 day", $dd); // add 7 days
}
}
if($json['recurrence'] == "2")
{
for($dd=$start; $dd<=$end;)
{
$new_date = date('Y-m-d', $dd);
$data[] = array(
'date' => $new_date,
'location' => $location
);
$dd = strtotime("+1 month", $dd); //add 1 month
}
}
echo "<pre>";
print_r($data);
I want to take a date and work out its week number.
So far, I have the following. It is returning 24 when it should be 42.
<?php
$ddate = "2012-10-18";
$duedt = explode("-",$ddate);
$date = mktime(0, 0, 0, $duedt[2], $duedt[1],$duedt[0]);
$week = (int)date('W', $date);
echo "Weeknummer: ".$week;
?>
Is it wrong and a coincidence that the digits are reversed? Or am I nearly there?
Today, using PHP's DateTime objects is better:
<?php
$ddate = "2012-10-18";
$date = new DateTime($ddate);
$week = $date->format("W");
echo "Weeknummer: $week";
It's because in mktime(), it goes like this:
mktime(hour, minute, second, month, day, year);
Hence, your order is wrong.
<?php
$ddate = "2012-10-18";
$duedt = explode("-", $ddate);
$date = mktime(0, 0, 0, $duedt[1], $duedt[2], $duedt[0]);
$week = (int)date('W', $date);
echo "Weeknummer: " . $week;
?>
$date_string = "2012-10-18";
echo "Weeknummer: " . date("W", strtotime($date_string));
Use PHP's date function
http://php.net/manual/en/function.date.php
date("W", $yourdate)
This get today date then tell the week number for the week
<?php
$date=date("W");
echo $date." Week Number";
?>
Just as a suggestion:
<?php echo date("W", strtotime("2012-10-18")); ?>
Might be a little simpler than all that lot.
Other things you could do:
<?php echo date("Weeknumber: W", strtotime("2012-10-18 01:00:00")); ?>
<?php echo date("Weeknumber: W", strtotime($MY_DATE)); ?>
Becomes more difficult when you need year and week.
Try to find out which week is 01.01.2017.
(It is the 52nd week of 2016, which is from Mon 26.12.2016 - Sun 01.01.2017).
After a longer search I found
strftime('%G-%V',strtotime("2017-01-01"))
Result: 2016-52
https://www.php.net/manual/de/function.strftime.php
ISO-8601:1988 week number of the given year, starting with the first week of the year with at least 4 weekdays, with Monday being the start of the week. (01 through 53)
The equivalent in mysql is DATE_FORMAT(date, '%x-%v')
https://www.w3schools.com/sql/func_mysql_date_format.asp
Week where Monday is the first day of the week (01 to 53).
Could not find a corresponding solution with DateTime.
At least not without solutions like "+1day, last monday".
Edit: since strftime is now deprecated, maybe you can also use date.
Didn't verify it though.
date('o-W',strtotime("2017-01-01"));
I have tried to solve this question for years now, I thought I found a shorter solution but had to come back again to the long story. This function gives back the right ISO week notation:
/**
* calcweek("2018-12-31") => 1901
* This function calculates the production weeknumber according to the start on
* monday and with at least 4 days in the new year. Given that the $date has
* the following format Y-m-d then the outcome is and integer.
*
* #author M.S.B. Bachus
*
* #param date-notation PHP "Y-m-d" showing the data as yyyy-mm-dd
* #return integer
**/
function calcweek($date) {
// 1. Convert input to $year, $month, $day
$dateset = strtotime($date);
$year = date("Y", $dateset);
$month = date("m", $dateset);
$day = date("d", $dateset);
$referenceday = getdate(mktime(0,0,0, $month, $day, $year));
$jan1day = getdate(mktime(0,0,0,1,1,$referenceday[year]));
// 2. check if $year is a leapyear
if ( ($year%4==0 && $year%100!=0) || $year%400==0) {
$leapyear = true;
} else {
$leapyear = false;
}
// 3. check if $year-1 is a leapyear
if ( (($year-1)%4==0 && ($year-1)%100!=0) || ($year-1)%400==0 ) {
$leapyearprev = true;
} else {
$leapyearprev = false;
}
// 4. find the dayofyearnumber for y m d
$mnth = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
$dayofyearnumber = $day + $mnth[$month-1];
if ( $leapyear && $month > 2 ) { $dayofyearnumber++; }
// 5. find the jan1weekday for y (monday=1, sunday=7)
$yy = ($year-1)%100;
$c = ($year-1) - $yy;
$g = $yy + intval($yy/4);
$jan1weekday = 1+((((intval($c/100)%4)*5)+$g)%7);
// 6. find the weekday for y m d
$h = $dayofyearnumber + ($jan1weekday-1);
$weekday = 1+(($h-1)%7);
// 7. find if y m d falls in yearnumber y-1, weeknumber 52 or 53
$foundweeknum = false;
if ( $dayofyearnumber <= (8-$jan1weekday) && $jan1weekday > 4 ) {
$yearnumber = $year - 1;
if ( $jan1weekday = 5 || ( $jan1weekday = 6 && $leapyearprev )) {
$weeknumber = 53;
} else {
$weeknumber = 52;
}
$foundweeknum = true;
} else {
$yearnumber = $year;
}
// 8. find if y m d falls in yearnumber y+1, weeknumber 1
if ( $yearnumber == $year && !$foundweeknum) {
if ( $leapyear ) {
$i = 366;
} else {
$i = 365;
}
if ( ($i - $dayofyearnumber) < (4 - $weekday) ) {
$yearnumber = $year + 1;
$weeknumber = 1;
$foundweeknum = true;
}
}
// 9. find if y m d falls in yearnumber y, weeknumber 1 through 53
if ( $yearnumber == $year && !$foundweeknum ) {
$j = $dayofyearnumber + (7 - $weekday) + ($jan1weekday - 1);
$weeknumber = intval( $j/7 );
if ( $jan1weekday > 4 ) { $weeknumber--; }
}
// 10. output iso week number (YYWW)
return ($yearnumber-2000)*100+$weeknumber;
}
I found out that my short solution missed the 2018-12-31 as it gave back 1801 instead of 1901. So I had to put in this long version which is correct.
How about using the IntlGregorianCalendar class?
Requirements: Before you start to use IntlGregorianCalendar make sure that libicu or pecl/intl is installed on the Server.
So run on the CLI:
php -m
If you see intl in the [PHP Modules] list, then you can use IntlGregorianCalendar.
DateTime vs IntlGregorianCalendar:
IntlGregorianCalendar is not better then DateTime. But the good thing about IntlGregorianCalendar is that it will give you the week number as an int.
Example:
$dateTime = new DateTime('21-09-2020 09:00:00');
echo $dateTime->format("W"); // string '39'
$intlCalendar = IntlCalendar::fromDateTime ('21-09-2020 09:00:00');
echo $intlCalendar->get(IntlCalendar::FIELD_WEEK_OF_YEAR); // integer 39
<?php
$ddate = "2012-10-18";
$duedt = explode("-",$ddate);
$date = mktime(0, 0, 0, $duedt[1], $duedt[2],$duedt[0]);
$week = (int)date('W', $date);
echo "Weeknummer: ".$week;
?>
You had the params to mktime wrong - needs to be Month/Day/Year, not Day/Month/Year
To get the week number for a date in North America I do like this:
function week_number($n)
{
$w = date('w', $n);
return 1 + date('z', $n + (6 - $w) * 24 * 3600) / 7;
}
$n = strtotime('2022-12-27');
printf("%s: %d\n", date('D Y-m-d', $n), week_number($n));
and get:
Tue 2022-12-27: 53
for get week number in jalai calendar you can use this:
$weeknumber = date("W"); //number week in year
$dayweek = date("w"); //number day in week
if ($dayweek == "6")
{
$weeknumberint = (int)$weeknumber;
$date2int++;
$weeknumber = (string)$date2int;
}
echo $date2;
result:
15
week number change in saturday
The most of the above given examples create a problem when a year has 53 weeks (like 2020). So every fourth year you will experience a week difference. This code does not:
$thisYear = "2020";
$thisDate = "2020-04-24"; //or any other custom date
$weeknr = date("W", strtotime($thisDate)); //when you want the weeknumber of a specific week, or just enter the weeknumber yourself
$tempDatum = new DateTime();
$tempDatum->setISODate($thisYear, $weeknr);
$tempDatum_start = $tempDatum->format('Y-m-d');
$tempDatum->setISODate($thisYear, $weeknr, 7);
$tempDatum_end = $tempDatum->format('Y-m-d');
echo $tempDatum_start //will output the date of monday
echo $tempDatum_end // will output the date of sunday
Very simple
Just one line:
<?php $date=date("W"); echo "Week " . $date; ?>"
You can also, for example like I needed for a graph, subtract to get the previous week like:
<?php $date=date("W"); echo "Week " . ($date - 1); ?>
Your code will work but you need to flip the 4th and the 5th argument.
I would do it this way
$date_string = "2012-10-18";
$date_int = strtotime($date_string);
$date_date = date($date_int);
$week_number = date('W', $date_date);
echo "Weeknumber: {$week_number}.";
Also, your variable names will be confusing to you after a week of not looking at that code, you should consider reading http://net.tutsplus.com/tutorials/php/why-youre-a-bad-php-programmer/
The rule is that the first week of a year is the week that contains the first Thursday of the year.
I personally use Zend_Date for this kind of calculation and to get the week for today is this simple. They have a lot of other useful functions if you work with dates.
$now = Zend_Date::now();
$week = $now->get(Zend_Date::WEEK);
// 10
To get Correct Week Count for Date 2018-12-31 Please use below Code
$day_count = date('N',strtotime('2018-12-31'));
$week_count = date('W',strtotime('2018-12-31'));
if($week_count=='01' && date('m',strtotime('2018-12-31'))==12){
$yr_count = date('y',strtotime('2018-12-31')) + 1;
}else{
$yr_count = date('y',strtotime('2018-12-31'));
}
function last_monday($date)
{
if (!is_numeric($date))
$date = strtotime($date);
if (date('w', $date) == 1)
return $date;
else
return date('Y-m-d',strtotime('last monday',$date));
}
$date = '2021-01-04'; //Enter custom date
$year = date('Y',strtotime($date));
$date1 = new DateTime($date);
$ldate = last_monday($year."-01-01");
$date2 = new DateTime($ldate);
$diff = $date2->diff($date1)->format("%a");
$diff = $diff/7;
$week = intval($diff) + 1;
echo $week;
//Returns 2.
try this solution
date( 'W', strtotime( "2017-01-01 + 1 day" ) );
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 have been developing on php 5.3.
However our production server is 5.2.6.
I have been using
$schedule = '31/03/2011 01:22 pm'; // example input
if (empty($schedule))
$schedule = date('Y-m-d H:i:s');
else {
$schedule = dateTime::createFromFormat('d/m/Y h:i a', $schedule);
$schedule = $schedule->format('Y-m-d H:i:s');
}
echo $schedule;
However that function is not available in 5.2
What is the easiest way to get around this (no chance of a php upgrade).
just include the next code
function DEFINE_date_create_from_format()
{
function date_create_from_format( $dformat, $dvalue )
{
$schedule = $dvalue;
$schedule_format = str_replace(array('Y','m','d', 'H', 'i','a'),array('%Y','%m','%d', '%I', '%M', '%p' ) ,$dformat);
// %Y, %m and %d correspond to date()'s Y m and d.
// %I corresponds to H, %M to i and %p to a
$ugly = strptime($schedule, $schedule_format);
$ymd = sprintf(
// This is a format string that takes six total decimal
// arguments, then left-pads them with zeros to either
// 4 or 2 characters, as needed
'%04d-%02d-%02d %02d:%02d:%02d',
$ugly['tm_year'] + 1900, // This will be "111", so we need to add 1900.
$ugly['tm_mon'] + 1, // This will be the month minus one, so we add one.
$ugly['tm_mday'],
$ugly['tm_hour'],
$ugly['tm_min'],
$ugly['tm_sec']
);
$new_schedule = new DateTime($ymd);
return $new_schedule;
}
}
if( !function_exists("date_create_from_format") )
DEFINE_date_create_from_format();
Because strtotime does poorly when confronted with D/M/Y and date_create_from_format isn't available, strptime may be your only hope here. It does some pretty oldschool things, like deal with years as if they are the number of years since 1900 and deal with months as if January was month zero. Here's some horrible example code that uses sprintf to reassemble the date into something DateTime understands:
$schedule = '31/03/2011 01:22 pm';
// %Y, %m and %d correspond to date()'s Y m and d.
// %I corresponds to H, %M to i and %p to a
$ugly = strptime($schedule, '%d/%m/%Y %I:%M %p');
$ymd = sprintf(
// This is a format string that takes six total decimal
// arguments, then left-pads them with zeros to either
// 4 or 2 characters, as needed
'%04d-%02d-%02d %02d:%02d:%02d',
$ugly['tm_year'] + 1900, // This will be "111", so we need to add 1900.
$ugly['tm_mon'] + 1, // This will be the month minus one, so we add one.
$ugly['tm_mday'],
$ugly['tm_hour'],
$ugly['tm_min'],
$ugly['tm_sec']
);
echo $ymd;
$new_schedule = new DateTime($ymd);
echo $new_schedule->format('Y-m-d H:i:s');
If it works, you should see the same, correct date and time printed twice.
I think it is much cleaner to extend the DateTime class and implement createFromFormat() yourself like this:-
class MyDateTime extends DateTime
{
public static function createFromFormat($format, $time, $timezone = null)
{
if(!$timezone) $timezone = new DateTimeZone(date_default_timezone_get());
$version = explode('.', phpversion());
if(((int)$version[0] >= 5 && (int)$version[1] >= 2 && (int)$version[2] > 17)){
return parent::createFromFormat($format, $time, $timezone);
}
return new DateTime(date($format, strtotime($time)), $timezone);
}
}
$dateTime = MyDateTime::createFromFormat('Y-m-d', '2013-6-13');
var_dump($dateTime);
var_dump($dateTime->format('Y-m-d'));
This will work in all versions of PHP >= 5.2.0.
See here for a demo http://3v4l.org/djucq
Since this is not really showing how to convert YYYY:DDD:HH:MM:SS time to unix seconds using the "z" option you have to create you own functions to convert the DOY to month and day of month. This is what I did:
function _IsLeapYear ($Year)
{
$LeapYear = 0;
# Leap years are divisible by 4, but not by 100, unless by 400
if ( ( $Year % 4 == 0 ) || ( $Year % 100 == 0 ) || ( $Year % 400 == 0 ) ) {
$LeapYear = 1;
}
return $LeapYear;
}
function _DaysInMonth ($Year, $Month)
{
$DaysInMonth = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
return ((_IsLeapYear($Year) && $Month == 2) ? 29 : $DaysInMonth[$Month - 1]);
}
function yydddhhssmmToTime($Year, $DOY, $Hour, $Min, $Sec)
{
$Day = $DOY;
for ($Month = 1; $Day > _DaysInMonth($Year, $Month); $Month++) {
$Day -= _DaysInMonth($Year, $Month);
}
$DayOfMonth = $Day;
return mktime($Hour, $Min, $Sec, $Month, $DayOfMonth, $Year);
}
$timeSec = yydddhhssmmToTime(2016, 365, 23, 23, 23);
$str = date("m/d/Y H:i:s", $timeSec);
echo "unix seconds: " . $timeis . " " . $str ."<br>";
The output on the page shows its working since I can convert back the seconds back to the original input values.
unix seconds: 1483140203 12/30/2016 23:23:23
$your_datetime_object=new DateTime($date);
$date_format_modified=date_format($your_datetime_object,'D M d Y');//Change the format of date time
I had the similar problem with the production server at 5.2, so I used the above datetime to create an object and then change the format to my liking as above.
Date and Time only
$dateTime = DateTime::createFromFormat('Y-m-d\TH:i:s', '2015-04-20T18:56:42');
ISO8601 no colons
$dateTime = DateTime::createFromFormat('Y-m-d\TH:i:sO', '2015-04-20T18:56:42+0000');
ISO8601 with colons
$date = $dateTime->format('c');
Salesforce ISO8601 format
DateTime::createFromFormat('Y-m-d\TH:i:s.uO', '2015-04-20T18:56:42.000+0000');
Hope this saves someone time!