Add 1 month function in php fails when current month has no day 31

Asked

Viewed 650 times

7

In this code PHP, I am checking the type of recurrence of an activity contained in a list of activities, and adding 1 month when it is monthly recurrence, 2 months when it is bimonthly recurrence and etc.

if($recorrencia == "UNICA")
        return;
    elseif($recorrencia == "MENSALMENTE")
        $data_vencimento = date('Y-m-d H:m:s', strtotime('+1 month', strtotime($atividade['DT_VENCIMENTO']))); /*1 mês*/
    elseif($recorrencia == "BIMESTRALMENTE")
        $data_vencimento = date('Y-m-d H:m:s', strtotime('+2 month', strtotime($atividade['DT_VENCIMENTO']))); /*2 mês*/
    elseif($recorrencia == "TRIMESTRALMENTE")
        $data_vencimento = date('Y-m-d H:m:s', strtotime('+3 month', strtotime($atividade['DT_VENCIMENTO']))); /*3 mês*/
    elseif($recorrencia == "SEMESTRALMENTE")
        $data_vencimento = date('Y-m-d H:m:s', strtotime('+6 month', strtotime($atividade['DT_VENCIMENTO']))); /*6 mês*/    
    elseif($recorrencia == "ANUALMENTE")
        $data_vencimento = date('Y-m-d H:m:s', strtotime('+12 month', strtotime($atividade['DT_VENCIMENTO']))); /*12 mês*/

However, the PHP looks like it’s actually adding 30 days, because when I have dates like the following:

31/05/2019

After rotating the function, the date becomes:

01/07/2019

I understand that is why in the month 06 does not have day 31, because all other days add correctly.

How could I get around this problem?

  • 1

    No time to write an answer now, but I am sorry to inform you that you will probably have to make a manual adjustment: https://stackoverflow.com/a/5760371

  • You can’t use days instead of months?

  • Check here https://stackoverflow.com/questions/38226215/how-to-include-the-end-date-in-a-dateperiod . Search for Dateinterval and Dateperiod

  • If it fails in the months of 30, then also in February!?

  • @hkotsubo I will try to implement here something similar to that of the link

  • @Edwardramos would give in the same.

  • @Rbz Yes, exact.

  • @Denied Tested on which versions of PHP?

Show 3 more comments

2 answers

0


I have the following suggestion, which I believe solves your problem clearly.

if($recorrencia == "UNICA") return;

$recorrencias           = [
    'MENSALMENTE'       => 1,
    'BIMESTRALMENTE'    => 2,
    'TRIMESTRALMENTE'   => 3,
    'SEMESTRALMENTE'    => 6,
    'ANUALMENTE'        => 12,
];
$meses                  = $recorrencias[ $recorrencia ];
$data_vencimento        = new \DateTime( $atividade['DT_VENCIMENTO'] );
$ano                    = $data_vencimento->format( 'Y' );
$mes                    = $data_vencimento->format( 'm' );
$dia                    = $data_vencimento->format( 'd' );

// Adiciona a quantidade de meses pretendida, mas define o dia como o último do mês
$data_vencimento->setDate($ano, $mes + $meses + 1, 0);

// Se o dia pretendido for menor que o último dia do mês, então faz a correção
if ( $dia < $data_vencimento->format( 'd' ) ) $data_vencimento->setDate( $ano, $mes + $meses, $dia );

$data_vencimento        = $data_vencimento->format( 'Y-m-d H:i:s' );
  • I followed that reasoning in my solution.

0

First, the question is: what "adding 1 month". 1 month = 30 days? 28 days? 29 days? 31?

The following code:

echo date("Y-m-d", strtotime("2019-01-31 +1 month"));

It has the following result:

2019-03-03

It is correct because the function converts the second part of the date (the month) to the next), which would be 02/31/2019. As there are 31 days in February and this year the month had 28 days, are added the missing days, playing the date for the day 3 March (if you do the same test with 2016, you will see that the final date is 2 March because February had 29 days in 2016).

Therefore, I think you should reformulate your question and/or need.

  • If you want to add 30 (or 60 or 90 days) to your date, simply add the number of days according to your need.
  • If you simply want to say that "bimonthly" is today + 2 months, add 2 to the current month.

There are other approaches too, but they all depend on the answer to the question "added 1 month" to contextualize correctly.

  • "That is correct" is relative. There are other languages and API’s that automatically adjust to the last day of the month (thus, 2019-01-31 + 1 month = 2019-02-28 - in Java is like this, in C# , and in Python the module dateutil also makes this adjustment). There is no "correct" in date arithmetic, because there is no "official" standard (as there is in mathematics, for example). What we have are different implementation decisions, each with its pros and cons.

  • Personally I prefer this adjustment, after all, if add 1 month to a date in January, why should the result be in March? (there is - or should be - a whole semantics in the operation "add X months", which should be maintained consistently - although I understand these adjustments that PHP makes, since many other languages also do so - for example: Javascript) But as I said, it’s decisions to implement each language, and we have to live with them...

  • No, it’s not relative. The subject is PHP, not Java. Mysql also does the same and this could even be done directly at the BD level, but I kept to the assumption in question, without making variations, since it was asked specifically about a PHP function and the answer is correct for that function. Speculate whether the chicken or the egg came first does not add anything in the answer to the colleague.

  • I’m just saying that the result depends on how each language has chosen to implement date arithmetic. PHP chose one way, other languages chose another. To say that only one of them is correct can give the impression that all the others are wrong, and I just wanted to emphasize this: that none is "more correct" than others, are just implementing decisions that we have to live with, and adjust the code accordingly. Note that at no time did I imply that your answer is wrong, I only questioned the statement that the behavior of PHP is the "correct way".

Browser other questions tagged

You are not signed in. Login or sign up in order to post.