2 ======================================================================
4 CREATOR: eric 16 May 2000
10 (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of either:
15 The LGPL as published by the Free Software Foundation, version
16 2.1, available at: http://www.fsf.org/copyleft/lesser.html
20 The Mozilla Public License Version 1.0. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
26 Processing starts when the caller generates a new recurrence
27 iterator via icalrecur_iterator_new(). This routine copies the
28 recurrence rule into the iterator and extracts things like start and
29 end dates. Then, it checks if the rule is legal, using some logic
30 from RFC2445 and some logic that probably should be in RFC2445.
32 Then, icalrecur_iterator_new() re-writes some of the BY*
33 arrays. This involves ( via a call to setup_defaults() ) :
35 1) For BY rule parts with no data ( ie BYSECOND was not specified )
36 copy the corresponding time part from DTSTART into the BY array. (
37 So impl->by_ptrs[BY_SECOND] will then have one element if is
38 originally had none ) This only happens if the BY* rule part data
39 would expand the number of occurrences in the occurrence set. This
40 lets the code ignore DTSTART later on and still use it to get the
41 time parts that were not specified in any other way.
43 2) For the by rule part that are not the same interval as the
44 frequency -- for HOURLY anything but BYHOUR, for instance -- copy the
45 first data element from the rule part into the first occurrence. For
46 example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the
47 first time to be returned to have an hour of 10.
49 Finally, for INTERVAL=YEARLY, the routine expands the rule to get
50 all of the days specified in the rule. The code will do this for
51 each new year, and this is the first expansion. This is a special
52 case for the yearly interval; no other frequency gets expanded this
53 way. The yearly interval is the most complex, so some special
54 processing is required.
56 After creating a new iterator, the caller will make successive calls
57 to icalrecur_iterator_next() to get the next time specified by the
58 rule. The main part of this routine is a switch on the frequency of
59 the rule. Each different frequency is handled by a different
62 For example, next_hour handles the case of INTERVAL=HOURLY, and it
63 is called by other routines to get the next hour. First, the routine
64 tries to get the next minute part of a time with a call to
65 next_minute(). If next_minute() returns 1, it has reached the end of
66 its data, usually the last element of the BYMINUTE array. Then, if
67 there is data in the BYHOUR array, the routine changes the hour to
68 the next one in the array. If INTERVAL=HOURLY, the routine advances
69 the hour by the interval.
71 If the routine used the last hour in the BYHOUR array, and the
72 INTERVAL=HOURLY, then the routine calls increment_monthday() to set
73 the next month day. The increment_* routines may call higher routine
74 to increment the month or year also.
76 The code for INTERVAL=DAILY is handled by next_day(). First, the
77 routine tries to get the next hour part of a time with a call to
78 next_hour. If next_hour() returns 1, it has reached the end of its
79 data, usually the last element of the BYHOUR array. This means that
80 next_day() should increment the time to the next day. If FREQUENCY==DAILY,
81 the routine increments the day by the interval; otherwise, it
82 increments the day by 1.
84 Next_day() differs from next_hour because it does not use the BYDAY
85 array to select an appropriate day. Instead, it returns every day (
86 incrementing by 1 if the frequency is not DAILY with INTERVAL!=1)
87 Any days that are not specified in an non-empty BYDAY array are
90 Generally, the flow of these routine is for a next_* call a next_*
91 routine of a lower interval ( next_day calls next_hour) and then to
92 possibly call an increment_* routine of an equal or higher
93 interval. ( next_day calls increment_monthday() )
95 When the call to the original next_* routine returns,
96 icalrecur_iterator_next() will check the returned data against other
97 BYrule parts to determine if is should be excluded by calling
98 check_contracting_rules. Generally, a contracting rule is any with a
99 larger time span than the interval. For instance, if
100 INTERVAL=DAILY, BYMONTH is a contracting rule part.
102 Check_contracting_rules() uses icalrecur_check_rulepart() to do its
103 work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule
104 is contracting, and if it is, and if the BY rule part has some data,
105 then the routine checks if the value of a component of the time is
106 part of the byrule part. For instance, for "INTERVAL=DAILY;
107 BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value
108 given to it has a month of either 6 or 10.
110 Finally, icalrecur_iterator_next() does a few other checks on the
111 time value, and if it passes, it returns the time.
113 A note about the end_of_data flag. The flag indicates that the
114 routine is at the end of its data -- the last BY rule if the routine
115 is using by rules, or the last day of the week/month/year/etc if
118 This flag is usually set early in a next_* routine and returned in
119 the end. The way it is used allows the next_* routine to set the
120 last time back to the first element in a BYxx rule, and then signal
121 to the higer level routine to increment the next higher level. For
122 instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month
123 runs though both TU and FR, it sets the week day back to TU and sets
124 end_of_data to 1x. This signals next_month to increment the month.
127 ======================================================================*/
133 #include "icalrecur.h"
135 #ifdef ICAL_NO_LIBICAL
136 #define icalerror_set_errno(x)
137 #define icalerror_check_arg_rv(x,y)
139 #include "icalerror.h"
140 #include "icalmemory.h"
143 #include <glib.h> /* for malloc */
144 #include <stdlib.h> /* for malloc */
145 #include <errno.h> /* for errno */
146 #include <string.h> /* for strdup and strchr*/
148 #include <stddef.h> /* For offsetof() macro */
152 #define TEMP_MAX 1024
155 #define BYDAYIDX impl->by_indices[BY_DAY]
156 #define BYDAYPTR impl->by_ptrs[BY_DAY]
158 #define BYMONIDX impl->by_indices[BY_MONTH]
159 #define BYMONPTR impl->by_ptrs[BY_MONTH]
161 #define BYMDIDX impl->by_indices[BY_MONTH_DAY]
162 #define BYMDPTR impl->by_ptrs[BY_MONTH_DAY]
164 #define BYWEEKIDX impl->by_indices[BY_WEEK_NO]
165 #define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO]
167 const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind);
168 icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str);
170 const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind);
171 icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str);
175 /*********************** Rule parsing routines ************************/
177 struct icalrecur_parser {
183 struct icalrecurrencetype rt;
186 const char* icalrecur_first_clause(struct icalrecur_parser *parser)
189 parser->this_clause = parser->copy;
191 idx = strchr(parser->this_clause,';');
194 parser->next_clause = 0;
200 parser->next_clause = idx;
202 return parser->this_clause;
206 const char* icalrecur_next_clause(struct icalrecur_parser *parser)
210 parser->this_clause = parser->next_clause;
212 if(parser->this_clause == 0){
216 idx = strchr(parser->this_clause,';');
219 parser->next_clause = 0;
224 parser->next_clause = idx;
227 return parser->this_clause;
231 void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
232 char** name, char** value)
236 *name = parser->this_clause;
238 idx = strchr(parser->this_clause,'=');
251 void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array,
252 int size, char* vals)
276 /* Get optional sign. HACK. sign is not allowed for all BYxxx
281 } else if (*t == '+'){
290 array[i] = ICAL_RECURRENCE_ARRAY_MAX;
296 void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals)
303 icalrecurrencetype_weekday wd;
304 short *array = parser->rt.by_day;
308 vals_copy = icalmemory_strdup(vals);
310 end = (char*)vals_copy+strlen(vals_copy);
325 /* Get optional sign. */
329 } else if (*t == '+'){
337 /* Get Optional weekno */
338 if( sscanf(t,"%d",&weekno) != 0){
340 int weeknolen = (n-t)-3; /* 3 -> one for \0, 2 for day name */
341 /* could use abs(log10(weekno))+1, but that needs libm */
348 wd = icalrecur_string_to_weekday(t);
350 array[i++] = sign* ((int)wd + 8*weekno);
351 array[i] = ICAL_RECURRENCE_ARRAY_MAX;
360 struct icalrecurrencetype icalrecurrencetype_from_string(const char* str)
362 struct icalrecur_parser parser;
364 memset(&parser,0,sizeof(parser));
365 icalrecurrencetype_clear(&parser.rt);
367 icalerror_check_arg_re(str!=0,"str",parser.rt);
370 /* Set up the parser struct */
372 parser.copy = icalmemory_strdup(parser.rule);
373 parser.this_clause = parser.copy;
375 if(parser.copy == 0){
376 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
380 /* Loop through all of the clauses */
381 for(icalrecur_first_clause(&parser);
382 parser.this_clause != 0;
383 icalrecur_next_clause(&parser))
386 icalrecur_clause_name_and_value(&parser,&name,&value);
389 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
390 icalrecurrencetype_clear(&parser.rt);
394 if (strcmp(name,"FREQ") == 0){
395 parser.rt.freq = icalrecur_string_to_freq(value);
396 } else if (strcmp(name,"COUNT") == 0){
397 parser.rt.count = atoi(value);
398 } else if (strcmp(name,"UNTIL") == 0){
399 parser.rt.until = icaltime_from_string(value);
400 } else if (strcmp(name,"INTERVAL") == 0){
401 parser.rt.interval = atoi(value);
402 } else if (strcmp(name,"WKST") == 0){
403 parser.rt.week_start = icalrecur_string_to_weekday(value);
404 } else if (strcmp(name,"BYSECOND") == 0){
405 icalrecur_add_byrules(&parser,parser.rt.by_second,
406 ICAL_BY_SECOND_SIZE,value);
407 } else if (strcmp(name,"BYMINUTE") == 0){
408 icalrecur_add_byrules(&parser,parser.rt.by_minute,
409 ICAL_BY_MINUTE_SIZE,value);
410 } else if (strcmp(name,"BYHOUR") == 0){
411 icalrecur_add_byrules(&parser,parser.rt.by_hour,
412 ICAL_BY_HOUR_SIZE,value);
413 } else if (strcmp(name,"BYDAY") == 0){
414 icalrecur_add_bydayrules(&parser,value);
415 } else if (strcmp(name,"BYMONTHDAY") == 0){
416 icalrecur_add_byrules(&parser,parser.rt.by_month_day,
417 ICAL_BY_MONTHDAY_SIZE,value);
418 } else if (strcmp(name,"BYYEARDAY") == 0){
419 icalrecur_add_byrules(&parser,parser.rt.by_year_day,
420 ICAL_BY_YEARDAY_SIZE,value);
421 } else if (strcmp(name,"BYWEEKNO") == 0){
422 icalrecur_add_byrules(&parser,parser.rt.by_week_no,
423 ICAL_BY_WEEKNO_SIZE,value);
424 } else if (strcmp(name,"BYMONTH") == 0){
425 icalrecur_add_byrules(&parser,parser.rt.by_month,
426 ICAL_BY_MONTH_SIZE,value);
427 } else if (strcmp(name,"BYSETPOS") == 0){
428 icalrecur_add_byrules(&parser,parser.rt.by_set_pos,
429 ICAL_BY_SETPOS_SIZE,value);
431 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
432 icalrecurrencetype_clear(&parser.rt);
444 #ifndef ICAL_NO_LIBICAL
446 struct { char* str;size_t offset; short limit; } recurmap[] =
448 {";BYSECOND=",offsetof(struct icalrecurrencetype,by_second),60},
449 {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),60},
450 {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),24},
451 {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),7},
452 {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),31},
453 {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),366},
454 {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),52},
455 {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),12},
456 {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),366},
460 /* A private routine in icalvalue.c */
461 void print_datetime_to_string(char* str, struct icaltimetype *data);
463 char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
471 if(recur->freq == ICAL_NO_RECURRENCE){
475 str = (char*)icalmemory_tmp_buffer(buf_sz);
478 icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ=");
479 icalmemory_append_string(&str,&str_p,&buf_sz,
480 icalrecur_freq_to_string(recur->freq));
482 if(recur->until.year != 0){
485 print_datetime_to_string(temp,&(recur->until));
487 icalmemory_append_string(&str,&str_p,&buf_sz,";UNTIL=");
488 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
491 if(recur->count != 0){
492 sprintf(temp,"%d",recur->count);
493 icalmemory_append_string(&str,&str_p,&buf_sz,";COUNT=");
494 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
497 if(recur->interval != 0){
498 sprintf(temp,"%d",recur->interval);
499 icalmemory_append_string(&str,&str_p,&buf_sz,";INTERVAL=");
500 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
503 for(j =0; recurmap[j].str != 0; j++){
504 short* array = (short*)(recurmap[j].offset+ (size_t)recur);
505 short limit = recurmap[j].limit;
507 /* Skip unused arrays */
508 if( array[0] != ICAL_RECURRENCE_ARRAY_MAX ) {
510 icalmemory_append_string(&str,&str_p,&buf_sz,recurmap[j].str);
513 i< limit && array[i] != ICAL_RECURRENCE_ARRAY_MAX;
515 if (j == 3) { /* BYDAY */
516 short dow = icalrecurrencetype_day_day_of_week(array[i]);
517 const char *daystr = icalrecur_weekday_to_string(dow);
520 pos = icalrecurrencetype_day_position(array[i]);
523 icalmemory_append_string(&str,&str_p,&buf_sz,daystr);
525 sprintf(temp,"%d%s",pos,daystr);
526 icalmemory_append_string(&str,&str_p,&buf_sz,temp);
530 sprintf(temp,"%d",array[i]);
531 icalmemory_append_string(&str,&str_p,&buf_sz, temp);
534 if( (i+1)<limit &&array[i+1]
535 != ICAL_RECURRENCE_ARRAY_MAX){
536 icalmemory_append_char(&str,&str_p,&buf_sz,',');
548 /************************* occurrence iteration routiens ******************/
565 struct icalrecur_iterator_impl {
567 struct icaltimetype dtstart; /* Hack. Make into time_t */
568 struct icaltimetype last; /* last time return from _iterator_next*/
569 int occurrence_no; /* number of step made on t iterator */
570 struct icalrecurrencetype rule;
577 short orig_data[9]; /* 1 if there was data in the byrule */
580 short *by_ptrs[9]; /* Pointers into the by_* array elements of the rule */
584 int icalrecur_iterator_sizeof_byarray(short* byarray)
589 byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX;
603 /* The split map indicates, for a particular interval, wether a BY_*
604 rule part expands the number of instances in the occcurrence set or
605 contracts it. 1=> contract, 2=>expand, and 3 means the pairing is
607 struct expand_split_map_struct
609 icalrecurrencetype_frequency frequency;
611 /* Elements of the 'map' array correspond to the BYxxx rules:
612 Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month*/
617 struct expand_split_map_struct expand_map[] =
619 {ICAL_SECONDLY_RECURRENCE,{1,1,1,1,1,1,1,1}},
620 {ICAL_MINUTELY_RECURRENCE,{2,1,1,1,1,1,1,1}},
621 {ICAL_HOURLY_RECURRENCE, {2,2,1,1,1,1,1,1}},
622 {ICAL_DAILY_RECURRENCE, {2,2,2,1,1,1,1,1}},
623 {ICAL_WEEKLY_RECURRENCE, {2,2,2,2,3,3,1,1}},
624 {ICAL_MONTHLY_RECURRENCE, {2,2,2,2,2,3,3,1}},
625 {ICAL_YEARLY_RECURRENCE, {2,2,2,2,2,2,2,2}},
626 {ICAL_NO_RECURRENCE, {0,0,0,0,0,0,0,0}}
632 /* Check that the rule has only the two given interday byrule parts. */
633 int icalrecur_two_byrule(struct icalrecur_iterator_impl* impl,
634 enum byrule one,enum byrule two)
640 memset(test_array,0,9);
645 for(itr = BY_DAY; itr != BY_SET_POS; itr++){
647 if( (test_array[itr] == 0 &&
648 impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX
650 (test_array[itr] == 1 &&
651 impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX
663 /* Check that the rule has only the one given interdat byrule parts. */
664 int icalrecur_one_byrule(struct icalrecur_iterator_impl* impl,enum byrule one)
669 for(itr = BY_DAY; itr != BY_SET_POS; itr++){
671 if ((itr==one && impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX) ||
672 (itr!=one && impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX)) {
680 int count_byrules(struct icalrecur_iterator_impl* impl)
685 for(itr = BY_DAY; itr <= BY_SET_POS; itr++){
686 if(impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX){
695 void setup_defaults(struct icalrecur_iterator_impl* impl,
696 enum byrule byrule, icalrecurrencetype_frequency req,
697 short deftime, int *timepart)
700 icalrecurrencetype_frequency freq;
701 freq = impl->rule.freq;
703 /* Re-write the BY rule arrays with data from the DTSTART time so
704 we don't have to explicitly deal with DTSTART */
706 if(impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX &&
707 expand_map[freq].map[byrule] != CONTRACT){
708 impl->by_ptrs[byrule][0] = deftime;
711 /* Initialize the first occurence */
712 if( freq != req && expand_map[freq].map[byrule] != CONTRACT){
713 *timepart = impl->by_ptrs[byrule][0];
719 int has_by_data(struct icalrecur_iterator_impl* impl, enum byrule byrule){
721 return (impl->orig_data[byrule] == 1);
725 int expand_year_days(struct icalrecur_iterator_impl* impl,short year);
728 icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
729 struct icaltimetype dtstart)
731 struct icalrecur_iterator_impl* impl;
732 icalrecurrencetype_frequency freq;
736 if ( ( impl = (struct icalrecur_iterator_impl *)
737 malloc(sizeof(struct icalrecur_iterator_impl))) == 0) {
738 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
742 memset(impl,0,sizeof(struct icalrecur_iterator_impl));
745 impl->last = dtstart;
746 impl->dtstart = dtstart;
748 impl->occurrence_no = 0;
749 freq = impl->rule.freq;
751 /* Set up convienience pointers to make the code simpler. Allows
752 us to iterate through all of the BY* arrays in the rule. */
754 impl->by_ptrs[BY_MONTH]=impl->rule.by_month;
755 impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no;
756 impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day;
757 impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day;
758 impl->by_ptrs[BY_DAY]=impl->rule.by_day;
759 impl->by_ptrs[BY_HOUR]=impl->rule.by_hour;
760 impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute;
761 impl->by_ptrs[BY_SECOND]=impl->rule.by_second;
762 impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos;
764 memset(impl->orig_data,0,9);
766 /* Note which by rules had data in them when the iterator was
767 created. We can't use the actuall by_x arrays, because the
768 empty ones will be given default values later in this
769 routine. The orig_data array will be used later in has_by_data */
771 impl->orig_data[BY_MONTH]
772 = (impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX);
773 impl->orig_data[BY_WEEK_NO]
774 =(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX);
775 impl->orig_data[BY_YEAR_DAY]
776 =(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
777 impl->orig_data[BY_MONTH_DAY]
778 =(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
779 impl->orig_data[BY_DAY]
780 = (impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
781 impl->orig_data[BY_HOUR]
782 = (impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX);
783 impl->orig_data[BY_MINUTE]
784 = (impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX);
785 impl->orig_data[BY_SECOND]
786 = (impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX);
787 impl->orig_data[BY_SET_POS]
788 = (impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX);
791 /* Check if the recurrence rule is legal */
793 /* If the BYYEARDAY appears, no other date rule part may appear. */
795 if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) ||
796 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) ||
797 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
798 icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){
800 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
805 /* BYWEEKNO and BYMONTH rule parts may not both appear.*/
807 if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
808 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
813 /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/
815 if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
816 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
822 /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
823 BYWEEKNO may appear. */
825 if(freq == ICAL_MONTHLY_RECURRENCE &&
826 icalrecur_one_byrule(impl,BY_WEEK_NO)){
827 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
833 /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
834 BYYEARDAY may appear. */
836 if(freq == ICAL_WEEKLY_RECURRENCE &&
837 icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
838 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
843 /* BYYEARDAY may only appear in YEARLY rules */
844 if(freq != ICAL_YEARLY_RECURRENCE &&
845 icalrecur_one_byrule(impl,BY_YEAR_DAY )) {
846 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
851 /* Rewrite some of the rules and set up defaults to make later
852 processing easier. Primarily, t involves copying an element
853 from the start time into the coresponding BY_* array when the
854 BY_* array is empty */
857 setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,impl->dtstart.second,
858 &(impl->last.second));
860 setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,impl->dtstart.minute,
861 &(impl->last.minute));
863 setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,impl->dtstart.hour,
866 setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,impl->dtstart.day,
869 setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,impl->dtstart.month,
870 &(impl->last.month));
873 if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){
875 if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){
877 /* Weekly recurrences with no BY_DAY data should occur on the
878 same day of the week as the start time . */
879 impl->by_ptrs[BY_DAY][0] = icaltime_day_of_week(impl->dtstart);
882 /* If there is BY_DAY data, then we need to move the initial
883 time to the start of the BY_DAY data. That is if the
884 start time is on a Wednesday, and the rule has
885 BYDAY=MO,WE,FR, move the initial time back to
886 monday. Otherwise, jumping to the next week ( jumping 7
887 days ahead ) will skip over some occurrences in the
890 /* This is probably a HACK. There should be some more
891 general way to solve this problem */
893 short dow = impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last);
896 /* initial time is after first day of BY_DAY data */
898 impl->last.day += dow;
899 impl->last = icaltime_normalize(impl->last);
906 /* For YEARLY rule, begin by setting up the year days array */
908 if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
909 expand_year_days(impl,impl->last.year);
913 /* If this is a monthly interval with by day data, then we need to
914 set the last value to the appropriate day of the month */
916 if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE &&
917 has_by_data(impl,BY_DAY)) {
919 short dow = icalrecurrencetype_day_day_of_week(
920 impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
921 short pos = icalrecurrencetype_day_position(
922 impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
926 icaltime_days_in_month(impl->last.month, impl->last.year);
929 /* Count up from the first day pf the month to find the
930 pos'th weekday of dow ( like the second monday. ) */
932 for(impl->last.day = 1;
933 impl->last.day <= days_in_month;
936 if(icaltime_day_of_week(impl->last) == dow){
937 if(++poscount == pos || pos == 0){
943 /* Count down from the last day pf the month to find the
944 pos'th weekday of dow ( like the second to last monday. ) */
946 for(impl->last.day = days_in_month;
950 if(icaltime_day_of_week(impl->last) == dow){
951 if(++poscount == pos ){
959 if(impl->last.day > days_in_month || impl->last.day == 0){
960 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
971 void icalrecur_iterator_free(icalrecur_iterator* i)
974 struct icalrecur_iterator_impl* impl =
975 (struct icalrecur_iterator_impl*)i;
977 icalerror_check_arg_rv((impl!=0),"impl");
984 void increment_year(struct icalrecur_iterator_impl* impl, int inc)
986 impl->last.year+=inc;
989 /* Increment month is different that the other incement_* routines --
990 it figures out the interval for itself, and uses BYMONTH data if
992 void increment_month(struct icalrecur_iterator_impl* impl)
996 if(has_by_data(impl,BY_MONTH) ){
997 /* Ignore the frequency and use the byrule data */
999 impl->by_indices[BY_MONTH]++;
1001 if (impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]
1002 ==ICAL_RECURRENCE_ARRAY_MAX){
1003 impl->by_indices[BY_MONTH] = 0;
1005 increment_year(impl,1);
1010 impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
1016 if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE){
1017 inc = impl->rule.interval;
1022 impl->last.month+=inc;
1024 /* Months are offset by one */
1027 years = impl->last.month / 12;
1029 impl->last.month = impl->last.month % 12;
1034 increment_year(impl,years);
1039 void increment_monthday(struct icalrecur_iterator_impl* impl, int inc)
1043 for(i=0; i<inc; i++){
1045 short days_in_month =
1046 icaltime_days_in_month(impl->last.month,impl->last.year);
1050 if (impl->last.day > days_in_month){
1051 impl->last.day = impl->last.day-days_in_month;
1052 increment_month(impl);
1058 void increment_hour(struct icalrecur_iterator_impl* impl, int inc)
1062 impl->last.hour+=inc;
1064 days = impl->last.hour / 24;
1065 impl->last.hour = impl->last.hour % 24;
1067 if (impl->days != 0){
1068 increment_monthday(impl,days);
1072 void increment_minute(struct icalrecur_iterator_impl* impl, int inc)
1076 impl->last.minute+=inc;
1078 hours = impl->last.minute / 60;
1079 impl->last.minute = impl->last.minute % 60;
1082 increment_hour(impl,hours);
1087 void increment_second(struct icalrecur_iterator_impl* impl, int inc)
1091 impl->last.second+=inc;
1093 minutes = impl->last.second / 60;
1094 impl->last.second = impl->last.second % 60;
1098 increment_minute(impl, minutes);
1104 void test_increment()
1106 struct icalrecur_iterator_impl impl;
1108 impl.last = icaltime_from_string("20000101T000000Z");
1110 printf("Orig: %s\n",icaltime_as_ctime(impl.last));
1112 increment_second(&impl,5);
1113 printf("+ 5 sec : %s\n",icaltime_as_ctime(impl.last));
1115 increment_second(&impl,355);
1116 printf("+ 355 sec : %s\n",icaltime_as_ctime(impl.last));
1118 increment_minute(&impl,5);
1119 printf("+ 5 min : %s\n",icaltime_as_ctime(impl.last));
1121 increment_minute(&impl,360);
1122 printf("+ 360 min : %s\n",icaltime_as_ctime(impl.last));
1123 increment_hour(&impl,5);
1124 printf("+ 5 hours : %s\n",icaltime_as_ctime(impl.last));
1125 increment_hour(&impl,43);
1126 printf("+ 43 hours : %s\n",icaltime_as_ctime(impl.last));
1127 increment_monthday(&impl,3);
1128 printf("+ 3 days : %s\n",icaltime_as_ctime(impl.last));
1129 increment_monthday(&impl,600);
1130 printf("+ 600 days : %s\n",icaltime_as_ctime(impl.last));
1136 short next_second(struct icalrecur_iterator_impl* impl)
1139 short has_by_data = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1140 short this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE);
1142 short end_of_data = 0;
1144 assert(has_by_data || this_frequency);
1147 /* Ignore the frequency and use the byrule data */
1149 impl->by_indices[BY_SECOND]++;
1151 if (impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]
1152 ==ICAL_RECURRENCE_ARRAY_MAX){
1153 impl->by_indices[BY_SECOND] = 0;
1160 impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]];
1163 } else if( !has_by_data && this_frequency ){
1164 /* Compute the next value from the last time and the frequency interval*/
1165 increment_second(impl, impl->rule.interval);
1169 /* If we have gone through all of the seconds on the BY list, then we
1170 need to move to the next minute */
1172 if(has_by_data && end_of_data && this_frequency ){
1173 increment_minute(impl,1);
1180 int next_minute(struct icalrecur_iterator_impl* impl)
1183 short has_by_data = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1184 short this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE);
1186 short end_of_data = 0;
1188 assert(has_by_data || this_frequency);
1191 if (next_second(impl) == 0){
1196 /* Ignore the frequency and use the byrule data */
1198 impl->by_indices[BY_MINUTE]++;
1200 if (impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]
1201 ==ICAL_RECURRENCE_ARRAY_MAX){
1203 impl->by_indices[BY_MINUTE] = 0;
1209 impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]];
1211 } else if( !has_by_data && this_frequency ){
1212 /* Compute the next value from the last time and the frequency interval*/
1213 increment_minute(impl,impl->rule.interval);
1216 /* If we have gone through all of the minutes on the BY list, then we
1217 need to move to the next hour */
1219 if(has_by_data && end_of_data && this_frequency ){
1220 increment_hour(impl,1);
1226 int next_hour(struct icalrecur_iterator_impl* impl)
1229 short has_by_data = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1230 short this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE);
1232 short end_of_data = 0;
1234 assert(has_by_data || this_frequency);
1236 if (next_minute(impl) == 0){
1241 /* Ignore the frequency and use the byrule data */
1243 impl->by_indices[BY_HOUR]++;
1245 if (impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]
1246 ==ICAL_RECURRENCE_ARRAY_MAX){
1247 impl->by_indices[BY_HOUR] = 0;
1253 impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]];
1255 } else if( !has_by_data && this_frequency ){
1256 /* Compute the next value from the last time and the frequency interval*/
1257 increment_hour(impl,impl->rule.interval);
1261 /* If we have gone through all of the hours on the BY list, then we
1262 need to move to the next day */
1264 if(has_by_data && end_of_data && this_frequency ){
1265 increment_monthday(impl,1);
1272 int next_day(struct icalrecur_iterator_impl* impl)
1275 short this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE);
1277 short has_by_data = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1279 assert(has_by_data || this_frequency);
1282 if (next_hour(impl) == 0){
1286 /* Always increment through the interval, since this routine is not
1287 called by any other next_* routine, and the days that are
1288 excluded will be taken care of by restriction filtering */
1291 increment_monthday(impl,impl->rule.interval);
1293 increment_monthday(impl,1);
1302 int next_yearday(struct icalrecur_iterator_impl* impl)
1305 short has_by_data = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1307 short end_of_data = 0;
1309 assert(has_by_data );
1311 if (next_hour(impl) == 0){
1315 impl->by_indices[BY_YEAR_DAY]++;
1317 if (impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]]
1318 ==ICAL_RECURRENCE_ARRAY_MAX){
1319 impl->by_indices[BY_YEAR_DAY] = 0;
1325 impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]];
1327 if(has_by_data && end_of_data){
1328 increment_year(impl,1);
1335 /* This routine is only called by next_week. It is certain that BY_DAY
1338 int next_weekday_by_week(struct icalrecur_iterator_impl* impl)
1341 short end_of_data = 0;
1342 short start_of_week, dow;
1343 struct icaltimetype next;
1345 if (next_hour(impl) == 0){
1349 assert( impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1353 impl->by_indices[BY_DAY]++; /* Look at next elem in BYDAY array */
1355 /* Are we at the end of the BYDAY array? */
1356 if (impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]
1357 ==ICAL_RECURRENCE_ARRAY_MAX){
1359 impl->by_indices[BY_DAY] = 0; /* Reset to 0 */
1360 end_of_data = 1; /* Signal that we're at the end */
1363 /* Add the day of week offset to to the start of this week, and use
1364 that to get the next day */
1365 dow = impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]];
1366 start_of_week = icaltime_start_doy_of_week(impl->last);
1368 dow--; /*Sun is 1, not 0 */
1370 if(dow+start_of_week <1 && !end_of_data){
1371 /* The selected date is in the previous year. */
1375 next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year);
1377 impl->last.day = next.day;
1378 impl->last.month = next.month;
1379 impl->last.year = next.year;
1386 int nth_weekday(short dow, short pos, struct icaltimetype t){
1388 short days_in_month = icaltime_days_in_month(t.month,t.year);
1389 short end_dow, start_dow;
1394 start_dow = icaltime_day_of_week(t);
1400 /* find month day of first occurrence of dow -- such as the
1401 month day of the first monday */
1403 wd = dow-start_dow+1;
1412 t.day = days_in_month;
1413 end_dow = icaltime_day_of_week(t);
1417 /* find month day of last occurrence of dow -- such as the
1418 month day of the last monday */
1420 wd = (end_dow - dow);
1426 wd = days_in_month - wd;
1435 int next_month(struct icalrecur_iterator_impl* impl)
1440 short this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
1442 assert( has_by_data(impl,BY_MONTH) || this_frequency);
1445 /* Iterate through the occurrences within a day. If we don't get to
1446 the end of the intra-day data, don't bother going to the next
1449 if (next_hour(impl) == 0){
1450 return data_valid; /* Signal that the data is valid */
1454 /* Now iterate through the occurrences within a month -- by days,
1455 weeks or weekdays. */
1457 if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){
1458 /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13 */
1460 short days_in_month = icaltime_days_in_month(impl->last.month,
1462 /* Iterate through the remaining days in the month and check if
1463 each day is listed in the BY_DAY array and in the BY_MONTHDAY
1464 array. This seems very inneficient, but I think it is the
1465 simplest way to account for both BYDAY=1FR (First friday in
1466 month) and BYDAY=FR ( every friday in month ) */
1468 for(day = impl->last.day+1; day <= days_in_month; day++){
1469 for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
1470 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
1472 icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
1473 short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
1474 short mday = BYMDPTR[j];
1477 impl->last.day = day;
1478 this_dow = icaltime_day_of_week(impl->last);
1480 if( (pos == 0 && dow == this_dow && mday == day) ||
1481 (nth_weekday(dow,pos,impl->last) == day && mday==day)){
1490 if ( day > days_in_month){
1492 increment_month(impl);
1493 data_valid = 0; /* signal that impl->last is invalid */
1497 } else if(has_by_data(impl,BY_DAY)){
1498 /* Cases like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR */
1499 /* For this case, the weekdays are relative to the
1500 month. BYDAY=FR -> First Friday in month, etc. */
1503 short days_in_month = icaltime_days_in_month(impl->last.month,
1506 assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
1508 /* Iterate through the remaining days in the month and check if
1509 each day is listed in the BY_DAY array. This seems very
1510 inneficient, but I think it is the simplest way to account
1511 for both BYDAY=1FR (First friday in month) and BYDAY=FR (
1512 every friday in month ) */
1514 for(day = impl->last.day+1; day <= days_in_month; day++){
1515 for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){
1516 short dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]);
1517 short pos = icalrecurrencetype_day_position(BYDAYPTR[idx]);
1520 impl->last.day = day;
1521 this_dow = icaltime_day_of_week(impl->last);
1523 if( (pos == 0 && dow == this_dow ) ||
1524 (nth_weekday(dow,pos,impl->last) == day)){
1532 if ( day > days_in_month){
1534 increment_month(impl);
1535 data_valid = 0; /* signal that impl->last is invalid */
1538 } else if (has_by_data(impl,BY_MONTH_DAY)) {
1539 /* Cases like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3 */
1542 assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX);
1546 /* Are we at the end of the BYDAY array? */
1547 if (BYMDPTR[BYMDIDX] ==ICAL_RECURRENCE_ARRAY_MAX){
1549 BYMDIDX = 0; /* Reset to 0 */
1550 increment_month(impl);
1553 day = BYMDPTR[BYMDIDX];
1556 day = icaltime_days_in_month(impl->last.month,impl->last.year)+
1560 impl->last.day = day;
1563 increment_month(impl);
1566 return data_valid; /* Signal that the data is valid */
1571 int next_week(struct icalrecur_iterator_impl* impl)
1573 short has_by_data = (impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX);
1574 short this_frequency = (impl->rule.freq == ICAL_WEEKLY_RECURRENCE);
1575 short end_of_data = 0;
1577 /* Increment to the next week day */
1578 if (next_weekday_by_week(impl) == 0){
1579 return 0; /* Have not reached end of week yet */
1582 /* If we get here, we have incremented through the entire week, and
1583 can increment to the next week */
1587 /* Use the Week Number byrule data */
1590 impl->by_indices[BY_WEEK_NO]++;
1592 if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]
1593 ==ICAL_RECURRENCE_ARRAY_MAX){
1594 impl->by_indices[BY_WEEK_NO] = 0;
1599 week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]];
1601 impl->last.day += week_no*7;
1603 impl->last = icaltime_normalize(impl->last);
1605 } else if( !has_by_data && this_frequency ){
1606 /* If there is no BY_WEEK_NO data, just jump forward 7 days. */
1607 increment_monthday(impl,7*impl->rule.interval);
1611 if(has_by_data && end_of_data && this_frequency ){
1612 increment_year(impl,1);
1620 pvl_list expand_by_day(struct icalrecur_iterator_impl* impl,short year)
1622 /* Try to calculate each of the occurrences. */
1624 pvl_list days_list = pvl_newlist();
1626 short start_dow, end_year_day, start_doy;
1627 struct icaltimetype tmp = impl->last;
1634 start_dow = icaltime_day_of_week(tmp);
1635 start_doy = icaltime_start_doy_of_week(tmp);
1637 /* Get the last day of the year*/
1639 tmp = icaltime_normalize(tmp);
1641 tmp = icaltime_normalize(tmp);
1643 end_year_day = icaltime_day_of_year(tmp);
1645 for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1647 icalrecurrencetype_day_day_of_week(BYDAYPTR[i]);
1648 short pos = icalrecurrencetype_day_position(BYDAYPTR[i]);
1651 /* add all of the days of the year with this day-of-week*/
1653 for(week = 0; week < 52 ; week ++){
1654 short doy = start_doy + (week * 7) + dow-1;
1656 if(doy > end_year_day){
1659 pvl_push(days_list, GINT_TO_POINTER((int)doy));
1663 } else if ( pos > 0) {
1665 /* First occurrence of dow in year */
1666 if( dow >= start_dow) {
1667 first = dow - start_dow + 1;
1669 first = dow - start_dow + 8;
1672 pvl_push(days_list,GINT_TO_POINTER(first+ (pos-1) * 7));
1674 } else { /* pos < 0 */
1683 /* For INTERVAL=YEARLY, set up the days[] array in the iterator to
1684 list all of the days of the current year that are specified in this
1687 int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
1691 struct icaltimetype t;
1694 #define HBD(x) has_by_data(impl,x)
1696 t.is_date = 1; /* Needed to make day_of_year routines work property */
1698 memset(&t,0,sizeof(t));
1699 memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days));
1701 flags = (HBD(BY_DAY) ? 1<<BY_DAY : 0) +
1702 (HBD(BY_WEEK_NO) ? 1<<BY_WEEK_NO : 0) +
1703 (HBD(BY_MONTH_DAY) ? 1<<BY_MONTH_DAY : 0) +
1704 (HBD(BY_MONTH) ? 1<<BY_MONTH : 0) +
1705 (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0);
1716 /* FREQ=YEARLY; BYMONTH=3,11*/
1718 for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1719 struct icaltimetype t;
1720 short month = impl->by_ptrs[BY_MONTH][j];
1728 doy = icaltime_day_of_year(t);
1730 impl->days[days_index++] = doy;
1736 case 1<<BY_MONTH_DAY: {
1737 /* FREQ=YEARLY; BYMONTHDAY=1,15*/
1742 case (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
1743 /* FREQ=YEARLY; BYMONTHDAY=1,15; BYMONTH=10 */
1745 for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1746 for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
1747 short month = impl->by_ptrs[BY_MONTH][j];
1748 short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
1756 doy = icaltime_day_of_year(t);
1758 impl->days[days_index++] = doy;
1766 case 1<<BY_WEEK_NO: {
1767 /* FREQ=YEARLY; BYWEEKNO=20,50 */
1772 case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
1773 /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */
1779 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR*/
1782 pvl_list days = expand_by_day(impl,year);
1785 for(i=pvl_head(days);i!=0;i=pvl_next(i)){
1786 short day = (short)GPOINTER_TO_INT(pvl_data(i));
1787 impl->days[days_index++] = day;
1793 case (1<<BY_DAY)+(1<<BY_MONTH): {
1794 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/
1797 for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1798 short month = impl->by_ptrs[BY_MONTH][j];
1799 short days_in_month = icaltime_days_in_month(month,year);
1801 struct icaltimetype t;
1802 memset(&t,0,sizeof(struct icaltimetype));
1808 for(t.day = 1; t.day <=days_in_month; t.day++){
1810 short current_dow = icaltime_day_of_week(t);
1812 for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
1814 enum icalrecurrencetype_weekday dow =
1815 icalrecurrencetype_day_day_of_week(impl->by_ptrs[BY_DAY][k]);
1817 if(current_dow == dow){
1818 short doy = icaltime_day_of_year(t);
1819 /* HACK, incomplete Nth day of week handling */
1820 impl->days[days_index++] = doy;
1829 case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : {
1830 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/
1835 case (1<<BY_DAY) + (1<<BY_MONTH_DAY) + (1<<BY_MONTH): {
1836 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=10; MYMONTH=6,11*/
1840 pvl_list days = expand_by_day(impl,year);
1842 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1843 short day = (short)GPOINTER_TO_INT(pvl_data(itr));
1844 struct icaltimetype tt;
1847 tt = icaltime_from_day_of_year(day,year);
1849 for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1850 for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){
1851 short mday = BYMDPTR[j];
1852 short month = BYMONPTR[i];
1854 if(tt.month == month && tt.day == mday){
1855 impl->days[days_index++] = day;
1866 case (1<<BY_DAY) + (1<<BY_WEEK_NO) : {
1867 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50*/
1871 pvl_list days = expand_by_day(impl,year);
1873 for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){
1874 short day = (short)GPOINTER_TO_INT(pvl_data(itr));
1875 struct icaltimetype tt;
1878 tt = icaltime_from_day_of_year(day,year);
1880 for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){
1881 short weekno = BYWEEKPTR[i];
1883 if(weekno== icaltime_week_number(tt)){
1884 impl->days[days_index++] = day;
1893 case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): {
1894 /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50; BYMONTHDAY=1,15*/
1899 case 1<<BY_YEAR_DAY: {
1900 for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
1901 short doy = impl->by_ptrs[BY_YEAR_DAY][j];
1902 impl->days[days_index++] = doy;
1918 int next_year(struct icalrecur_iterator_impl* impl)
1920 struct icaltimetype next;
1922 if (next_hour(impl) == 0){
1926 if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
1927 impl->days_index = 0;
1928 increment_year(impl,impl->rule.interval);
1929 expand_year_days(impl,impl->last.year);
1932 next = icaltime_from_day_of_year(impl->days[impl->days_index],impl->last.year);
1934 impl->last.day = next.day;
1935 impl->last.month = next.month;
1940 int icalrecur_check_rulepart(struct icalrecur_iterator_impl* impl,
1941 short v, enum byrule byrule)
1945 if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX){
1946 for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
1947 if(impl->by_ptrs[byrule][itr] == v){
1956 int check_contract_restriction(struct icalrecur_iterator_impl* impl,
1957 enum byrule byrule, short v)
1961 icalrecurrencetype_frequency freq = impl->rule.freq;
1963 if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX &&
1964 expand_map[freq].map[byrule] == CONTRACT){
1965 for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){
1966 if(impl->by_ptrs[byrule][itr] == v){
1974 /* This is not a contracting byrule, or it has no data, so the
1981 int check_contracting_rules(struct icalrecur_iterator_impl* impl)
1989 check_contract_restriction(impl,BY_SECOND,impl->last.second) &&
1990 check_contract_restriction(impl,BY_MINUTE,impl->last.minute) &&
1991 check_contract_restriction(impl,BY_HOUR,impl->last.hour) &&
1992 check_contract_restriction(impl,BY_DAY,day_of_week) &&
1993 check_contract_restriction(impl,BY_WEEK_NO,week_no) &&
1994 check_contract_restriction(impl,BY_MONTH_DAY,impl->last.day) &&
1995 check_contract_restriction(impl,BY_MONTH,impl->last.month) &&
1996 check_contract_restriction(impl,BY_YEAR_DAY,year_day) )
2005 struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
2008 struct icalrecur_iterator_impl* impl =
2009 (struct icalrecur_iterator_impl*)itr;
2011 if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
2012 (!icaltime_is_null_time(impl->rule.until) &&
2013 icaltime_compare(impl->last,impl->rule.until) > 0)) {
2014 return icaltime_null_time();
2017 if(impl->occurrence_no == 0
2018 && icaltime_compare(impl->last,impl->dtstart) >= 0){
2020 impl->occurrence_no++;
2026 switch(impl->rule.freq){
2028 case ICAL_SECONDLY_RECURRENCE: {
2032 case ICAL_MINUTELY_RECURRENCE: {
2036 case ICAL_HOURLY_RECURRENCE: {
2040 case ICAL_DAILY_RECURRENCE: {
2044 case ICAL_WEEKLY_RECURRENCE: {
2048 case ICAL_MONTHLY_RECURRENCE: {
2049 valid = next_month(impl);
2052 case ICAL_YEARLY_RECURRENCE:{
2057 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
2058 return icaltime_null_time();
2062 if(impl->last.year >= 2038 ){
2064 return icaltime_null_time();
2067 } while(!check_contracting_rules(impl)
2068 || icaltime_compare(impl->last,impl->dtstart) < 0
2072 /* Ignore null times and times that are after the until time */
2073 if( !icaltime_is_null_time(impl->rule.until) &&
2074 icaltime_compare(impl->last,impl->rule.until) > 0 ) {
2075 return icaltime_null_time();
2078 impl->occurrence_no++;
2084 /************************** Type Routines **********************/
2087 void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
2089 memset(recur,ICAL_RECURRENCE_ARRAY_MAX_BYTE,
2090 sizeof(struct icalrecurrencetype));
2092 recur->week_start = ICAL_MONDAY_WEEKDAY;
2093 recur->freq = ICAL_NO_RECURRENCE;
2094 recur->interval = 1;
2095 memset(&(recur->until),0,sizeof(struct icaltimetype));
2099 /* The 'day' element of icalrecurrencetype_weekday is encoded to allow
2100 reporesentation of both the day of the week ( Monday, Tueday), but
2101 also the Nth day of the week ( First tuesday of the month, last
2102 thursday of the year) These routines decode the day values.
2104 The day's position in the period ( Nth-ness) and the numerical value
2105 of the day are encoded together as: pos*7 + dow
2107 A position of 0 means 'any' or 'every'
2111 enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
2116 short icalrecurrencetype_day_position(short day)
2120 wd = icalrecurrencetype_day_day_of_week(day);
2122 pos = (abs(day)-wd)/8 * ((day<0)?-1:1);
2129 /****************** Enumeration Routines ******************/
2131 struct {icalrecurrencetype_weekday wd; const char * str; }
2133 {ICAL_SUNDAY_WEEKDAY,"SU"},
2134 {ICAL_MONDAY_WEEKDAY,"MO"},
2135 {ICAL_TUESDAY_WEEKDAY,"TU"},
2136 {ICAL_WEDNESDAY_WEEKDAY,"WE"},
2137 {ICAL_THURSDAY_WEEKDAY,"TH"},
2138 {ICAL_FRIDAY_WEEKDAY,"FR"},
2139 {ICAL_SATURDAY_WEEKDAY,"SA"},
2143 const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
2147 for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
2148 if ( wd_map[i].wd == kind) {
2149 return wd_map[i].str;
2156 icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str)
2160 for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
2161 if ( strcmp(str,wd_map[i].str) == 0){
2162 return wd_map[i].wd;
2166 return ICAL_NO_WEEKDAY;
2172 icalrecurrencetype_frequency kind;
2175 {ICAL_SECONDLY_RECURRENCE,"SECONDLY"},
2176 {ICAL_MINUTELY_RECURRENCE,"MINUTELY"},
2177 {ICAL_HOURLY_RECURRENCE,"HOURLY"},
2178 {ICAL_DAILY_RECURRENCE,"DAILY"},
2179 {ICAL_WEEKLY_RECURRENCE,"WEEKLY"},
2180 {ICAL_MONTHLY_RECURRENCE,"MONTHLY"},
2181 {ICAL_YEARLY_RECURRENCE,"YEARLY"},
2182 {ICAL_NO_RECURRENCE,0}
2185 const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
2189 for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
2190 if ( freq_map[i].kind == kind ) {
2191 return freq_map[i].str;
2197 icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str)
2201 for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) {
2202 if ( strcmp(str,freq_map[i].str) == 0){
2203 return freq_map[i].kind;
2206 return ICAL_NO_RECURRENCE;
2209 /* Fill an array with the 'count' number of occurrences generated by
2210 the rrule. Note that the times are returned in UTC, but the times
2211 are calculated in local time. YOu will have to convert the results
2212 back into local time before using them. */
2214 int icalrecur_expand_recurrence(char* rule, time_t start,
2215 int count, time_t* array)
2217 struct icalrecurrencetype recur;
2218 icalrecur_iterator* ritr;
2220 struct icaltimetype icstart, next;
2223 memset(array, 0, count*sizeof(time_t));
2225 icstart = icaltime_from_timet(start,0);
2227 recur = icalrecurrencetype_from_string(rule);
2229 for(ritr = icalrecur_iterator_new(recur,icstart),
2230 next = icalrecur_iterator_next(ritr);
2231 !icaltime_is_null_time(next) && i < count;
2232 next = icalrecur_iterator_next(ritr)){
2234 tt = icaltime_as_timet(next);
2242 icalrecur_iterator_free(ritr);