2010-11-25 13 views
3

J'ai besoin de créer des fonctions PHP qui me permettent de monter/descendre des unités de datetime. Plus précisément, je dois être en mesure de passer au mois suivant/précédent de l'actuel.Vous ne pouvez pas obtenir le mois précédent à partir de DateTime en PHP- Est-ce un (assez gros) bug?

Je pensais que je pouvais le faire en utilisant DateTime :: add/sub (P1M). Cependant, en essayant d'obtenir le mois précédent, il bousille si la valeur de date = 31- ressemble il est en train d'essayer de compter depuis 30 jours au lieu de décrémenter la valeur du mois !:

$prevMonth = new DateTime('2010-12-31'); 

Essayez de décrémenter le mois :

$prevMonth->sub(new DateInterval('P1M')); // = '2010-12-01' 
$prevMonth->add(DateInterval::createFromDateString('-1 month')); // = '2010-12-01' 
$prevMonth->sub(DateInterval::createFromDateString('+1 month')); // = '2010-12-01' 
$prevMonth->add(DateInterval::createFromDateString('previous month')); // = '2010-12-01' 

Cela semble certainement être un comportement incorrect. Quelqu'un a-t-il un aperçu? Action de grâce

REMARQUE: version PHP 5.3.3

+0

Bien qu'il ne s'agisse probablement que d'une faute de frappe, '$ prevMonth = new DateTime ($ '2010-12-31');' est une erreur de syntaxe. – JAL

+3

Est-ce que ça a à voir avec ce dont Rasmus parle ici? On dirait que ça ... http://twitter.com/rasmus/status/29243336217 http://twitter.com/rasmus/status/29243723623 http://twitter.com/rasmus/status/29243824255 – JAL

+0

@Alex JL- Whoah ne sait pas comment vous l'avez trouvé, mais c'est tout - voir spécifiquement ceci: http://www.gnu.org/software/tar/manual/html_node/Relative-items-in-date-strings.html#SEC120 Postez votre commentez comme une réponse, donc je peux vous donner du crédit! – Yarin

Répondre

6

(Crédit appartient en fait à Alex pour avoir signalé dans les commentaires)

Le problème n'est pas une, mais PHP GNU un, comme indiqué ici:

Relative items in date strings

la clé est ici différenciant entre le concept de « cette date du mois dernier », qui, parce mois sont « fuz zy unités 'avec des nombres différents de dates, est impossible à définir pour une date comme Dec 31 (parce que le 31 novembre n'existe pas), et le concept de' mois dernier, indépendamment de la date '. Si tout ce qui nous intéresse est le mois précédent, la seule façon de garantir un calcul correct de DateInterval est de réinitialiser la valeur de la date au 1er, ou un autre nombre que chaque mois aura. Ce qui me frappe vraiment, c'est la façon dont ce problème n'est pas documenté, en PHP et ailleurs, en considérant le nombre de logiciels dépendants de la date auxquels cela affecte probablement.

est ici un moyen sûr de le manipuler:

/* 
Handles month/year increment calculations in a safe way, 
avoiding the pitfall of 'fuzzy' month units. 

Returns a DateTime object with incremented month/year values, and a date value == 1. 
*/ 
function incrementDate($startDate, $monthIncrement = 0, $yearIncrement = 0) { 

    $startingTimeStamp = $startDate->getTimestamp(); 
    // Get the month value of the given date: 
    $monthString = date('Y-m', $startingTimeStamp); 
    // Create a date string corresponding to the 1st of the give month, 
    // making it safe for monthly/yearly calculations: 
    $safeDateString = "first day of $monthString"; 
    // Increment date by given month/year increments: 
    $incrementedDateString = "$safeDateString $monthIncrement month $yearIncrement year"; 
    $newTimeStamp = strtotime($incrementedDateString); 
    $newDate = DateTime::createFromFormat('U', $newTimeStamp); 
    return $newDate; 
} 
+0

Votre méthode est presque correcte. Vous devez incrémenter le mois ++ avant de l'utiliser. Comme '0, 0' retournera 1 mois, et' 1, 0' affichera la même date et ainsi de suite. Donc, c'est fondamentalement hors un mois. – mark

+0

PS: a écrit quelques cas de test pour cela à https://github.com/dereuromark/tools/commit/b62c4d91f263d4882f80a69083b0347c518fc2d7 – mark

+0

son tort pour moi, car "mai" ne vient pas comme le mois précédent en juin. –

-1

(Cela devrait peut-être un commentaire, mais il est à long pour un)

Voici comment cela fonctionne sur Windows 7 Apache 2.2.15 avec PHP 5.3.3:

<?php $dt = new DateTime('2010-12-31'); 
$dt->sub(new DateInterval('P1M')); 
print $dt->format('Y-m-d').'<br>'; 
$dt->add(DateInterval::createFromDateString('-1 month')); 
print $dt->format('Y-m-d').'<br>'; 
$dt->sub(DateInterval::createFromDateString('+1 month')); 
print $dt->format('Y-m-d').'<br>'; 
$dt->add(DateInterval::createFromDateString('previous month')); 
print $dt->format('Y-m-d').'<br>'; ?> 

2010-12-01 
2010-11-01 
2010-10-01 
2010-09-01 

Cela semble confirmer que c'est lié au GNU ci-dessus.

Remarque: IMO le code ci-dessous fonctionne comme prévu.

$dt->sub(new DateInterval('P1M')); 

mois en cours: 12
mois dernier: 11
Nombre de jours au cours du mois 12: 31
Nombre de jours au cours du mois 11: 30
31 décembre - 31 jours = 31 novembre
novembre 31st = 1 nov + 31 jours = 1er décembre (30 + 1)

+0

Je ne vous suis pas - Les résultats que vous donnez ont le même problème que le mien: 2010-12-31 -1 mois = 2010-12-01. ? Et je travaille sur Mac OS X btw – Yarin

+0

@ Yarin, oui 2010-12-31 = 2010-12-01, d'une manière bizarre et tordue, c'est logique. D'un certain point de vue, cela peut ne pas sembler être un bug, mais d'autres le font. –

+0

J'ai downvoted ceci parce que vous avez seulement reproduit mes résultats et n'ai offert aucune solution. – Yarin

2

Le meilleur moyen d'y parvenir est, à mon avis, d'utiliser mktime.

Comme ceci:

$date = mktime(0,0,0,date('m')-1,date('d'),date('Y')); 
echo date('d-m-Y', $date); 

Greetz Michael

documentation p.s mktime se trouve ici: http://nl2.php.net/mktime

+1

Pour ceux qui se demandent, il s'enroulera autour des années correctement. – Decko

+1

Il enveloppe correctement les années, mais pas les mois. En cours d'exécution le 31 mai, résultats dans "01-05-2012"! – Eli

1

Vous pouvez aller vieille école et il suffit d'utiliser les fonctions de date et strtotime.

 
$date = '2010-12-31'; 
$monthOnly = date('Y-m', strtotime($date)); 
$previousMonth = date('Y-m-d', strtotime($monthOnly . ' -1 month')); 
+0

Ce code a besoin d'un espace devant le '-1', et puis ça marche (je l'ai déprécié parce que je pensais qu'il était cassé, mais maintenant je ne vais pas me laisser upvote jusqu'à ce que la réponse soit éditée ...?) Alors merci, si vous l'éditez, je vais corriger le vote – Yarin

+0

@Yarin - Désolé pour ça, nouveau pour ce lieu. – oreX