Libical
2.0
|
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 |
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_iterator * | icalrecur_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) |
icalarray * | icalrecurrencetype_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 [] |
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.
#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.
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 |
Increment month is different that the other increment_* routines – it figures out the interval for itself, and uses BYMONTH data if available.
|
static |