128#include "icalerror_p.h"
140#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
142static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER;
147#if defined(HAVE_LIBICU)
148#include <unicode/ucal.h>
149#include <unicode/ustring.h>
154#undef ICAL_BY_MONTH_SIZE
155#undef ICAL_BY_WEEKNO_SIZE
156#undef ICAL_BY_YEARDAY_SIZE
158#define ICAL_BY_MONTH_SIZE 13
159#define ICAL_BY_WEEKNO_SIZE 54
160#define ICAL_BY_YEARDAY_SIZE 367
165#if defined(HAVE_LIBICU)
166#define MAX_TIME_T_YEAR 20000
168#if (SIZEOF_ICALTIME_T > 4)
171#define MAX_TIME_T_YEAR 2582
175#define MAX_TIME_T_YEAR 2037
179#define LEAP_MONTH 0x1000
184static short daymask_find_next_bit(
const unsigned long *days,
short start_index);
188static const struct freq_map {
206 if (strcasecmp(
str, freq_map[i].
str) == 0) {
207 return freq_map[i].kind;
219 return freq_map[i].str;
225static const struct skip_map {
239 if (strcasecmp(
str, skip_map[i].
str) == 0) {
240 return skip_map[i].kind;
252 return skip_map[i].str;
258static const struct wd_map {
276 if (wd_map[i].wd ==
kind) {
277 return wd_map[i].str;
289 if (strcasecmp(
str, wd_map[i].
str) == 0) {
299static void icalrecur_free_by(icalrecurrence_by_data *by)
308 if (by->size == size) {
313 icalrecur_free_by(by);
317 if ((by->data == NULL) || (by->size == 0)) {
318 if ((by->data != NULL) || (by->size != 0)) {
336 if (size > by->size) {
337 memset(&by->data[by->size], 0, (
size_t)(size - by->size) *
sizeof(by->data[0]));
347struct icalrecur_parser {
353 struct icalrecurrencetype *rt;
366struct expand_split_map_struct {
382static const struct expand_split_map_struct expand_map[] = {
384 {
ICAL_SECONDLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 1, 1}},
385 {
ICAL_MINUTELY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 2, 1}},
386 {
ICAL_HOURLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 2, 2, 1}},
387 {
ICAL_DAILY_RECURRENCE, {1, 3, 3, 1, 1, 2, 2, 2, 1}},
388 {
ICAL_WEEKLY_RECURRENCE, {1, 3, 3, 3, 2, 2, 2, 2, 1}},
389 {
ICAL_MONTHLY_RECURRENCE, {1, 3, 3, 2, 2, 2, 2, 2, 1}},
390 {
ICAL_YEARLY_RECURRENCE, {2, 2, 2, 2, 2, 2, 2, 2, 1}},
393static const struct recur_map {
399 {
"BYMONTH", ICAL_BY_MONTH_SIZE, 1, 0},
400 {
"BYWEEKNO", ICAL_BY_WEEKNO_SIZE, -1, 0},
401 {
"BYYEARDAY", ICAL_BY_YEARDAY_SIZE, -1, 0},
402 {
"BYMONTHDAY", ICAL_BY_MONTHDAY_SIZE, -1, 0},
403 {
"BYDAY", ICAL_BY_DAY_SIZE, 0, 0},
404 {
"BYHOUR", ICAL_BY_HOUR_SIZE, 0, 1},
405 {
"BYMINUTE", ICAL_BY_MINUTE_SIZE, 0, 1},
406 {
"BYSECOND", ICAL_BY_SECOND_SIZE, 0, 1},
407 {
"BYSETPOS", ICAL_BY_SETPOS_SIZE, -1, 0},
410static const char *icalrecur_first_clause(
struct icalrecur_parser *parser)
414 parser->this_clause = parser->copy;
416 idx = strchr(parser->this_clause,
';');
419 parser->next_clause = 0;
425 parser->next_clause = idx;
427 return parser->this_clause;
430static const char *icalrecur_next_clause(
struct icalrecur_parser *parser)
434 parser->this_clause = parser->next_clause;
436 if (parser->this_clause == 0) {
440 idx = strchr(parser->this_clause,
';');
443 parser->next_clause = 0;
447 parser->next_clause = idx;
450 return parser->this_clause;
453static void icalrecur_clause_name_and_value(
struct icalrecur_parser *parser,
454 char **name,
char **value)
458 *name = parser->this_clause;
460 idx = strchr(parser->this_clause,
'=');
476static void sort_byrules(icalrecurrence_by_data *by)
478 short *array = by->data;
482 for (i = 1; i < by->size; i++) {
483 for (j = i - 1; j >= 0 && array[j] > array[j + 1]; j--) {
484 short tmp = array[j + 1];
486 array[j + 1] = array[j];
496static void sort_bysetpos(icalrecurrence_by_data *by)
499#define SIGN(A) ((A) < 0 ? -1 : 1)
501 short *array = by->data;
505 for (i = 1; i < by->size; i++) {
507 j >= 0 && ((SIGN(array[j]) == SIGN(array[j + 1]) && abs(array[j]) > abs(array[j + 1])) ||
508 (array[j] < 0 && array[j + 1] > 0));
510 short tmp = array[j + 1];
512 array[j + 1] = array[j];
523static int icalrecur_add_byrules(
const struct icalrecur_parser *parser, icalrecurrence_by_data *by,
524 int min,
int size,
char *vals)
528 int max = size - (min == 0);
556 long v = strtol(t, &t_end, 10);
567 if (min >= 0 || v <= -max) {
589 by->data[i++] = (short)v;
619static void sort_bydayrules(
struct icalrecur_parser *parser)
622 short *array = by->data;
624 int week_start, i, j;
628 for (i = 0; i < by->size; i++) {
629 for (j = 0; j < i; j++) {
640 short tmp = array[j];
649static int icalrecur_add_bydayrules(
struct icalrecur_parser *parser,
670 if (idx >= by->size) {
692 const long tmpl = strtol(t, &t_end, 10);
693 weekno = (
signed char)tmpl;
697 if (weekno != tmpl) {
703 if ((weekno == 0) && (t != t_end)) {
738 sort_bydayrules(parser);
753 memset(rule, 0,
sizeof(*rule));
755 icalrecurrencetype_clear(rule);
765 icalmemory_free_buffer(p); \
772 SAFEFREE(recur->
by[i].data);
784 icalerror_check_arg_rv((recur != NULL),
"recur");
785 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
792 icalerror_check_arg_rv((recur != NULL),
"recur");
793 icalerror_check_arg_rv((recur->
refcount > 0),
"recur->refcount > 0");
801 icalrecurrencetype_free(recur, 1);
804static void *icalrecur_memdup(
void *p,
size_t size,
int *error)
806 if ((p == NULL) || (size == 0)) {
812 memcpy(newp, p, size);
820static icalrecurrence_by_data icalrecur_by_dup(icalrecurrence_by_data *
by,
int *error)
822 icalrecurrence_by_data newby = {0, 0};
824 newby.data = icalrecur_memdup(
by->data, (
size_t)
by->size *
sizeof(
by->data[0]), error);
826 newby.size =
by->size;
837 icalerror_check_arg_rz((recur != NULL),
"recur");
844 memcpy(res, recur,
sizeof(*res));
856 icalrecurrence_by_data *src_by = &recur->
by[i];
857 icalrecurrence_by_data *dst_by = &res->
by[i];
858 *dst_by = icalrecur_by_dup(src_by, &error);
862 icalrecurrencetype_free(res, 1);
871 struct icalrecur_parser parser = {0};
874 icalerror_check_arg_re(
str != 0,
"str", 0);
884 parser.this_clause = parser.copy;
886 if (parser.copy == 0) {
893 for (icalrecur_first_clause(&parser);
894 parser.this_clause != 0; icalrecur_next_clause(&parser)) {
898 icalrecur_clause_name_and_value(&parser, &name, &value);
901 if (strlen(parser.this_clause) > 0) {
911 }
else if (strcasecmp(name,
"FREQ") == 0) {
921 }
else if (strcasecmp(name,
"RSCALE") == 0) {
922 if (parser.rt->
rscale != NULL) {
928 }
else if (strcasecmp(name,
"SKIP") == 0) {
938 }
else if (strcasecmp(name,
"COUNT") == 0) {
943 parser.rt->
count = 0;
945 const long v = strtol(value, &v_end, 10);
946 if (value != v_end) {
947 parser.rt->
count = v;
950 if (parser.rt->
count < 1) {
954 }
else if (strcasecmp(name,
"UNTIL") == 0) {
964 }
else if (strcasecmp(name,
"INTERVAL") == 0) {
971 const long tmp = strtol(value, &v_temp, 10);
972 if (value != v_temp) {
987 }
else if (strcasecmp(name,
"WKST") == 0) {
996 sort_bydayrules(&parser);
999 }
else if (strncasecmp(name,
"BY", 2) == 0) {
1003 if (strcasecmp(name + 2, recur_map[byrule].
str + 2) == 0) {
1005 r = icalrecur_add_bydayrules(&parser, value);
1007 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
1008 r = icalrecur_add_byrules(&parser, by,
1009 recur_map[byrule].min,
1010 recur_map[byrule].size,
1027 icalrecurrencetype_clear(parser.rt);
1033 icalrecurrence_by_data *by = &parser.rt->
by[byrule];
1036 expand_map[parser.rt->
freq].map[byrule] == ILLEGAL) {
1042 icalrecurrencetype_clear(parser.rt);
1045 icalrecur_free_by(by);
1073 size_t buf_sz = 200;
1074 char temp[20] = {0};
1084 if (recur->
rscale != 0) {
1103 snprintf(temp,
sizeof(temp),
"%d", recur->
interval);
1118 const icalrecurrence_by_data *by = &recur->
by[j];
1126 int limit = recur_map[j].size - 1;
1127 for (i = 0; i < limit && i < by->size; i++) {
1136 snprintf(temp,
sizeof(temp),
"%d%s", pos, daystr);
1142 snprintf(temp,
sizeof(temp),
"%dL",
1146 snprintf(temp,
sizeof(temp),
"%d", by->data[i]);
1150 if ((i + 1) < limit && by->size > i + 1) {
1160 print_date_to_string(temp, &(recur->
until));
1162 print_datetime_to_string(temp, &(recur->
until));
1169 else if (recur->
count != 0) {
1170 snprintf(temp,
sizeof(temp),
"%d", recur->
count);
1182#define BITS_PER_LONG ((unsigned short)(8 * sizeof(unsigned long)))
1185#define LONGS_PER_BITS(n) (((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
1187#define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
1188#define ICAL_YEARDAYS_MASK_OFFSET 4
1191typedef struct icalrecurrence_iterator_by_data {
1192 icalrecurrence_by_data by;
1199} icalrecurrence_iterator_by_data;
1201struct icalrecur_iterator_impl {
1202 struct icaltimetype dtstart;
1203 struct icalrecurrencetype *rule;
1205 struct icaltimetype rstart;
1206 struct icaltimetype istart;
1207 struct icaltimetype iend;
1208 struct icaltimetype last;
1209 int32_t occurrence_no;
1212 int32_t recurrence_set_size;
1213 short sp_idxp, sp_idxn;
1216#if defined(HAVE_LIBICU)
1221 struct icaltimetype period_start;
1235 unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
1242static void daysmask_clearall(
unsigned long mask[])
1245 sizeof(
unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
1249#if defined(UNDEFINED_SANITIZER) && defined(__clang__)
1250 __attribute__((no_sanitize(
"integer")))
1252 unsigned long makeMask(
unsigned long mask,
int leftshift)
1254 return mask << leftshift;
1257static void daysmask_set_range(
unsigned long days[],
int fromDayIncl,
int untilDayExcl,
int v)
1259 int fromBitIdx = fromDayIncl + ICAL_YEARDAYS_MASK_OFFSET;
1260 int untilBitIdx = untilDayExcl + ICAL_YEARDAYS_MASK_OFFSET;
1262 for (
int word_idx = fromBitIdx / BITS_PER_LONG;
1263 word_idx < (int)((untilBitIdx + BITS_PER_LONG - 1) / BITS_PER_LONG);
1265 int lowerBitIdxIncl = (fromBitIdx <= (int)(word_idx * BITS_PER_LONG))
1267 : (fromBitIdx - (int)(word_idx * BITS_PER_LONG));
1268 int upperBitIdxExcl = (untilBitIdx >= (int)((word_idx + 1) * BITS_PER_LONG))
1269 ? (int)BITS_PER_LONG
1270 : (int)(untilBitIdx - (int)(word_idx * BITS_PER_LONG));
1272 unsigned long mask = (
unsigned long)-1;
1273 if (lowerBitIdxIncl > 0) {
1274 mask &= makeMask(((
unsigned long)-1), lowerBitIdxIncl);
1276 if ((upperBitIdxExcl > 0) && (upperBitIdxExcl < (
int)BITS_PER_LONG)) {
1277 mask &= ((
unsigned long)-1) >> (BITS_PER_LONG - upperBitIdxExcl);
1281 days[word_idx] |= mask;
1283 days[word_idx] &= ~mask;
1288static int daysmask_setbit(
unsigned long mask[],
short n,
int v)
1292 n += ICAL_YEARDAYS_MASK_OFFSET;
1295 prev = (mask[n / BITS_PER_LONG] & (1UL << (n % BITS_PER_LONG))) ? 1 : 0;
1297 prev = (mask[n / BITS_PER_LONG] & (1UL >> (-n % BITS_PER_LONG))) ? 1 : 0;
1302 mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
1304 mask[n / BITS_PER_LONG] |= (1UL >> (-n % BITS_PER_LONG));
1308 mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
1310 mask[n / BITS_PER_LONG] &= ~(1UL >> (-n % BITS_PER_LONG));
1318static unsigned long daysmask_getbit(
const unsigned long mask[],
short n)
1320 n += ICAL_YEARDAYS_MASK_OFFSET;
1321 return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
1326 return (impl->bydata[byrule].orig_data == 1);
1329static void recur_iterator_set_static_single_by_value(icalrecur_iterator *impl,
1332 icalrecurrence_iterator_by_data *by = &impl->bydata[byrule];
1334 by->by.data = &by->buffer_value;
1335 by->by.data[0] = value;
1338static void setup_defaults(icalrecur_iterator *impl,
1343 if (impl->dtstart.is_date && recur_map[byrule].isTime) {
1349 recur_iterator_set_static_single_by_value(impl, byrule, 0);
1350 }
else if (expand_map[freq].map[byrule] == EXPAND) {
1353 if (impl->bydata[byrule].by.size == 0) {
1354 recur_iterator_set_static_single_by_value(impl, byrule, (
short)deftime);
1361static int weeks_in_year(
int year)
1367 return (52 + is_long);
1376static void __get_start_time(icalrecur_iterator *impl,
icaltimetype date,
1386 *
hour = impl->rstart.hour;
1394 *
minute = impl->rstart.minute;
1402 *
second = impl->rstart.second;
1408#if defined(HAVE_LIBICU)
1426 UErrorCode status = U_ZERO_ERROR;
1428 icalarray *calendars;
1433 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1434 while ((cal = uenum_next(en, NULL, &status))) {
1443static void set_second(icalrecur_iterator *impl,
int second)
1445 ucal_set(impl->rscale, UCAL_SECOND, (int32_t)
second);
1448static void set_minute(icalrecur_iterator *impl,
int minute)
1450 ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)
minute);
1453static void set_hour(icalrecur_iterator *impl,
int hour)
1455 ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)
hour);
1458static void __set_month(icalrecur_iterator *impl,
int month)
1464 ucal_set(impl->rscale, UCAL_MONTH, (int32_t)
month);
1465 if (is_leap_month) {
1466 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1470static int set_month(icalrecur_iterator *impl,
int month)
1472 UErrorCode status = U_ZERO_ERROR;
1475 __set_month(impl,
month);
1477 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1480 (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1482 if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1483 actual_month |= LEAP_MONTH;
1486 if (actual_month !=
month) {
1487 switch (impl->rule->skip) {
1497 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1507 (
int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1510static int get_months_in_year(icalrecur_iterator *impl,
int year)
1512 UErrorCode status = U_ZERO_ERROR;
1515 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1519 (
int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1520 UCAL_ACTUAL_MAXIMUM, &status));
1523static int get_days_in_year(icalrecur_iterator *impl,
int year)
1525 UErrorCode status = U_ZERO_ERROR;
1528 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1531 return (
int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1532 UCAL_ACTUAL_MAXIMUM, &status);
1535static void set_day_of_year(icalrecur_iterator *impl,
int doy)
1538 doy += get_days_in_year(impl, 0);
1541 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1544static int get_start_of_week(icalrecur_iterator *impl)
1546 UErrorCode status = U_ZERO_ERROR;
1549 doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1550 dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1551 dow -= (int)impl->rule->week_start;
1559static int get_day_of_week(icalrecur_iterator *impl)
1561 UErrorCode status = U_ZERO_ERROR;
1563 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1566static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
1568 UErrorCode status = U_ZERO_ERROR;
1573 last_millis = ucal_getMillis(impl->rscale, &status);
1576 ucal_setDate(impl->rscale,
1577 (int32_t)tt.
year, (int32_t)
month, (int32_t)tt.
day, &status);
1579 ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1582 weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1585 ucal_setMillis(impl->rscale, last_millis, &status);
1590static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
1592 UErrorCode status = U_ZERO_ERROR;
1594 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1597 month = impl->rstart.month;
1599 __set_month(impl,
month);
1601 return (
int)ucal_getLimit(impl->rscale,
1602 UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1605static void prepare_rscale_adjusted(icalrecur_iterator *impl,
1608 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1611 month = impl->rstart.month;
1613 __set_month(impl,
month);
1616 day = impl->rstart.day;
1617 }
else if (
day < 0) {
1618 day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1619 UCAL_ACTUAL_MAXIMUM, status);
1621 ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)
day);
1624static int get_day_of_year(icalrecur_iterator *impl,
1627 UErrorCode status = U_ZERO_ERROR;
1628 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1629 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1632static int get_day_of_week_adjusted(icalrecur_iterator *impl,
1635 UErrorCode status = U_ZERO_ERROR;
1636 prepare_rscale_adjusted(impl,
year,
month,
day, &status);
1637 return (
int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1640static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1644 UErrorCode status = U_ZERO_ERROR;
1645 UCalendar *cal = impl->rscale;
1646 int is_leap_month = 0;
1648 if (normalize && (impl->rscale != impl->greg)) {
1650 UDate millis = ucal_getMillis(impl->rscale, &status);
1652 ucal_setMillis(impl->greg, millis, &status);
1656 (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1659 tt.
year = (int)ucal_get(cal, UCAL_YEAR, &status);
1660 tt.
day = (int)ucal_get(cal, UCAL_DATE, &status);
1662 (int)ucal_get(cal, UCAL_MONTH, &status);
1663 if (is_leap_month) {
1664 tt.
month |= LEAP_MONTH;
1668 tt.
hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1669 tt.
minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1670 tt.
second = (int)ucal_get(cal, UCAL_SECOND, &status);
1676static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1677 int day,
int year,
int *weekno)
1679 ucal_set(impl->rscale, UCAL_YEAR, (int32_t)
year);
1681 day += get_days_in_year(impl, 0) + 1;
1684 ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)
day);
1687 UErrorCode status = U_ZERO_ERROR;
1689 *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1692 return occurrence_as_icaltime(impl, 0);
1695static void increment_year(icalrecur_iterator *impl,
int inc)
1697 UErrorCode status = U_ZERO_ERROR;
1699 ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1702static void __increment_month(icalrecur_iterator *impl,
int inc)
1704 UErrorCode status = U_ZERO_ERROR;
1706 ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1709static void increment_monthday(icalrecur_iterator *impl,
int inc)
1711 UErrorCode status = U_ZERO_ERROR;
1713 ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1716static void increment_hour(icalrecur_iterator *impl,
int inc)
1718 UErrorCode status = U_ZERO_ERROR;
1720 ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1723static void increment_minute(icalrecur_iterator *impl,
int inc)
1725 UErrorCode status = U_ZERO_ERROR;
1727 ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1730static void increment_second(icalrecur_iterator *impl,
int inc)
1732 UErrorCode status = U_ZERO_ERROR;
1734 ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1737static bool validate_byrule(icalrecur_iterator *impl,
1739 short (*decode_val)(
short *,
bool),
1742 if (has_by_data(impl, byrule)) {
1743 UErrorCode status = U_ZERO_ERROR;
1744 const icalrecurrence_by_data *by_ptr = &impl->bydata[byrule].by;
1746 (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1749 for (idx = 0; idx < by_ptr->size; idx++) {
1750 short val = decode_val ? decode_val(&by_ptr->data[idx], decode_flags) : by_ptr->data[idx];
1752 if (abs(val) > max) {
1761static short decode_month(
short *
month,
bool is_hebrew)
1763 if (is_hebrew && *
month > 5) {
1774static short decode_day(
short *
day,
bool flags)
1781static bool initialize_rscale(icalrecur_iterator *impl)
1785 char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = {0};
1786 UErrorCode status = U_ZERO_ERROR;
1787 UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1788 bool is_hebrew =
false;
1796 if (src && !strncmp(src, prefix, strlen(prefix))) {
1798 src += strlen(prefix);
1802 size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1804 tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1805 if (U_FAILURE(status)) {
1812 (void)uloc_setKeywordValue(
"calendar",
"gregorian",
1813 locale,
sizeof(locale), &status);
1816 impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1818 ucal_setDateTime(impl->greg,
1819 (int32_t)dtstart.
year,
1820 (int32_t)(dtstart.
month - 1),
1821 (int32_t)dtstart.
day,
1822 (int32_t)dtstart.
hour,
1824 (int32_t)dtstart.
second, &status);
1826 if (!impl->greg || U_FAILURE(status)) {
1833 impl->rscale = impl->greg;
1840 for (r = rule->
rscale; *r; r++) {
1841 *r = tolower((
int)*r);
1845 en = ucal_getKeywordValuesForLocale(
"calendar",
"",
false, &status);
1846 while ((cal = uenum_next(en, NULL, &status))) {
1847 if (!strcmp(cal, rule->
rscale)) {
1848 is_hebrew = (strcmp(rule->
rscale,
"hebrew") == 0);
1859 (void)uloc_setKeywordValue(
"calendar", rule->
rscale,
1860 locale,
sizeof(locale), &status);
1863 impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1865 UDate millis = ucal_getMillis(impl->greg, &status);
1867 ucal_setMillis(impl->rscale, millis, &status);
1869 if (!impl->rscale || U_FAILURE(status)) {
1877 &decode_month, is_hebrew) ||
1878 !validate_byrule(impl,
ICAL_BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day,
false) ||
1881 !validate_byrule(impl,
ICAL_BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL,
false) ||
1882 !validate_byrule(impl,
ICAL_BY_SET_POS, UCAL_DAY_OF_YEAR, NULL,
false)) {
1888 ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1889 ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, (int32_t)rule->
week_start);
1892 impl->rstart = occurrence_as_icaltime(impl, 0);
1898static void set_start(icalrecur_iterator *impl,
icaltimetype date)
1900 UErrorCode status = U_ZERO_ERROR;
1902 impl->last.is_date = impl->rstart.is_date;
1903 impl->last.zone = impl->rstart.zone;
1905 if (impl->rstart.is_date) {
1906 ucal_setDate(impl->greg,
1908 (int32_t)(date.
month - 1),
1909 (int32_t)date.
day, &status);
1915 ucal_setDateTime(impl->greg,
1917 (int32_t)(date.
month - 1),
1925 if (impl->rscale != impl->greg) {
1926 UDate millis = ucal_getMillis(impl->greg, &status);
1927 ucal_setMillis(impl->rscale, millis, &status);
1931static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
1933 UErrorCode status = U_ZERO_ERROR;
1935 impl->last.is_date = impl->rstart.is_date;
1936 impl->last.zone = impl->rstart.zone;
1938 if (impl->rstart.is_date) {
1939 ucal_setDate(impl->greg,
1941 (int32_t)(date.
month - 1),
1942 (int32_t)date.
day, &status);
1944 ucal_setDateTime(impl->greg,
1946 (int32_t)(date.
month - 1),
1954 if (impl->rscale != impl->greg) {
1955 UDate millis = ucal_getMillis(impl->greg, &status);
1956 ucal_setMillis(impl->rscale, millis, &status);
1965 if (impl->rscale == impl->greg) {
1967 diff = __greg_month_diff(a, b);
1972 UErrorCode status = U_ZERO_ERROR;
1977 millis = ucal_getMillis(impl->rscale, &status);
1979 set_day_of_year(impl, 1);
1980 diff = get_months_in_year(impl,
year) - a.
month;
1982 diff += get_months_in_year(impl,
year);
1987 ucal_setMillis(impl->rscale, millis, &status);
1996 UErrorCode status = U_ZERO_ERROR;
2001 millis = ucal_getMillis(impl->rscale, &status);
2003 set_day_of_year(impl, 1);
2005 diff = __day_diff(impl, a, b);
2008 ucal_setMillis(impl->rscale, millis, &status);
2013static void reset_period_start(icalrecur_iterator *impl)
2017 (void)get_day_of_year(impl, start.
year, start.
month, start.
day);
2028 icalarray *calendars =
icalarray_new(
sizeof(
const char **), 1);
2029 const char *cal =
"GREGORIAN";
2036static void set_second(icalrecur_iterator *impl,
int second)
2038 impl->last.second =
second;
2041static void set_minute(icalrecur_iterator *impl,
int minute)
2043 impl->last.minute =
minute;
2046static void set_hour(icalrecur_iterator *impl,
int hour)
2048 impl->last.hour =
hour;
2051static int set_month(icalrecur_iterator *impl,
int month)
2053 return (impl->last.month =
month);
2057#define get_months_in_year(impl, year) (12)
2060static int get_days_in_year(icalrecur_iterator *impl,
int year)
2067static void set_day_of_year(icalrecur_iterator *impl,
int doy)
2072 doy += get_days_in_year(impl, impl->last.year);
2077 impl->last.day = next.day;
2078 impl->last.month = next.month;
2079 impl->last.year = next.year;
2082static int get_start_of_week(
const icalrecur_iterator *impl)
2087static int get_day_of_week(
const icalrecur_iterator *impl)
2094static int get_week_number(icalrecur_iterator *impl,
struct icaltimetype tt)
2109 week = weeks_in_year(tt.
year - 1);
2110 }
else if (week > weeks_in_year(tt.
year)) {
2118static int get_days_in_month(icalrecur_iterator *impl,
int month,
int year)
2125static struct icaltimetype get_dtstart_adjusted(icalrecur_iterator *impl,
2140 }
else if (
day < 0) {
2148static int get_day_of_year(icalrecur_iterator *impl,
2154static int get_day_of_week_adjusted(icalrecur_iterator *impl,
2161static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
2167static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
2168 int day,
int year,
int *weekno)
2173 day += get_days_in_year(impl,
year) + 1;
2179 *weekno = get_week_number(impl, tt);
2184static void increment_year(icalrecur_iterator *impl,
int inc)
2186 impl->last.
year += inc;
2189static void __increment_month(icalrecur_iterator *impl,
int inc)
2193 impl->last.month += inc;
2198 years = impl->last.month / 12;
2200 impl->last.month = impl->last.month % 12;
2202 if (impl->last.month < 0) {
2203 impl->last.month = impl->last.month + 12;
2210 increment_year(impl, years);
2214static void increment_monthday(icalrecur_iterator *impl,
int inc)
2219static void increment_hour(icalrecur_iterator *impl,
int inc)
2224static void increment_minute(icalrecur_iterator *impl,
int inc)
2229static void increment_second(icalrecur_iterator *impl,
int inc)
2234static bool initialize_rscale(icalrecur_iterator *impl)
2236 if (impl->rule->rscale && strcasecmp(impl->rule->rscale,
"GREGORIAN")) {
2241 impl->rstart = impl->dtstart;
2247static void set_start(icalrecur_iterator *impl,
icaltimetype date)
2249 impl->last.year = date.
year;
2250 impl->last.month = date.
month;
2251 impl->last.day = date.
day;
2252 impl->last.is_date = impl->dtstart.is_date;
2253 impl->last.zone = impl->dtstart.zone;
2255 if (!impl->dtstart.is_date) {
2256 __get_start_time(impl, date, &impl->last.hour,
2257 &impl->last.minute, &impl->last.second);
2261static void set_datetime(icalrecur_iterator *impl,
icaltimetype date)
2271 return __greg_month_diff(a, b);
2277 return __day_diff(impl, a, b);
2280static void reset_period_start(icalrecur_iterator *impl)
2283 impl->last.year = impl->period_start.year;
2284 impl->last.month = impl->period_start.month;
2285 impl->last.day = impl->period_start.day;
2290static int get_second(icalrecur_iterator *impl)
2292 return occurrence_as_icaltime(impl, 1).second;
2295static int get_minute(icalrecur_iterator *impl)
2297 return occurrence_as_icaltime(impl, 1).minute;
2300static int get_hour(icalrecur_iterator *impl)
2302 return occurrence_as_icaltime(impl, 1).hour;
2305static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start);
2306static void increment_month(icalrecur_iterator *impl,
int inc);
2307static void expand_month_days(icalrecur_iterator *impl,
int year,
int month);
2308static void expand_year_days(icalrecur_iterator *impl,
int year);
2309static int next_yearday(icalrecur_iterator *impl,
2310 void (*next_period)(icalrecur_iterator *,
int));
2311static int prev_yearday(icalrecur_iterator *impl,
2312 void (*next_period)(icalrecur_iterator *,
int));
2314static void adjust_to_byday(icalrecur_iterator *impl)
2327 short this_dow = (short)get_day_of_week(impl);
2328 short dow = (short)(impl->bydata[
ICAL_BY_DAY].by.data[0] - this_dow);
2331 if (dow != 0 && this_dow < (
short)impl->rule->week_start) {
2335 if ((this_dow < impl->bydata[
ICAL_BY_DAY].by.data[0] && dow >= 0) || dow < 0) {
2337 increment_monthday(impl, dow);
2349 icalrecur_iterator *impl;
2361#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
2365 !IN_RANGE(dtstart.
year, 0, MAX_TIME_T_YEAR) ||
2366 !IN_RANGE(dtstart.
month, 1, 12) ||
2367 !IN_RANGE(dtstart.
day, 1,
2369 (!dtstart.
is_date && (!IN_RANGE(dtstart.
hour, 0, 23) ||
2370 !IN_RANGE(dtstart.
minute, 0, 59) ||
2371 !IN_RANGE(dtstart.
second, 0, 59)))) {
2381 memset(impl, 0,
sizeof(icalrecur_iterator));
2383 impl->dtstart = dtstart;
2385#if defined(HAVE_LIBICU)
2411 impl->bydata[byrule].by = impl->rule->by[byrule];
2418 impl->bydata[byrule].orig_data =
2419 (short)(impl->rule->by[byrule].size > 0);
2422 if (expand_map[freq].map[byrule] == ILLEGAL &&
2423 has_by_data(impl, byrule)) {
2427 impl->bydata[byrule].orig_data = 0;
2436 if (!initialize_rscale(impl)) {
2461 if (!__iterator_set_start(impl, dtstart)) {
2471 icalerror_check_arg_rv((impl != 0),
"impl");
2473#if defined(HAVE_LIBICU)
2475 if (impl->rscale && (impl->rscale != impl->greg)) {
2476 ucal_close(impl->rscale);
2479 ucal_close(impl->greg);
2511 diff = get_days_in_year(impl,
year) -
2514 diff += get_days_in_year(impl,
year);
2530static void increment_month(icalrecur_iterator *impl,
int inc)
2532 __increment_month(impl, inc);
2535 struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2537 while (this.year < MAX_TIME_T_YEAR) {
2538 icalrecurrence_iterator_by_data *bydata = &impl->bydata[
ICAL_BY_MONTH];
2539 for (bydata->index = 0;
2540 bydata->index < bydata->by.size; bydata->index++) {
2541 if (this.month == bydata->by.data[bydata->index]) {
2546 __increment_month(impl, inc);
2547 this = occurrence_as_icaltime(impl, 0);
2552static int next_unit(icalrecur_iterator *impl,
2554 int (*next_sub_unit)(icalrecur_iterator *),
2555 void (*set_unit)(icalrecur_iterator *,
int),
2556 int (*get_unit)(icalrecur_iterator *),
2558 void (*increment_unit)(icalrecur_iterator *,
int))
2561 (impl->bydata[by_unit].by.size > 0);
2562 int this_frequency = (impl->rule->freq == frequency);
2564 int end_of_data = 0;
2566 icalassert(has_by_unit || this_frequency);
2569 if (next_sub_unit && next_sub_unit(impl) == 0) {
2576 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2577 if (this_frequency) {
2580 size_t stalledCnt = 0;
2581 while ((impl->last.year < MAX_TIME_T_YEAR) && (stalledCnt++ < max_recurrence_time_count)) {
2582 int last_unit = get_unit(impl);
2584 while (bydata->index < bydata->by.size) {
2585 int cur_by = bydata->by.data[bydata->index];
2586 if ((cur_by >= last_unit) &&
2587 ((cur_by - last_unit) % impl->rule->interval) == 0) {
2588 set_unit(impl, cur_by);
2597 if (last_unit + impl->rule->interval < period_len) {
2598 int diff = period_len - last_unit;
2599 multiplier = (diff / impl->rule->interval + (diff % impl->rule->interval > 0));
2601 increment_unit(impl, multiplier * impl->rule->interval);
2606 if (bydata->by.size <= bydata->index) {
2612 if (bydata->index < bydata->by.size) {
2613 set_unit(impl, bydata->by.data[bydata->index]);
2620 increment_unit(impl, impl->rule->interval);
2627static int next_second(icalrecur_iterator *impl)
2630 &set_second, &get_second, 60, &increment_second);
2633static int next_minute(icalrecur_iterator *impl)
2636 &set_minute, &get_minute, 60, &increment_minute);
2639static int next_hour(icalrecur_iterator *impl)
2642 &set_hour, &get_hour, 24, &increment_hour);
2645static int next_day(icalrecur_iterator *impl)
2648 NULL, NULL, 0, &increment_monthday);
2651static int prev_unit(icalrecur_iterator *impl,
2653 int (*prev_sub_unit)(icalrecur_iterator *),
2654 void (*set_unit)(icalrecur_iterator *,
int),
2655 int (*get_unit)(icalrecur_iterator *),
2656 void (*increment_unit)(icalrecur_iterator *,
int))
2659 (impl->bydata[by_unit].by.size > 0);
2660 int this_frequency = (impl->rule->freq == frequency);
2662 int end_of_data = 0;
2664 icalassert(has_by_unit || this_frequency);
2667 if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2672 icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2673 if (this_frequency) {
2676 while (impl->last.year > 0) {
2677 int last_unit = get_unit(impl);
2678 while (bydata->index >= 0) {
2679 int cur_by = bydata->by.data[bydata->index];
2680 if ((cur_by <= last_unit) && (impl->rule->interval > 0) &&
2681 ((last_unit - cur_by) % impl->rule->interval) == 0) {
2682 set_unit(impl, cur_by);
2687 bydata->index = bydata->by.size - 1;
2689 if (last_unit - impl->rule->interval > 0) {
2690 multiplier = (last_unit / impl->rule->interval + (last_unit % impl->rule->interval > 0));
2692 increment_unit(impl, -multiplier * impl->rule->interval);
2697 if (bydata->index < 0) {
2699 bydata->by.size - 1;
2704 set_unit(impl, bydata->by.data[bydata->index]);
2709 increment_unit(impl, -impl->rule->interval);
2716static int prev_second(icalrecur_iterator *impl)
2719 &set_second, &get_second, &increment_second);
2722static int prev_minute(icalrecur_iterator *impl)
2725 &set_minute, &get_minute, &increment_minute);
2728static int prev_hour(icalrecur_iterator *impl)
2731 &set_hour, &get_hour, &increment_hour);
2734static int prev_day(icalrecur_iterator *impl)
2737 NULL, NULL, &increment_monthday);
2741static void expand_bymonth_days(icalrecur_iterator *impl,
int year,
int month)
2744 int days_in_month = get_days_in_month(impl,
month,
year);
2747 short doy = ICAL_BY_YEARDAY_SIZE, mday = impl->bydata[
ICAL_BY_MONTH_DAY].by.data[i];
2748 int this_month =
month;
2750 if (abs(mday) > days_in_month) {
2751 int days_in_year = get_days_in_year(impl,
year);
2753 switch (impl->rule->skip) {
2765 if (this_month > get_months_in_year(impl,
year)) {
2766 doy = days_in_year + 1;
2777 if (this_month == 0) {
2786 if (doy == ICAL_BY_YEARDAY_SIZE) {
2787 doy = get_day_of_year(impl,
year, this_month, mday);
2790 daysmask_setbit(impl->days, doy, 1);
2791 if (doy < impl->days_index) {
2792 impl->days_index = doy;
2798static void expand_by_day(icalrecur_iterator *impl,
int year,
2799 int doy_offset,
int last_day,
2800 int first_dow,
int last_dow,
2804 unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2807 memcpy(bydays, impl->days,
sizeof(bydays));
2809 daysmask_set_range(impl->days, doy_offset + 1, doy_offset + last_day + 1, 0);
2811 for (i = 0; i < impl->bydata[
ICAL_BY_DAY].by.size; i++) {
2815 int first_matching_day, last_matching_day;
2816 int day, this_weekno;
2820 first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2821 last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2826 day = first_matching_day;
2828 }
else if (pos > 0) {
2830 day = first_matching_day + (pos - 1) * 7;
2832 if (
day > last_matching_day) {
2838 day = last_matching_day + (pos + 1) * 7;
2840 if (
day < first_matching_day) {
2845 if (doy_offset < 0) {
2848 (void)__icaltime_from_day_of_year(impl,
day + doy_offset,
year,
2858 int nweeks = weeks_in_year(
year);
2865 weekno += nweeks + 1;
2868 if (weekno == this_weekno) {
2878 const unsigned long daysmask = daysmask_getbit(bydays,
day + doy_offset);
2879 int new_val = is_limiting
2885 if (!daysmask_setbit(impl->days,
day + doy_offset, new_val) && new_val) {
2886 if (
day + doy_offset < impl->days_index) {
2887 impl->days_index =
day + doy_offset;
2892 }
while (!pos && ((
day += 7) <= last_day) && ++this_weekno);
2899static void expand_month_days(icalrecur_iterator *impl,
int year,
int month)
2901 int doy_offset, days_in_month, first_dow;
2903 daysmask_clearall(impl->days);
2907 impl->period_start = occurrence_as_icaltime(impl, 0);
2909 doy_offset = get_day_of_year(impl,
year,
month, 1) - 1;
2910 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
2911 days_in_month = get_days_in_month(impl,
month,
year);
2920 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2922 last_dow = get_day_of_week_adjusted(impl,
year,
month, days_in_month);
2924 expand_by_day(impl,
year, doy_offset, days_in_month,
2925 first_dow, last_dow,
2930static void __next_month(icalrecur_iterator *impl,
int inc)
2935 increment_month(impl, inc);
2936 this = occurrence_as_icaltime(impl, 0);
2937 expand_month_days(impl, this.year, this.month);
2940static int next_month(icalrecur_iterator *impl)
2942 return next_yearday(impl, &__next_month);
2945static int prev_month(icalrecur_iterator *impl)
2947 return prev_yearday(impl, &__next_month);
2950static int next_weekday_by_week(icalrecur_iterator *impl)
2952 int end_of_data = 0;
2954 if (next_hour(impl) == 0) {
2978 dow -= (int)impl->rule->week_start;
2983 int start_of_week = get_start_of_week(impl);
2985 if (dow + start_of_week < 1) {
2991 increment_year(impl, -1);
2994 set_day_of_year(impl, start_of_week + dow);
3000static bool next_week(icalrecur_iterator *impl)
3004 if (next_weekday_by_week(impl) == 0) {
3012 increment_monthday(impl, 7 * impl->rule->interval);
3017static int prev_weekday_by_week(icalrecur_iterator *impl)
3019 int end_of_data = 0;
3020 int start_of_week, dow;
3022 if (prev_hour(impl) == 0) {
3044 dow -= (int)impl->rule->week_start;
3049 start_of_week = get_start_of_week(impl);
3051 if (dow + start_of_week < 1) {
3053 increment_year(impl, -1);
3056 set_day_of_year(impl, start_of_week + dow);
3061static int prev_week(icalrecur_iterator *impl)
3065 if (prev_weekday_by_week(impl) == 0) {
3073 increment_monthday(impl, 7 * -impl->rule->interval);
3081static void expand_year_days(icalrecur_iterator *impl,
int year)
3084 short days_in_year = (short)get_days_in_year(impl,
year);
3087 daysmask_clearall(impl->days);
3091 impl->period_start = occurrence_as_icaltime(impl, 0);
3105 if (abs(doy) > days_in_year) {
3106 switch (impl->rule->skip) {
3118 doy = days_in_year + 1;
3130 }
else if (doy < 0) {
3131 doy += days_in_year + 1;
3134 daysmask_setbit(impl->days, doy, 1);
3135 if (doy < impl->days_index) {
3136 impl->days_index = doy;
3152 int nweeks = weeks_in_year(
year);
3156 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3163 set_day_of_year(impl, 1);
3164 start_doy += get_start_of_week(impl) - 1;
3166 start_doy += (get_day_of_week_adjusted(impl, impl->dtstart.year,
3167 impl->dtstart.month, impl->dtstart.day) -
3168 (int)impl->rule->week_start + 7) %
3171 (void)get_days_in_year(impl,
year);
3178 weekno += nweeks + 1;
3179 }
else if (weekno > nweeks) {
3183 doy = start_doy + 7 * (weekno - 1);
3185 daysmask_setbit(impl->days, doy, 1);
3186 if (doy < impl->days_index) {
3187 impl->days_index = doy;
3196 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3206 int first_dow, last_dow;
3208 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3215 if (
month > 0 &&
month < ICAL_BY_MONTH_SIZE) {
3216 int doy_offset, days_in_month;
3220 get_day_of_year(impl,
year,
month, 1) - 1;
3221 first_dow = get_day_of_week_adjusted(impl,
year,
month, 1);
3224 days_in_month = get_days_in_month(impl,
month,
year);
3225 last_dow = get_day_of_week_adjusted(impl,
year,
3226 month, days_in_month);
3228 expand_by_day(impl,
year, doy_offset, days_in_month,
3229 first_dow, last_dow, limiting);
3234 short doy_offset = 0, last_day;
3240 (void)__icaltime_from_day_of_year(impl, 1,
year, &weekno);
3247 set_day_of_year(impl, 1);
3248 doy_offset += get_start_of_week(impl) - 1;
3249 last_day = (7 * weeks_in_year(
year)) - doy_offset - 1;
3251 first_dow = (int)impl->rule->week_start;
3252 last_dow = (first_dow + 6) % 7;
3255 first_dow = get_day_of_week_adjusted(impl,
year, 1, 1);
3258 set_day_of_year(impl, days_in_year);
3259 last_dow = get_day_of_week(impl);
3261 last_day = days_in_year;
3264 expand_by_day(impl,
year, doy_offset, last_day, first_dow, last_dow, limiting);
3269static void __next_year(icalrecur_iterator *impl,
int inc)
3274 increment_year(impl, inc);
3275 this = occurrence_as_icaltime(impl, 0);
3276 expand_year_days(impl, this.year);
3279static int next_year(icalrecur_iterator *impl)
3281 return next_yearday(impl, &__next_year);
3284static int prev_year(icalrecur_iterator *impl)
3286 return prev_yearday(impl, &__next_year);
3289static short daymask_find_next_bit(
const unsigned long *days,
short start_index)
3291 short days_index = start_index;
3293 short startBitIndex;
3294 unsigned short wordIdx;
3296 if (days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3297 return ICAL_YEARDAYS_MASK_SIZE;
3301 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3302 wordIdx = (
unsigned short)(startBitIndex / BITS_PER_LONG);
3304 if (startBitIndex >= 0) {
3305 v >>= startBitIndex % BITS_PER_LONG;
3307 v <<= -startBitIndex % BITS_PER_LONG;
3312 days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
3315 unsigned short maxWordIdx = (
unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)) - 1;
3316 while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
3324 days_index += BITS_PER_LONG;
3333 int maskSize = (int)(BITS_PER_LONG / 2);
3334 mask = (((
unsigned long)1) << maskSize) - 1;
3337 if ((v & mask) == 0) {
3339 days_index += maskSize;
3349static short daymask_find_prev_bit(
const unsigned long *days,
short start_index)
3351 short days_index = start_index;
3353 short startBitIndex;
3356 if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3357 return -ICAL_YEARDAYS_MASK_OFFSET;
3361 startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3362 wordIdx = (int)(startBitIndex / BITS_PER_LONG);
3364 v = makeMask(v, BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1);
3368 days_index -= (startBitIndex % BITS_PER_LONG) + 1;
3371 while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
3379 days_index -= BITS_PER_LONG;
3388 int maskSize = (int)(BITS_PER_LONG / 2);
3389 mask = ((((
unsigned long)1) << maskSize) - 1) << maskSize;
3392 if ((v & mask) == 0) {
3394 days_index -= maskSize;
3397 mask = makeMask(mask, maskSize);
3404static int next_yearday(icalrecur_iterator *impl,
3405 void (*next_period)(icalrecur_iterator *,
int))
3407 if (next_hour(impl) == 0) {
3413 reset_period_start(impl);
3416 impl->days_index = daymask_find_next_bit(impl->days, impl->days_index + 1);
3420 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3425 next_period(impl, impl->rule->interval);
3427 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3439 if (impl->days_index < 1) {
3441 increment_year(impl, -1);
3444 set_day_of_year(impl, impl->days_index);
3449static int prev_yearday(icalrecur_iterator *impl,
3450 void (*next_period)(icalrecur_iterator *,
int))
3452 if (prev_hour(impl) == 0) {
3458 reset_period_start(impl);
3461 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3465 while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3469 next_period(impl, -impl->rule->interval);
3471 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3472 impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3481 if (impl->days_index < 1) {
3483 increment_year(impl, -1);
3486 set_day_of_year(impl, impl->days_index);
3491static int days_in_current_month(icalrecur_iterator *impl)
3493 return get_days_in_month(impl, impl->last.month, impl->last.year);
3496static int days_in_current_year(icalrecur_iterator *impl)
3498 return get_days_in_year(impl, impl->last.year);
3501static inline int has_contract_restriction(icalrecur_iterator *impl,
3504 return impl->bydata[byrule].by.size > 0 &&
3505 expand_map[impl->rule->freq].map[byrule] == CONTRACT;
3508static bool check_contract_restriction(icalrecur_iterator *impl,
3510 int (*get_total)(icalrecur_iterator *))
3512 if (has_contract_restriction(impl, byrule)) {
3515 for (
int itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3516 short byval = impl->bydata[byrule].by.data[itr];
3517 if ((byval < 0) && (total == 0)) {
3520 total = get_total(impl);
3529 if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
3542static bool check_contracting_rules(icalrecur_iterator *impl)
3544 struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3549#define CHECK_CONTRACT_RESTRICTION(by, v, get_total) \
3550 (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
3558 CHECK_CONTRACT_RESTRICTION(
ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3559 CHECK_CONTRACT_RESTRICTION(
3561 CHECK_CONTRACT_RESTRICTION(
3566#undef CHECK_CONTRACT_RESTRICTION
3580static void setup_setpos(icalrecur_iterator *impl,
int next)
3583 int days_index = impl->days_index;
3586 bydata_indices[byrule] = impl->bydata[byrule].index;
3590 impl->recurrence_set_size = 1;
3591 int period_change = 1;
3593 switch (impl->rule->freq) {
3599 period_change = (next ? next_second : prev_second)(impl);
3602 period_change = (next ? next_minute : prev_minute)(impl);
3605 period_change = (next ? next_hour : prev_hour)(impl);
3608 period_change = (next ? next_weekday_by_week : prev_weekday_by_week)(impl);
3613 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3616 period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3622 if (period_change == 0 && check_contracting_rules(impl)) {
3623 impl->recurrence_set_size++;
3625 }
while (period_change == 0);
3632 impl->set_pos = impl->recurrence_set_size;
3633 impl->sp_idxp = impl->sp_pmax;
3634 impl->sp_idxn = impl->sp_pmax + 1;
3640 set_datetime(impl, last);
3642 impl->days_index = days_index;
3644 impl->bydata[byrule].index = bydata_indices[byrule];
3652static inline int setpos_cmp(
int s1,
int s2,
int next)
3655 return (next ? -1 : 1);
3656 }
else if (s2 < s1) {
3657 return (next ? 1 : -1);
3668static bool check_setpos(icalrecur_iterator *impl,
int next)
3677 if (impl->sp_pmax >= 0) {
3678 set_pos = by->data[impl->sp_idxp];
3680 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3681 if (next && impl->sp_idxp < impl->sp_pmax) {
3683 }
else if (!next && impl->sp_idxp > 0) {
3688 set_pos = by->data[impl->sp_idxp];
3690 if (impl->set_pos == set_pos) {
3695 if (impl->sp_pmax < by->size - 1) {
3696 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3697 while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3698 if (next && impl->sp_idxn > impl->sp_pmax + 1) {
3700 }
else if (!next && impl->sp_idxn < by->size - 1) {
3705 set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3707 if (impl->set_pos == set_pos) {
3718 (impl->rule->count != 0 && impl->occurrence_no >= impl->rule->count) ||
3725 if ((impl->occurrence_no == 0) &&
3727 check_setpos(impl, 1) &&
3728 check_contracting_rules(impl)) {
3729 impl->occurrence_no++;
3733 int period_change = 1;
3736 icalrecur_iterator impl_last = *impl;
3739 size_t stalledCnt = 0;
3741 int lastTimeCompare = 0;
3743 int checkContractingRules = (int)check_contracting_rules(impl);
3744 size_t cntRecurrences = 0;
3747 switch (impl->rule->freq) {
3754 period_change = next_minute(impl);
3758 period_change = next_hour(impl);
3762 period_change = next_day(impl);
3766 period_change = (int)next_week(impl);
3770 period_change = next_month(impl);
3774 period_change = next_year(impl);
3782 impl->last = occurrence_as_icaltime(impl, 1);
3786 if (impl->last.year > MAX_TIME_T_YEAR ||
3793 set_datetime(impl, impl_last.last);
3798 int new_ccr = (int)check_contracting_rules(impl);
3800 if (checkContractingRules == 0 || period_change) {
3801 setup_setpos(impl, 1);
3806 checkContractingRules = new_ccr;
3811 if (lastTimeCompare == 0) {
3812 if (stalledCnt++ == max_recurrence_time_count) {
3818 }
while ((cntRecurrences++ < max_recurrences) &&
3819 ((lastTimeCompare == 0) ||
3821 (!check_contracting_rules(impl)) ||
3822 (hasSetPos && !check_setpos(impl, 1))));
3824 impl->occurrence_no++;
3836 int period_change = 1;
3837 icalrecur_iterator impl_last = *impl;
3839 int checkContractingRules = (int)check_contracting_rules(impl);
3843 switch (impl->rule->freq) {
3849 period_change = prev_minute(impl);
3853 period_change = prev_hour(impl);
3857 period_change = prev_day(impl);
3861 period_change = prev_week(impl);
3865 period_change = prev_month(impl);
3869 period_change = prev_year(impl);
3877 impl->last = occurrence_as_icaltime(impl, 1);
3884 set_datetime(impl, impl_last.last);
3889 int new_ccr = (int)check_contracting_rules(impl);
3891 if (checkContractingRules == 0 || period_change) {
3892 setup_setpos(impl, 0);
3897 checkContractingRules = new_ccr;
3900 }
while (impl->last.year > MAX_TIME_T_YEAR ||
3906 (hasSetPos && !check_setpos(impl, 0)) ||
3907 !check_contracting_rules(impl));
3909 impl->occurrence_no--;
3916static void set_bydata_start(icalrecurrence_iterator_by_data *bydata,
int tfield)
3920 bdi < bydata->by.size; bdi++) {
3921 if (bydata->by.data[bdi] == tfield) {
3922 bydata->index = bdi;
3928static bool __iterator_set_start(icalrecur_iterator *impl,
icaltimetype start)
3931 short interval = impl->rule->interval;
3934 impl->istart = start;
3935 impl->occurrence_no = 0;
3936 impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3939 set_start(impl, start);
3946 if ((interval > 1) &&
3947 (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3950 set_day_of_year(impl, 1);
3951 increment_year(impl, interval - diff);
3955 start = occurrence_as_icaltime(impl, 0);
3958 int start_weekno = get_week_number(impl, start);
3959 if (start_weekno > 5 &&
3964 increment_year(impl, -1);
3965 expand_year_days(impl, start.
year - 1);
3966 int days_in_year = get_days_in_year(impl, start.
year - 1);
3967 impl->days_index = daymask_find_next_bit(impl->days, days_in_year + 1);
3968 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3969 increment_year(impl, 1);
3971 }
else if (start_weekno < 45 &&
3972 start.
month == 12) {
3976 increment_year(impl, 1);
3977 expand_year_days(impl, start.
year + 1);
3978 impl->days_index = daymask_find_next_bit(impl->days, -ICAL_YEARDAYS_MASK_OFFSET);
3979 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3980 increment_year(impl, -1);
3987 while (start.
year < MAX_TIME_T_YEAR && impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3988 expand_year_days(impl, start.
year);
4001 if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
4002 increment_year(impl, interval);
4003 start = occurrence_as_icaltime(impl, 0);
4008 set_day_of_year(impl, impl->days_index);
4009 if (impl->days_index < 1) {
4010 increment_year(impl, -1);
4019 if ((interval > 1) &&
4020 (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
4023 increment_monthday(impl, -impl->istart.day + 1);
4024 __increment_month(impl, interval - diff);
4028 start = occurrence_as_icaltime(impl, 0);
4032 while (start.
year < 20000) {
4033 expand_month_days(impl, start.
year, start.
month);
4034 if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
4037 increment_month(impl, impl->rule->interval);
4038 start = occurrence_as_icaltime(impl, 0);
4042 set_day_of_year(impl, impl->days_index);
4050 recur_iterator_set_static_single_by_value(impl,
ICAL_BY_DAY, (
short)get_day_of_week(impl));
4052 adjust_to_byday(impl);
4056 impl->rstart = occurrence_as_icaltime(impl, 0);
4060 start = occurrence_as_icaltime(impl, 0);
4062 if ((interval > 1) &&
4063 (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
4066 increment_monthday(impl, 7 * (interval - diff));
4072 if ((interval > 1) &&
4073 (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
4076 increment_monthday(impl, interval - diff);
4081 if ((interval > 1) &&
4082 (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
4085 increment_hour(impl, interval - diff);
4087 set_bydata_start(&impl->bydata[
ICAL_BY_HOUR], impl->istart.hour);
4091 if ((interval > 1) &&
4092 (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
4095 increment_minute(impl, interval - diff);
4097 set_bydata_start(&impl->bydata[
ICAL_BY_MINUTE], impl->istart.minute);
4101 if ((interval > 1) &&
4102 (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
4105 increment_second(impl, interval - diff);
4107 set_bydata_start(&impl->bydata[
ICAL_BY_SECOND], impl->istart.second);
4115 impl->last = occurrence_as_icaltime(impl, 1);
4117 setup_setpos(impl, 1);
4121 if (impl->last.year > MAX_TIME_T_YEAR) {
4133 if (impl->rule->count > 0) {
4143 start = impl->dtstart;
4151 return __iterator_set_start(impl, start);
4184 from = impl->rule->until;
4191 if (!__iterator_set_start(impl, from)) {
4212 impl->days_index = 0;
4230 icalrecurrencetype_free(recur, 0);
4232 memset(recur, 0,
sizeof(*recur));
4256 pos = (abs(
day) - wd) / 8 * ((
day < 0) ? -1 : 1);
4263 short s_weekday = (short)weekday;
4264 short a_position = (short)(8 * abs(position));
4265 return (s_weekday + a_position) * ((position < 0) ? -1 : 1);
4270 return (
month & LEAP_MONTH) != 0;
4275 return (
month & ~LEAP_MONTH);
4280 return (
short)
month | (is_leap ? LEAP_MONTH : 0);
4284 icaltime_t start,
int count, icaltime_t *array)
4287 icalrecur_iterator *ritr;
4290 memset(array, 0, (
size_t)count *
sizeof(icaltime_t));
4323#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4324 if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4329 myHandling = invalidRruleHandling;
4331#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4332 if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
4342#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4343 if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4348 invalidRruleHandling = newSetting;
4350#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4351 if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
icalarray * icalarray_new(size_t element_size, size_t increment_size)
enum icalrequeststatus kind
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Error handling for libical.
icalerrorenum
Represents the different types of errors that can be triggered in libical.
@ ICAL_MALFORMEDDATA_ERROR
@ ICAL_UNIMPLEMENTED_ERROR
#define icalerrno
Access the current icalerrno value.
size_t icallimit_get(icallimits_kind kind)
Defines the interface for getting/setting internal library limits.
@ ICAL_LIMIT_RECURRENCE_SEARCH
@ ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL
void icalmemory_free_buffer(void *buf)
Releases a buffer.
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Common memory management routines.
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
struct icalrecurrencetype * icalrecurrencetype_new_from_string(const char *str)
struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
void icalrecurrencetype_ref(struct icalrecurrencetype *recur)
icalrecurrencetype_skip icalrecur_string_to_skip(const char *str)
bool icalrecur_iterator_set_end(icalrecur_iterator *impl, struct icaltimetype end)
short icalrecurrencetype_encode_month(int month, bool is_leap)
short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
bool icalrecurrencetype_month_is_leap(short month)
const char * icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
const char * icalrecur_skip_to_string(icalrecurrencetype_skip kind)
bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
struct icalrecurrencetype * icalrecurrencetype_clone(struct icalrecurrencetype *recur)
const char * icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting)
void icalrecur_iterator_free(icalrecur_iterator *impl)
icalrecurrencetype_frequency icalrecur_string_to_freq(const char *str)
struct icalrecurrencetype * icalrecurrencetype_new(void)
bool icalrecur_iterator_set_range(icalrecur_iterator *impl, struct icaltimetype from, struct icaltimetype to)
icalarray * icalrecurrencetype_rscale_supported_calendars(void)
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void)
int icalrecurrencetype_day_position(short day)
bool icalrecur_expand_recurrence(const char *rule, icaltime_t start, int count, icaltime_t *array)
char * icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
void icalrecurrencetype_unref(struct icalrecurrencetype *recur)
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char *str)
int icalrecurrencetype_month_month(short month)
char * icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
Routines for dealing with recurring time.
icalrecurrencetype_weekday
icalrecurrencetype_frequency
@ ICAL_SECONDLY_RECURRENCE
@ ICAL_MINUTELY_RECURRENCE
@ ICAL_MONTHLY_RECURRENCE
icalrecurrencetype_byrule
@ ICAL_BYRULE_NO_CONTRACTION
ical_invalid_rrule_handling
@ ICAL_RRULE_TREAT_AS_ERROR
@ ICAL_RRULE_IGNORE_INVALID
struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const bool is_date, const icaltimezone *zone)
Constructor.
struct icaltimetype icaltime_from_string(const char *str)
int icaltime_day_of_year(const struct icaltimetype t)
int icaltime_start_doy_week(const struct icaltimetype t, int fdow)
bool icaltime_is_leap_year(const int year)
int icaltime_day_of_week(const struct icaltimetype t)
struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
bool icaltime_is_valid_time(const struct icaltimetype t)
int icaltime_days_in_month(const int month, const int year)
struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone)
int icaltime_days_in_year(const int year)
bool icaltime_is_null_time(const struct icaltimetype t)
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
icaltime_t icaltime_as_timet(const struct icaltimetype tt)
int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
struct icaltimetype icaltime_null_time(void)
const char * icaltimezone_get_location(const icaltimezone *zone)
const char * icaltimezone_get_tzid(icaltimezone *zone)
const char * icaltimezone_tzid_prefix(void)
Timezone handling routines.
struct _icaltimezone icaltimezone
Defines the data structure representing iCalendar parameter values.
icalrecurrencetype_frequency freq
icalrecurrencetype_skip skip
icalrecurrencetype_weekday week_start
struct icaltimetype until
icalrecurrence_by_data by[ICAL_BY_NUM_PARTS]
const icaltimezone * zone