Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a getNextRecurrence method #91

Open
AdamEsterle opened this issue May 20, 2016 · 9 comments
Open

Add a getNextRecurrence method #91

AdamEsterle opened this issue May 20, 2016 · 9 comments

Comments

@AdamEsterle
Copy link

AdamEsterle commented May 20, 2016

I calculated the RecurrenceCollection and used it to find the number of Recurrences
$recurrences->count()

and the first and last start dates
$recurrences->first()->getStart()
$recurrences->last()->getStart()

but I went to calculate what the next recurrence would be and there was no easy solution. I had to do a recalculation with another transform with a different end date.

It would be nice to just be able to call a method and have it return the next recurrence (perhaps by ignoring the current end date?)

@simshaun
Copy link
Owner

Can you clarify this request?

@rukavina
Copy link

I guess we're always most interested in the next occurrences, and I also find it hard to get.
Eg. if DTSTART is far back in the past, what are the options to get next occurrence. It seems non-efficient to skip all past occurrences and to get new one, and even you can hit the array virtual limit.
Or maybe I'm doing something wrong?

Thanks

@simshaun
Copy link
Owner

As part of the refactoring I'll be doing for #94, I'll keep this issue in mind.

I'm thinking that part of the public API will be $occurrenceGenerator->getNextRecurrence($recurrence), which should solve this issue.

@simshaun simshaun added this to the 2.0 milestone Aug 11, 2016
@jpmurray
Copy link

I'm not sure if that's what @rukavina wanted to say, but trying to find the next occurence, from the present moment is what I think would be really great.

Like he said, if the rule had a DTSTART far away in the past, and you just want to know when the next recurrence will happen, it's kind of counter productive to generate all of the recurrences...

@brianmuse
Copy link

Like he said, if the rule had a DTSTART far away in the past, and you just want to know when the next recurrence will happen, it's kind of counter productive to generate all of the recurrences...

I'm not sure this is possible to do. You typically need some sort of seed date to start from. What we've don't is use redis to cache seed dates from previous expansions so we can pick a valid one to start from. The only tricky part of that is if there is a COUNT you have to factor that in too. You have to know which in the series your seed date is.

@lasergoat
Copy link

lasergoat commented Nov 23, 2016

@AdamEsterle using @simshaun's built in array transformer with a virtual limit worked for me:

    use Carbon\Carbon;
    use Recurr\Rule;
    use Recurr\Transformer\ArrayTransformer;
    use Recurr\Transformer\ArrayTransformerConfig;
    use Recurr\Transformer\Constraint\AfterConstraint;

    $rule = 'FREQ=MONTHLY;COUNT=12';

    $rrule = new Rule(
        $rule,
        Carbon::now(),
        null
    );

    $next_run_at = self::nextOccurrence($rule, $rrule, Carbon::now(), $this->count);
    ...

    public static function nextOccurrence($ruleText, Rule $rrule, Carbon $lastRun = null, $count = 0)
    {
        $maxCount = $rrule->getCount();

        if ($maxCount !== null && $count >= $maxCount)
        {
            return null;
        }

        $constraint = null;
        $arrayTransformer = new ArrayTransformer();
        $transformerConfig = new ArrayTransformerConfig();
        $transformerConfig->enableLastDayOfMonthFix();
        $transformerConfig->setVirtualLimit(2);
        $arrayTransformer->setConfig($transformerConfig);

        // if there is a last run supplied, meaning we are looking
        // for the next date, not the first date,
        // we will be adding an after contstraint
        if ($lastRun !== null)
        {
            // if there was a last run, and the 
            // original ruleText just happens
            // to contain a DTSTART, we *MUST* set the rrule object 
            // to use now's DTSTART instead... otherwise, the 
            // virtual limit will be surpassed and our result will be null
            if (str_contains($ruleText, 'DTSTART=')) {
                $rrule->setStartDate($lastRun);
            }

            $constraint = new AfterConstraint($lastRun, false);
        }

        $occurrences = $arrayTransformer->transform($rrule, $constraint)->map(function($occurrence) {
            return Carbon::instance($occurrence->getStart());
        });

        if (!$occurrences->isEmpty())
        {
            return $occurrences->first();
        }
        else
        {
            return null;
        }
    }

one weird thing is you have to set a virtual limit of 2, not one. you can ignore my $count logic unless you need it.

Also, the virtual limit will cause your results of nextOccurrence() to be null if you provide a DTSTART too far in the past. That's why there is a if (str_contains($ruleText, 'DTSTART=')) { block. str_contains is a laravel helper method.

What happens for me is I have a rule like: FREQ=MONTHLY;DTSTART=20150701T000000Z;
And when I go to calculate the next date, it's null because the virtual limit of 2 caused the actual next run at date to not be found. That's why I have to reset the rule Text's start date to the last run at date. This works for me, because my cron calls this every minute... If your cron went every year, then you couldn't use the last run date, you'd need to use Carbon::now().

His library uses a big array for all the generated recurrences - so I'm essentially tricking it into only making two - using only one also breaks something which I forget now.

EDITED added case for resetting dtstart

@benjaminkohl
Copy link

I see this request is five years old. Is there really no way to calculate the next occurrence of a date/time interval? It seems that would be the most critical thing to be able to do with a date/time interval.

@simshaun
Copy link
Owner

simshaun commented Apr 5, 2021

The lib was a port of rrule.js and it wasn't designed with that in mind at the time.

I started a rewrite based on an iterator design, but haven't finished it; It's not a priority for me at this time.

@benjaminkohl
Copy link

Okay, thank you. I'll just leave a note here for anyone that is curious. For anyone that is looking to calculate occurrences, we found this package: https://github.com/rlanvin/php-rrule

We are still using this recurr package to write the rules then the rrule package has classes that provide methods for calculating occurrences based on the rule string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants