Giter VIP home page Giter VIP logo

rrule's Introduction

πŸ” Recurrence rule parsing & calculation as defined in the iCalendar RFC

Build, Test & Lint Coverage

How to use this package

Create a RecurrenceRule:

// Every two weeks on Tuesday and Thursday, but only in December.
final rrule = RecurrenceRule(
  frequency: Frequency.weekly,
  interval: 2,
  byWeekDays: {
    ByWeekDayEntry(DateTime.tuesday),
    ByWeekDayEntry(DateTime.thursday),
  },
  byMonths: {12},
);

And get its recurrences by evaluating it from a start date:

final Iterable<DateTime> instances = rrule.getInstances(
  start: DateTime.now().copyWith(isUtc: true),
);

⚠️ rrule doesn't care about any time-zone-related stuff. All supplied DateTimes must have isUtc set to true (because UTC doesn't have complications like summer and winter time), but the actual time zone is then ignored, e.g., when generating human-readable text.

If you have a DateTime instance in the local time zone, you can use rrule's helper dateTime.copyWith(isUtc: true) which keeps the date and time but sets isUtc to true. To convert the generated instances back to your local time zone, you can use dateTime.copyWith(isUtc: false).

To limit returned instances (besides using RecurrenceRule.until or RecurrenceRule.count), you can use Dart's default Iterable functions:

final firstThreeInstances = instances.take(3);

final onlyThisYear = instances.takeWhile(
  (instance) => instance.year == DateTime.now().year,
);

final startingNextYear = instances.where(
  (instance) => instance.year > DateTime.now().year,
);

Machine-readable String conversion

You can convert between RecurrenceRules and iCalendar/RFC 5545-compliant Strings by using RecurrenceRuleStringCodec or the following convenience methods:

final string = 'RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH;BYMONTH=12';
final rrule = RecurrenceRule.fromString(string);

assert(rrule.toString() == string); // true

(Same RRULE as the first one)

Human-readable Text conversion

You can convert a RecurrenceRule to a human-readable Strings by using RecurrenceRule.toText():

// First, load the localizations (currently, only English is supported):
final l10n = await RruleL10nEn.create();

final rrule = RecurrenceRule.fromString(
  'RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH;BYMONTH=12',
);

final text = 'Every other week in December on Tuesday & Thursday';
assert(rrule.toText(l10n: l10n) == string); // true

(Same RRULE as the first one)

A few more examples:

  • RRULE:INTERVAL=4;FREQ=HOURLY: Every 4 hours
  • RRULE:FREQ=DAILY;BYSETPOS=1,-2;BYMONTH=1,12;BYMONTHDAY=1,-1: Daily in January & December on the 1st & 2nd-to-last instance of the 1st & last day of the month
  • RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR: Weekly on weekdays
  • RRULE:INTERVAL=2;FREQ=WEEKLY: Every other week
  • RRULE:FREQ=MONTHLY;BYDAY=-3TU: Monthly on the 3rd-to-last Tuesday
  • RRULE:FREQ=YEARLY;BYDAY=+13FR: Annually on the 13th Friday of the year
  • RRULE:FREQ=YEARLY;BYSETPOS=1,-2;BYMONTH=1,12;BYWEEKNO=1,-1;BYYEARDAY=1,-1;BYMONTHDAY=1,-1;BYDAY=MO,WE: Annually on the 1st & 2nd-to-last instance of every Monday & Wednesday that are also the 1st or last day of the month, that are also the 1st or last day of the year, that are also in the 1st or last week of the year, and that are also in January or December

While this already supports really complex RRULEs, some of them are not (yet) supported. See RecurrenceRule.canFullyConvertToText for more information.

JSON conversion

You can convert between RecurrenceRules and jCal/RFC 7265-compliant JSON using the following convenience methods:

final json = <String, dynamic>{
  'freq': 'WEEKLY',
  'interval': 2,
  'byday': ['TU', 'TH'],
  'bymonth': [12],
};
final rrule = RecurrenceRule.fromJson(json);

expect(rrule.toJson(), json);

(Same RRULE as the first one)

Limitations

  • custom week starts are not supported (WKST in the specification) – Monday is the only valid value (encoded as MO)
  • leap seconds are not supported (limitation of Dart's DateTime)
  • only years 0–9999 in the Common Era are supported (limitation of the iCalendar RFC, but if you have a use case, this should be easy to extend)

Thanks

The recurrence calculation code of RecurrencRules is mostly a partial port of rrule.js, though with a lot of modifications to use Dart's DateTime and not having to do date/time calculations manually. You can find the license of rrule.js in the file LICENSE-rrule.js.txt.

rrule's People

Contributors

absar avatar dependabot[bot] avatar hbdejesus avatar hbdejesus-ffuf avatar jonasbadstuebner avatar jonaswanke avatar plammens avatar polrk avatar thomassth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rrule's Issues

It cannot parse its own strings when current locale does not use Latin numbers

When an RRule is created from locales other than the ones with Latin numbers rrule.toString generates the string in localized numerals for UNTIL date, which is not right, since rrule cannot parse back the localized string to RRule, this is due to the fact that iCalDateTimePattern is using intl.DateFormat.format and then tries to parse back using simple DateTime.tryParse which is not aware of locale.

It has another issue iCalDateTimePattern is defined as a global final, which means once initialized it will capture current locale, if user changes the locale from App, rrule will not change the date/time format until the app is fully closed and reopened.

Fix would be to manually create the toString instead of using intl.DateFormat.format, like how dart does in toIso8601String to produce ISO-8601 compliant strings

Code to reproduce the issue:
Instead of generating RRULE:FREQ=DAILY;UNTIL=19971224T000000 it generates Arabic RRULE:FREQ=DAILY;UNTIL=Ω‘Ω©Ω©Ω§Ω‘Ω’Ω’Ω€TΩ Ω Ω Ω Ω Ω , then it's unable to parse back the generated string:

import 'package:intl/intl.dart';
import 'package:rrule/rrule.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:intl/date_symbol_data_local.dart';

void main() async {
  Intl.defaultLocale = 'ar';
  await initializeDateFormatting();

  group('arabic', () {
    test('arabic', () {
      final rrule = RecurrenceRule(frequency: Frequency.daily, until: DateTime.utc(1997, 12, 24));
      final string = rrule.toString();
      print(string);
      // expect(string, 'RRULE:FREQ=DAILY;UNTIL=19971224T000000');

      final fromString = RecurrenceRule.fromString(string);
      expect(fromString, rrule);
    });
  });
}
  • Package version: rrule 0.2.13

DTSTART support

According to documentation of iCalendar-RFC-5545 (https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html), rrules might contain DTSTART, which is also my case, because the web application of our project, saves the format rrule like that "DTSTART:20220629T000000Z\nRRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR,SA,SU". Unfortunately as it seems, this is the only lib for rrule on dart, and i didn't find the DTSTART support in documentation. Please, help me out!

EXDATE

Is it possible to add EXDATE to the recurrence rules please?

Set<int> byHours,Set<int> bySeconds use List<int> instead would be better

E.g.

repeat every day at 08:00, 09:23, 09:50, 10:23 (these times input by the user, could be totally random)

Set Example

if use Set,will gernate:
byHours: {8,9,9,10}
byMinutes: {0,23,50}, the minute (23) of "10:23" will removed because using Set.
instances:

  1. 08:00 ← 1
  2. 08:23
  3. 08:50
  4. 09:00
  5. 09:23 ← 5
  6. 09:50 ← 6
  7. 10:00
  8. 10:23
  9. 10:50 ← 9
  10. ....

In this case, setting bysetpos to 1,5,6,9 will get the correct result, but this is impossible to implement by code because the information of byMinutes has been lost and the user's input could be totally random.

List Example

if use List, will gernate:
byHours: {8,9,9,10}
byMinutes: {0,23,50,23}
instances:

  1. 08:00 ← 1 = 0 * byMinutes.length + 1
  2. 08:23
  3. 08:50
  4. 08:23
  5. 09:00
  6. 09:23 ← 6 = 1 * byMinutes.length + 1
  7. 09:50
  8. 09:23
  9. 09:00
  10. 09:23
  11. 09:50 ←11 = 2 * byMinutes.length + 1
  12. 09:23
  13. 10:00
  14. 10:23
  15. 10:50
  16. 10:23 ←16 = 3 * byMinutes.length + 1
  17. ...

In this case, setting bysetpos to 1,6,11,16 will get the correct result, and it's easy to implement by code because =there is a pattern in bysetpos: index * byMinutes.length + 1

RangeError for "RRULE:FREQ=DAILY"

Describe the bug
A basic RRULE of "FREQ=DAILY" and no other parts fails, outputting: Error: RangeError (index): Invalid value: Not in inclusive range 0..4: 5

This seems to be because the code assumes there will always be a ";" separator (codecs/string/ical.dart#L156)

    var index = name.length;
    final parameters = <String, List<String>>{};
    while (contentLine[index] == ';') {
      // Add 1 for the ";" separator.
      index++;

From the RFC: recur-rule-part *( ";" recur-rule-part )

Environment:

  • Package version: rrule: ^0.1.1

Monthly + byWeekDays + getInstances = fails assertion

  1. Create a monthly rrule that specifies byWeekDays.
  2. Get instances with the start time as now().
  3. Any attempt to access the iterable of instances throws an exception due to a failed assertion.

Expected

Instances in the future

Actual

Failed assertion:

_AssertionError ('package:rrule/src/iteration/date_set_filtering.dart': Failed assertion: line 143 pos 10: 'current == date': is not true.)

Example:

import 'package:rrule/rrule.dart';

void main() {
  final rrule = RecurrenceRule(
    frequency: Frequency.monthly,
    byWeekDays: {ByWeekDayEntry(DateTime.friday, 1)},
  );

  final Iterable<DateTime> instances = rrule.getInstances(
    start: DateTime.now().copyWith(isUtc: true),
  );

  final DateTime nextDueDate = instances.first.copyWith(isUtc: false);
}

Environment:

  • Package version: 0.2.13
  • Flutter: 3.13.3
  • Dart: 3.1.1

toText() error

Describe the bug
When I try to use RruleL10nEn.create(), I get this error:

E/flutter ( 3416): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The method 'getJson' was called on null.
E/flutter ( 3416): Receiver: null
E/flutter ( 3416): Tried calling: getJson("cultures", "cultures.json")

Environment:

^0.1.1

Mimic rrule.js in toText()

Describe the bug

The toText() function of this package has different outputs than the toText() function in rrule.js.

e.g. this package - 'Weekly on Monday'
rrule.js - 'every week on Monday'

For my uses, this is an obvious UX bug and my only solution is to do things on the server using rrule.js. Ideally, this package would have the same toText() output as rrule.js

Environment:

  • Package version: 0.1.1

Package doesn't allow to use intl v0.18

Describe the bug
Most recent version of intl is 0.18. RRule package doesn't allow to use it. Trying to install most up to date version on intl leads to an error
Because rrule 0.2.10 depends on intl ^0.17.0 and no versions of rrule match >0.2.10 <0.3.0, rrule ^0.2.10 requires intl ^0.17.0.

Environment:

  • Package version: 0.2.10

I cannot build the app since the last update

Describe the bug
../../development/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.9/lib/src/iteration/date_set.dart:89:17: Error: The getter 'firstDayOfMonth' isn't defined for the class 'DateTime'.
../…/iteration/date_set.dart:89
[ ] - 'DateTime' is from 'dart:core'.
[ ] Try correcting the name to the name of an existing getter, or defining a getter or field named 'firstDayOfMonth'.
[ ] start: base.firstDayOfMonth.dayOfYear - 1,

[ ] ^^^^^^^^^^^^^^^
[ ] ../../development/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.9/lib/src/iteration/date_set.dart:90:15: Error: The getter 'lastDayOfMonth' isn't defined for the class 'DateTime'.
../…/iteration/date_set.dart:90

[ ] - 'DateTime' is from 'dart:core'.
[ ] Try correcting the name to the name of an existing getter, or defining a getter or field named 'lastDayOfMonth'.
[ ] end: base.lastDayOfMonth.dayOfYear,
[ ] ^^^^^^^^^^^^^^
[ ] ../../development/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.9/lib/src/iteration/date_set_filtering.dart:97:14: Error: The getter 'firstDayOfYear' isn't defined for the class 'DateTime'.
../…/iteration/date_set_filtering.dart:97

[ ] - 'DateTime' is from 'dart:core'.
[ ] Try correcting the name to the name of an existing getter, or defining a getter or field named 'firstDayOfYear'.
[ ] date.firstDayOfYear,
[ ] ^^^^^^^^^^^^^^

[ ] ../../development/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.9/lib/src/iteration/date_set_filtering.dart:98:14: Error: The getter 'lastDayOfYear' isn't defined for the class 'DateTime'.
../…/iteration/date_set_filtering.dart:98
[ ] - 'DateTime' is from 'dart:core'.
[ ] Try correcting the name to the name of an existing getter, or defining a getter or field named 'lastDayOfYear'.
[ ] date.lastDayOfYear,

Environment:
flutter: 3.0.1
rrule: ^0.2.9

No warning for creating a RRULE with BYWEEKNO, but with non-YEARLY frequency

Describe the bug
I previously created an RRULE for an event, which used BYWEEKNO, and the frequency was DAILY.

This is for a project in which events should be exported from a local timetable to Google Calendar. I was surprised to see that, despite the fact that events are added accordingly, I couldn't edit them on my phone.

After reverse-engineering the problem, I realized that it was related to the BYWEEKNO field. Then I found out that, according to RFC5545, BYWEEKNO "MUST NOT be used when the FREQ rule part is set to anything other than YEARLY." (here is a useful discussion on this subject, which I have found while looking for the problem).

Therefore, it would be useful if this package reported an error when a RRULE doesn't respect this standard.

Environment:

  • Package version: 0.1.2

assertion after>start is not true

Describe the bug
I cannot generate dates when

    My rule is ''daily, 5 times ''': 
                  var res = r
              .getInstances(
                 start: DateTIme(2021,5,10),
                 after:DateTIme(2021,5,10),
                before: DateTIme(2021,5,20)
                includeAfter: true,
                 includeBefore: true,
               )
              .toList();

Environment:

  • Package version: rrule: ^0.2.3

Microseconds are removed from start time

Describe the bug
DateTime.copyWith doesn't copy microseconds. (Note: this issue only applies to non-web platforms since the web doesn't have microsecond resolution.)

Reproducible example:

final start = DateTime(2023, 03, 20, 00, 00, 00, 000, 123);

print(start.copyWith(isUtc: true));

Output:

2023-03-20 00:00:00.000Z

Expected:

2023-03-16 00:00:00.000123Z

Suppose we have an rrule with frequency <= daily and without any byHour, byMinute, or bySecond.
When you give a start time to rrule.getInstances() with a start time start that includes microseconds, and you use start.copyWith(isUtc: true) to make it compatible with rrule calculations, the converted DateTime doesn't have microseconds, and therefore neither do the the occurrences.
This causes trouble when making comparisons with the original start.

For instance:

final rrule = RecurrenceRule(frequency: Frequency.daily);
final start = DateTime.now();
final instances = rrule
    .getInstances(start: start.copyWith(isUtc: true))
    .map((o) => o.copyWith(isUtc: false));
print(instances.first == start);

Output:

false

Expected:

true

Environment:

  • Package version: 0.2.12

Incorrect toText for last day of month

image

for rrule string "RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR,SA,SU"

it returns "Monthly on the last instance of weekdays, every Saturday & Sunday"

I think it is better to show "Every month on the last day"

Add toText params: showCount and showUntil?

Could we add some params to toText like:

rrule.toText(
  l10n: l10n,
  showCount: false,
  showUntil: false,
);

Currently this is what's displayed:
Weekly on Monday, 10 times
Weekly on Monday, until Monday, May 15, 2023, 12:00:00 AM

Maybe if we can remove it with arguments so it displays:
Weekly on Monday

Awesome package, many thanks!

All DateTimes must be in UTC!

I'm slightly confused as to why "all DateTimes must be in UTC".

Requirement: I want a recurrence every business day of the week (MON-FRI) at 23:59:59 in local time .
Scenario: Let's say user is on UTC-4 and wants a recurrence every day of the week MON-FRI at 23:59:59 in local time. If we convert FRI 23:59:59 to UTC, we now have SAT 03:59:59 which breaks my requirement of every business day of the week. So in the end, when retrieving the instances, I would only have a total of 4 instances since the 5th instance (Friday) is actually on Saturday_UTC, but Friday_Local.

I guess is this a new requirement? To support local recurrence, or is there any other way to deal with that? I am not sure if anyone would care about days of the week in UTC time, since business days/weekends are always relevant in local time.

Obviously I could fake my local time to be UTC, but that would be wrong by definition, and it would only be to retrieve instances.

Convert String rrule to RecurrenceRule

Hello,

I store rrule events as strings in Firestore. Is there any simple way to convert strings to RecurrenceRule objects. I saw in your documentation that you can convert a RecurrenceRule to string but did not see the opposite.

Fabrice

Flutter [channel=dev] compilation error, 'sortBy' is defined in multiple extensions and neither is more specific

Describe the bug
Using the rrule package with Flutter on channel dev stops the compilation with the following error:

../../Library/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.1.1/lib/src/codecs/text/encoder.dart:383:57: Error: The method 'sortBy' is defined in multiple extensions for 'List<T>' and neither is more specific.
 - 'List' is from 'dart:core'.
Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    final negative = where((e) => key(e) < 0).toList()..sortBy(key);
                                                        ^^^^^^
../../Library/flutter/.pub-cache/hosted/pub.dartlang.org/basics-0.4.0+1/lib/list_basics.dart:93:8: Context: This is one of the extension members.
  void sortBy(Comparable Function(E) sortKey) {
       ^^^^^^
../../Library/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0-nullsafety.5/lib/src/list_extensions.dart:192:8: Context: This is one of the extension members.
  void sortBy<K extends Comparable<K>>(K Function(E element) keyOf,
       ^^^^^^

Environment:

[βœ“] Flutter (Channel dev, 1.24.0-10.2.pre, on Mac OS X 10.15.7 19H15 darwin-x64, locale en-PL)
β€’ Flutter version 1.24.0-10.2.pre at /Users/shemhazai/Library/flutter
β€’ Framework revision 022b333a08 (16 hours ago), 2020-11-18 11:35:09 -0800
β€’ Engine revision 07c1eed46b
β€’ Dart version 2.12.0 (build 2.12.0-29.10.beta)

rrule: version 0.1.1

The problem does not occur on stable & beta flutter channels.

Giving a start time with milli/microseconds to getInstances makes it skip the first instance

Describe the bug
Suppose we have an rrule with frequency <= daily and without any byHour, byMinute, or bySecond.
When you give a start time to rrule.getInstances() that includes units smaller than seconds (e.g. by passing start: DateTime.now()), the first instance is skipped.

Reproducible example:

final rrule = RecurrenceRule(frequency: Frequency.daily);

final instances = rrule.getInstances(
    start: DateTime.utc(
  2023,
  03,
  16,
  00,
  00,
  00,
  123,  // milliseconds
));
print(instances.first);

Output:

2023-03-17 00:00:00.000Z

Expected:

2023-03-16 00:00:00.123Z

Environment:

  • Package version: 0.2.11

intl should be reverted to v0.17.0 until Flutter stable supports v0.18.0

Describe the bug
We cannot use rrule 0.2.11 or 0.2.12 with Flutter stable 3.7.7, since Flutter usually pins the dependency on intl currently it is 0.17. Hence ideally this package and any other packages should follow flutter.
The issue: Because App depends on flutter_localizations from sdk which depends on intl 0.17.0, intl 0.17.0 is required. So, because App depends on intl ^0.18.0, version solving failed, the 0.18.0 in error is due to rrule using intl 0.18.0
Unfortunately we cannot use rrule 0.2.10 since rrule 0.2.12 has a bug fix.

Environment:

  • Package version: rrule: ^0.2.12
  • Flutter: Channel stable, 3.7.7

WKST limitation inquiry

Hi, very impressed by the library however I'm a little unsure how the WKST limitation will impact me. I created a simple event on Google Calendar that repeats every week on Monday. Here's the resulting rrule string:

"FREQ=WEEKLY;WKST=SU;BYDAY=MO"

Considering your default WKST is always Monday, will the library be able to parse that rrule accurately?

Thanks again for the great work!

Samuel

getting instances for rrule yearly, 'every 2nd tuesday of January' fails

When requesting all instances for Rrule 'RRULE:FREQ=YEARLY;COUNT=4;INTERVAL=1;BYDAY=2TU;BYMONTH=1',
it fails with error message:

"The BYDAY rule part MUST NOT be specified with a numeric value when '
'the FREQ rule part is not set to MONTHLY or YEARLY." '
'β€” https://tools.ietf.org/html/rfc5545#section-3.3.10',

However, it seems to be a legit rrule. (Tested also with (https://jakubroztocil.github.io/rrule/) )

Error is in src/iteration/date_set_filtering.dart
Beginning from line 64 (_isFilteredByWeekDays);

if (rrule.frequency == Frequency.yearly && rrule.byMonths.isEmpty)
-> I think byMonths can be deleted here as this seems to be the reason. It works fine without it.
But I am not sure if there is a specific reason?

You can also test with below:

void main() {
  group('instancesAndRruleToRruleString', () {
    test('yearly every second Tuesday of January (for 4 years)', () {
      final rrule = RecurrenceRule(
          frequency: Frequency.yearly,
          count: 4,
          interval: 1,
          byMonths: {1},
          byWeekDays: {ByWeekDayEntry(DateTime.tuesday, 2)});

      final rruleString = rrule.toString();
      print('RRULE_STRING: $rruleString');

      final instances = rrule.getAllInstances(start: DateTime.now().toUtc());
      print('RRULE_INSTANCES: $instances');
      expect(rruleString,
          'RRULE:FREQ=YEARLY;COUNT=4;INTERVAL=1;BYDAY=2TU;BYMONTH=1');
    });
  });
}

Wrong instances for rule RRULE:FREQ=MONTHLY;INTERVAL=1;WKST=MO,

[2020-01-01 00:00:00.000, 2020-02-01 00:00:00.000, 2020-04-01 00:00:00.000, 2020-06-01 00:00:00.000, 2020-08-01 00:00:00.000, 2020-09-01 00:00:00.000, 2020-11-01 00:00:00.000]

RecurrenceRule: RRULE:FREQ=MONTHLY;INTERVAL=1;WKST=MO
StartDate: 2020-01-01 00:00:00.000
EndDate: 2020-12-31 23:59:59.999999

Generator

  final recurrenceRule = RecurrenceRule(frequency: Frequency.monthly, interval: 1, weekStart: DayOfWeek.monday);
  final instances = recurrenceRule.getInstances(start: LocalDateTime.dateTime(start));

Describe the bug
You see, that moths 03, 04, 05, 07, 08, 10, 12 are missing.

Environment:

  • Package version: 0.1.1

SETPOS/bySetPositions is not behaving correctly

When testing for #66 to be correct, I ran into an issue, that occurs also without the change I made there, so I open a seperate ticket here.

I tested the following configuration:
RRULE:FREQ=WEEKLY;BYDAY=MO,WE;BYSETPOS=2,3

Which corresponds to the following setup:

test('#??: works', () {
  final rrule = RecurrenceRule(
    frequency: Frequency.weekly,
    byWeekDays: [
      ByWeekDayEntry(DateTime.monday),
      ByWeekDayEntry(DateTime.wednesday),
    ],
    bySetPositions: const [2, 3],
  );
  final start = DateTime.utc(2024, 02, 01);

  final instances = rrule.getInstances(start: start).take(5);
  expect(
    instances,
    equals([
      DateTime.utc(2024, 02, 07),
      DateTime.utc(2024, 02, 14),
      DateTime.utc(2024, 02, 21),
      DateTime.utc(2024, 02, 28),
      DateTime.utc(2024, 03, 06),
    ]),
  );
});

I get a test failure:

TestFailure (Expected: [
            DateTime:2024-02-07 00:00:00.000Z,
            DateTime:2024-02-14 00:00:00.000Z,
            DateTime:2024-02-21 00:00:00.000Z,
            DateTime:2024-02-28 00:00:00.000Z,
            DateTime:2024-03-06 00:00:00.000Z
          ]
  Actual: TakeIterable<DateTime>:[
            DateTime:2024-02-07 00:00:00.000Z,
            DateTime:2024-02-14 00:00:00.000Z,
            DateTime:2024-02-12 00:00:00.000Z,
            DateTime:2024-02-21 00:00:00.000Z,
            DateTime:2024-02-19 00:00:00.000Z
          ]

I set a breakpoint here:

yield date + timeList[timePosition];

This is on the first hit on the breakpoint:
grafik

This is on the second one:
grafik

It seems like the setPostion is wrongly calculated. In this setup, setPosition: 3 should never occur, since the events are looked at "on a weekly basis" and a new week starts a new set. Therefore only positions 1 and 2 exist.
In this setup, only Wednesdays should be yielded...right?

Environment:

  • Package version: main-branch with fix from #67

Unable to add events to Google Calendar without 'Z' suffix in UNTIL's time of rrule

Describe the bug
I am trying to import events from a local calendar to Google Calendar.
Here are some examples:

RRULE:FREQ=YEARLY;UNTIL=20210605T000000;INTERVAL=1;BYDAY=WE;BYWEEKNO=3,9,11,13,15,17,19,21,41,43,45,47,49,51

RRULE:FREQ=YEARLY;UNTIL=20210605T000000;INTERVAL=1;BYDAY=WE;BYWEEKNO=2,4,10,12,14,16,18,20,22,42,44,46,48,50

The error encountered is
DetailedApiRequestError(status: 400, message: Invalid recurrence rule.)

A fix I found was replacing the time data inside the "UNTIL" field, it is "T000000" here, with an "T000000Z", as below:

RRULE:FREQ=YEARLY;UNTIL=20210605T000000Z;INTERVAL=1;BYDAY=WE;BYWEEKNO=3,9,11,13,15,17,19,21,41,43,45,47,49,51

RRULE:FREQ=YEARLY;UNTIL=20210605T000000Z;INTERVAL=1;BYDAY=WE;BYWEEKNO=2,4,10,12,14,16,18,20,22,42,44,46,48,50

I implemented this fix after finding this paragraph in RFC5545:

  The date with UTC time, or absolute time, is identified by a LATIN
  CAPITAL LETTER Z suffix character, the UTC designator, appended to
  the time value.  For example, the following represents January 19,
  1998, at 0700 UTC:

Environment:

  • Package version: 0.1.2

Error after upgrading flutter

Describe the bug

error logs:

../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:113:48: Error: The property 'firstDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.

  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    DateTime get lastDayOfMonth => plusMonths(1).firstDayOfMonth.subtract(1.days);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:112:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => DateTimeRrule(atStartOfDay).copyWith(day: 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:217:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => isUtc ? DateTime.utc(year, month, 1) : DateTime(year, month, 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set.dart:89:17: Error: The property 'firstDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    start: base.firstDayOfMonth.dayOfYear - 1,
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:217:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => isUtc ? DateTime.utc(year, month, 1) : DateTime(year, month, 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:112:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => DateTimeRrule(atStartOfDay).copyWith(day: 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set.dart:90:15: Error: The property 'lastDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    end: base.lastDayOfMonth.dayOfYear,
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:220:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => isUtc ? DateTime.utc(year, month + 1, 0) : DateTime(year, month + 1, 0);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:113:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => plusMonths(1).firstDayOfMonth.subtract(1.days);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:97:14: Error: The property 'firstDayOfYear' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.firstDayOfYear,
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:223:16: Context: This is one of the extension members.
    DateTime get firstDayOfYear => isUtc ? DateTime.utc(year, 1, 1) : DateTime(year, 1, 1);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:108:16: Context: This is one of the extension members.
    DateTime get firstDayOfYear =>
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:98:14: Error: The property 'lastDayOfYear' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.lastDayOfYear,
    ^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:226:16: Context: This is one of the extension members.
    DateTime get lastDayOfYear => isUtc ? DateTime.utc(year, 12, 31) : DateTime(year, 12, 31);
    ^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:110:16: Context: This is one of the extension members.
    DateTime get lastDayOfYear =>
    ^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:104:14: Error: The property 'firstDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.firstDayOfMonth,
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:217:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => isUtc ? DateTime.utc(year, month, 1) : DateTime(year, month, 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:112:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => DateTimeRrule(atStartOfDay).copyWith(day: 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:105:14: Error: The property 'lastDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.lastDayOfMonth,
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:220:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => isUtc ? DateTime.utc(year, month + 1, 0) : DateTime(year, month + 1, 0);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:113:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => plusMonths(1).firstDayOfMonth.subtract(1.days);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:112:12: Error: The property 'firstDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.firstDayOfMonth,
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:217:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => isUtc ? DateTime.utc(year, month, 1) : DateTime(year, month, 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:112:16: Context: This is one of the extension members.
    DateTime get firstDayOfMonth => DateTimeRrule(atStartOfDay).copyWith(day: 1);
    ^^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/iteration/date_set_filtering.dart:113:12: Error: The property 'lastDayOfMonth' is defined in multiple extensions for 'DateTime' and neither is more specific.
  • 'DateTime' is from 'dart:core'.
    Try using an explicit extension application of the wanted extension or hiding unwanted extensions from scope.
    date.lastDayOfMonth,
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/time-2.1.1/lib/src/extensions.dart:220:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => isUtc ? DateTime.utc(year, month + 1, 0) : DateTime(year, month + 1, 0);
    ^^^^^^^^^^^^^^
    ../../snap/flutter/common/flutter/.pub-cache/hosted/pub.dartlang.org/rrule-0.2.8/lib/src/utils.dart:113:16: Context: This is one of the extension members.
    DateTime get lastDayOfMonth => plusMonths(1).firstDayOfMonth.subtract(1.days);

Environment:

  • Package version:

toText dont take local time into account

Describe the bug
While creating a recurrence, We convert from local time to utc time, but when we try to show to the user with toText(), it shows the utc time but it should show the Local time.

example:
My Time zone is UTC+1;

var untilDate = DateTime.parse('2022-06-16 14:57:30');
The generate recurrence is :

var rr = RecurrenceRule (RRULE:FREQ=DAILY;UNTIL=20220616T135700;INTERVAL=1);

When I use rr.toText() is shows until 2022-06-16 13:57:30 but it show show 2022-06-16 14:57:30

Environment:

rrule: 0.2.9

Decode `RRule` JSON representation that contain lists of type `List<dynamic>`.

Thanks for this awesome library! ❀️

Context

I have an RRule JSON representation stored in Firestore and when it's retrieved, the lists in the received JSON are of type List<dynamic>.

Issue

Decoding a JSON containing a byday key and a corresponding value of type List<dynamic> is causing a FormatException exception to be thrown even if the value is a valid list of decodable day names.
E.x.
The following JSON:

{
    byday: ['WE', 'SU'], 
    freq: 'WEEKLY', 
    interval: 1
}

can't be decoded if ['WE', 'SU'] is represented by a List<dynamic> instead of List<String>.

The cause of this is that the _parseSet function isn't handling the case where the passed json parameter is of type List<dynamic>

  Set<R> _parseSet<T, R>(String name, dynamic json, R Function(T) parse) {
    if (json == null) {
      return const {};
    } else if (json is T) {
      return {parse(json)};
    } else if (json is List<T>) {
      return json.map(parse).toSet();
    } else {
      throw FormatException('Invalid JSON in `$name`.');
    }
  }

I think it would be useful if the parsing function try to cast the json variable to List<T> to attempt to handle it.

byWeekDays with Weekly is wrongly unsupported

Describe the bug
Combining byWeekDays with weekly throws an assert error, however it is a legit case.

The code

RecurrenceRule(
            frequency: Frequency.weekly,
            interval: 1,
            byWeekDays: {ByWeekDayEntry(DateTime.wednesday, DateTime.friday)},
            until: DateTime(2022, 10, 1, 0, 0, 0).toUtc(),
);

throws

assert([Frequency.monthly, Frequency.yearly].contains(frequency) || byWeekDays.noneHasOccurrence,)
The BYDAY rule part MUST NOT be specified with a numeric value when the FREQ rule part is not set to MONTHLY or YEARLY.```

but I believe it is a wrong interpretation of the specification.

Indeed, it must not be specified by a numeric value, but it can be with an array value to describe a usecase like
"Repeat every week on Wednesdays and Fridays" as in

FREQ=WEEKLY;BYDAY=WE,FR;INTERVAL=1;UNTIL=20221001T000000Z

Same for count:

FREQ=WEEKLY;BYDAY=WE,FR;INTERVAL=1;COUNT=10

Expected result
Using another library https://github.com/teambition/rrule-go

r, _ := rrule.NewRRule(rrule.ROption{
	Freq:      rrule.WEEKLY,
	Interval:  1,
	Byweekday: []rrule.Weekday{rrule.WE, rrule.FR},
	Until:     time.Date(2022, 10, 1, 0, 0, 0, 0, time.UTC),
})

yields

"Wed,2022-09-21",
"Fri,2022-09-23",
"Wed,2022-09-28",
"Fri,2022-09-30"

which is cover this case correctly.

Environment:

  • Package version: 0.2.10

Localization improvements in RruleL10n

Localization base class RruleL10n needs some improvements to cater locales where grammar is quite different than English

  • Showing the string weekdays through getter RruleL10n.weekdaysString instead of actual days like Monday, Tuesdays should be optional when all week days are selected, since in some languages there is no word for weekdays(days which are not holidays), and the translations feel weird when trying to force some words, instead the selected days should be shown, for this a new getter e.g. shouldSimplifyWorkingNonWorkingDays can be created in RruleL10n with default value true for backward compatibility, or weekdaysString getter can be made nullable and simplify week days only if this is not null
  • Somewhere in the documentation it should be mentioned: RruleL10n.weekdays has to be set for each locale as the default might not be correct where work days are not from Monday to Friday, since the library is not picking the weekends from Intl package
  • Need to pass Frequency to RruleL10n.until to be able to format the date differently based on frequency and weekdays, example I need to hide day(Mon, Tue) when frequency is Yearly but show it for other frequencies

@JonasWanke let me know what do you think about it, if you agree I can create a PR

Generating date with count parameter for the same day return unexpected result

Describe the bug

If I try to generate instances with this parameters, I get And unexpected result:
the event starts on 2021-06-17, When I generate instance between 2021-06-24 and 2021-06-25 I still get a result, but that event should occurs only 5 times! So at 2021-06-24 , It should have end up already.

var start = DateTime.parse('2021-06-17 19:00:00.000Z');
 var after = DateTime.parse(' 2021-06-24 04:00:00.000Z');
var before = DateTime.parse(' 2021-06-25 03:59:59.000Z');
var rule = RecurrenceRule.fromString("RRULE:FREQ=DAILY;COUNT=5;INTERVAL=1");

var res = rule
              .getInstances(
                start:start,
                after: after,
                before: before,
                includeAfter: true,
                includeBefore: true,
              )
              .toList();
          print(res);

Output
[2021-06-24 19:00:00.000Z]

Environment:

  • Package version: rrule: ^0.2.3

Cannot set yearly recurring on nth weekday of month

Describe the bug

When setting FREQ=YEARLY;INTERVAL=1;BYMINUTE=0;BYHOUR=0;BYDAY=4SU;BYMONTH=11;WKST=SU getInstances() has the assert error ""The BYDAY rule part MUST NOT be specified with a numeric value when the FREQ rule part is not set to MONTHLY or YEARLY." β€” https://tools.ietf.org/html/rfc5545#section-3.3.10"".

This come from lib/src/iteration/date_set_filtering.dart in _isFilteredByWeekDays(). None of the conditions allow for this:

if (rrule.frequency == Frequency.yearly && rrule.byMonths.isEmpty) {
} else if (rrule.frequency == Frequency.monthly) {
} else {

Environment:

  • Package version: 0.1.1

Support `RecurrenceSet`s

A RecurrenceSet consists of three lists, all of which can be empty:

/// `RRULE`, https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.3
final List<RecurrenceRule> recurrenceRules;

/// `RDATE`, https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.2
final List<DateTime> recurrenceDateTimes;

/// `EXDATE`, https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.1
final List<DateTime> exclusionDateTimes;

This is similar to RRuleSet in rrule.js, but without exrule (at least for now).

No way to clear until and count in RecurrenceRule

Describe the bug
Unable to clear count and until fields of RecurrenceRule, since copyWith will ignore null values passed in.

Workaround is to create a new RecurrenceRule and pass in the parameters.

Solution could be adding .clearUntil and .clearCount methods.

Environment:

  • Package version: 0.1.2

Adding the dtstart property in the RRULE

If you look at a more elaborated approach here http://jkbrzt.github.io/rrule/,

You will notice the property dtstart which is the start date of the rule. It should work similarly but opposite to the until property.

It will be great to have this included as a property.
I also will like to work on this if no one is available to contribute

Instance generation with SETPOS fails after generating some instances

Describe the bug
Instance generation with SETPOS fails after generating some instances.

Steps to reproduce:
Create this:
RRULE:FREQ=MONTHLY;UNTIL=20230106T141500;INTERVAL=1;BYDAY=WE,SA;BYMONTH=10,12;BYSETPOS=2,4

What happens: Take (dynamic result = myRule.getInstances(start: start).take(5)) the next 5 instances starting at 2022-10-09 14:15:00 and see that you can only get one (12OCT2022) before an IntegerDivisionByZeroException happens.

The exception occurs when you try to use the result in any way, such as printing the instances (print(result)) or looping through them (for (DateTime dt in result)),

Expected behavior: The four instances shown below can be retrieved
image

Link to website in case it will help: https://exontrol.com/exicalendar.jsp?config=/js#calendar

Environment:

  • Package version: 0.2.10

Support for Norwegian human readable text

I find this library very useful and interesting. Please provide the support for Norwegian Human Readable Text. I find the support for english but please make it possible for Norwegian too.

Thanks

Dutch translations

Opening this feature request for adding Dutch translations. I might create a PR for this myself but cannot guarantee it yet at this time.

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.