Libical  2.0
Data Structures | Macros | Enumerations | Functions | Variables
icalrecur.c File Reference

Implementation of routines for dealing with recurring time. More...

#include "icalrecur.h"
#include "icalerror.h"
#include "icalmemory.h"
#include "icaltimezone.h"
#include "icalvalue.h"
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>

Data Structures

struct  expand_split_map_struct
 
struct  freq_map
 
struct  icalrecur_iterator_impl
 
struct  icalrecur_parser
 
struct  recur_map
 
struct  skip_map
 
struct  wd_map
 

Macros

#define BITS_PER_LONG   (8 * sizeof(unsigned long))
 
#define BYDAYIDX   impl->by_indices[BY_DAY]
 
#define BYDAYPTR   impl->by_ptrs[BY_DAY]
 
#define BYMDIDX   impl->by_indices[BY_MONTH_DAY]
 
#define BYMDPTR   impl->by_ptrs[BY_MONTH_DAY]
 
#define BYMONIDX   impl->by_indices[BY_MONTH]
 
#define BYMONPTR   impl->by_ptrs[BY_MONTH]
 
#define BYWEEKIDX   impl->by_indices[BY_WEEK_NO]
 
#define BYWEEKPTR   impl->by_ptrs[BY_WEEK_NO]
 
#define BYYDIDX   impl->by_indices[BY_YEAR_DAY]
 
#define BYYDPTR   impl->by_ptrs[BY_YEAR_DAY]
 
#define get_months_in_year(impl, year)   (12)
 
#define ICAL_BY_MONTH_SIZE   13 /* 1 to 12 */
 
#define ICAL_BY_WEEKNO_SIZE   54 /* 1 to 53 */
 
#define ICAL_BY_YEARDAY_SIZE   367 /* 1 to 366 */
 
#define ICAL_YEARDAYS_MASK_OFFSET   4
 
#define ICAL_YEARDAYS_MASK_SIZE   (ICAL_BY_YEARDAY_SIZE + 7)
 
#define IN_RANGE(val, min, max)   (val >= min && val <= max)
 
#define LEAP_MONTH   0x1000
 
#define LONGS_PER_BITS(n)   ((n + BITS_PER_LONG -1 ) / BITS_PER_LONG)
 
#define MAX_TIME_T_YEAR   2037
 
#define RSCALE_IS_SUPPORTED   0
 

Enumerations

enum  byrule {
  NO_CONTRACTION = -1, BY_SECOND = 0, BY_MINUTE = 1, BY_HOUR = 2,
  BY_DAY = 3, BY_MONTH_DAY = 4, BY_YEAR_DAY = 5, BY_WEEK_NO = 6,
  BY_MONTH = 7, BY_SET_POS
}
 
enum  expand_table { UNKNOWN = 0, CONTRACT = 1, EXPAND = 2, ILLEGAL = 3 }
 

Functions

struct icaltimetype __icaltime_from_day_of_year (icalrecur_iterator *impl, int day, int year, int *weekno)
 
static void __increment_month (icalrecur_iterator *impl, int inc)
 
static void __next_month (icalrecur_iterator *impl)
 
static void __next_year (icalrecur_iterator *impl)
 
static int check_contract_restriction (icalrecur_iterator *impl, enum byrule byrule, int v)
 
static int check_contracting_rules (icalrecur_iterator *impl)
 
static int check_set_position (icalrecur_iterator *impl, int set_pos)
 
static void daysmask_clearall (unsigned long mask[])
 
static unsigned long daysmask_getbit (unsigned long mask[], short n)
 
static void daysmask_setbit (unsigned long mask[], short n, int v)
 
static int expand_by_day (icalrecur_iterator *impl, int year, int doy_offset, int last_day, int first_dow, int last_dow, int is_limiting)
 
static int expand_bymonth_days (icalrecur_iterator *impl, int year, int month)
 
static int expand_month_days (icalrecur_iterator *impl, int year, int month)
 
static int expand_year_days (icalrecur_iterator *impl, int year)
 
static void filter_bysetpos (icalrecur_iterator *impl, int pos_total, int start_doy, int end_doy)
 
static int get_day_of_week (icalrecur_iterator *impl)
 
static int get_day_of_year (icalrecur_iterator *impl, int year, int month, int day, int *dow)
 
static int get_days_in_month (icalrecur_iterator *impl, int month, int year)
 
static int get_days_in_year (icalrecur_iterator *impl, int year)
 
static int get_start_of_week (icalrecur_iterator *impl)
 
static int get_week_number (icalrecur_iterator *impl, struct icaltimetype tt)
 
static int has_by_data (icalrecur_iterator *impl, enum byrule byrule)
 
static int icalrecur_add_bydayrules (struct icalrecur_parser *parser, const char *vals)
 
static int icalrecur_add_byrules (struct icalrecur_parser *parser, short *array, int min, int size, char *vals)
 
int icalrecur_check_rulepart (icalrecur_iterator *impl, int v, enum byrule byrule)
 
static void icalrecur_clause_name_and_value (struct icalrecur_parser *parser, char **name, char **value)
 
int icalrecur_expand_recurrence (char *rule, time_t start, int count, time_t *array)
 
static const char * icalrecur_first_clause (struct icalrecur_parser *parser)
 
const char * icalrecur_freq_to_string (icalrecurrencetype_frequency kind)
 
void icalrecur_iterator_free (icalrecur_iterator *i)
 
icalrecur_iteratoricalrecur_iterator_new (struct icalrecurrencetype rule, struct icaltimetype dtstart)
 
struct icaltimetype icalrecur_iterator_next (icalrecur_iterator *impl)
 
int icalrecur_iterator_sizeof_byarray (short *byarray)
 
static const char * icalrecur_next_clause (struct icalrecur_parser *parser)
 
const char * icalrecur_skip_to_string (icalrecurrencetype_skip kind)
 
icalrecurrencetype_frequency icalrecur_string_to_freq (const char *str)
 
icalrecurrencetype_skip icalrecur_string_to_skip (const char *str)
 
icalrecurrencetype_weekday icalrecur_string_to_weekday (const char *str)
 
const char * icalrecur_weekday_to_string (icalrecurrencetype_weekday kind)
 
char * icalrecurrencetype_as_string (struct icalrecurrencetype *recur)
 
char * icalrecurrencetype_as_string_r (struct icalrecurrencetype *recur)
 
void icalrecurrencetype_clear (struct icalrecurrencetype *recur)
 
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week (short day)
 
int icalrecurrencetype_day_position (short day)
 
struct icalrecurrencetype icalrecurrencetype_from_string (const char *str)
 
int icalrecurrencetype_month_is_leap (short month)
 
int icalrecurrencetype_month_month (short month)
 
int icalrecurrencetype_rscale_is_supported (void)
 
icalarrayicalrecurrencetype_rscale_supported_calendars (void)
 
static void increment_hour (icalrecur_iterator *impl, int inc)
 
static void increment_minute (icalrecur_iterator *impl, int inc)
 
static void increment_month (icalrecur_iterator *impl)
 
static void increment_monthday (icalrecur_iterator *impl, int inc)
 
static void increment_second (icalrecur_iterator *impl, int inc)
 
static void increment_year (icalrecur_iterator *impl, int inc)
 
static int initialize_iterator (icalrecur_iterator *impl)
 
static int next_day (icalrecur_iterator *impl)
 
static int next_hour (icalrecur_iterator *impl)
 
static int next_minute (icalrecur_iterator *impl)
 
static int next_month (icalrecur_iterator *impl)
 
static int next_second (icalrecur_iterator *impl)
 
static int next_week (icalrecur_iterator *impl)
 
static int next_weekday_by_week (icalrecur_iterator *impl)
 
static int next_year (icalrecur_iterator *impl)
 
static int next_yearday (icalrecur_iterator *impl, void(*next_period)(icalrecur_iterator *))
 
static struct icaltimetype occurrence_as_icaltime (icalrecur_iterator *impl, int normalize)
 
static void set_day_of_year (icalrecur_iterator *impl, int doy)
 
static void set_hour (icalrecur_iterator *impl, int hour)
 
static void set_minute (icalrecur_iterator *impl, int minute)
 
static int set_month (icalrecur_iterator *impl, int month)
 
static void set_second (icalrecur_iterator *impl, int second)
 
static void setup_defaults (icalrecur_iterator *impl, enum byrule byrule, int deftime)
 
static void sort_bydayrules (struct icalrecur_parser *parser)
 
static int weeks_in_year (int year)
 

Variables

static const struct expand_split_map_struct expand_map []
 
static struct freq_map freq_map []
 
static struct recur_map recur_map []
 
static struct skip_map skip_map []
 
static struct wd_map wd_map []
 

Detailed Description

Implementation of routines for dealing with recurring time.

How this code works:

Processing starts when the caller generates a new recurrence iterator via icalrecur_iterator_new(). This routine copies the recurrence rule into the iterator and extracts things like start and end dates. Then, it checks if the rule is legal, using some logic from RFC5545 and some logic that probably should be in RFC5545.

If compiled with support for Non-Gregorian Recurrence Rules (RFC7529), icalrecur_iterator_new() verifies that the given RSCALE is supported and configures ICU4C to convert occurrences to/from non-Gregorian dates.

Then, icalrecur_iterator_new() re-writes some of the BY* arrays. This involves ( via a call to setup_defaults() ) :

1) For BY rule parts with no data ( ie BYSECOND was not specified ) copy the corresponding time part from DTSTART into the BY array. ( So impl->by_ptrs[BY_SECOND] will then have one element if is originally had none ) This only happens if the BY* rule part data would expand the number of occurrences in the occurrence set. This lets the code ignore DTSTART later on and still use it to get the time parts that were not specified in any other way.

2) For the by rule part that are not the same interval as the frequency – for HOURLY anything but BYHOUR, for instance – copy the first data element from the rule part into the first occurrence. For example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the first time to be returned to have an hour of 10.

Finally, for INTERVAL=YEARLY, the routine expands the rule to get all of the days specified in the rule. The code will do this for each new year, and this is the first expansion. This is a special case for the yearly interval; no other frequency gets expanded this way. The yearly interval is the most complex, so some special processing is required.

After creating a new iterator, the caller will make successive calls to icalrecur_iterator_next() to get the next time specified by the rule. The main part of this routine is a switch on the frequency of the rule. Each different frequency is handled by a different routine.

For example, next_hour handles the case of INTERVAL=HOURLY, and it is called by other routines to get the next hour. First, the routine tries to get the next minute part of a time with a call to next_minute(). If next_minute() returns 1, it has reached the end of its data, usually the last element of the BYMINUTE array. Then, if there is data in the BYHOUR array, the routine changes the hour to the next one in the array. If INTERVAL=HOURLY, the routine advances the hour by the interval.

If the routine used the last hour in the BYHOUR array, and the INTERVAL=HOURLY, then the routine calls increment_monthday() to set the next month day. The increment_* routines may call higher routine to increment the month or year also.

The code for INTERVAL=DAILY is handled by next_day(). First, the routine tries to get the next hour part of a time with a call to next_hour. If next_hour() returns 1, it has reached the end of its data, usually the last element of the BYHOUR array. This means that next_day() should increment the time to the next day. If FREQUENCY==DAILY, the routine increments the day by the interval; otherwise, it increments the day by 1.

Next_day() differs from next_hour because it does not use the BYDAY array to select an appropriate day. Instead, it returns every day ( incrementing by 1 if the frequency is not DAILY with INTERVAL!=1) Any days that are not specified in an non-empty BYDAY array are filtered out later.

Generally, the flow of these routine is for a next_* call a next_* routine of a lower interval ( next_day calls next_hour) and then to possibly call an increment_* routine of an equal or higher interval. ( next_day calls increment_monthday() )

When the call to the original next_* routine returns, icalrecur_iterator_next() will check the returned data against other BYrule parts to determine if is should be excluded by calling check_contracting_rules. Generally, a contracting rule is any with a larger time span than the interval. For instance, if INTERVAL=DAILY, BYMONTH is a contracting rule part.

Check_contracting_rules() uses icalrecur_check_rulepart() to do its work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule is contracting, and if it is, and if the BY rule part has some data, then the routine checks if the value of a component of the time is part of the byrule part. For instance, for "INTERVAL=DAILY; BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value given to it has a month of either 6 or 10.

Finally, icalrecur_iterator_next() does a few other checks on the time value, and if it passes, it returns the time.

A note about the end_of_data flag. The flag indicates that the routine is at the end of its data – the last BY rule if the routine is using by rules, or the last day of the week/month/year/etc if not.

This flag is usually set early in a next_* routine and returned in the end. The way it is used allows the next_* routine to set the last time back to the first element in a BYxx rule, and then signal to the higher level routine to increment the next higher level. For instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month runs though both TU and FR, it sets the week day back to TU and sets end_of_data to 1x. This signals next_month to increment the month.


Macro Definition Documentation

#define MAX_TIME_T_YEAR   2037

This is the last year we will go up to, since 32-bit time_t values only go up to the start of 2038.

Function Documentation

int icalrecur_expand_recurrence ( char *  rule,
time_t  start,
int  count,
time_t *  array 
)

Fill an array with the 'count' number of occurrences generated by the rrule. Note that the times are returned in UTC, but the times are calculated in local time. YOu will have to convert the results back into local time before using them.

void icalrecur_iterator_free ( icalrecur_iterator )

Free the iterator

icalrecur_iterator* icalrecur_iterator_new ( struct icalrecurrencetype  rule,
struct icaltimetype  dtstart 
)

Create a new recurrence rule iterator

struct icaltimetype icalrecur_iterator_next ( icalrecur_iterator )

Get the next occurrence from an iterator

enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week ( short  day)

The 'day' element of icalrecurrencetype_weekday is encoded to allow representation of both the day of the week ( Monday, Tuesday), but also the Nth day of the week ( First tuesday of the month, last thursday of the year) These routines decode the day values.

The day's position in the period ( Nth-ness) and the numerical value of the day are encoded together as: pos*7 + dow

A position of 0 means 'any' or 'every'

int icalrecurrencetype_day_position ( short  day)

0 == any of day of week. 1 == first, 2 = second, -2 == second to last, etc

struct icalrecurrencetype icalrecurrencetype_from_string ( const char *  str)

Recurrance rule parser Convert between strings and recurrencetype structures.

int icalrecurrencetype_month_is_leap ( short  month)

The 'month' element of the by_month array is encoded to allow representation of the "L" leap suffix (RFC 7529). These routines decode the month values.

The "L" suffix is encoded by setting a high-order bit

static void increment_month ( icalrecur_iterator impl)
static

Increment month is different that the other increment_* routines – it figures out the interval for itself, and uses BYMONTH data if available.

Variable Documentation

const struct expand_split_map_struct expand_map[]
static
Initial value:
= {
{ICAL_SECONDLY_RECURRENCE, { 1, 1, 1, 1, 1, 1, 3, 1, 1 }},
{ICAL_MINUTELY_RECURRENCE, { 2, 1, 1, 1, 1, 1, 3, 1, 1 }},
{ICAL_HOURLY_RECURRENCE, { 2, 2, 1, 1, 1, 1, 3, 1, 1 }},
{ICAL_DAILY_RECURRENCE, { 2, 2, 2, 1, 1, 3, 3, 1, 1 }},
{ICAL_WEEKLY_RECURRENCE, { 2, 2, 2, 2, 3, 3, 3, 1, 1 }},
{ICAL_MONTHLY_RECURRENCE, { 2, 2, 2, 2, 2, 3, 3, 1, 1 }},
{ICAL_YEARLY_RECURRENCE, { 2, 2, 2, 2, 2, 2, 2, 2, 1 }},
{ICAL_NO_RECURRENCE, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
}
struct freq_map freq_map[]
static
Initial value:
= {
{ICAL_SECONDLY_RECURRENCE, "SECONDLY"},
{ICAL_MINUTELY_RECURRENCE, "MINUTELY"},
{ICAL_HOURLY_RECURRENCE, "HOURLY"},
{ICAL_DAILY_RECURRENCE, "DAILY"},
{ICAL_WEEKLY_RECURRENCE, "WEEKLY"},
{ICAL_MONTHLY_RECURRENCE, "MONTHLY"},
{ICAL_YEARLY_RECURRENCE, "YEARLY"},
{ICAL_NO_RECURRENCE, 0}
}
struct recur_map recur_map[]
static
Initial value:
= {
{ ";BYSECOND=", offsetof(struct icalrecurrencetype, by_second),
62 - 1 },
{ ";BYMINUTE=", offsetof(struct icalrecurrencetype, by_minute),
61 - 1 },
{ ";BYHOUR=", offsetof(struct icalrecurrencetype, by_hour),
25 - 1 },
{ ";BYDAY=", offsetof(struct icalrecurrencetype, by_day),
7*( 54 -1)+1 - 1 },
{ ";BYMONTHDAY=", offsetof(struct icalrecurrencetype, by_month_day),
32 - 1 },
{ ";BYYEARDAY=", offsetof(struct icalrecurrencetype, by_year_day),
367 - 1 },
{ ";BYWEEKNO=", offsetof(struct icalrecurrencetype, by_week_no),
54 - 1 },
{ ";BYMONTH=", offsetof(struct icalrecurrencetype, by_month),
13 - 1 },
{ ";BYSETPOS=", offsetof(struct icalrecurrencetype, by_set_pos),
367 - 1 },
{ 0, 0, 0 }
}
Definition: icalrecur.h:152
struct skip_map skip_map[]
static
Initial value:
= {
{ICAL_SKIP_BACKWARD, "BACKWARD"},
{ICAL_SKIP_FORWARD, "FORWARD"},
{ICAL_SKIP_OMIT, "OMIT"},
{ICAL_SKIP_UNDEFINED, 0}
}
struct wd_map wd_map[]
static
Initial value:
= {
{ICAL_SUNDAY_WEEKDAY, "SU"},
{ICAL_MONDAY_WEEKDAY, "MO"},
{ICAL_TUESDAY_WEEKDAY, "TU"},
{ICAL_WEDNESDAY_WEEKDAY, "WE"},
{ICAL_THURSDAY_WEEKDAY, "TH"},
{ICAL_FRIDAY_WEEKDAY, "FR"},
{ICAL_SATURDAY_WEEKDAY, "SA"},
{ICAL_NO_WEEKDAY, 0}
}