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);