summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rw-r--r--Install.txt1
-rw-r--r--libical.pc.in5
-rwxr-xr-xscripts/setup-travis.sh2
-rw-r--r--src/libical/CMakeLists.txt8
-rw-r--r--src/libical/ical_file.cmake2
-rw-r--r--src/libical/icalrecur.c1694
-rw-r--r--src/libical/icalrecur.h43
-rw-r--r--src/test/CMakeLists.txt10
-rw-r--r--src/test/icalrecur_withicu_test.out72
10 files changed, 1344 insertions, 504 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d5aad80c..c96d97ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -94,6 +94,17 @@ endif()
# must have Perl to create the derived stuff
find_package(Perl REQUIRED)
+# libicu is highly recommended for RSCALE support
+# libicu can be found at http://www.icu-project.org
+# RSCALE info at http://tools.ietf.org/html/draft-daboo-icalendar-rscale
+find_package(ICU)
+if(ICU_FOUND)
+ set(HAVE_LIBICU 1)
+endif()
+if(ICU_I18N_FOUND)
+ set(HAVE_LIBICU_I18N 1)
+endif()
+
# MSVC specific definitions
if(WIN32)
if(MSVC)
diff --git a/Install.txt b/Install.txt
index a1168928..8dbdb1e5 100644
--- a/Install.txt
+++ b/Install.txt
@@ -13,6 +13,7 @@ To build a debug version pass -DCMAKE_BUILD_TYPE=Debug to cmake.
To build libical you will need:
- CMake version 2.8.9 or higher
- Perl
+ - libicu (not required but strongly recommended)
- a C compiler (let us know if the build fails with your C compiler)
Building on Unix with gcc or clang:
diff --git a/libical.pc.in b/libical.pc.in
index 6ca4c7c8..7774dce2 100644
--- a/libical.pc.in
+++ b/libical.pc.in
@@ -3,9 +3,10 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
threadslib=@PTHREAD_LIBS@
-
+iculib=@ICU_LIBRARIES@ @ICU_I18N_LIBRARIES@
+
Name: libical
Description: An implementation of basic iCAL protocols
Version: @VERSION@
-Libs: -L${libdir} -lical -licalss -licalvcal ${threadslib}
+Libs: -L${libdir} -lical -licalss -licalvcal ${threadslib} ${iculib}
Cflags: -I${includedir}
diff --git a/scripts/setup-travis.sh b/scripts/setup-travis.sh
index ede3e46b..22318d57 100755
--- a/scripts/setup-travis.sh
+++ b/scripts/setup-travis.sh
@@ -13,4 +13,6 @@ else
echo "yes" | sudo add-apt-repository ppa:kalakris/cmake
sudo apt-get update -qq
sudo apt-get install cmake
+ #comment out for now as it causes an Exception: SegFault in the regression test
+ #sudo apt-get install libicu-dev
fi
diff --git a/src/libical/CMakeLists.txt b/src/libical/CMakeLists.txt
index 2c960496..776eb86c 100644
--- a/src/libical/CMakeLists.txt
+++ b/src/libical/CMakeLists.txt
@@ -10,6 +10,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/libical
${CMAKE_BINARY_DIR}/src/libical
+ ${LIBICU_INCLUDE_DIRS}
)
if(WIN32)
@@ -266,6 +267,13 @@ add_dependencies(ical ical-header)
target_link_libraries(ical ${CMAKE_THREAD_LIBS_INIT})
+if(ICU_FOUND)
+ target_link_libraries(ical ${ICU_LIBRARIES})
+endif()
+if(ICU_I18N_FOUND)
+ target_link_libraries(ical ${ICU_I18N_LIBRARIES})
+endif()
+
if(WINCE)
target_link_libraries(ical ${WCECOMPAT_LIBRARIES})
endif()
diff --git a/src/libical/ical_file.cmake b/src/libical/ical_file.cmake
index 21da3508..783dbea6 100644
--- a/src/libical/ical_file.cmake
+++ b/src/libical/ical_file.cmake
@@ -7,6 +7,7 @@ set(COMBINEDHEADERSICAL
${TOPS}/src/libical/icalperiod.h
${TOPS}/src/libical/icalenums.h
${TOPS}/src/libical/icaltypes.h
+ ${TOPS}/src/libical/icalarray.h
${TOPS}/src/libical/icalrecur.h
${TOPS}/src/libical/icalattach.h
${TOPB}/src/libical/icalderivedvalue.h
@@ -16,7 +17,6 @@ set(COMBINEDHEADERSICAL
${TOPB}/src/libical/icalderivedproperty.h
${TOPS}/src/libical/icalproperty.h
${TOPS}/src/libical/pvl.h
- ${TOPS}/src/libical/icalarray.h
${TOPS}/src/libical/icalcomponent.h
${TOPS}/src/libical/icaltimezone.h
${TOPS}/src/libical/icalparser.h
diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c
index 9510ac82..cd57458e 100644
--- a/src/libical/icalrecur.c
+++ b/src/libical/icalrecur.c
@@ -135,6 +135,13 @@
#include "config.h"
#endif
+#ifdef HAVE_LIBICU
+#include <unicode/ucal.h>
+#define RSCALE_IS_SUPPORTED 1
+#else
+#define RSCALE_IS_SUPPORTED 0
+#endif
+
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
@@ -162,7 +169,9 @@ typedef long intptr_t;
#include "icalerror.h"
#include "icalmemory.h"
+#include "icaltimezone.h"
+#include <ctype.h> /* for tolower */
#include <stdlib.h> /* for malloc, strtol */
#include <errno.h> /* for errno */
#include <string.h> /* for strdup and strchr*/
@@ -208,6 +217,14 @@ icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str);
const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind);
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
+const char* icalrecur_skip_to_string(icalrecurrencetype_skip kind);
+icalrecurrencetype_skip icalrecur_string_to_skip(const char* str);
+
+int icalrecurrencetype_rscale_is_supported(void)
+{
+ return RSCALE_IS_SUPPORTED;
+}
+
/*********************** Rule parsing routines ************************/
@@ -291,7 +308,6 @@ void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
_unused(parser)
char *t, *n;
int i=0;
- int sign = 1;
int v;
n = vals;
@@ -310,21 +326,17 @@ void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
*n = 0;
n++;
}
-
- /* Get optional sign. HACK. sign is not allowed for all BYxxx
- rule parts */
- if( *t == '-'){
- sign = -1;
- t++;
- } else if (*t == '+'){
- sign = 1;
- t++;
- } else {
- sign = 1;
- }
- v = atoi(t) * sign ;
+ /* HACK. sign is not allowed for all BYxxx rule parts */
+ v = strtol(t, &t, 10);
+ /* Check for leap month suffix (RSCALE only) */
+ if (icalrecurrencetype_rscale_is_supported() &&
+ array == parser->rt.by_month && *t == 'L') {
+ /* The "L" suffix in a BYMONTH recur-rule-part
+ is encoded by multiplying the month by 256 */
+ v <<= 8;
+ }
array[i++] = (short)v;
array[i] = ICAL_RECURRENCE_ARRAY_MAX;
@@ -461,6 +473,12 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
if (strcasecmp(name,"FREQ") == 0){
parser.rt.freq = icalrecur_string_to_freq(value);
+ } else if (icalrecurrencetype_rscale_is_supported() &&
+ strcasecmp(name,"RSCALE") == 0){
+ parser.rt.rscale = icalmemory_tmp_copy(value);
+ } else if (icalrecurrencetype_rscale_is_supported() &&
+ strcasecmp(name,"SKIP") == 0){
+ parser.rt.skip = icalrecur_string_to_skip(value);
} else if (strcasecmp(name,"COUNT") == 0){
parser.rt.count = atoi(value);
} else if (strcasecmp(name,"UNTIL") == 0){
@@ -562,6 +580,17 @@ char* icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
icalmemory_append_string(&str,&str_p,&buf_sz,
icalrecur_freq_to_string(recur->freq));
+ if(recur->rscale != 0){
+ icalmemory_append_string(&str,&str_p,&buf_sz,";RSCALE=");
+ icalmemory_append_string(&str,&str_p,&buf_sz, recur->rscale);
+
+ if(recur->skip != ICAL_SKIP_NONE && recur->skip != ICAL_SKIP_BACKWARD){
+ icalmemory_append_string(&str,&str_p,&buf_sz,";SKIP=");
+ icalmemory_append_string(&str,&str_p,&buf_sz,
+ icalrecur_skip_to_string(recur->skip));
+ }
+ }
+
if(recur->until.year != 0){
temp[0] = 0;
@@ -612,6 +641,11 @@ char* icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
icalmemory_append_string(&str,&str_p,&buf_sz,temp);
}
+ } else if (j == 7 /* BYMONTH */
+ && icalrecurrencetype_month_is_leap(array[i])) {
+ snprintf(temp,sizeof(temp),"%dL",
+ icalrecurrencetype_month_month(array[i]));
+ icalmemory_append_string(&str,&str_p,&buf_sz, temp);
} else {
snprintf(temp,sizeof(temp),"%d",array[i]);
icalmemory_append_string(&str,&str_p,&buf_sz, temp);
@@ -661,6 +695,12 @@ struct icalrecur_iterator_impl {
int occurrence_no; /* number of step made on t iterator */
struct icalrecurrencetype rule;
+#ifdef HAVE_LIBICU
+ UCalendar *greg; /* Gregorian calendar */
+ UCalendar *rscale; /* RSCALE calendar */
+ struct icaltimetype rstart; /* DTSTART in RSCALE */
+#endif
+
short days[366];
short days_index;
@@ -673,7 +713,8 @@ struct icalrecur_iterator_impl {
};
-static void increment_year(icalrecur_iterator* impl, int inc);
+static int check_contract_restriction(icalrecur_iterator* impl,
+ enum byrule byrule, int v);
int icalrecur_iterator_sizeof_byarray(short* byarray)
{
@@ -790,6 +831,916 @@ static int count_byrules(icalrecur_iterator* impl)
}
*/
+
+#ifdef HAVE_LIBICU
+/*
+ * Callbacks for recurrence rules with RSCALE support (using ICU)
+ *
+ * References:
+ * - http://tools.ietf.org/html/draft-daboo-icalendar-rscale
+ * - http://en.wikipedia.org/wiki/Intercalation_%28timekeeping%29
+ * - http://icu-project.org/apiref/icu4c/ucal_8h.html
+ * - http://cldr.unicode.org/development/development-process/design-proposals/chinese-calendar-support
+ * - http://cldr.unicode.org/development/development-process/design-proposals/islamic-calendar-types
+ *
+ * ICU Notes:
+ * - Months are 0-based
+ * - Leap months in Chinese and Hebrew calendars are handled differently
+ */
+
+icalarray* icalrecurrencetype_rscale_supported_calendars(void)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ UEnumeration *en;
+ icalarray *calendars;
+ const char *cal;
+
+ calendars = icalarray_new(sizeof(const char **), 20);
+
+ en = ucal_getKeywordValuesForLocale("calendar", NULL, FALSE, &status);
+ while ((cal = uenum_next(en, NULL, &status))) {
+ cal = icalmemory_tmp_copy(cal);
+ icalarray_append(calendars, &cal);
+ }
+ uenum_close(en);
+
+ return calendars;
+}
+
+static void set_second(icalrecur_iterator* impl, int second)
+{
+ ucal_set(impl->rscale, UCAL_SECOND, second);
+}
+
+static void set_minute(icalrecur_iterator* impl, int minute)
+{
+ ucal_set(impl->rscale, UCAL_MINUTE, minute);
+}
+
+static void set_hour(icalrecur_iterator* impl, int hour)
+{
+ ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, hour);
+}
+
+static int set_day_of_week(icalrecur_iterator* impl, int dow, int pos)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ int max_pos = ucal_getLimit(impl->rscale, UCAL_DAY_OF_WEEK_IN_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+
+ if (pos > max_pos || pos < -max_pos) return 0;
+
+ ucal_set(impl->rscale, UCAL_DAY_OF_WEEK, dow);
+ if (pos) ucal_set(impl->rscale, UCAL_DAY_OF_WEEK_IN_MONTH, pos);
+
+ return 1;
+}
+
+static void set_day_of_month(icalrecur_iterator* impl, int day)
+{
+ if (day < 0) {
+ UErrorCode status = U_ZERO_ERROR;
+ int days_in_month = ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ day = days_in_month + day + 1;
+ }
+ ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, day);
+}
+
+static void set_month(icalrecur_iterator* impl, int month)
+{
+ int is_leap_month = icalrecurrencetype_month_is_leap(month);
+
+ month = icalrecurrencetype_month_month(month);
+ if (month < 0) {
+ UErrorCode status = U_ZERO_ERROR;
+ int months_in_year = ucal_getLimit(impl->rscale, UCAL_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ month = months_in_year + month + 1;
+ }
+ else month--; /* UCal is 0-based */
+
+ ucal_set(impl->rscale, UCAL_MONTH, month);
+ if (is_leap_month) ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
+}
+
+static void set_day_of_year(icalrecur_iterator* impl, int doy)
+{
+ if (doy < 0) {
+ UErrorCode status = U_ZERO_ERROR;
+ int days_in_year = ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ doy = days_in_year + doy + 1;
+ }
+ ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, doy);
+}
+
+static int get_start_of_week(icalrecur_iterator* impl)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ int doy, dow;
+
+ doy = ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
+ dow = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+ dow -= impl->rule.week_start;
+ if (dow < 0) dow += 7;
+
+ return (doy - dow);
+}
+
+static int get_day_of_week(icalrecur_iterator* impl)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ return ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+}
+
+static int get_week_number(icalrecur_iterator* impl, struct icaltimetype tt)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ UDate last_millis;
+ int month, weekno;
+
+ /* Save existing rscale date */
+ last_millis = ucal_getMillis(impl->rscale, &status);
+
+ month = icalrecurrencetype_month_month(tt.month) - 1; /* UCal is 0-based */
+ ucal_setDate(impl->rscale, tt.year, month, tt.day, &status);
+ if (icalrecurrencetype_month_is_leap(tt.month))
+ ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
+
+ weekno = ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
+
+ /* Restore saved rscale date */
+ ucal_setMillis(impl->rscale, last_millis, &status);
+
+ return weekno;
+}
+
+static int get_days_in_month(icalrecur_iterator* impl, int month, int year)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_set(impl->rscale, UCAL_YEAR, year);
+
+ if (!month) month = impl->rstart.month;
+ set_month(impl, month);
+
+ return ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+}
+
+static int skip_leap(icalrecur_iterator *impl, int day, int month)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ int my_day = ucal_get(impl->rscale, UCAL_DAY_OF_MONTH, &status);
+ int my_month = ucal_get(impl->rscale, UCAL_MONTH, &status);
+
+ if (day < 0) {
+ int days_in_month = ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ my_day -= days_in_month + 1;
+ }
+
+ if (month < 0) {
+ int months_in_year = ucal_getLimit(impl->rscale, UCAL_MONTH,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ my_month -= months_in_year + 1;
+ }
+ else my_month++; /* UCal is 0-based */
+
+ if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) my_month <<= 8;
+
+ if (my_day != day) {
+ if (impl->rule.skip == ICAL_SKIP_YES) return 1;
+
+ else if (impl->rule.skip == ICAL_SKIP_BACKWARD)
+ ucal_add(impl->rscale, UCAL_DAY_OF_YEAR, -1, &status);
+ }
+ else if (my_month != month) {
+ if (impl->rule.skip == ICAL_SKIP_YES) return 1;
+
+ else if (impl->rule.skip == ICAL_SKIP_BACKWARD)
+ ucal_add(impl->rscale, UCAL_MONTH, -1, &status);
+ }
+
+ return 0;
+}
+
+static int get_day_of_year(icalrecur_iterator* impl,
+ int year, int month, int day, int *dow)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_set(impl->rscale, UCAL_YEAR, year);
+
+ if (!month) month = impl->rstart.month;
+ set_month(impl, month);
+
+ if (!day) day = impl->rstart.day;
+ set_day_of_month(impl, day);
+
+ if (skip_leap(impl, day, month)) {
+ if (dow) *dow = 0;
+ return 0;
+ }
+
+ if (dow) *dow = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+
+ return ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
+}
+
+static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator* impl,
+ int normalize)
+{
+ struct icaltimetype tt = impl->dtstart;
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar *cal = impl->rscale;
+ int is_leap_month = 0;
+
+ if (impl->greg && normalize) {
+ /* Convert to Gregorian date */
+ UDate millis = ucal_getMillis(impl->rscale, &status);
+ ucal_setMillis(impl->greg, millis, &status);
+ cal = impl->greg;
+ }
+ else is_leap_month = ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
+
+ tt.year = ucal_get(cal, UCAL_YEAR, &status);
+ tt.day = ucal_get(cal, UCAL_DATE, &status);
+ tt.month = ucal_get(cal, UCAL_MONTH, &status) + 1; /* UCal is 0-based */
+ if (is_leap_month) tt.month <<= 8;
+
+ if (!tt.is_date) {
+ tt.hour = ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
+ tt.minute = ucal_get(cal, UCAL_MINUTE, &status);
+ tt.second = ucal_get(cal, UCAL_SECOND, &status);
+ }
+
+ return tt;
+}
+
+struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator* impl,
+ int day, int year, int *weekno)
+{
+ ucal_set(impl->rscale, UCAL_YEAR, year);
+ if (day < 0) {
+ UErrorCode status = U_ZERO_ERROR;
+ int days_in_year = ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ day = days_in_year + day + 1;
+ }
+ ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, day);
+
+ if (weekno) {
+ UErrorCode status = U_ZERO_ERROR;
+ *weekno = ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
+ }
+
+ return occurrence_as_icaltime(impl, 0);
+}
+
+static int is_day_in_byday(icalrecur_iterator *impl, struct icaltimetype tt)
+{
+ int idx;
+
+ for (idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++) {
+ UErrorCode status = U_ZERO_ERROR;
+ int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
+ int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
+ int this_dow = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+
+ if (dow == this_dow) {
+ int day;
+
+ if (pos == 0) return 1; /* Just a dow, like "TU" or "FR" */
+
+ /* Get day of pos */
+ ucal_set(impl->rscale, UCAL_DAY_OF_WEEK_IN_MONTH, pos);
+ day = ucal_get(impl->rscale, UCAL_DAY_OF_MONTH, &status);
+
+ /* Reset current day */
+ ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, tt.day);
+
+ /* Compare day of pos to current day */
+ if (day == tt.day) return 1; /* pos+dow: "3FR" or -1TU" */
+ }
+ }
+
+ return 0;
+}
+
+static void increment_year(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_YEAR, inc, &status);
+}
+
+static void __increment_month(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_MONTH, inc, &status);
+}
+
+static void increment_monthday(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, inc, &status);
+}
+
+static void increment_hour(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, inc, &status);
+}
+
+static void increment_minute(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_MINUTE, inc, &status);
+}
+
+static void increment_second(icalrecur_iterator* impl, int inc)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_add(impl->rscale, UCAL_SECOND, inc, &status);
+}
+
+static void expand_by_day_init(icalrecur_iterator* impl, int year,
+ int *start_dow, int *end_dow, int *end_year_day)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ ucal_set(impl->rscale, UCAL_YEAR, year);
+ ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, 1);
+
+ /* Find the day that 1st Jan falls on, 1 (Sun) to 7 (Sat). */
+ *start_dow = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+
+ /* Get the last day of the year */
+ *end_year_day = ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
+ UCAL_ACTUAL_MAXIMUM, &status);
+ ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, *end_year_day);
+
+ *end_dow = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+}
+
+static void setup_defaults(icalrecur_iterator* impl, enum byrule byrule,
+ icalrecurrencetype_frequency req,
+ int deftime, UCalendarDateFields field)
+{
+ icalrecurrencetype_frequency freq = impl->rule.freq;
+
+ if (expand_map[freq].map[byrule] != CONTRACT) {
+
+ /* Re-write the BY rule arrays with data from the DTSTART time so
+ we don't have to explicitly deal with DTSTART */
+ if (impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX) {
+ impl->by_ptrs[byrule][0] = (short) deftime;
+ }
+
+ /* Initialize the first occurrence */
+ if (freq != req) {
+ short first = impl->by_ptrs[byrule][0];
+
+ if (field == UCAL_MONTH) set_month(impl, first);
+ else ucal_set(impl->rscale, field, first);
+ }
+ }
+}
+
+static int initialize_iterator(icalrecur_iterator* impl)
+{
+ struct icalrecurrencetype rule = impl->rule;
+ struct icaltimetype dtstart = impl->dtstart;
+ const char *gregorian = "@calendar-gregorian";
+ UErrorCode status = U_ZERO_ERROR;
+ const char *tzid = UCAL_UNKNOWN_ZONE_ID;
+
+ if (dtstart.zone)
+ tzid = icaltimezone_get_tzid((icaltimezone *) dtstart.zone);
+
+ /* Create Gregorian calendar and set to DTSTART */
+ impl->greg = ucal_open((const UChar *) tzid, -1, gregorian,
+ UCAL_DEFAULT, &status);
+ if (impl->greg) {
+ ucal_setDateTime(impl->greg, dtstart.year,
+ dtstart.month-1 /* UCal is 0-based */, dtstart.day,
+ dtstart.hour, dtstart.minute, dtstart.second, &status);
+ }
+ if (!impl->greg || U_FAILURE(status)) return 0;
+
+ if (!rule.rscale) {
+ /* Use Gregorian as RSCALE */
+ impl->rscale = impl->greg;
+ impl->greg = NULL;
+
+ /* When RSCALE is not present SKIP defaults to YES */
+ impl->rule.skip = ICAL_SKIP_YES;
+ }
+ else {
+ /* Create locale for RSCALE calendar (lowercasing) */
+ char *r, *l, *locale = icalmemory_tmp_buffer(strlen(rule.rscale) + 11);
+ strcpy(locale, "@calendar=");
+ for (r = rule.rscale, l = locale+10; *r; *l++ = tolower(*r++)); *l = 0;
+
+ /* Create RSCALE calendar and set to DTSTART */
+ impl->rscale = ucal_open((const UChar *) tzid, -1, locale,
+ UCAL_DEFAULT, &status);
+ if (impl->rscale) {
+ UDate millis = ucal_getMillis(impl->greg, &status);
+ ucal_setMillis(impl->rscale, millis, &status);
+ }
+ if (!impl->rscale || U_FAILURE(status)) return 0;
+
+ if (rule.skip == ICAL_SKIP_NONE) {
+ /* When RSCALE is present SKIP defaults to BACKWARD */
+ impl->rule.skip = ICAL_SKIP_BACKWARD;
+ }
+
+ /* Hebrew calendar:
+ Translate RSCALE months to ICU (numbered 1-13, where 6 is leap) */
+ if (!strcmp(locale+10, "hebrew")) {
+ int idx;
+
+ for (idx = 0; BYMONPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++) {
+ if (BYMONPTR[idx] > 5 /* 5L == 1280 */) {
+ /* Translate 5L to 6, 6-12 to 7-13 */
+ BYMONPTR[idx] =
+ icalrecurrencetype_month_month(BYMONPTR[idx]) + 1;
+ }
+ }
+ }
+ }
+
+ /* Set iCalendar defaults */
+ ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
+ ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, rule.week_start);
+
+ /* Get rstart (DTSTART in RSCALE) */
+ impl->rstart = occurrence_as_icaltime(impl, 0);
+
+ /* Set up defaults for BY_* arrays */
+ setup_defaults(impl, BY_SECOND, ICAL_SECONDLY_RECURRENCE,
+ impl->rstart.second, UCAL_SECOND);
+
+ setup_defaults(impl, BY_MINUTE, ICAL_MINUTELY_RECURRENCE,
+ impl->rstart.minute, UCAL_MINUTE);
+
+ setup_defaults(impl, BY_HOUR, ICAL_HOURLY_RECURRENCE,
+ impl->rstart.hour, UCAL_HOUR_OF_DAY);
+
+ setup_defaults(impl, BY_MONTH_DAY, ICAL_DAILY_RECURRENCE,
+ impl->rstart.day, UCAL_DAY_OF_MONTH);
+
+ setup_defaults(impl, BY_MONTH, ICAL_MONTHLY_RECURRENCE,
+ impl->rstart.month, UCAL_MONTH);
+
+ return 1;
+}
+
+static int check_contracting_rules(icalrecur_iterator* impl)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ struct icaltimetype last = occurrence_as_icaltime(impl, 0);
+ int day_of_week = ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
+ int week_no = ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
+ int year_day = ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
+
+ if (!check_contract_restriction(impl, BY_SECOND, last.second) ||
+ !check_contract_restriction(impl, BY_MINUTE, last.minute) ||
+ !check_contract_restriction(impl, BY_HOUR, last.hour) ||
+ !check_contract_restriction(impl, BY_DAY, day_of_week) ||
+ !check_contract_restriction(impl, BY_WEEK_NO, week_no) ||
+ !check_contract_restriction(impl, BY_MONTH_DAY, last.day) ||
+ !check_contract_restriction(impl, BY_YEAR_DAY, year_day)) {
+ return 0;
+ }
+ else if (BYMONPTR[0] != ICAL_RECURRENCE_ARRAY_MAX &&
+ expand_map[impl->rule.freq].map[BY_MONTH] == CONTRACT) {
+ /* Handle BYMONTH separately, due to leap months and fwd/bwd skip */
+ UErrorCode status = U_ZERO_ERROR;
+ UDate last_millis;
+ int pass = 0;
+ int idx;
+
+ /* Save existing rscale date */
+ last_millis = ucal_getMillis(impl->rscale, &status);
+
+ for (idx = 0; BYMONPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++) {
+ short month = BYMONPTR[idx];
+
+ if (month == last.month) {
+ pass = 1;
+ break;
+ }
+ else if (icalrecurrencetype_month_is_leap(month)
+ && impl->rule.skip != ICAL_SKIP_YES) {
+ /* BYMONTH is a leap month and we aren't skipping it.
+ If current year isn't a leap year, try fwd/bwd skip */
+ set_month(impl, month);
+ month = icalrecurrencetype_month_month(month);
+ if (!ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status) &&
+ last.month == month + (short) impl->rule.skip) {
+ pass = 1;
+ break;
+ }
+ }
+ }
+
+ /* Restore saved rscale date */
+ ucal_setMillis(impl->rscale, last_millis, &status);
+
+ return pass;
+ }
+ else {
+ /* BYMONTH is not a contracting byrule, or has no data, so the test passes */
+ return 1;
+ }
+}
+
+#else /* !HAVE_LIBICU */
+
+/*
+ * Callbacks for recurrence rules without RSCALE (Gregorian only)
+ */
+
+icalarray* icalrecurrencetype_rscale_supported_calendars(void)
+{
+ return NULL;
+}
+
+static void set_second(icalrecur_iterator* impl, int second)
+{
+ impl->last.second = second;
+}
+
+static void set_minute(icalrecur_iterator* impl, int minute)
+{
+ impl->last.minute = minute;
+}
+
+static void set_hour(icalrecur_iterator* impl, int hour)
+{
+ impl->last.hour = hour;
+}
+
+static int set_day_of_week(icalrecur_iterator* impl, int dow, int pos)
+{
+ int poscount = 0;
+ int days_in_month =
+ icaltime_days_in_month(impl->last.month, impl->last.year);
+
+ if(pos >= 0){
+ /* Count up from the first day pf the month to find the
+ pos'th weekday of dow ( like the second monday. ) */
+
+ for(impl->last.day = 1;
+ impl->last.day <= days_in_month;
+ impl->last.day++){
+
+ if(icaltime_day_of_week(impl->last) == dow){
+ if(++poscount == pos || pos == 0){
+ break;
+ }
+ }
+ }
+ } else {
+ /* Count down from the last day pf the month to find the
+ pos'th weekday of dow ( like the second to last monday. ) */
+ pos = -pos;
+ for(impl->last.day = days_in_month;
+ impl->last.day != 0;
+ impl->last.day--){
+
+ if(icaltime_day_of_week(impl->last) == dow){
+ if(++poscount == pos ){
+ break;
+ }
+ }
+ }
+ }
+
+
+ if(impl->last.day > days_in_month || impl->last.day == 0){
+ return 0;
+ }
+
+ return 1;
+}
+
+static void set_day_of_month(icalrecur_iterator* impl, int day)
+{
+ impl->last.day = day;
+}
+
+static void set_month(icalrecur_iterator* impl, int month)
+{
+ impl->last.month = month;
+}
+
+static void set_day_of_year(icalrecur_iterator* impl, int doy)
+{
+ struct icaltimetype next;
+
+ if (doy < 0) doy += icaltime_days_in_year(impl->last.year) + 1;
+
+ next = icaltime_from_day_of_year(doy, impl->last.year);
+
+ impl->last.day = next.day;
+ impl->last.month = next.month;
+ impl->last.year = next.year;
+}
+
+static int get_start_of_week(icalrecur_iterator* impl)
+{
+ return icaltime_start_doy_week(impl->last, impl->rule.week_start);
+}
+
+static int get_day_of_week(icalrecur_iterator* impl)
+{
+ return icaltime_day_of_week(impl->last);
+}
+
+/* Calculate ISO weeks per year
+ http://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year */
+static int weeks_in_year(int year)
+{
+ /* Long years occur when year starts on Thu or leap year starts on Wed */
+ int dow = icaltime_day_of_week(icaltime_from_day_of_year(1, year));
+ int is_long = (dow == 5 || (dow == 4 && icaltime_is_leap_year(year)));
+
+ return (52 + is_long);
+}
+
+/* Calculate ISO week number
+ http://en.wikipedia.org/wiki/ISO_week_date#Calculation */
+static int get_week_number(icalrecur_iterator* impl, struct icaltimetype tt)
+{
+ int dow, week;
+ _unused(impl);
+
+ /* Normalize day of week so that week_start day is 1 */
+ dow = icaltime_day_of_week(tt) - (impl->rule.week_start - 1);
+ if (dow <= 0) dow += 7;
+
+ week = (icaltime_day_of_year(tt) - dow + 10) / 7;
+ if (week < 1) {
+ /* Last week of preceding year */
+ week = weeks_in_year(tt.year - 1);
+ }
+ else if (week > weeks_in_year(tt.year)) {
+ /* First week of following year */
+ week = 1;
+ }
+
+ return week;
+}
+
+static int get_days_in_month(icalrecur_iterator* impl, int month, int year)
+{
+ _unused(impl);
+
+ return icaltime_days_in_month(month, year);
+}
+
+static int get_day_of_year(icalrecur_iterator* impl,
+ int year, int month, int day, int *dow)
+{
+ struct icaltimetype t = impl->dtstart;
+
+ t.is_date = 1;
+ t.year = year;
+
+ if (!month) month = impl->dtstart.month;
+ t.month = month;
+
+ if (!day) day = impl->dtstart.day;
+ t.day = day;
+
+ if (day > icaltime_days_in_month(month, year)) {
+ /* Skip leap days */
+ if (dow) *dow = 0;
+ return 0;
+ }
+
+ if (dow) *dow = icaltime_day_of_week(t);
+
+ return icaltime_day_of_year(t);
+}
+
+static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator* impl,
+ int normalize)
+{
+ return (normalize ? icaltime_normalize(impl->last) : impl->last);
+}
+
+struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator* impl,
+ int day, int year, int *weekno)
+{
+ struct icaltimetype tt;
+
+ if (day < 0) day += icaltime_days_in_year(year) + 1;
+
+ tt = icaltime_from_day_of_year(day, year);
+
+ if (weekno) *weekno = get_week_number(impl, tt);
+ return tt;
+}
+
+/* Returns the day of the month for the current month of t that is the
+ pos'th instance of the day-of-week dow */
+
+static int nth_weekday(int dow, int pos, struct icaltimetype t){
+
+ int days_in_month = icaltime_days_in_month(t.month, t.year);
+ int end_dow, start_dow;
+ int wd;
+
+ if(pos >= 0){
+ t.day = 1;
+ start_dow = icaltime_day_of_week(t);
+
+ if (pos != 0) {
+ pos--;
+ }
+
+ /* find month day of first occurrence of dow -- such as the
+ month day of the first monday */
+
+ wd = dow-start_dow+1;
+
+ if (wd <= 0){
+ wd = wd + 7;
+ }
+
+ wd = wd + pos * 7;
+
+ } else {
+ t.day = days_in_month;
+ end_dow = icaltime_day_of_week(t);
+
+ pos++;
+
+ /* find month day of last occurrence of dow -- such as the
+ month day of the last monday */
+
+ wd = (end_dow - dow);
+
+ if (wd < 0){
+ wd = wd+ 7;
+ }
+
+ wd = days_in_month - wd;
+
+ wd = wd + pos * 7;
+ }
+
+ return wd;
+}
+
+static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){
+
+ int idx;
+
+ for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
+ int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
+ int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
+ int this_dow = icaltime_day_of_week(tt);
+
+ if( (pos == 0 && dow == this_dow ) || /* Just a dow, like "TU" or "FR" */
+ (nth_weekday(dow,pos,tt) == tt.day)){ /*pos+wod: "3FR" or -1TU" */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void increment_year(icalrecur_iterator* impl, int inc)
+{
+ impl->last.year+=inc;
+}
+
+static void __increment_month(icalrecur_iterator* impl, int inc)
+{
+ int years;
+
+ impl->last.month+=inc;
+
+ /* Months are offset by one */
+ impl->last.month--;
+
+ years = impl->last.month / 12;
+
+ impl->last.month = impl->last.month % 12;
+
+ impl->last.month++;
+
+ if (years != 0){
+ increment_year(impl,years);
+ }
+}
+
+static void increment_month(icalrecur_iterator* impl);
+
+static void increment_monthday(icalrecur_iterator* impl, int inc)
+{
+ int i;
+
+ if (inc < 0) {
+ impl->last.day += inc;
+ icaltime_normalize(impl->last);
+ return;
+ }
+
+ for(i=0; i<inc; i++){
+
+ int days_in_month =
+ icaltime_days_in_month(impl->last.month, impl->last.year);
+
+ impl->last.day++;
+
+ if (impl->last.day > days_in_month){
+ impl->last.day = impl->last.day-days_in_month;
+ increment_month(impl);
+ }
+ }
+}
+
+static void increment_hour(icalrecur_iterator* impl, int inc)
+{
+ int days;
+
+ impl->last.hour+=inc;
+
+ days = impl->last.hour / 24;
+ impl->last.hour = impl->last.hour % 24;
+
+ if (days != 0){
+ increment_monthday(impl,days);
+ }
+}
+
+static void increment_minute(icalrecur_iterator* impl, int inc)
+{
+ int hours;
+
+ impl->last.minute+=inc;
+
+ hours = impl->last.minute / 60;
+ impl->last.minute = impl->last.minute % 60;
+
+ if (hours != 0){
+ increment_hour(impl,hours);
+ }
+
+}
+
+static void increment_second(icalrecur_iterator* impl, int inc)
+{
+ int minutes;
+
+ impl->last.second+=inc;
+
+ minutes = impl->last.second / 60;
+ impl->last.second = impl->last.second % 60;
+
+ if (minutes != 0)
+ {
+ increment_minute(impl, minutes);
+ }
+}
+
+static void expand_by_day_init(icalrecur_iterator* impl, int year,
+ int *start_dow, int *end_dow, int *end_year_day)
+{
+ struct icaltimetype tmp = impl->last;
+
+ tmp.year = year;
+ tmp.month = 1;
+ tmp.day = 1;
+ tmp.is_date = 1;
+
+ /* Find the day that 1st Jan falls on, 1 (Sun) to 7 (Sat). */
+ *start_dow = icaltime_day_of_week(tmp);
+
+ /* Get the last day of the year */
+ tmp.year = year;
+ tmp.month = 12;
+ tmp.day = 31;
+ tmp.is_date = 1;
+
+ *end_dow = icaltime_day_of_week(tmp);
+ *end_year_day = icaltime_day_of_year(tmp);
+}
+
static void setup_defaults(icalrecur_iterator* impl,
enum byrule byrule, icalrecurrencetype_frequency req,
int deftime, int *timepart)
@@ -814,6 +1765,61 @@ static void setup_defaults(icalrecur_iterator* impl,
}
+static int initialize_iterator(icalrecur_iterator* impl)
+{
+ /* When RSCALE is not present SKIP defaults to YES */
+ impl->rule.skip = ICAL_SKIP_YES;
+
+ /* Set up defaults for BY_* arrays */
+ setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,
+ impl->dtstart.second,
+ &(impl->last.second));
+
+ setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,
+ impl->dtstart.minute,
+ &(impl->last.minute));
+
+ setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,
+ impl->dtstart.hour,
+ &(impl->last.hour));
+
+ setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,
+ impl->dtstart.day,
+ &(impl->last.day));
+
+ setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,
+ impl->dtstart.month,
+ &(impl->last.month));
+
+ return 1;
+}
+
+static int check_contracting_rules(icalrecur_iterator* impl)
+{
+
+ int day_of_week = icaltime_day_of_week(impl->last);
+ int week_no = get_week_number(impl, impl->last);
+ int year_day = icaltime_day_of_year(impl->last);
+
+ if (
+ check_contract_restriction(impl,BY_SECOND, impl->last.second) &&
+ check_contract_restriction(impl,BY_MINUTE, impl->last.minute) &&
+ check_contract_restriction(impl,BY_HOUR, impl->last.hour) &&
+ check_contract_restriction(impl,BY_DAY, day_of_week) &&
+ check_contract_restriction(impl,BY_WEEK_NO, week_no) &&
+ check_contract_restriction(impl,BY_MONTH_DAY, impl->last.day) &&
+ check_contract_restriction(impl,BY_MONTH, impl->last.month) &&
+ check_contract_restriction(impl,BY_YEAR_DAY, year_day) )
+ {
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+#endif /* HAVE_LIBICU */
+
+
static int has_by_data(icalrecur_iterator* impl, enum byrule byrule){
return (impl->orig_data[byrule] == 1);
@@ -939,31 +1945,25 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
return 0;
}
+ /* BYWEEKNO may only appear in YEARLY rules */
+ if(freq != ICAL_YEARLY_RECURRENCE &&
+ icalrecur_one_byrule(impl,BY_WEEK_NO )) {
+ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+ free(impl);
+ return 0;
+ }
+
/* Rewrite some of the rules and set up defaults to make later
processing easier. Primarily, t involves copying an element
from the start time into the corresponding BY_* array when the
BY_* array is empty */
- setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,
- impl->dtstart.second,
- &(impl->last.second));
-
- setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,
- impl->dtstart.minute,
- &(impl->last.minute));
-
- setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,
- impl->dtstart.hour,
- &(impl->last.hour));
-
- setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,
- impl->dtstart.day,
- &(impl->last.day));
-
- setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,
- impl->dtstart.month,
- &(impl->last.month));
+ if (initialize_iterator(impl) == 0) {
+ icalerror_set_errno(ICAL_INTERNAL_ERROR);
+ icalrecur_iterator_free(impl);
+ return 0;
+ }
if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){
@@ -972,7 +1972,7 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
/* Weekly recurrences with no BY_DAY data should occur on the
same day of the week as the start time . */
- impl->by_ptrs[BY_DAY][0] = (short)icaltime_day_of_week(impl->dtstart);
+ impl->by_ptrs[BY_DAY][0] = (short)get_day_of_week(impl);
} else {
/* If there is BY_DAY data, then we need to move the initial
@@ -986,13 +1986,13 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
/* This depends on impl->by_ptrs[BY_DAY] being correctly sorted by
* day. This should probably be abstracted to make such assumption
* more explicit. */
- short dow = (short)(impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last));
+ short this_dow = (short)get_day_of_week(impl);
+ short dow = (short)(impl->by_ptrs[BY_DAY][0]-this_dow);
- if((icaltime_day_of_week(impl->last) < impl->by_ptrs[BY_DAY][0] && dow >= 0) || dow < 0)
+ if((this_dow < impl->by_ptrs[BY_DAY][0] && dow >= 0) || dow < 0)
{
/* initial time is after first day of BY_DAY data */
- impl->last.day += dow;
- impl->last = icaltime_normalize(impl->last);
+ increment_monthday(impl, dow);
}
}
@@ -1004,12 +2004,12 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
YEARLY rules work by expanding one year at a time. */
if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
- struct icaltimetype next;
+ struct icaltimetype last = occurrence_as_icaltime(impl, 0);
icalerror_clear_errno();
/* Fail after hitting the year 20000 if no expanded days match */
- while (impl->last.year < 20000) {
- expand_year_days(impl, impl->last.year);
+ while (last.year < 20000) {
+ expand_year_days(impl, last.year);
if( icalerrno != ICAL_NO_ERROR) {
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
free(impl);
@@ -1018,13 +2018,11 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
break; /* break when no days are expanded */
increment_year(impl,impl->rule.interval);
+ last = occurrence_as_icaltime(impl, 0);
}
/* Copy the first day into last. */
- next = icaltime_from_day_of_year(impl->days[0], impl->last.year);
-
- impl->last.day = next.day;
- impl->last.month = next.month;
+ set_day_of_year(impl, impl->days[0]);
}
@@ -1039,42 +2037,7 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
int pos = icalrecurrencetype_day_position(
impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
- int poscount = 0;
- int days_in_month =
- icaltime_days_in_month(impl->last.month, impl->last.year);
-
- if(pos >= 0){
- /* Count up from the first day pf the month to find the
- pos'th weekday of dow ( like the second monday. ) */
-
- for(impl->last.day = 1;
- impl->last.day <= days_in_month;
- impl->last.day++){
-
- if(icaltime_day_of_week(impl->last) == dow){
- if(++poscount == pos || pos == 0){
- break;
- }
- }
- }
- } else {
- /* Count down from the last day pf the month to find the
- pos'th weekday of dow ( like the second to last monday. ) */
- pos = -pos;
- for(impl->last.day = days_in_month;
- impl->last.day != 0;
- impl->last.day--){
-
- if(icaltime_day_of_week(impl->last) == dow){
- if(++poscount == pos ){
- break;
- }
- }
- }
- }
-
-
- if(impl->last.day > days_in_month || impl->last.day == 0){
+ if(set_day_of_week(impl,dow,pos) == 0){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
free(impl);
return 0;
@@ -1084,16 +2047,16 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
and the first day of BY_DAY data != first BY_MONTH_DAY data,
back up one week, so we don't return false data */
if (has_by_data(impl, BY_MONTH_DAY)) {
- if (impl->last.day != impl->by_ptrs[BY_MONTH_DAY][0]) {
- impl->last.day -= 7;
- icaltime_normalize(impl->last);
+ struct icaltimetype last = occurrence_as_icaltime(impl, 0);
+
+ if (last.day != impl->by_ptrs[BY_MONTH_DAY][0]) {
+ increment_monthday(impl, -7);
}
}
- } else if (has_by_data(impl,BY_MONTH_DAY)) {
- impl->last = icaltime_normalize(impl->last);
}
+ impl->last = occurrence_as_icaltime(impl, 1);
return impl;
@@ -1104,13 +2067,13 @@ void icalrecur_iterator_free(icalrecur_iterator* i)
{
icalerror_check_arg_rv((i!=0),"impl");
- free(i);
+#ifdef HAVE_LIBICU
+ if (i->greg) ucal_close(i->greg);
+ if (i->rscale) ucal_close(i->rscale);
+#endif
-}
+ free(i);
-static void increment_year(icalrecur_iterator* impl, int inc)
-{
- impl->last.year+=inc;
}
/** Increment month is different that the other incement_* routines --
@@ -1118,8 +2081,6 @@ static void increment_year(icalrecur_iterator* impl, int inc)
available. */
static void increment_month(icalrecur_iterator* impl)
{
- int years;
-
if(has_by_data(impl,BY_MONTH) ){
/* Ignore the frequency and use the byrule data */
@@ -1133,8 +2094,7 @@ static void increment_month(icalrecur_iterator* impl)
}
- impl->last.month =
- impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
+ set_month(impl,impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]);
} else {
@@ -1146,86 +2106,10 @@ static void increment_month(icalrecur_iterator* impl)
inc = 1;
}
- impl->last.month+=inc;
-
- /* Months are offset by one */
- impl->last.month--;
-
- years = impl->last.month / 12;
-
- impl->last.month = impl->last.month % 12;
-
- impl->last.month++;
-
- if (years != 0){
- increment_year(impl,years);
- }
+ __increment_month(impl,inc);
}
}
-static void increment_monthday(icalrecur_iterator* impl, int inc)
-{
- int i;
-
- for(i=0; i<inc; i++){
-
- int days_in_month =
- icaltime_days_in_month(impl->last.month, impl->last.year);
-
- impl->last.day++;
-
- if (impl->last.day > days_in_month){
- impl->last.day = impl->last.day-days_in_month;
- increment_month(impl);
- }
- }
-}
-
-
-static void increment_hour(icalrecur_iterator* impl, int inc)
-{
- int days;
-
- impl->last.hour+=inc;
-
- days = impl->last.hour / 24;
- impl->last.hour = impl->last.hour % 24;
-
- if (days != 0){
- increment_monthday(impl,days);
- }
-}
-
-static void increment_minute(icalrecur_iterator* impl, int inc)
-{
- int hours;
-
- impl->last.minute+=inc;
-
- hours = impl->last.minute / 60;
- impl->last.minute = impl->last.minute % 60;
-
- if (hours != 0){
- increment_hour(impl,hours);
- }
-
-}
-
-static void increment_second(icalrecur_iterator* impl, int inc)
-{
- int minutes;
-
- impl->last.second+=inc;
-
- minutes = impl->last.second / 60;
- impl->last.second = impl->last.second % 60;
-
- if (minutes != 0)
- {
- increment_minute(impl, minutes);
- }
-}
-
#if 0
#include "ical.h"
void test_increment()
@@ -1283,8 +2167,7 @@ static int next_second(icalrecur_iterator* impl)
}
- impl->last.second =
- impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]];
+ set_second(impl, impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]);
} else if( !has_by_second && this_frequency ){
@@ -1332,8 +2215,7 @@ static int next_minute(icalrecur_iterator* impl)
end_of_data = 1;
}
- impl->last.minute =
- impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]];
+ set_minute(impl, impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]);
} else if( !has_by_minute && this_frequency ){
/* Compute the next value from the last time and the frequency interval*/
@@ -1376,8 +2258,7 @@ static int next_hour(icalrecur_iterator* impl)
end_of_data = 1;
}
- impl->last.hour =
- impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]];
+ set_hour(impl, impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]);
} else if( !has_by_hour && this_frequency ){
/* Compute the next value from the last time and the frequency interval*/
@@ -1458,75 +2339,6 @@ static int next_yearday(icalrecur_iterator* impl)
}
*/
-/* Returns the day of the month for the current month of t that is the
- pos'th instance of the day-of-week dow */
-
-static int nth_weekday(int dow, int pos, struct icaltimetype t){
-
- int days_in_month = icaltime_days_in_month(t.month, t.year);
- int end_dow, start_dow;
- int wd;
-
- if(pos >= 0){
- t.day = 1;
- start_dow = icaltime_day_of_week(t);
-
- if (pos != 0) {
- pos--;
- }
-
- /* find month day of first occurrence of dow -- such as the
- month day of the first monday */
-
- wd = dow-start_dow+1;
-
- if (wd <= 0){
- wd = wd + 7;
- }
-
- wd = wd + pos * 7;
-
- } else {
- t.day = days_in_month;
- end_dow = icaltime_day_of_week(t);
-
- pos++;
-
- /* find month day of last occurrence of dow -- such as the
- month day of the last monday */
-
- wd = (end_dow - dow);
-
- if (wd < 0){
- wd = wd+ 7;
- }
-
- wd = days_in_month - wd;
-
- wd = wd + pos * 7;
- }
-
- return wd;
-}
-
-static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){
-
- int idx;
-
- for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
- int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
- int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
- int this_dow = icaltime_day_of_week(tt);
-
- if( (pos == 0 && dow == this_dow ) || /* Just a dow, like "TU" or "FR" */
- (nth_weekday(dow,pos,tt) == tt.day)){ /*pos+wod: "3FR" or -1TU" */
- return 1;
- }
- }
-
- return 0;
-}
-
int check_set_position(icalrecur_iterator* impl, int set_pos)
{
int i;
@@ -1566,29 +2378,23 @@ static int next_month(icalrecur_iterator* impl)
*/
if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){
- int day, idx,j;
- int days_in_month = icaltime_days_in_month(impl->last.month,
- impl->last.year);
+ int day, j;
+ struct icaltimetype last = occurrence_as_icaltime(impl, 0);
+ int days_in_month = get_days_in_month(impl, last.month, last.year);
/* Iterate through the remaining days in the month and check if
each day is listed in the BY_DAY array and in the BY_MONTHDAY
array. This seems very inneficient, but I think it is the
simplest way to account for both BYDAY=1FR (First friday in
month) and BYDAY=FR ( every friday in month ) */
- for(day = impl->last.day+1; day <= days_in_month; day++){
- for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
+ for(day = last.day+1; day <= days_in_month; day++){
+ set_day_of_month(impl, day);
+ last = occurrence_as_icaltime(impl, 0);
+ if(is_day_in_byday(impl, last)){
for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
- int dow =
- icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
- int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
int mday = BYMDPTR[j];
- int this_dow;
- impl->last.day = day;
- this_dow = icaltime_day_of_week(impl->last);
-
- if( (pos == 0 && dow == this_dow && mday == day) ||
- (nth_weekday(dow,pos,impl->last) == day && mday==day)){
+ if(mday == day){
goto MDEND;
}
}
@@ -1598,7 +2404,7 @@ static int next_month(icalrecur_iterator* impl)
MDEND:
if ( day > days_in_month){
- impl->last.day = 1;
+ set_day_of_month(impl, 1);
increment_month(impl);
data_valid = 0; /* signal that impl->last is invalid */
}
@@ -1620,8 +2426,9 @@ static int next_month(icalrecur_iterator* impl)
BYDAY=FR ( every friday in month ) */
int day;
- int days_in_month = icaltime_days_in_month(impl->last.month,
- impl->last.year);
+ struct icaltimetype last = occurrence_as_icaltime(impl, 0);
+ int days_in_month = get_days_in_month(impl, last.month, last.year);
+ int last_day = last.day;
int set_pos_counter = 0;
int set_pos_total = 0;
@@ -1631,23 +2438,25 @@ static int next_month(icalrecur_iterator* impl)
/* Count the past positions for the BYSETPOS calculation */
if(has_by_data(impl,BY_SET_POS)){
- int last_day = impl->last.day;
+
for(day = 1; day <= days_in_month; day++){
- impl->last.day = day;
+ set_day_of_month(impl, day);
+ last = occurrence_as_icaltime(impl, 0);
- if(is_day_in_byday(impl,impl->last)){
+ if(is_day_in_byday(impl,last)){
set_pos_total++;
if(day <= last_day)
set_pos_counter++;
}
}
- impl->last.day = last_day;
+ set_day_of_month(impl, last_day);
}
- for(day = impl->last.day+1; day <= days_in_month; day++){
- impl->last.day = day;
+ for(day = last_day+1; day <= days_in_month; day++){
+ set_day_of_month(impl, day);
+ last = occurrence_as_icaltime(impl, 0);
- if(is_day_in_byday(impl,impl->last)){
+ if(is_day_in_byday(impl,last)){
/* If there is no BYSETPOS rule, calculate only by BYDAY
If there is BYSETPOS rule, take into account the occurence
matches with BYDAY */
@@ -1662,14 +2471,15 @@ static int next_month(icalrecur_iterator* impl)
data_valid = found;
if ( day > days_in_month){
- impl->last.day = 1;
+ set_day_of_month(impl, 1);
increment_month(impl);
+ last = occurrence_as_icaltime(impl, 0);
/* Did moving to the next month put us on a valid date? if
so, note that the new data is valid, if, not, mark it
invalid */
- if(is_day_in_byday(impl,impl->last)){
+ if(is_day_in_byday(impl,last)){
/* If there is no BYSETPOS rule or BYSETPOS=1, new data is valid */
if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl,1))
data_valid = 1;
@@ -1683,11 +2493,18 @@ static int next_month(icalrecur_iterator* impl)
* Rules Like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3
*/
- } else if (has_by_data(impl,BY_MONTH_DAY)) {
+ } else {
int day, days_in_month;
+ struct icaltimetype last;
assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ last = occurrence_as_icaltime(impl, 0);
+ if (last.day < BYMDPTR[BYMDIDX] && impl->rule.skip == ICAL_SKIP_FORWARD) {
+ /* We skipped forward a day, backup to the previous month */
+ __increment_month(impl,-1);
+ }
+
BYMDIDX++;
/* Are we at the end of the BYDAY array? */
@@ -1697,45 +2514,35 @@ static int next_month(icalrecur_iterator* impl)
increment_month(impl);
}
- days_in_month = icaltime_days_in_month(impl->last.month,
- impl->last.year);
+ last = occurrence_as_icaltime(impl, 0);
+ days_in_month = get_days_in_month(impl, last.month, last.year);
day = BYMDPTR[BYMDIDX];
if (day < 0) {
- day = icaltime_days_in_month(impl->last.month, impl->last.year) + day + 1;
+ day = days_in_month + day + 1;
}
if ( day > days_in_month){
- impl->last.day = 1;
-
- /* Did moving to the next month put us on a valid date? if
- so, note that the new data is valid, if, not, mark it
- invalid */
-
- if(is_day_in_byday(impl,impl->last)){
- data_valid = 1;
- } else {
- data_valid = 0; /* signal that impl->last is invalid */
- }
+ switch (impl->rule.skip) {
+ case ICAL_SKIP_YES:
+ day = 1;
+ data_valid = 0; /* signal that impl->last is invalid */
+ break;
+ case ICAL_SKIP_FORWARD:
+ day = 1;
+ __increment_month(impl,1);
+ break;
+ case ICAL_SKIP_BACKWARD:
+ day = days_in_month;
+ break;
+ case ICAL_SKIP_NONE:
+ break;
+ }
}
- impl->last.day = day;
-
- } else {
- int days_in_month;
-
- assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
- impl->last.day = BYMDPTR[0];
-
- increment_month(impl);
+ set_day_of_month(impl, day);
- days_in_month = icaltime_days_in_month(impl->last.month,
- impl->last.year);
- if (impl->last.day > days_in_month){
- impl->last.day = days_in_month;
- data_valid = 0; /* signal that impl->last is invalid */
- }
}
return data_valid;
@@ -1747,7 +2554,6 @@ static int next_weekday_by_week(icalrecur_iterator* impl)
int end_of_data = 0;
int start_of_week, dow;
- struct icaltimetype next;
if (next_hour(impl) == 0){
return 0;
@@ -1760,7 +2566,6 @@ static int next_weekday_by_week(icalrecur_iterator* impl)
/* If we get here, we need to step to tne next day */
for (;;) {
- struct icaltimetype tt = icaltime_null_time();
BYDAYIDX++; /* Look at next elem in BYDAY array */
/* Are we at the end of the BYDAY array? */
@@ -1778,11 +2583,7 @@ static int next_weekday_by_week(icalrecur_iterator* impl)
dow += 7;
}
- tt.year = impl->last.year;
- tt.day = impl->last.day;
- tt.month = impl->last.month;
-
- start_of_week = icaltime_start_doy_week(tt, impl->rule.week_start);
+ start_of_week = get_start_of_week(impl);
if(dow+start_of_week <1){
/* The selected date is in the previous year. */
@@ -1791,11 +2592,7 @@ static int next_weekday_by_week(icalrecur_iterator* impl)
}
}
- next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year);
-
- impl->last.day = next.day;
- impl->last.month = next.month;
- impl->last.year = next.year;
+ set_day_of_year(impl, start_of_week + dow);
return end_of_data;
}
@@ -1814,34 +2611,8 @@ static int next_week(icalrecur_iterator* impl)
/* If we get here, we have incremented through the entire week, and
can increment to the next week */
- if( has_by_data(impl,BY_WEEK_NO)){
- /*FREQ=WEEKLY;BYWEEK=20*/
- /* Use the Week Number byrule data */
- int week_no;
-
- impl->by_indices[BY_WEEK_NO]++;
-
- if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]
- ==ICAL_RECURRENCE_ARRAY_MAX){
- impl->by_indices[BY_WEEK_NO] = 0;
-
- end_of_data = 1;
- }
-
- week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]];
-
- impl->last.day += week_no*7;
-
- impl->last = icaltime_normalize(impl->last);
-
- } else {
- /* Jump to the next week */
- increment_monthday(impl,7*impl->rule.interval);
- }
-
- if( has_by_data(impl,BY_WEEK_NO) && end_of_data){
- increment_year(impl,1);
- }
+ /* Jump to the next week */
+ increment_monthday(impl,7*impl->rule.interval);
return end_of_data;
@@ -1855,24 +2626,8 @@ static pvl_list expand_by_day(icalrecur_iterator* impl, int year)
pvl_list days_list = pvl_newlist();
int start_dow, end_dow, end_year_day;
- struct icaltimetype tmp = impl->last;
-
- tmp.year= year;
- tmp.month = 1;
- tmp.day = 1;
- tmp.is_date = 1;
-
- /* Find the day that 1st Jan falls on, 1 (Sun) to 7 (Sat). */
- start_dow = icaltime_day_of_week(tmp);
-
- /* Get the last day of the year*/
- tmp.year= year;
- tmp.month = 12;
- tmp.day = 31;
- tmp.is_date = 1;
- end_dow = icaltime_day_of_week(tmp);
- end_year_day = icaltime_day_of_year(tmp);
+ expand_by_day_init(impl, year, &start_dow, &end_dow, &end_year_day);
for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
/* This is 1 (Sun) to 7 (Sat). */
@@ -1921,43 +2676,6 @@ static pvl_list expand_by_day(icalrecur_iterator* impl, int year)
}
-/* Calculate ISO weeks per year
- http://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year */
-static int iso_weeks_in_year(int year)
-{
- /* Long years occur when year starts on Thu or leap year starts on Wed */
- int dow = icaltime_day_of_week(icaltime_from_day_of_year(1, year));
- int is_long = (dow == 5 || (dow == 4 && icaltime_is_leap_year(year)));
-
- return (52 + is_long);
-}
-
-/* Calculate ISO week number
- http://en.wikipedia.org/wiki/ISO_week_date#Calculation */
-static int iso_week_number(icalrecur_iterator* impl, struct icaltimetype tt)
-{
- int dow, week;
-
- /* Normalize day of week so that week_start day is 1 */
- dow = icaltime_day_of_week(tt) - (impl->rule.week_start - 1);
- if (dow <= 0) dow += 7;
-
- week = (icaltime_day_of_year(tt) - dow + 10) / 7;
- if (week < 1) {
- /* Last week of preceding year */
- week = iso_weeks_in_year(tt.year - 1);
- }
- else if (week > iso_weeks_in_year(tt.year)) {
- /* First week of following year */
- week = 1;
- }
-
- return week;
-}
-
-#define is_bogus_date(tt) (tt.day > icaltime_days_in_month(tt.month, tt.year))
-
-
/* For INTERVAL=YEARLY, set up the days[] array in the iterator to
list all of the days of the current year that are specified in this
rule. */
@@ -2000,9 +2718,9 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
int first_week, last_week;
t.month = month;
t.day = 1;
- first_week = iso_week_number(impl,t);
- t.day = icaltime_days_in_month(month,year);
- last_week = iso_week_number(impl,t);
+ first_week = get_week_number(impl,t);
+ t.day = get_days_in_month(impl,month,year);
+ last_week = get_week_number(impl,t);
for(j=first_week; j<last_week; j++) {
valid_weeks[j] = 1;
}
@@ -2030,12 +2748,9 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
case 0: {
/* FREQ=YEARLY; */
- t = impl->dtstart;
- t.year = impl->last.year;
+ int doy = get_day_of_year(impl, year, 0, 0, NULL);
- if (!is_bogus_date(t)) {
- impl->days[days_index++] = (short)icaltime_day_of_year(t);
- }
+ if (doy != 0) impl->days[days_index++] = (short)doy;
break;
}
@@ -2046,16 +2761,10 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
int month = impl->by_ptrs[BY_MONTH][j];
int doy;
- t = impl->dtstart;
- t.year = year;
- t.month = month;
- t.is_date = 1;
-
- if (!is_bogus_date(t)) {
- doy = icaltime_day_of_year(t);
+ doy = get_day_of_year(impl, year, month, 0, NULL);
- impl->days[days_index++] = (short)doy;
- }
+ if (doy != 0) impl->days[days_index++] = (short)doy;
+
}
break;
}
@@ -2067,16 +2776,10 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
int month_day = impl->by_ptrs[BY_MONTH_DAY][k];
int doy;
- t = impl->dtstart;
- t.day = month_day;
- t.year = year;
- t.is_date = 1;
+ doy = get_day_of_year(impl, year, 0, month_day, NULL);
- if (!is_bogus_date(t)) {
- doy = icaltime_day_of_year(t);
+ if (doy != 0) impl->days[days_index++] = (short)doy;
- impl->days[days_index++] = (short)doy;
- }
}
break;
}
@@ -2091,16 +2794,10 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
int month_day = impl->by_ptrs[BY_MONTH_DAY][k];
int doy;
- t.day = month_day;
- t.month = month;
- t.year = year;
- t.is_date = 1;
+ doy = get_day_of_year(impl, year, month, month_day, NULL);
- if (!is_bogus_date(t)) {
- doy = icaltime_day_of_year(t);
+ if (doy != 0) impl->days[days_index++] = (short)doy;
- impl->days[days_index++] = (short)doy;
- }
}
}
@@ -2154,22 +2851,14 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
int month = impl->by_ptrs[BY_MONTH][j];
- int days_in_month = icaltime_days_in_month(month,year);
+ int days_in_month = get_days_in_month(impl,month,year);
int first_dow, last_dow, doy_offset;
- t.year = year;
- t.month = month;
- t.day = 1;
- t.is_date = 1;
-
- first_dow = icaltime_day_of_week(t);
-
/* This holds the day offset used to calculate the day of the year
from the month day. Just add the month day to this. */
- doy_offset = icaltime_day_of_year(t) - 1;
+ doy_offset = get_day_of_year(impl, year, month, 1, &first_dow) - 1;
- t.day = days_in_month;
- last_dow = icaltime_day_of_week(t);
+ get_day_of_year(impl, year, month, days_in_month, &last_dow);
if(has_by_data(impl,BY_SET_POS)) {
/*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12; BYSETPOS=1*/
@@ -2234,7 +2923,7 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
short day = (short)(intptr_t)pvl_data(itr);
struct icaltimetype tt;
- tt = icaltime_from_day_of_year(day,year);
+ tt = __icaltime_from_day_of_year(impl,day,year,NULL);
for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
int mday = BYMDPTR[j];
@@ -2262,7 +2951,7 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
struct icaltimetype tt;
int i;
- tt = icaltime_from_day_of_year(day,year);
+ tt = __icaltime_from_day_of_year(impl,day,year,NULL);
for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
@@ -2291,14 +2980,12 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
short day = (short)(intptr_t)pvl_data(itr);
- struct icaltimetype tt;
- int i;
+ int this_weekno, i;
- tt = icaltime_from_day_of_year(day,year);
+ __icaltime_from_day_of_year(impl,day,year,&this_weekno);
for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
int weekno = BYWEEKPTR[i];
- int this_weekno = iso_week_number(impl,tt);
if(weekno== this_weekno){
impl->days[days_index++] = day;
}
@@ -2319,13 +3006,11 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
case (1<<BY_DAY) + (1<<BY_YEAR_DAY) : {
/*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYYEARDAY=1,15*/
- for(j = 0;BYYDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX;j++) {
+ for(j = 0; BYYDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX;j++) {
short day = BYYDPTR[j];
- struct icaltimetype tt;
-
- if (day < 0) day += icaltime_days_in_year(year) + 1;
-
- tt = icaltime_from_day_of_year(day,year);
+ struct icaltimetype tt;
+
+ tt = __icaltime_from_day_of_year(impl,day,year,NULL);
if(is_day_in_byday(impl,tt)){
impl->days[days_index++] = day;
@@ -2336,12 +3021,8 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
}
case 1<<BY_YEAR_DAY: {
- for(j=0;BYYDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
- short day = BYYDPTR[j];
-
- if (day < 0) day += icaltime_days_in_year(year) + 1;
-
- impl->days[days_index++] = day;
+ for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ impl->days[days_index++] = impl->by_ptrs[BY_YEAR_DAY][j];
}
break;
}
@@ -2359,8 +3040,6 @@ static int expand_year_days(icalrecur_iterator* impl, int year)
static int next_year(icalrecur_iterator* impl)
{
- struct icaltimetype next;
-
if (next_hour(impl) == 0){
return 0;
}
@@ -2369,17 +3048,16 @@ static int next_year(icalrecur_iterator* impl)
impl->days_index = 0;
for (;;) {
+ struct icaltimetype last;
increment_year(impl,impl->rule.interval);
- expand_year_days(impl,impl->last.year);
+ last = occurrence_as_icaltime(impl,0);
+ expand_year_days(impl,last.year);
if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
break;
}
}
- next = icaltime_from_day_of_year(impl->days[impl->days_index], impl->last.year);
-
- impl->last.day = next.day;
- impl->last.month = next.month;
+ set_day_of_year(impl, impl->days[impl->days_index]);
return 1;
}
@@ -2425,30 +3103,6 @@ static int check_contract_restriction(icalrecur_iterator* impl,
}
-static int check_contracting_rules(icalrecur_iterator* impl)
-{
-
- int day_of_week = icaltime_day_of_week(impl->last);
- int week_no = iso_week_number(impl,impl->last);
- int year_day = icaltime_day_of_year(impl->last);
-
- if (
- check_contract_restriction(impl,BY_SECOND, impl->last.second) &&
- check_contract_restriction(impl,BY_MINUTE, impl->last.minute) &&
- check_contract_restriction(impl,BY_HOUR, impl->last.hour) &&
- check_contract_restriction(impl,BY_DAY, day_of_week) &&
- check_contract_restriction(impl,BY_WEEK_NO, week_no) &&
- check_contract_restriction(impl,BY_MONTH_DAY, impl->last.day) &&
- check_contract_restriction(impl,BY_MONTH, impl->last.month) &&
- check_contract_restriction(impl,BY_YEAR_DAY, year_day) )
- {
-
- return 1;
- } else {
- return 0;
- }
-}
-
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
{
int valid = 1;
@@ -2460,6 +3114,7 @@ struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
}
if(impl->occurrence_no == 0
+ && check_contracting_rules(impl)
&& icaltime_compare(impl->last,impl->dtstart) >= 0){
impl->occurrence_no++;
@@ -2503,6 +3158,8 @@ struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
return icaltime_null_time();
}
}
+
+ impl->last = occurrence_as_icaltime(impl, 1);
if(impl->last.year > MAX_TIME_T_YEAR ){
/* HACK */
@@ -2539,6 +3196,8 @@ void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
recur->interval = 1;
memset(&(recur->until),0,sizeof(struct icaltimetype));
recur->count = 0;
+ recur->rscale = NULL;
+ recur->skip = ICAL_SKIP_NONE;
}
/** The 'day' element of icalrecurrencetype_weekday is encoded to
@@ -2569,6 +3228,25 @@ int icalrecurrencetype_day_position(short day)
return pos;
}
+/**
+ * The 'month' element of the by_month array is encoded to allow
+ * representation of the "L" leap suffix (draft-daboo-icalendar-rscale).
+ * These routines decode the month values.
+ *
+ * The "L" suffix is encoded by multiplying the month by 256
+ */
+
+int icalrecurrencetype_month_is_leap(short month)
+{
+ return abs(month) > 255;
+}
+
+int icalrecurrencetype_month_month(short month)
+{
+ if (abs(month) > 255) month >>= 8;
+ return month;
+}
+
/****************** Enumeration Routines ******************/
@@ -2650,6 +3328,40 @@ icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
return ICAL_NO_RECURRENCE;
}
+static struct {
+ icalrecurrencetype_skip kind;
+ const char* str;
+} skip_map[] = {
+ {ICAL_SKIP_BACKWARD,"BACKWARD"},
+ {ICAL_SKIP_FORWARD,"FORWARD"},
+ {ICAL_SKIP_YES,"YES"},
+ {ICAL_SKIP_NONE,0}
+};
+
+const char* icalrecur_skip_to_string(icalrecurrencetype_skip kind)
+{
+ int i;
+
+ for (i=0; skip_map[i].kind != ICAL_SKIP_NONE ; i++) {
+ if ( skip_map[i].kind == kind ) {
+ return skip_map[i].str;
+ }
+ }
+ return 0;
+}
+
+icalrecurrencetype_skip icalrecur_string_to_skip(const char* str)
+{
+ int i;
+
+ for (i=0; skip_map[i].kind != ICAL_SKIP_NONE ; i++) {
+ if ( strcasecmp(str,skip_map[i].str) == 0){
+ return skip_map[i].kind;
+ }
+ }
+ return ICAL_SKIP_NONE;
+}
+
/** 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 884bf049..78030913 100644
--- a/src/libical/icalrecur.h
+++ b/src/libical/icalrecur.h
@@ -71,6 +71,7 @@ whatever timezone that dtstart is in.
#include <time.h>
#include "icaltime.h"
+#include "icalarray.h"
/*
* Recurrance enumerations
@@ -104,6 +105,14 @@ typedef enum icalrecurrencetype_weekday
ICAL_SATURDAY_WEEKDAY
} icalrecurrencetype_weekday;
+typedef enum icalrecurrencetype_skip
+{
+ ICAL_SKIP_BACKWARD = 0,
+ ICAL_SKIP_FORWARD,
+ ICAL_SKIP_YES,
+ ICAL_SKIP_NONE
+} icalrecurrencetype_skip;
+
enum {
ICAL_RECURRENCE_ARRAY_MAX = 0x7f7f,
ICAL_RECURRENCE_ARRAY_MAX_BYTE = 0x7f
@@ -115,18 +124,20 @@ enum {
* Recurrence type routines
*/
-/* See RFC 2445 Section 4.3.10, RECUR Value, for an explaination of
- the values and fields in struct icalrecurrencetype */
-
+/* See RFC 5545 Section 3.3.10, RECUR Value, and draft-daboo-icalendar-rscale
+ * for an explanation of the values and fields in struct icalrecurrencetype.
+ *
+ * The maximums below are based on Chinese/Hebrew leap years (13 months)
+ */
#define ICAL_BY_SECOND_SIZE 61
#define ICAL_BY_MINUTE_SIZE 61
#define ICAL_BY_HOUR_SIZE 25
-#define ICAL_BY_DAY_SIZE 364 /* 7 days * 52 weeks */
+#define ICAL_BY_DAY_SIZE 385 /* 7 days * 55 weeks */
#define ICAL_BY_MONTHDAY_SIZE 32
-#define ICAL_BY_YEARDAY_SIZE 367
-#define ICAL_BY_WEEKNO_SIZE 54
-#define ICAL_BY_MONTH_SIZE 13
-#define ICAL_BY_SETPOS_SIZE 367
+#define ICAL_BY_YEARDAY_SIZE 386
+#define ICAL_BY_WEEKNO_SIZE 57
+#define ICAL_BY_MONTH_SIZE 14
+#define ICAL_BY_SETPOS_SIZE 386
/** Main struct for holding digested recurrence rules */
struct icalrecurrencetype
@@ -160,9 +171,16 @@ struct icalrecurrencetype
short by_week_no[ICAL_BY_WEEKNO_SIZE];
short by_month[ICAL_BY_MONTH_SIZE];
short by_set_pos[ICAL_BY_SETPOS_SIZE];
+
+ /* For RSCALE extension (draft-daboo-icalendar-rscale) */
+ char *rscale;
+ icalrecurrencetype_skip skip;
};
+int icalrecurrencetype_rscale_is_supported(void);
+icalarray* icalrecurrencetype_rscale_supported_calendars(void);
+
void icalrecurrencetype_clear(struct icalrecurrencetype *r);
/**
@@ -182,6 +200,15 @@ int icalrecurrencetype_day_position(short day);
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
+/**
+ * The 'month' element of the by_month array is encoded to allow
+ * representation of the "L" leap suffix (draft-daboo-icalendar-rscale).
+ * These routines decode the month values.
+ */
+
+int icalrecurrencetype_month_is_leap(short month);
+int icalrecurrencetype_month_month(short month);
+
/** Recurrance rule parser */
/** Convert between strings and recurrencetype structures. */
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 322b5e40..47bec45e 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -79,12 +79,18 @@ if(NOT WIN32) #since we currently do not have a Windows reference file
add_executable(icalrecurtest ${icalrecurtest_SRCS})
target_link_libraries(icalrecurtest ical icalss icalvcal)
set(test_cmd "${CMAKE_BINARY_DIR}/src/test/icalrecurtest${CMAKE_EXECUTABLE_SUFFIX}")
- set(test_args "")
+ if(ICU_FOUND)
+ set(test_args "-r")
+ set(reference_data "icalrecur_withicu_test.out")
+ else()
+ set(test_args "")
+ set(reference_data "icalrecur_test.out")
+ endif()
add_test(NAME icalrecurtest
COMMAND ${CMAKE_COMMAND}
-D test_cmd=${test_cmd}
-D test_args:string=${test_args}
- -D output_blessed=${CMAKE_SOURCE_DIR}/src/test/icalrecur_test.out
+ -D output_blessed=${CMAKE_SOURCE_DIR}/src/test/${reference_data}
-D output_test=${CMAKE_BINARY_DIR}/bin/test.out
-P ${CMAKE_SOURCE_DIR}/cmake/run_test.cmake
)
diff --git a/src/test/icalrecur_withicu_test.out b/src/test/icalrecur_withicu_test.out
new file mode 100644
index 00000000..2e55c44f
--- /dev/null
+++ b/src/test/icalrecur_withicu_test.out
@@ -0,0 +1,72 @@
+
+RRULE:RSCALE=ETHIOPIC;FREQ=YEARLY;BYMONTH=13;BYMONTHDAY=-1;COUNT=6
+DTSTART:20140910
+INSTANCES:20140910,20150911,20160910,20170910,20180910,20190911
+
+RRULE:RSCALE=CHINESE;FREQ=YEARLY;UNTIL=20180101
+DTSTART:20130210
+INSTANCES:20130210,20140131,20150219,20160208,20170128
+
+RRULE:RSCALE=CHINESE;FREQ=MONTHLY;COUNT=4
+DTSTART:20140920
+INSTANCES:20140920,20141020,20141119,20141218
+
+RRULE:RSCALE=ISLAMIC-CIVIL;FREQ=MONTHLY;COUNT=4
+DTSTART:20131025
+INSTANCES:20131025,20131124,20131224,20140122
+
+RRULE:RSCALE=ISLAMIC-CIVIL;FREQ=YEARLY;BYMONTH=9;COUNT=5
+DTSTART:20130709
+INSTANCES:20130709,20140629,20150618,20160607,20170527
+
+RRULE:RSCALE=DANGI;FREQ=DAILY;BYMONTHDAY=8;BYMONTH=4;UNTIL=20160101
+DTSTART:20131025
+INSTANCES:20140408,20150408
+
+RRULE:RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9;COUNT=3
+DTSTART:20131025
+INSTANCES:20141003,20151022,20161010
+
+RRULE:RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;SKIP=YES;COUNT=2
+DTSTART:20131025
+INSTANCES:20141102,21091102
+
+RRULE:RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=4L;SKIP=YES;UNTIL=21000101
+DTSTART:20131025
+INSTANCES:20200601,20580531,20690530,20770531,20880530,20960531
+
+RRULE:RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3
+DTSTART:20131025
+INSTANCES:20141102,20151022,20161010
+
+RRULE:RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4
+DTSTART:20131025
+INSTANCES:20141003,20141102,20151022,20161010
+
+RRULE:RSCALE=HEBREW;FREQ=YEARLY;SKIP=YES;COUNT=4
+DTSTART:20140205
+INSTANCES:20140205,20160214,20190210,20220206
+
+RRULE:RSCALE=HEBREW;FREQ=YEARLY;SKIP=FORWARD;COUNT=4
+DTSTART:20140205
+INSTANCES:20140205,20150224,20160214,20170303
+
+RRULE:RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD;COUNT=5
+DTSTART:20140208
+INSTANCES:20140208,20150227,20160217,20170306,20180223
+
+RRULE:RSCALE=GREGORIAN;FREQ=MONTHLY;COUNT=4
+DTSTART:20140131
+INSTANCES:20140131,20140228,20140331,20140430
+
+RRULE:RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=FORWARD;COUNT=4
+DTSTART:20140131
+INSTANCES:20140131,20140301,20140331,20140501
+
+RRULE:RSCALE=GREGORIAN;FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28,29;SKIP=FORWARD;COUNT=5
+DTSTART:20150201
+INSTANCES:20150228,20150301,20160228,20160229,20170228
+
+RRULE:RSCALE=GREGORIAN;FREQ=MONTHLY;INTERVAL=3;SKIP=FORWARD;COUNT=4
+DTSTART:20140131
+INSTANCES:20140131,20140501,20140731,20141031