2009-11-17 6 views
3

J'ai une application de calendrier qui utilise les nouvelles classes PHP DateTime. J'ai une façon que je gérer les événements récurrents, mais il semble pirater-ish et je voulais voir si les gars vous avez de meilleures idées:PHP 5.3 DateTime pour les événements récurrents

  1. J'ai un événement récurrent qui commence 11/16/2009 (16 nov, 2009)
  2. Il se produira tous les 3 jours
  3. l'événement se reproduira indéfiniment

Disons que l'utilisateur regarde le calendrier pour décembre, 3100 - cet événement doit montrer qu'il répéter tous les 3 jours comme normal. La question est - comment puis-je calculer ces jours dans ce mois?

=========================================

Voici comment je fais essentiellement, mais je sais que je me manque quelque chose plus facile:

  1. je calcule la différence de jours entre le début du mois d'être regardé (1 déc 3100) et le début de l'événement date (16 nov 2009) stocké comme $ daysDiff
  2. Je soustrais le module, de sorte que j'obtiens un facteur de 3 jours depuis le début comme ceci: $ daysDiff - ($ daysDiff% 3)
  3. Pour le bien de l'argument laisse dire que me donne 29, 3100 comme une date.
  4. Je puis ajouter 3 jours à cette date à plusieurs reprises jusqu'à ce que j'avoir toutes les dates au sein de décembre 3100

Mon principal problème est à l'étape 1. Le PHP DateInterval :: date_diff fonction ne calcule pas les différences de jours. Cela me donnera des années, des mois et des jours. Je dois ensuite truquer les chiffres pour obtenir une date approximative vers le 31 décembre. 11/16/2009 + (1090 ans * 365,25 jours) + (9 mois * 30,5 jours) + 15 jours

Quand vous allez REAL loin dans l'avenir comme l'année 9999, cette estimation peut être décalée d'un mois, alors je dois soustraire beaucoup d'intervalles de 3 jours pour obtenir où j'ai besoin.

Répondre

3

Vous pouvez formater votre date en tant qu'horodatage unix, puis utiliser la division modulaire pour trouver la première instance du mois sélectionné, puis effectuer un incrément de 3 jours à partir de là. Donc, pour votre exemple:

$startDate = new DateTime(20091116); 
$startTimestamp = $startDate->format('u'); 
$recursEvery = 259200; // 60*60*24*3 = seconds in 3 days 

// find the first occurrence in the selected month (September) 
$calendarDate = new DateTime(31000901); // init to Sept 1, 3100 
while (0 != (($calendarDate->format('u') - $startTimestamp) % $recursEvery)) { 
    $calendarDate->modify('+1 day'); 
} 

$effectiveDates = array(); 
while ($calendarDate->format('m') == 9) { 
    $effectiveDates[] = clone $calendarDate; 
    $calendarDate->modify('+3 day'); 
} 

//$effectiveDates is an array of every date the event occurs in September, 3100. 

De toute évidence, vous avez quelques variables pour permuter afin que l'utilisateur peut sélectionner un mois, mais cela devrait être l'algorithme de base. En outre, assurez-vous que vos DateTimes sont la date correcte, mais avec l'heure définie comme 00:00:00, sinon la première boucle ne sera jamais résolue. Cela suppose également que vous avez vérifié que la date sélectionnée est postérieure à la date de début de l'événement.

+0

... Bien sûr, la chose timestamp unix ne fonctionnera pas pour l'année 3100 sauf si vous utilisez 64 bits. – keithjgrant

+0

Salut Keith - J'aime la façon dont vous pensez avec cela, mais c'est malheureusement une limitation pour moi aussi (sur 32 bits). Il semble que tout se passe comme prévu: "math" avec ces dates ne peuvent pas être faites, regardez mon "fudging" façon de le faire ci-dessus – John

+0

Si vous placez un '@' devant l'horodatage comme suit: new DateTime ('@' .$ timestamp); whoa, ne s'est pas rendu compte que c'était la dernière fois active en 09 .... désolé. – Adam

9

Cela peut être fait en utilisant un bien DatePeriod comme Iterator puis filterd à la date de début que vous souhaitez afficher:

<?php 
class DateFilterIterator extends FilterIterator { 
    private $starttime; 
    public function __construct(Traversable $inner, DateTime $start) { 
     parent::__construct(new IteratorIterator($inner)); 
     $this->starttime = $start->getTimestamp(); 
    } 
    public function accept() { 
     return ($this->starttime < $this->current()->getTimestamp()); 
    } 
} 

$db = new DateTime('2009-11-16'); 
$de = new DateTime('2020-12-31 23:59:59'); 
$di = DateInterval::createFromDateString('+3 days'); 
$df = new DateFilterIterator(
    new DatePeriod($db, $di, $de), 
    new DateTime('2009-12-01')); 

foreach ($df as $dt) 
{ 
    echo $dt->format("l Y-m-d H:i:s\n"); 
} 
?>