Giter VIP home page Giter VIP logo

Comments (17)

dmfs avatar dmfs commented on July 20, 2024 1

RFC 5545 Section 3.8.5.3 states:

The "DTSTART" property defines
the first instance in the recurrence set. The "DTSTART" property
value SHOULD be synchronized with the recurrence rule, if
specified. The recurrence set generated with a "DTSTART" property
value not synchronized with the recurrence rule is undefined.

It basically says "don't expect anything if the start date doesn't match the rule".
However, it also says "The "DTSTART" property defines the first instance in the recurrence set", which is why we've chosen this implementation.

We probably can add a parameter to modify this behavior. It's basically two classes that inject the start instance as the first instance: SanityFilter and FastWeeklIterator.

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

Thanks for the quick answer!

However... I don't think it has something to do with the specification.
It is more about how the iterator works.

Here is my updated rule:

FREQ=MONTHLY;DTSTART=20080328T140000Z;WKST=MO;BYDAY=FR;BYMONTH=3,6,9,12;BYSETPOS=-1

It now has the start date synced with the rule. It generates dates as expected. I use this demo app for tests: http://jkbrzt.github.io/rrule/

Now, I want to get a slice of this rule for a particular time period. Let's say - all events for the 27th week of the year. For that I use RecurrenceIterator with the start date (specified as per example here) set to the first day of the week of interest. Now, I expect the iterator to return dates for events that satisfy two requirements:

  • events that occur on or after the first day of this week (the start date for the iterator),
  • events that fit the rule.
    There are no events of course that fit the rule in July, but that is what I want to find out, right?

As per mentioned above example - I set the start date for the RecurrenceIterator - not the rule itself. So I expect iterator to return all dates that fit the rule, starting the date specified, but including the start date itself only if it fits the rule.

The result is the same - iterator returns me the start date even if it doesn't fit the rule.

Specification most likely doesn't say anything about how the iterator should be implemented. In this case iterator is just a tool to get dates specified by the rule. It should not interfere with results.

I might be missing something. If so, please point me to a correct way of solving my problem, described above. Otherwise, the iterator should be fixed, I think.

With respect,
and thank you!

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

Ok, now I understand your use case. I think it just doesn't work the way you think. In general you can not just start iterating at an arbitrary date. It might work for many rules, but not all of them.

Consider the following rule and DTSTART:

DTSTART;VALUE=DATE;20030815
FREQ=WEEKLY;INTERVAL=4;BYDAY=FR

This just means Friday every 4 weeks starting on August 15 2003 (which was a Friday).

Now try to start iterating from today. To be able to tell if a multiple of 4 weeks has passed you need to know the start date, you can't just start at any time otherwise you may get wrong results.

There is a similar issue for rules containing a COUNT part. To know how many instances you have left, you need to know how many you've skipped.

When the new SKIP parameter comes into play it gets even worse.

To provide a proper solution for your use case we've added the fastForward method (see https://github.com/dmfs/lib-recur/blob/master/src/org/dmfs/rfc5545/recurrenceset/RecurrenceSetIterator.java#L224). You can use it to skip all instances before the given date (given in milliseconds since the Epoch). In general this is much faster than iterating all the instances, because it can omit certain calculations.

I agree that certain cases can be optimized, but for our uses cases we don't need that, so we didn't do that yet. Also, the iterator is really fast, compared to the other two Java iterators I'm aware of (the one in Android and the google-rfc-2445 library), so there probably is no need for optimization here.

I suggest you give fastForward a chance.

The RecurrenceRuleIterator class has a fastForward too, see https://github.com/dmfs/lib-recur/blob/master/src/org/dmfs/rfc5545/recur/RecurrenceRuleIterator.java#L241

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

Btw. I really need to update the README file. We've improved the interface since then (we ditched the Calendar class) and it doesn't even mention the RecurrenceSet and RecurrenceSetIterator classes, which present the preferred way of iterating recurrences, because they add support for RDATEs, EXDATEs and even (deprecated) EXRULEs.

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

Hi there!

I gave fastForward a try - didn't work. Either I am not doing it right, or fastForward does not affect the iterator at all. I am setting the starts date to Sunday (6/28/2015), then fastForward the iterator to Monday (6/29/2015), and the first date returned is still Sunday (6/28/2015). 😞

Now try to start iterating from today. To be able to tell if a multiple of 4 weeks has passed you need to know the start date, you can't just start at any time otherwise you may get wrong results.
In my opinion this is implementation question.

Check same rule here, but without DTSTART or with DTSTART that doesn't fit the rule. It works. This is just a matter of how you handle these things.

I understand that you are following the specification, but here is what I think:
the start date for iterator should not interfere with the rule. It should be a kind of a filter - do not return anything before this date.

Appreciate you help!
Hope we will find a solution here 😃

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

Which fastForward did you use? Please be aware that the method expects a milliseconds timestamp in UTC, even if you're iterating events in a different time zone or if you're iterating all-day events. Maybe that's the problem.
We do have a unit test for fastForward that should fail if something's not working: https://github.com/dmfs/lib-recur/blob/master/test/org/dmfs/rfc5545/recur/RecurrenceIteratorTest.java#L317

What was your rule? If you post your code snippet I may be able to tell what the problem is.

Now try to start iterating from today. To be able to tell if a multiple of 4 weeks has passed you need to know the start date, you can't just start at any time otherwise you may get wrong results.
In my opinion this is implementation question.

Check same rule here, but without DTSTART or with DTSTART that doesn't fit the rule. It works. This is just a matter of how you handle these things.

For some reason this site hangs when I add a DTSTART. However, it can't be correct. Try to use a DTSTART that's one or two weeks in the future. You'll get a completely different set of results, because without the right start date you won't know when to start counting the interval of 4 weeks.

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

Hi there!

I used both RecurrenceRuleIterator.fastForward(DateTime until) and RecurrenceRuleIterator.fastForward(long until). Let me clean up my code and I'll post it here.


For some reason this site hangs when I add a DTSTART

Just copy/paste your date. I use the following date format: m/d/yyyy.

Try to use a DTSTART that's one or two weeks in the future. You'll get a completely different set of results, because without the right start date you won't know when to start counting the interval of 4 weeks.

Here is the rule: FREQ=MONTHLY;DTSTART=20160101T160000Z;WKST=MO;BYDAY=FR;BYMONTH=3,6,9,12;BYSETPOS=-1. Starts next year (and the start date doesn't follow the rule). Still returns everything right.

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

Ok, Marten!

fastForward finally worked for me. Yay! Thank you for your help! I ended up using milliseconds with TimeZone.getDefault(), otherwise it was complaining about timezone or not working at all. I guess it worth including my case into README, ha 😄.

However, I continue to doubt that the iterator should inject it's start date value in the recurrence set. I think it would be less confusing if it would use the start date value as a filter, just like the demo app I mentioned earlier...

If not to change the implementation, then a flag to modify the current behavior for the iterator would be just awesome to have.

I would look into it, if you could point me the place where this injection happens...

Thanks!

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

Ok. Here is an example of how this should work:

        String ruleStr = "FREQ=MONTHLY;WKST=MO;BYDAY=FR;BYMONTH=3,6,9,12;BYSETPOS=-1";

        // This is when the event actually starts, you always need to provide this
        DateTime firstInstance = DateTime.parse("20160101T160000Z");

        // parse the rule
        RecurrenceRule rrule = new RecurrenceRule(ruleStr);

        // get an iterator passing the first instance as parameter
        RecurrenceRuleIterator iterator = rrule.iterator(firstInstance);

        // fast forward to a specific date, effectively skipping all instances before that date
        iterator.fastForward(DateTime.parse("20160606T160000Z"));

        // iterate the instances
        int i = 10;
        while (i-- > 0 && iterator.hasNext())
        {
            System.out.printf("%s\n", iterator.nextDateTime().toString());
        }

        /*
         * Output:
         * 
         * 20160624T160000Z
         * 20160930T160000Z
         * 20161230T160000Z
         * 20170331T160000Z
         * 20170630T160000Z
         * 20170929T160000Z
         * 20171229T160000Z
         * 20180330T160000Z
         * 20180629T160000Z
         * 20180928T160000Z
         */

Please note that the format of the start date and the fast forward date should be the same, i.e. both all day (or floating) or both absolute. You should not mix floating and absolute times. The iterator is able to handle different time zones for absolute times though (it will automatically move the fast forward date to the time zone of the start date).

Also note that DTSTART is not a valid part of a recurrence rule as of RFC 5545. This implementation ignores a DTSTART part in lax modes and it will throw an exception in strict modes.

As I mentioned earlier you can not just start to iterate a rule at any time that's not the first instance. in some cases you may need to know the "history" to be able how to iterate the next event.

Consider the following rule:
FREQ=WEEKLY;INTERVAL=2;BYDAY=TH

if you start to iterate from today (2015-07-30) you get these instances:

2015-07-30
2015-08-13
2015-08-27

if you start Thursday next week (2015-08-06), you get these instances:

2015-08-06
2015-08-20
2015-09-03

Even though it's the same rule the results are different, because the start date is different.

Even if it's easy to add a switch to omit unsynchronized start instances, it's even easier to use it in a wrong way and to get wrong results. I'd prefer to stick with fastForward since that's always correct. If you feel fast forward is to slow for you (which I doubt) we still can try to detect special cases that allow certain optimization (i.e. INTERVAL=1 && SKIP=OMIT).

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

Hi there!

Ok. Here is an example of how this should work...

Exactly how I made it work yesterday. Thank you again and I appreciate your help!

Consider the following rule FREQ=WEEKLY;INTERVAL=2;BYDAY=TH ...

Right. I don't know how to put this, but rules like this allow you to figure out whole bunch of starting dates. It is just that those dates, should you pick one and then another, will yield you different results. I also wouldn't call the results wrong since results are correct and correspond to the rule. We don't know the use case here, but I can image few. Of course, user most likely will know the starting date, but why should we mess the results if this is the LAX mode and the date wasn't provided? Either throw an exception or give something that corresponds to the rule.

It this case any Thursday can be the starting date, and those will be legitimate dates, based on the rule. You just have to pick one to start, which is obvious - the immediate one after the start date (if the start date is not Thursday itself). And this is in my opinion where the Iterator has a flaw. fastForward method works just fine - it is able to figure this out. So should the Iterator, shouldn't it? At least the results would look like something expected, and won't have dates completely out of order.

Hope this makes sense.
Cheers!

from lib-recur.

ekubovsky avatar ekubovsky commented on July 20, 2024

btw, fastForward is fast enough =)

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

I'm closing this, because the current behavior is intended and the fast-forward mechanism is the correct way of handling this.

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

FYI: release 0.10 no longer iterates any start date which doesn't match the rule. From now on this is handled by RecurrenceSet. I came to the conclusion that this is the better way of doing it.

from lib-recur.

bonjourrohit89 avatar bonjourrohit89 commented on July 20, 2024

Hi, I have the following rule
String rrule="FREQ=DAILY;BYDAY=SA,SU;UNTIL=20171130T000000Z" and my start date is "20171101T000000Z"

Here start date is 1 November 2017 and end date is 30 November 2017 now the event will be occur only on saturday and sunday as per rrule mean (4,5,11,12,18,19,25,26Nov) but Problem is that event is also occuring for 1 November where day is Wednesday which should not be happen. Please tell me why I am getting 1 November event. Below is my code ,please check and provide the best solution for this

       DateTime firstInstance = DateTime.parse("20171101T000000Z");
       RecurrenceRule rRuleForSession = null;
                        try {
                            rRuleForSession = new RecurrenceRule(rrule);
                        } catch (InvalidRecurrenceRuleException e) {
                            e.printStackTrace();
                        }
        RecurrenceSet rset = new RecurrenceSet();
        rset.addInstances(new RecurrenceRuleAdapter(rRuleForSession));
       rset.addExceptions(new RecurrenceList("20171130T000000Z", firstInstance.getTimeZone()));

       RecurrenceSetIterator itrRecurrence = 
       rset.iterator(firstInstance.getTimeZone(),firstInstance.getTimestamp());
                        
                 int maxInstance = 1;
                        while (itrRecurrence.hasNext() && --maxInstance >= 0) {
                            long nextInstance = itrRecurrence.next();
                            DateTime dateTime = new DateTime(TimeZone.getDefault(), nextInstance);
                            Toast.makeText(baseActivity,dateTime.toString(),Toast.LENGTH_SHORT).show();
                             }

from lib-recur.

devbikash avatar devbikash commented on July 20, 2024

@dmfs As per your comment
FYI: release 0.10 no longer iterates any start date which doesn't match the rule. From now on this is handled by RecurrenceSet. I came to the conclusion that this is the better way of doing it.

but this is not working

RecurrenceSet rset = new RecurrenceSet();
rset.addInstances(new RecurrenceRuleAdapter(new RecurrenceRule("FREQ=WEEKLY;BYDAY=FR;INTERVAL=1;")));
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 16);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);

    RecurrenceSetIterator iterator = rset.iterator(TimeZone.getDefault(), cal.getTimeInMillis());
    int limit = 10;
    while (iterator.hasNext() && --limit >= 0)
    {
        long nextInstance = iterator.next();
        System.out.println(new Date(nextInstance));
    }

Reponse
Thu May 31 16:00:00 IST 2018
Fri Jun 01 16:00:00 IST 2018
Fri Jun 08 16:00:00 IST 2018
Fri Jun 15 16:00:00 IST 2018
Fri Jun 22 16:00:00 IST 2018
Fri Jun 29 16:00:00 IST 2018
Fri Jul 06 16:00:00 IST 2018
Fri Jul 13 16:00:00 IST 2018
Fri Jul 20 16:00:00 IST 2018
Fri Jul 27 16:00:00 IST 2018

from lib-recur.

dmfs avatar dmfs commented on July 20, 2024

@devbikash It looks correct to me. What output do you expect?

My comment above was probably a bit too brief and lacking context.

What I meant was RecurrenceRuleIterator no longer iterates the first instance if it's not synchronized with the rule. RecurrenceRuleIterator now only iterates instances which match the rule.

RecurrenceSet however, now always adds the start date when creating the RecurrenceSetIterator. This is to comply with RFC 5545 which says the start date is the first instance of a recurring event (unless "masked" by an EXDATE).

So if you just want to generate the instances of an RRULE, you can go with RecurrenceRuleIterator which should do what you expect.

from lib-recur.

fanyh123 avatar fanyh123 commented on July 20, 2024

RFC 5545 第 3.8.5.3 节指出:

“DTSTART”属性定义
重复集中的第一个实例。
如果
指定,“DTSTART”属性值应该与重复规则同步。
使用与重复规则不同步的“DTSTART”属性值生成的重复集是未定义的。

它基本上是说“如果开始日期不符合规则,不要指望任何东西”。
但是,它还说““DTSTART”属性定义了重复集中的第一个实例”,这就是我们选择此实现的原因。

我们可能可以添加一个参数来修改此行为。基本上有两个类将 start 实例作为第一个实例注入:SanityFilter 和 FastWeeklIterator。

RFC 5545 Section 3.8.5.3 states:

The "DTSTART" property defines
the first instance in the recurrence set. The "DTSTART" property
value SHOULD be synchronized with the recurrence rule, if
specified. The recurrence set generated with a "DTSTART" property
value not synchronized with the recurrence rule is undefined.

It basically says "don't expect anything if the start date doesn't match the rule".
However, it also says "The "DTSTART" property defines the first instance in the recurrence set", which is why we've chosen this implementation.

We probably can add a parameter to modify this behavior. It's basically two classes that inject the start instance as the first instance: SanityFilter and FastWeeklIterator.

thanks

from lib-recur.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.