Vous cherchez à créer une fonction qui le fera en PHP.
Je dois ajouter un certain nombre de mois à une date, sans toutefois dépasser le dernier jour du mois.
Par exemple:
Add 1 month to January (1-28th), 2011, should produce February (1-28th), 2011.
Add 1 month to January 30th, 2011, should produce February 28th, 2011.
Add 3 months to January 31st, 2011, should produce April 30th, 2011.
Add 13 months to January 30th, 2011, should produce February 29th, 2012.
Add 1 month to October 31st, 2011, should produce November 30th, 2011.
Si j'utilise l'ajout de date en PHP, j'obtiens des dépassements:
Adding 1 month to January 30th, 2011, results in March 2nd, 2011.
Ma spécification ne me permet pas de déborder dans un nouveau mois.
Quelle est la méthode la plus simple pour y parvenir?
Vous pouvez comparer le jour du mois avant et après avoir ajouté 1 mois. Si ce n'est pas la même chose, vous avez dépassé le mois prochain.
function add($date_str, $months)
{
$date = new DateTime($date_str);
// We extract the day of the month as $start_day
$start_day = $date->format('j');
// We add 1 month to the given date
$date->modify("+{$months} month");
// We extract the day of the month again so we can compare
$end_day = $date->format('j');
if ($start_day != $end_day)
{
// The day of the month isn't the same anymore, so we correct the date
$date->modify('last day of last month');
}
return $date;
}
$result = add('2011-01-28', 1); // 2011-02-28
$result = add('2011-01-31', 3); // 2011-04-30
$result = add('2011-01-30', 13); // 2012-02-29
$result = add('2011-10-31', 1); // 2011-11-30
$result = add('2011-12-30', 1); // 2011-02-28
Autant que je sache, la portée de ce problème est très limitée, il est donc préférable de tester un type d'erreur et de le corriger.
Tout ce que vous voulez faire, c'est vous assurer que l'ajout de "un mois" à une date tardive comme le 29, le 30 ou le 31 ne vous poussera pas à avancer le 1er, le 2 ou le 3 du mois prochain.
La façon dont date_modify () fonctionne (en l’utilisant à titre d’exemple de date "2012-01-31" avec une chaîne telle que "+1 mois"), c’est qu’elle augmente d’abord le numéro du mois de 1, puis trouve le 31e jour à partir du début. de ce mois. C'est pourquoi il déborde le 3 mars.
Lorsque ce n'est pas ce que vous souhaitiez, tout ce que vous avez à faire est d'utiliser à nouveau date_modify (), en lui disant maintenant de revenir en arrière de quelques jours (3 jours dans cet exemple). Étant donné que vous souhaitez uniquement revenir au dernier jour du mois précédent, le nombre de jours pour lequel vous souhaitez revenir est toujours le même que le jour du mois de votre date erronée.
Il ne reste plus qu'à vous assurer que vous n'appliquez pas cette correction lorsqu'elle n'est pas nécessaire, comme lorsque PHP devait s'améliorer à l'avenir. Ceci est relativement facile, car la portée des problèmes possibles est très limitée.
Mon code ci-dessous ajoute "+1 mois", vérifie si cela a provoqué une modification brutale du jour du mois, de bas en haut, et ajuste la date si c'est le cas.
//Create the date, store its day-of-month, and add X months
$myDateTimeISO = "2012-01-31";
$addThese = 1;
$myDateTime = new DateTime($myDateTimeISO);
$myDayOfMonth = date_format($myDateTime,'j');
date_modify($myDateTime,"+$addThese months");
//Find out if the day-of-month has dropped
$myNewDayOfMonth = date_format($myDateTime,'j');
if ($myDayOfMonth > 28 && $myNewDayOfMonth < 4){
//If so, fix by going back the number of days that have spilled over
date_modify($myDateTime,"-$myNewDayOfMonth days");
}
echo date_format($myDateTime,"Y-m-d");
Résultats en: 2012-02-29 (oui, c'était une année bissextile).
PS: Si vous voulez ajouter des années, le problème et les symptômes sont presque identiques. Encore une fois, il vous suffit de vérifier si le jour du mois résultant est le 1/3 et si le jour du mois est le 29/30/31. Si tel est le cas, vous devez revenir en arrière "-X jours" à l'aide de date_modify, où X correspond au jour du mois résultant.
cela semble fonctionner pour moi et donne le résultat souhaité:
<?php
$date = "2011-01-30";
list($year,$month,$day) = explode("-",$date);
// add month here
$month++;
// to avoid a month-wrap, set the day to the number of days of the new month if it's too high
$day = min($day,date("t",strtotime($year."-".$month."-01")));
$date = $year."-".$month."-".$day;
// 2011-02-28
echo $date;
?>
MODIFIER:
Après avoir lu le commentaire de Crend Kings, je pense que nous avons besoin de plus d’informations ici. Quel est le résultat souhaité dans les cas suivants:
2011-01-30 > 2011-02-28
2011-01-28 > 2011-02-28 or 2011-02-26 ?
2011-02-01 > 2011-03-01 or 2011-03-03 ?
en mots: la méthode devrait-elle ajouter le nombre de jours du mois prochain, ce qui correspond à ce que fait Crend King et donne des résultats tels que 2011-02-26 et 2011-03-03 (ce qui ne me semble pas être le résultat souhaité ) ou cela devrait-il ajouter un mois et laisser le jour tel quel, au lieu d’un jour trop "élevé" comme le fait mon code? Je suis confus...
Pour toute personne intéressée, j’ai adopté une approche solide pour faire face à ce problème.
/**
* @var \DateInterval
*/
private $remainder;
/**
* @param \DateTimeImmutable $date
* @param string $modifier
* @return \DateTimeImmutable
*/
private function nextInterval(\DateTimeImmutable $date, $modifier)
{
$dayNumber = (int)$date->format('j');
$next = $date->modify($modifier);
if (!is_null($this->remainder)) {
$next = $next->add($this->remainder);
$dayNumber += $this->remainder->days;
$this->remainder = null;
}
// This should in general only apply to months which do not have the same daynumber in that month after adding
if ($dayNumber !== (int)$next->format('j')) {
$n = $next->modify('last day of last month');
$this->remainder = $n->diff($next);
$next = $n;
}
return $next;
}
Résultats:
2014-11-30
2014-12-30
2015-01-30
2015-02-28
2015-03-30
2015-04-30
2015-05-30
et
2015-12-30
2016-01-30
2016-02-29
2016-03-30
2016-04-30