J'ai beaucoup travaillé avec le DateTime class
et j'ai récemment rencontré ce que je pensais être un bug lors de l'ajout de mois. Après quelques recherches, il semble que ce n'était pas un bug, mais fonctionnant comme prévu. Selon la documentation trouvée ici :
Exemple # 2 Attention lors de l'ajout ou de la soustraction de mois
<?php
$date = new DateTime('2000-12-31');
$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
?>
The above example will output: 2001-01-31 2001-03-03
Quelqu'un peut-il justifier pourquoi cela n'est pas considéré comme un bogue?
De plus, quelqu'un a-t-il des solutions élégantes pour corriger le problème et faire en sorte que +1 mois fonctionnera comme prévu au lieu de comme prévu?
strtotime()
stackoverflow.com/questions/7119777/…Réponses:
Pourquoi ce n'est pas un bug:
Le comportement actuel est correct. Ce qui suit se produit en interne:
+1 month
augmente de un le numéro du mois (à l'origine 1). Cela rend la date2010-02-31
.Le deuxième mois (février) ne compte que 28 jours en 2010, donc PHP corrige automatiquement cela en continuant simplement à compter les jours à partir du 1er février. Vous vous retrouvez ensuite le 3 mars.
Comment obtenir ce que vous voulez:
Pour obtenir ce que vous voulez, c'est: vérifier manuellement le mois suivant. Ajoutez ensuite le nombre de jours du mois prochain.
J'espère que vous pourrez vous-même le coder. Je ne fais que donner quoi faire.
Manière PHP 5.3:
Pour obtenir le comportement correct, vous pouvez utiliser l'une des nouvelles fonctionnalités de PHP 5.3 qui introduit la strophe de temps relatif
first day of
. Cette strophe peut être utilisé en combinaison avecnext month
,fifth month
ou+8 months
pour aller au premier jour du mois spécifié. Au lieu de+1 month
ce que vous faites, vous pouvez utiliser ce code pour obtenir le premier jour du mois prochain comme ceci:Ce script sortira correctement
February
. Les choses suivantes se produisent lorsque PHP traite cettefirst day of next month
strophe:next month
augmente de un le numéro du mois (à l'origine 1). Cela rend la date 2010-02-31.first day of
définit le numéro du jour sur1
, ce qui donne la date 01/02/2010.la source
Voici une autre solution compacte utilisant entièrement les méthodes DateTime, modifiant l'objet sur place sans créer de clones.
Il sort:
la source
$dt->modify()->modify()
. Fonctionne aussi bien.Cela peut être utile:
la source
$dateTime->modify('first day of next month')->modify('-1day')
Ma solution au problème:
la source
Je partage le sentiment du PO selon lequel cela est contre-intuitif et frustrant, mais il en va de même pour déterminer ce que
+1 month
signifie les scénarios où cela se produit. Considérez ces exemples:Vous commencez avec le 31/01/2015 et souhaitez ajouter un mois 6 fois pour obtenir un cycle de planification pour l'envoi d'une newsletter par e-mail. Compte tenu des attentes initiales du PO, cela reviendrait:
Tout de suite, notez que nous nous attendons
+1 month
à vouloir direlast day of month
ou, alternativement, à ajouter 1 mois par itération mais toujours en référence au point de départ. Au lieu d'interpréter cela comme "le dernier jour du mois", nous pourrions le lire comme "le 31e jour du mois prochain ou le dernier jour disponible au cours de ce mois". Cela signifie que nous sautons du 30 avril au 31 mai au lieu du 30 mai. Notez que ce n'est pas parce que c'est "le dernier jour du mois", mais parce que nous voulons "le plus proche disponible à la date du mois de début".Supposons donc qu'un de nos utilisateurs s'abonne à une autre newsletter pour commencer le 30/01/2015. À quoi sert la date intuitive
+1 month
? Une interprétation serait "30e jour du mois suivant ou le plus proche disponible" qui renverrait:Ce serait bien sauf lorsque notre utilisateur reçoit les deux newsletters le même jour. Supposons qu'il s'agit d'un problème du côté de l'offre plutôt que du côté de la demande. de nombreuses newsletters. Dans cet esprit, nous revenons à l'autre interprétation de "+1 mois" comme "envoyer l'avant-dernier jour de chaque mois" qui renverrait:
Nous avons maintenant évité tout chevauchement avec le premier set, mais nous nous retrouvons également avec avril et 29 juin, ce qui correspond certainement à nos intuitions originales qui
+1 month
devraient simplement revenirm/$d/Y
ou à l'attrait et au simplem/30/Y
pour tous les mois possibles. Considérons maintenant une troisième interprétation de l'+1 month
utilisation des deux dates:31 janvier
30 janvier
Ce qui précède a quelques problèmes. Février est ignoré, ce qui pourrait être un problème à la fois en termes d'approvisionnement (par exemple, s'il y a une allocation de bande passante mensuelle et que février est gaspillé et mars est doublé) et de demande (les utilisateurs se sentent trompés par rapport à février et perçoivent le mois de mars supplémentaire. comme tentative de corriger l'erreur). En revanche, notez que les deux dates définissent:
Compte tenu des deux derniers sets, il ne serait pas difficile de simplement revenir en arrière sur l'une des dates si elle tombe en dehors du mois suivant (donc revenez au 28 février et au 30 avril dans le premier set) et ne perdez pas de sommeil sur le chevauchement occasionnel et divergence entre le modèle «dernier jour du mois» et «avant-dernier jour du mois». Mais s'attendre à ce que la bibliothèque choisisse entre "la plus jolie / naturelle", "l'interprétation mathématique du 31/02 et des autres débordements du mois" et "par rapport au premier du mois ou au mois dernier" se terminera toujours par le non-respect des attentes de quelqu'un et certains horaires doivent ajuster la «mauvaise» date pour éviter le problème du monde réel que la «mauvaise» interprétation introduit.
Donc, encore une fois, même si je m'attendrais également
+1 month
à renvoyer une date qui est en fait le mois suivant, ce n'est pas aussi simple que l'intuition et étant donné les choix, aller avec les mathématiques sur les attentes des développeurs Web est probablement le choix sûr.Voici une solution alternative qui est toujours aussi maladroite que toute autre mais qui, à mon avis, donne de bons résultats:
Ce n'est pas optimal, mais la logique sous-jacente est la suivante: si l'ajout d'un mois entraîne une date autre que le mois prochain prévu, supprimez cette date et ajoutez 4 semaines à la place. Voici les résultats avec les deux dates de test:
31 janvier
30 janvier
(Mon code est un gâchis et ne fonctionnerait pas dans un scénario pluriannuel. Je souhaite à quiconque de réécrire la solution avec un code plus élégant tant que le principe sous-jacent est conservé intact, c'est-à-dire si +1 mois renvoie une date géniale, utilisez +4 semaines à la place.)
la source
J'ai créé une fonction qui renvoie un DateInterval pour m'assurer que l'ajout d'un mois affiche le mois suivant et supprime les jours suivants.
la source
En conjonction avec la réponse de shamittomar, cela pourrait alors être ceci pour ajouter des mois "en toute sécurité":
la source
J'ai trouvé un moyen plus court de le contourner en utilisant le code suivant:
la source
Voici une implémentation d'une version améliorée de la réponse de Juhana à une question connexe:
Cette version
$createdDate
suppose que vous avez affaire à une période mensuelle récurrente, comme un abonnement, qui a commencé à une date précise, comme le 31. Il faut toujours$createdDate
tellement tard que les dates "récurrentes" ne passeront pas à des valeurs inférieures car elles sont repoussées à travers des mois de moindre valeur (par exemple, les 29e, 30e ou 31e dates récurrentes ne seront finalement pas bloquées le 28 après le passage à travers une année non bissextile février).Voici un code de pilote pour tester l'algorithme:
Quelles sorties:
la source
Ceci est une version améliorée de la réponse de Kasihasi à une question connexe. Cela ajoutera ou soustrayera correctement un nombre arbitraire de mois à une date.
Par exemple:
affichera:
la source
Si vous voulez simplement éviter de sauter un mois, vous pouvez effectuer quelque chose comme ceci pour obtenir la date et exécuter une boucle le mois suivant en réduisant la date de un et en revérifiant jusqu'à une date valide où $ starting_calculated est une chaîne valide pour strtotime (ie mysql datetime ou "now"). Cela trouve la toute fin du mois à 1 minute à minuit au lieu de sauter le mois.
la source
Extension pour la classe DateTime qui résout le problème d'ajout ou de soustraction de mois
https://gist.github.com/66Ton99/60571ee49bf1906aaa1c
la source
Si vous utilisez
strtotime()
juste utiliser$date = strtotime('first day of +1 month');
la source
J'avais besoin d'un rendez-vous pour «ce mois-ci l'année dernière» et cela devient assez vite désagréable quand ce mois est février dans une année bissextile. Cependant, je crois que cela fonctionne ...: - / L'astuce semble être de baser votre changement sur le 1er jour du mois.
la source
la source
sortira
2
(février). fonctionnera aussi pendant d'autres mois.la source
Pendant des jours:
Important:
La méthode
add()
de la classe DateTime modifie la valeur de l'objet afin qu'après avoir appeléadd()
un objet DateTime, elle retourne le nouvel objet date et modifie également l'objet lui-même.la source
vous pouvez également le faire avec uniquement date () et strtotime (). Par exemple, pour ajouter 1 mois à la date d'aujourd'hui:
si vous souhaitez utiliser la classe datetime c'est bien aussi mais c'est tout aussi simple. plus de détails ici
la source
La réponse acceptée explique déjà pourquoi ce n'est pas un mais, et certaines autres réponses posent une solution soignée avec des expressions php comme
first day of the +2 months
. Le problème avec ces expressions est qu'elles ne sont pas complétées automatiquement.La solution est cependant assez simple. Tout d'abord, vous devriez trouver des abstractions utiles qui reflètent votre espace de problème. Dans ce cas, c'est un fichier
ISO8601DateTime
. Deuxièmement, il devrait y avoir plusieurs implémentations qui peuvent apporter une représentation textuelle souhaitée. Par exemple,Today
,Tomorrow
,The first day of this month
,Future
- représentent tous une implémentation spécifique duISO8601DateTime
concept.Donc, dans votre cas, une implémentation dont vous avez besoin est
TheFirstDayOfNMonthsLater
. C'est facile à trouver simplement en regardant la liste des sous-classes dans n'importe quel IDE. Voici le code:La même chose avec les derniers jours d'un mois. Pour plus d'exemples de cette approche, lisez ceci .
la source
la source