diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index a82075f2..1298a454 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -1,6 +1,12 @@
Release Highlights
==================
+Version 3.1.0:
+--------------
+ * New publicly available functions:
+ + icalrecurrencetype_encode_day
+ + icalrecurrencetype_encode_month
+
Version 3.0.3:
--------------
* VTODO COMPLETED property can be a DATE-TIME or DATE (for backward compatibility)
diff --git a/src/libical-glib/api/i-cal-recurrence-type.xml b/src/libical-glib/api/i-cal-recurrence-type.xml
index 6c68ed84..8f26a4f0 100644
--- a/src/libical-glib/api/i-cal-recurrence-type.xml
+++ b/src/libical-glib/api/i-cal-recurrence-type.xml
@@ -72,6 +72,12 @@
Decode a day to a position of the weekday.
+
+
+
+
+ Encodes the @weekday and @position into a single value, which can by split by i_cal_recurrence_type_day_day_of_week() and i_cal_recurrence_type_day_position().
+
@@ -79,8 +85,14 @@
-
- Decode a month and check whether it is NOT a leap month.
+
+ Decode a month from an encoded value by i_cal_recurrence_type_encode_month().
+
+
+
+
+
+ Encodes the @month and the @is_leap flag into a single value, which can be split by i_cal_recurrence_type_month_is_leap() and i_cal_recurrence_type_month_month().
diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c
index 96b972bc..5beeab93 100644
--- a/src/libical/icalrecur.c
+++ b/src/libical/icalrecur.c
@@ -531,7 +531,7 @@ static int icalrecur_add_bydayrules(struct icalrecur_parser *parser,
return -1;
}
- array[i++] = (short)(sign * (wd + 8 * weekno));
+ array[i++] = icalrecurrencetype_encode_day(wd, sign * weekno);
array[i] = ICAL_RECURRENCE_ARRAY_MAX;
}
@@ -3022,6 +3022,11 @@ int icalrecurrencetype_day_position(short day)
return pos;
}
+short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
+{
+ return (weekday + (8 * abs(position))) * ((position < 0) ? -1 : 1);
+}
+
/**
* The 'month' element of the by_month array is encoded to allow
* representation of the "L" leap suffix (RFC 7529).
@@ -3040,6 +3045,11 @@ int icalrecurrencetype_month_month(short month)
return (month & ~LEAP_MONTH);
}
+short icalrecurrencetype_encode_month(int month, int is_leap)
+{
+ return month | (is_leap ? LEAP_MONTH : 0);
+}
+
/** Fill an array with the 'count' number of occurrences generated by
* the rrule. Note that the times are returned in UTC, but the times
* are calculated in local time. YOu will have to convert the results
diff --git a/src/libical/icalrecur.h b/src/libical/icalrecur.h
index 48ceb055..45409950 100644
--- a/src/libical/icalrecur.h
+++ b/src/libical/icalrecur.h
@@ -177,7 +177,7 @@ struct icalrecurrencetype
short by_month_day[ICAL_BY_MONTHDAY_SIZE];
short by_year_day[ICAL_BY_YEARDAY_SIZE];
short by_week_no[ICAL_BY_WEEKNO_SIZE];
- short by_month[ICAL_BY_MONTH_SIZE];
+ short by_month[ICAL_BY_MONTH_SIZE]; /* Encoded value, see below */
short by_set_pos[ICAL_BY_SETPOS_SIZE];
/* For RSCALE extension (RFC 7529) */
@@ -206,6 +206,12 @@ LIBICAL_ICAL_EXPORT enum icalrecurrencetype_weekday icalrecurrencetype_day_day_o
/** 0 == any of day of week. 1 == first, 2 = second, -2 == second to last, etc */
LIBICAL_ICAL_EXPORT int icalrecurrencetype_day_position(short day);
+/** Encodes the 'weekday' and 'position' into a form, which can be stored
+ * to icalrecurrencetype::by_day array. Use icalrecurrencetype_day_day_of_week()
+ * and icalrecurrencetype_day_position() to split the encoded value back into the parts.
+ */
+LIBICAL_ICAL_EXPORT short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position);
+
/**
* The 'month' element of the by_month array is encoded to allow
* representation of the "L" leap suffix (RFC 7529).
@@ -216,6 +222,12 @@ LIBICAL_ICAL_EXPORT int icalrecurrencetype_month_is_leap(short month);
LIBICAL_ICAL_EXPORT int icalrecurrencetype_month_month(short month);
+/** Encodes the 'month' and the 'is_leap' into a form, which can be stored
+ * to icalrecurrencetype::by_month array. Use icalrecurrencetype_month_is_leap()
+ * and icalrecurrencetype_month_month() to split the encoded value back into the parts
+ */
+LIBICAL_ICAL_EXPORT short icalrecurrencetype_encode_month(int month, int is_leap);
+
/** Recurrance rule parser */
/** Convert between strings and recurrencetype structures. */
diff --git a/src/test/libical-glib/recurrence-type.py b/src/test/libical-glib/recurrence-type.py
index 2da2c193..25065a7e 100755
--- a/src/test/libical-glib/recurrence-type.py
+++ b/src/test/libical-glib/recurrence-type.py
@@ -29,6 +29,26 @@ assert(ICalGLib.RecurrenceType.day_position(15) == 1);
assert(ICalGLib.RecurrenceType.day_position(16) == 2);
assert(ICalGLib.RecurrenceType.day_position(25) == 3);
+encoded = ICalGLib.RecurrenceType.encode_day(ICalGLib.RecurrenceTypeWeekday.MONDAY_WEEKDAY, 0);
+assert(ICalGLib.RecurrenceType.day_day_of_week(encoded) == ICalGLib.RecurrenceTypeWeekday.MONDAY_WEEKDAY);
+assert(ICalGLib.RecurrenceType.day_position(encoded) == 0);
+
+encoded = ICalGLib.RecurrenceType.encode_day(ICalGLib.RecurrenceTypeWeekday.THURSDAY_WEEKDAY, -3);
+assert(ICalGLib.RecurrenceType.day_day_of_week(encoded) == ICalGLib.RecurrenceTypeWeekday.THURSDAY_WEEKDAY);
+assert(ICalGLib.RecurrenceType.day_position(encoded) == -3);
+
+encoded = ICalGLib.RecurrenceType.encode_day(ICalGLib.RecurrenceTypeWeekday.FRIDAY_WEEKDAY, 2);
+assert(ICalGLib.RecurrenceType.day_day_of_week(encoded) == ICalGLib.RecurrenceTypeWeekday.FRIDAY_WEEKDAY);
+assert(ICalGLib.RecurrenceType.day_position(encoded) == 2);
+
+encoded = ICalGLib.RecurrenceType.encode_month(3, 0);
+assert(ICalGLib.RecurrenceType.month_month(encoded) == 3);
+assert(not ICalGLib.RecurrenceType.month_is_leap(encoded));
+
+encoded = ICalGLib.RecurrenceType.encode_month(12, 1);
+assert(ICalGLib.RecurrenceType.month_month(encoded) == 12);
+assert(ICalGLib.RecurrenceType.month_is_leap(encoded));
+
string = "COUNT=10;FREQ=DAILY";
recurrence = ICalGLib.RecurrenceType.from_string(string);
assert(recurrence.as_string_r() == "FREQ=DAILY;COUNT=10");
diff --git a/src/test/regression.c b/src/test/regression.c
index 8d069a2c..01a63512 100644
--- a/src/test/regression.c
+++ b/src/test/regression.c
@@ -1216,6 +1216,88 @@ void test_recur()
/* test_increment();*/
}
+void test_recur_encode_by_day()
+{
+ struct icalrecurrencetype rt;
+ int ii;
+
+ rt = icalrecurrencetype_from_string("FREQ=WEEKLY;BYDAY=WE");
+ ok("Is weekly recurrence", (rt.freq == ICAL_WEEKLY_RECURRENCE));
+ ok("The by_day[0] is set", (rt.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The by_day[1] is not set", (rt.by_day[1] == ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The day of week is Wednesday", (icalrecurrencetype_day_day_of_week(rt.by_day[0]) == ICAL_WEDNESDAY_WEEKDAY));
+ ok("The position is 0", (icalrecurrencetype_day_position(rt.by_day[0]) == 0));
+ ok("Encoded value matches", (icalrecurrencetype_encode_day(ICAL_WEDNESDAY_WEEKDAY, 0) == rt.by_day[0]));
+
+ rt = icalrecurrencetype_from_string("FREQ=MONTHLY;BYDAY=2FR");
+ ok("Is monthly recurrence", (rt.freq == ICAL_MONTHLY_RECURRENCE));
+ ok("The by_day[0] is set", (rt.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The by_day[1] is not set", (rt.by_day[1] == ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The day of week is Friday", (icalrecurrencetype_day_day_of_week(rt.by_day[0]) == ICAL_FRIDAY_WEEKDAY));
+ ok("The position is 2", (icalrecurrencetype_day_position(rt.by_day[0]) == 2));
+ ok("Encoded value matches", (icalrecurrencetype_encode_day(ICAL_FRIDAY_WEEKDAY, 2) == rt.by_day[0]));
+
+ rt = icalrecurrencetype_from_string("FREQ=YEARLY;BYDAY=-3MO");
+ ok("Is yearly recurrence", (rt.freq == ICAL_YEARLY_RECURRENCE));
+ ok("The by_day[0] is set", (rt.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The by_day[1] is not set", (rt.by_day[1] == ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The day of week is Monday", (icalrecurrencetype_day_day_of_week(rt.by_day[0]) == ICAL_MONDAY_WEEKDAY));
+ ok("The position is -3", (icalrecurrencetype_day_position(rt.by_day[0]) == -3));
+ ok("Encoded value matches", (icalrecurrencetype_encode_day(ICAL_MONDAY_WEEKDAY, -3) == rt.by_day[0]));
+
+ for (ii = -5; ii <= 5; ii++) {
+ icalrecurrencetype_weekday wd;
+
+ for (wd = ICAL_SUNDAY_WEEKDAY; wd <= ICAL_SATURDAY_WEEKDAY; wd++) {
+ short encoded;
+
+ if (VERBOSE)
+ printf(" Trying weekday %d and position %d\n", wd, ii);
+
+ encoded = icalrecurrencetype_encode_day(wd, ii);
+
+ ok("Decoded day of week matches", (icalrecurrencetype_day_day_of_week(encoded) == wd));
+ ok("Decoded position matches", (icalrecurrencetype_day_position(encoded) == ii));
+ }
+ }
+}
+
+void test_recur_encode_by_month()
+{
+ struct icalrecurrencetype rt;
+ int ii, jj;
+
+ rt = icalrecurrencetype_from_string("FREQ=WEEKLY;BYMONTH=2");
+ ok("Is weekly recurrence", (rt.freq == ICAL_WEEKLY_RECURRENCE));
+ ok("The by_month[0] is set", (rt.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The by_month[1] is not set", (rt.by_month[1] == ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The month is 2", (icalrecurrencetype_month_month(rt.by_month[0]) == 2));
+ ok("Is not leap month", (icalrecurrencetype_month_is_leap(rt.by_month[0]) == 0));
+ ok("Encoded value matches", (icalrecurrencetype_encode_month(2, 0) == rt.by_month[0]));
+
+ rt = icalrecurrencetype_from_string("FREQ=MONTHLY;BYMONTH=3L");
+ ok("Is monthly recurrence", (rt.freq == ICAL_MONTHLY_RECURRENCE));
+ ok("The by_month[0] is set", (rt.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The by_month[1] is not set", (rt.by_month[1] == ICAL_RECURRENCE_ARRAY_MAX));
+ ok("The month is 3", (icalrecurrencetype_month_month(rt.by_month[0]) == 3));
+ ok("Is leap month", (icalrecurrencetype_month_is_leap(rt.by_month[0]) != 0));
+ ok("Encoded value matches", (icalrecurrencetype_encode_month(3, 1) == rt.by_month[0]));
+
+ for (ii = 0; ii <= 1; ii++) {
+ for (jj = 1; jj <= 12; jj++) {
+ short encoded;
+
+ if (VERBOSE)
+ printf(" Trying month %d as %sleap\n", jj, ii ? "" : "not ");
+
+ encoded = icalrecurrencetype_encode_month(jj, ii);
+
+ ok("Decoded month matches", (icalrecurrencetype_month_month(encoded) == jj));
+ ok("Decoded is-leap matches", ((icalrecurrencetype_month_is_leap(encoded) ? 1 : 0) == ii));
+ }
+ }
+}
+
void test_expand_recurrence()
{
time_t arr[10];
@@ -4108,6 +4190,8 @@ int main(int argc, char *argv[])
test_run("Test day of year of week start", test_start_of_week, do_test, do_header);
test_run("Test recur parser", test_recur_parser, do_test, do_header);
test_run("Test recur", test_recur, do_test, do_header);
+ test_run("Test recur encode by_day", test_recur_encode_by_day, do_test, do_header);
+ test_run("Test recur encode by_month", test_recur_encode_by_month, do_test, do_header);
test_run("Test Recurring Events File", test_recur_file, do_test, do_header);
test_run("Test parameter bug", test_recur_parameter_bug, do_test, do_header);
test_run("Test Array Expansion", test_expand_recurrence, do_test, do_header);