Tizen Native API  6.5
Calendar

The Calendar Service API provides functions for managing calendars(including events, to-dos). This API allows you not only to store information about calendar but also to query calendar information.

Required Header

#include <calendar.h>

Overview

A calendar is a system of organizing days for social purposes and is composed of records like events and to-dos. These records are made up of another sub records like alarms, attendees or extended ones. Events could have recurrence rule, instances would be generated.

calendar_model.png
Figure: Calendar model

The Calendar-Service provides an interface to manage the information of records.

Table: Calendar service properties.
* Managing information stored in database.
* Aggregating information from various accounts.
* Notifying changes of information.
* Searching information.
* Vcalendar supports ver1.0(vcs) / 2.0(ics).

Related Features

This API is related with the following features:

  • http://tizen.org/feature/calendar
    It is recommended to design feature related codes in your application for reliability.
    You can check if a device supports the related features for this API by using System Information, thereby controlling the procedure of your application.
    To ensure your application is only running on the device with specific features, please define the features in your manifest file using the manifest editor in the SDK.
    More details on featuring your application can be found from Feature Element.

Calendar service module works in a way similar to client-service architecture. In this architecture Tizen application is a client side and has to connect to service before using calendar service APIs. Connection/disconnection MUST be done with use of calendar_connect() / calendar_disconnect().

 calendar_connect();

 // jobs for records

 calendar_disconnect(); *

Entities

Calendar service manages information related to following entities.

  • Calendar books
    • Represents where event and to-do should belong to.
    • Created by one of calendars sources below
      1. Local device, which has no account.
      2. Service providers such as Google or Yahoo, with account.
      3. Applications like ChatON, Joyn, Facebook, etc.
    • Have properties like name, account id, color.
  • Events
    • Have properties like summary, start_time, description.
    • Single entry can be available on each calendar book.
  • To-dos
    • Similar with event entry.

Relationship between entities

entities.png
Figure: Relationship between entities

Multiple Address books from Accounts

Each account can create multiple calendar books. Calendar book name does not need to be unique on the device because it is handled with id. Local device address book has no account and its related account id is zero.

Views

View/Property To access and handle entities, views are provided.
According to data-view declarations, generic access functions are used (calendar_db_insert_record(), calendar_record_get_int(), …).
Data-View is almost same as database "VIEW" which limits access and guarantees the performance by offering various views for the proper purpose. _calendar_instance_utime, _calendar_instance_localtime views are not offered but combination with another view are provided.

Table: Calendar views
Editable view Read only view
_calendar_book
_calendar_event
_calendar_todo
_calendar_timezone
_calendar_attendee
_calendar_alarm
_calendar_extended_property

_calendar_updated_info
_calendar_event_calendar_book
_calendar_todo_calendar_book
_calendar_event_calendar_book_attendee
_calendar_instance_utime_calendar_book
_calendar_instance_localtime_calendar_book
_calendar_instance_utime_calendar_book_extended
_calendar_instance_localtime_calendar_book_extended

_calendar_updated_info is used when identifying record changes depending on version. The other read only views are combination of editable views for UI convenience.
_calendar_event + _calendar_book = _calendar_event_calendar_book
_calendar_instance_utime + _calendar_book = _calendar_instance_utime_calendar_book
_calendar_event + _calendar_book + _calendar_attendee = _calendar_event_calendar_book_attendee

Properties

Record types which have *_id as their properties, hold identifiers of other records - e.g. attendee and alarm views hold id of their corresponding events or to-dos in event_id or todo_id property respectively (as children of the corresponding events or to-dos record). Properties of type 'record' are other records. For example, a event record has 'attendee' and 'alarm', which means that records of those types can be children of event type records. In calendar_view.h header file, view macros are found and below figure shows what macro means.

view_property.png
Figure: Properties

There is an example how to create event with view.

 // create an event with _calendar_event view.
 calendar_record_h event = NULL;
 calendar_record_create(_calendar_event._uri, &event);

 // set event summary to _calendar_event view.
 calendar_record_set_str(event, _calendar_event.summary, "Meeting");

Version

Calendar service uses version system in below APIs.

Table: Version related APIs
calendar_db_get_current_version(int* calendar_db_version)
calendar_db_get_changes_by_version(const char *view_uri, int calendar_book_id, int calendar_db_version, calendar_list_h *record_list, int *current_calendar_db_version)
calendar_db_get_last_change_version(int* last_change_version)
calendar_db_get_changes_exception_by_version(const char *view_uri, int original_event_id, int calendar_db_version, calendar_list_h *list)

Whenever modifications are in DB, version number is increased every time.
If sync applications like Google, Facebook sync at version 13 and they try to sync again every 1 minute, they want to get changes from version 14 to current version.
To get current version, calendar_db_get_current_version() is used and calendar_db_get_changes_by_version() is used to get the modified record list. calendar_db_get_changes_exception_by_version() is used to get modified instances in recurring event. (see exception in Events and instances)

Records

An important concept in Calendar service APIs is a record. It may be helpful to know that a record represents an actual record in the internal database, but in general, you can think of a record as a structure describing a single (but complex) view, like a calendar event or a time zone. A record has many properties, for example, a to-do record has the to-do's description, priority, progress, created, last modified and completed time, plus many others.
A record can also contain an identifier field, which holds an identifier of another record. Setting this field's value establishes a relation between the records, for example, a calendar event contains the identifier of a calendar book to which it belongs.

URI

A record's type is identified by a structure called the view. For example, the _calendar_event view describes the properties of the calendar event record. Every view has a special field - _uri - that uniquely identifies the view. In many cases you will need to provide the _uri value to indicate what type of record you wish to create or operate on.

 // create an event and get handle
 calendar_record_h event = NULL;
 calendar_record_create(_calendar_event._uri, &event);

Record handle

To use a record, you must obtain its handle. There are many ways to obtain it, including creating a new record and referring to child records of a record.

 // create an event and get handle
 calendar_record_h event = NULL;
 calendar_record_create(_calendar_event._uri, &event);

 // get record handle with id
 calendar_record_h event2 = NULL
 calendar_db_get_record(_calendar_event._uri, event_id, &event2);

Basic types

Records contain properties of basic types: integer, lli (long integer, long long int), double, string and calendar_time_s.
The calendar_time_s type holds either a long long int, or three integers (year, month, day). There are setter and getter functions for each type:

Table: Setter and getter functions
Property Setter Getter
integer calendar_record_set_int() calendar_record_get_int()
long long integer calendar_record_set_lli() calendar_record_get_lli()
double calendar_record_set_double() calendar_record_get_double()
string calendar_record_set_str() calendar_record_get_str()
calendar_time_s calendar_record_set_caltime() calendar_record_get_caltime()

A record's type is identified by a structure called the view. For example, the _calendar_event view describes the properties of the calendar event record. Every view has a special field - _uri - that uniquely identifies the view. In many cases you will need to provide the _uri value to indicate what type of record you wish to create or operate on.

Child

Records of certain type also hold 'child list' properties. If a record has property of this type, it can be a parent of other records, called child records. For example, attendee records can hold an event's identifier in their event_id property. The event is the parent record of the child attendee records. To use a record, you must obtain its handle. There are many ways to obtains it, including creating a new record and referring to child records of a record. Sample code:
the code below creates an event and inserts it into default event book (see below on calendar books).

 // create an event
 calendar_record_h event;
 calendar_record_create(_calendar_event._uri, &event);

 // set event summary
 calendar_record_set_str(event, _calendar_event.summary, "Meeting");

 // put the event into the default calendar book for events
 calendar_record_set_int(event, _calendar_event.calendar_book_id, book_id);

 // add alarm as child
 calendar_record_h alarm = NULL;
 calendar_record_create(_calendar_alarm._uri, &alarm);
 calendar_record_set_int(alarm, _calendar_alarm.tick_unit, CALENDAR_ALARM_TIME_UNIT_MINUTE);
 calendar_record_set_int(alarm, _calendar_alarm.tick, 5);
 calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);

 // insert calendar book into the database
 int event_id = 0;
 calendar_db_insert_record(event, &event_id);

 // destroy
 calendar_record_destroy(event, true);

Calendar Books

A calendar book is a placeholder for other records in Calendar API. Every event and to-do has to belong to a calendar book. There are three built-in calendar books.

Table: Calendar books
DEFAULT_EVENT_CALENDAR_BOOK_ID Event book
DEFAULT_TODO_CALENDAR_BOOK_ID Todo book
DEFAULT_BIRTHDAY_CALENDAR_BOOK_ID Birthday book

There is an example how calendar book id is set.

 calendar_record_h event = NULL;

 calendar_record_create(_calendar_event._uri, &event);

 // set default calendar book id
 calendar_record_set_int(event, _calendar_event.calendar_id, DEFAULT_EVENT_CALENDAR_BOOK_ID);

 // set other fields

 int event_id = 0;
 calendar_db_insert_record(event &event_id);

 // destroy
 calendar_record_destroy(event, true);

To receive a list of existing calendar books, use the following:

 calendar_list_h calendar_book_list = NULL;
 calendar_db_get_all_records(_calendar_book._uri, 0, 0, &calendar_book_list);

Events and Instances

Two important concepts are event and instance. An event record describes various properties of the event, like description, categories, priority and many others. It also contains information on when the event takes place, there can be more than one instance of the event. Each instance has its corresponding instance record.
If event is inserted with rrule, alarm and attendee, its data is saved to each database. Generated instances based on rrule are also stored in instance database.

view_db.png
Figure: Views and databases

For example, if an event has the following properties:

Table: Event and instance example
event instances
start date on 2012-10-09 (Tuesday)
frequency set to 'WEEKLY'
interval set to 1
count set to 3
2012-10-09 Tuesday
2012-10-16 Tuesday
2012-10-23 Tuesday

Interval is a multiplier of frequency, which means that if it is set to N, instances occur every N weeks (or whatever was set in frequency attribute). The recurrence model in Calendar API is compliant with iCalendar specification (www.ietf.org/rfc/rfc2445.txt). The following event properties have the same functionality as their corresponding values in iCalendar:

Table: Recurrence rules.
Recurrence rule property comment
freq Yearly, monthly, weekly, daily
count Until count. If count is 3, 3 instances are generated
interval The interval is positive integer representing how often the recurrence rule repeats
byday MO, TU, WE, TH, FR, SA, SU
bymonthday Days of month
byyearday Days of year
byweekno Ordinals specifying weeks of the year
bymonth Months of year
bysetpos Values which corresponds to the nth occurrence within the set of events
wkst The day on which the workweek starts

Exceptions

If one of instances is modified in summary, date… or deleted, this is called exception.
For example, if 2nd instance date is modified from 16th to 17th, 17th is the exception.

Table: Exception example
event instances exceptions
start date on 2012-10-09 (Tuesday)
frequency set to 'WEEKLY'
interval set to 1
count set to 3
2012-10-09 Tuesday
2012-10-23 Tuesday
2012-10-17 Tuesday

To get changes in exception, calendar_db_get_changes_exception_by_version() is called. These instances and exceptions are deleted together when original event is deleted.

Calendar Time Structure

The calendar time structure, calendar_caltime_s, is defined as follows:

 typedef struct
 {
     calendar_time_type_e type;
     union {
         long long int utime;
         struct {
             int year;
             int month;
             int mday;
         } date;
     } time;
 } calendar_time_s;

The structure should be used when setting the calendar time type (_CALENDAR_PROPERTY_CALTIME) properties of records. It can hold two types of data: UTC time (long long int) and date, given as year, month and day of the month (three integers). These types are identified by values of calendar_time_type_e, which are CALENDAR_TIME_UTIME and CALENDAR_TIME_LOCALTIME, respectively. The data type determines the usage of the structure.

Table: Data types
Identifier Type Name Purpose
CALENDAR_TIME_UTIME long long int utime UTC time, used to describe non-all-day events
CALENDAR_TIME_LOCALTIME struct date date only (year, month and day of the month), used to describe all day events

UTC Time Usage

Structures with UTC time should be used for non-all-day events. In such cases, the API user should convert local time to UTC time. The local time zone identifier should be stored in the record, in the corresponding property. For example, when setting starting time of an event, the local time zone should be stored in start_tzid. When converting local time to UTC time, the function below can be useful. The function converts the given date and time to the corresponding UTC time, considering the given time zone (first argument).

 #define ms2sec(ms) (long long int)(ms / 1000.0)

 long long int _time_convert_itol(char *tzid, int y, int mon, int d, int h, int min, int s)
 {
     int ret = 0;
     i18n_uchar utf16_timezone[CAL_STR_SHORT_LEN64] = {0};
     i18n_ustring_copy_ua_n(utf16_timezone, tzid, sizeof(utf16_timezone)/sizeof(i18n_uchar));

     i18n_ucalendar_h ucal = NULL;
     char *loc_default = NULL;
     i18n_ulocale_get_default((const char **)&loc_default);
     ret = i18n_ucalendar_create(utf16_timezone, -1, loc_default, I18N_UCALENDAR_GREGORIAN, &ucal);
     if (I18N_ERROR_NONE != ret) {
           dlog_print(DLOG_DEBUG, LOG_TAG, "i18n_ucalendar_create() Fail (%d)\n", ret);
         return -1;
     }

     i18n_ucalendar_set_date_time(ucal, y, mon - 1, d, h, min, s);

     i18n_udate date;
     ret = i18n_ucalendar_get_millisecond(ucal, &date);
     if (I18N_ERROR_NONE != ret) {
         dlog_print(DLOG_DEBUG, LOG_TAG, "i18n_ucalendar_create() Fail (%d)\n", ret);
         i18n_ucalendar_destroy(ucal);
         return -1;
     }
     i18n_ucalendar_destroy(ucal);

     return ms2sec(date);
 }

Sample code:

 // fill calendar time structures (start and end time)
 calendar_time_s st = {0};
 calendar_time_s et = {0};

 st.type = CALENDAR_TIME_UTIME;
 st.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 11, 0, 0);

 et.type = CALENDAR_TIME_UTIME;
 et.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 12, 0, 0);

 // create an event record
 // ...

 // set local time zone of start time
 calendar_record_set_str(event, _calendar_event.start_tzid, "Asia/Seoul");

 // set start time
 calendar_record_set_caltime(event, _calendar_event.start_time, st);

 // set local time zone of end time
 calendar_record_set_str(event, _calendar_event.end_tzid, "Asia/Seoul");

 // set end time
 calendar_record_set_caltime(event, _calendar_event.end_time, et);

Date Usage

Another usage of time structure is an all day event. In case of such events, the structure's type field MUST be set to CALENDAR_TIME_LOCALTIME. Only the date (no time) will be stored. Such structures can be used to set start and end time of an event.
Both start and end time of the event MUST be set. The end date value MUST be later in time than the value of the start date.

 // range is 2days: 2014/02/17 00:00:00 ~ 2014/02/19 00:00:00

 calendar_time_s st = {0};
 st.type = CALENDAR_TIME_LOCALTIME;
 st.time.date.year = 2014;
 st.time.date.month = 2;
 st.time.date.mday = 17;

 calendar_time_s et = {0};
 et.type = CALENDAR_TIME_LOCALTIME;
 et.time.date.year = 2014;
 et.time.date.month = 2;
 et.time.date.mday = 19;

 // create an event record
 // ...

Recurring Events

To create a recurring event in Calendar API, you MUST set frequency. There is a sample code how to create recurring event. Firstly, set start and end time.

 calendar_time_s st = {0};
 st.type = CALENDAR_TIME_UTIME;
 st.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 11, 0, 0);

 calendar_time_s et = {0};
 et.type = CALENDAR_TIME_UTIME;
 et.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 12, 0, 0);

Then, the remaining properties should be set. Each frequency needs other proper fields except daily event. If other values are not inserted, these values are calculated based on start time.

Table: Frequency properties
Freq Property Comment
CALENDAR_RECURRENCE_YEARLY byyearday Every 100th day
byweekno Every 34th week
bymonthday Every 1st February (birthday)
byday Every 1st Monday of May (holiday)
CALENDAR_RECURRENCE_MONTHLY bymonthday Every 29th (payday)
byday Every last Friday
CALENDAR_RECURRENCE_WEEKLY byday Every week
CALENDAR_RECURRENCE_DAILY - -

If byday is not set in weekly event, default byday is followed start day of week. By the same token, default interval is 1 and default range type is endless.

Table: Range types
Range type Comment
CALENDAR_RANGE_NONE Endless(max date is 2036/12/31)
CALENDAR_RANGE_UNTIL Should set until property
CALENDAR_RANGE_COUNT Should set count property
 calendar_record_h event;
 calendar_record_create(_calendar_event._uri, &event);

 calendar_record_set_str(event, _calendar_event.start_tzid, "Asia/Seoul");
 calendar_record_set_caltime(event, _calendar_event.start_time, st);
 calendar_record_set_str(event, _calendar_event.end_tzid, "Asia/Seoul");
 calendar_record_set_caltime(event, _calendar_event.end_time, et);

 calendar_record_set_int(event, _calendar_event.freq, CALENDAR_RECURRENCE_WEEKLY);
 calendar_record_set_int(event, _calendar_event.interval, 1)
 calendar_record_set_int(event, _calendar_event.count, 3);

The last step is inserting the event into the database. Records representing instances of the event are created when the event record is inserted.

 int event_id = 0;
 calendar_db_insert_record(event, &event_id);

 calendar_record_destroy(event, true);

Filters and Queries

Queries are used to retrieve data which satisfies given criteria, like an integer property being greater than a given value, or a string property containing a given substring. The criteria are defined by creating filters and adding conditions to them, joining them with logical operators. Also, instead of a condition, another filter can be added, which can be used to create more complex filters. Operator precedence in filters determined by the order in which the conditions and filters are added. When a filter is ready, it can be set as a property of a query. Other query properties allow configuring how the returned results are grouped and sorted. Operator precedence in filters is determined by the order in which the conditions and filters are added are added. For example, if the following sequence is added:

Table: Filter and conditions
Filter with conditions Result
Contidion C1
OR
Contidion C2
AND
Condition C3
(C1 OR C2) AND C3
Filter F1:
Condition C1
OR
Condition C2

Filter F2:
Condition C3
OR
Condition C4

Filter F3:
Condition C5
AND
F1
AND
F2
(C5 AND F1) AND F2
Which is:
(C5 AND (C1 OR C2)) AND (C3 OR C4)

The following code creates a filter, accepting events with high priority or those that include the word "meeting" in their description.

 calendar_filter_h filter = NULL;

 // create a filter returning event type records
 calendar_filter_create(_calendar_event._uri, &filter);

 // add 'priority equals high' condition
 calendar_filter_add_int(filter, _calendar_event.priority, CALENDAR_MATCH_EQUAL, CALENDAR_EVENT_PRIORITY_HIGH);

 // add OR operator
 calendar_filter_add_operator(filter, CALENDAR_FILTER_OPERATOR_OR);

 // add 'description contains "meeting"' condition
 calendar_filter_add_str(filter, _calendar_event.description, CALENDAR_MATCH_CONTAINS, "meeting");

The filter should be inserted into a query and the query should be executed:

 calendar_query_h query = NULL;
 calendar_list_h list = NULL;

 // create a query returning event type records
 calendar_query_create(_calendar_event._uri, &query);

 // add the filter
 calendar_query_set_filter(query, filter);

 // execute the query, results are returned in a list
 calendar_db_get_records_with_query(query, 0, 0, &list);

 calendar_filter_destroy(filter);
 calendar_query_destroy(query);

 // use the list
 // ...

 calendar_list_destroy(list, true);

Projections

Useful concept in Calendar service APIs is projection, related to searching with filters and queries. Projection allows you to query the Data for just those specific properties of a record that you actually need, at lower latency and cost than retrieving the entire properties. Sample code:
create filter which will get event id, summary and start time from the record with its summary has “test” (string filter). Create a query and add the filter to it. Results are received in a list.

 calendar_query_h query = NULL;
 calendar_filter_h filter = NULL;

 // set query with filter
 calendar_query_create(_calendar_event_calendar_book_attendee._uri, &query);
 calendar_filter_create(_calendar_event_calendar_book_attendee._uri, &filter);
 calendar_filter_add_str(filter, _calendar_event.summary, CALENDAR_MATCH_CONTAINS, "test");
 calendar_query_set_filter(query, filter);

 // set projection
 unsigned int projection[3];
 projection[0]=_calendar_event_calendar_book_attendee.event_id;
 projection[1]=_calendar_event_calendar_book_attendee.summary;
 projection[2]=_calendar_event_calendar_book_attendee.start_time;

 // get list
 calendar_query_set_projection(query, projection, 3);
 calendar_db_get_records_with_query(query, 0, 0, &list);

 // destroy handle
 calendar_filter_destroy(filter);
 calendar_query_destroy(query);
 calendar_list_destroy(list, true);

Reminders

Alarm and reminder is the similar terminology but reminder is used as package name storages, shown in below figure 5. When alarm alerts, calendar-service sends notification to all packages which is registered in reminder DB table with calendar_reminder_add_receiver(). This shows how to set alarm and how alarm works.

alarm_process.png
Figure: Alarm process

After adding receiver, registered alarm would be alerted on reserved time by inserting alarm as child.

Table: Alarm fields
tick_unit related field comment
CALENDAR_ALARM_TIME_UNIT_SPECIFIC time This represents the number of seconds elapsed since the Epoch, 1970-01-01 00:00:00 +0000 (UTC)
CALENDAR_ALARM_TIME_UNIT_WEEK tick The number of weeks before start time
CALENDAR_ALARM_TIME_UNIT_DAY tick The number of days before start time
CALENDAR_ALARM_TIME_UNIT_HOUR tick The number of hours before start time
CALENDAR_ALARM_TIME_UNIT_MINUTE tick The number of minutes before start time

Below example shows the alarm which is set 1 minute before start time.

 // set alarm with normal unit
 calendar_record_h alarm = NULL;
 calendar_record_create(_calendar_alarm._uri, &alarm);
 calendar_record_set_int(alarm, _calendar_alarm.tick_unit, CALENDAR_ALARM_TIME_UNIT_MINUTE);
 calendar_record_set_int(alarm, _calendar_alarm.tick, 1); // before 1min (60secs)

 // add alarm as child
 calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);

With CALENDAR_ALARM_TIME_UNIT_SPECIFIC, the alarm could be set regardless of the start time.

 // set alarm with specific unit
 calendar_record_h alarm = NULL;
 calendar_record_create(_calendar_alarm._uri, &alarm);
 calendar_record_set_int(alarm, _calendar_alarm.tick_unit, CALENDAR_ALARM_TIME_UNIT_SPECIFIC);
 // suppose start time is 1404036000(Sun, 29 Jun 2014 10:00:00 GMT) and alarm is set 60secs after from start time.
 calendar_time_s ct;
 ct.type = CALENDAR_TIME_UTIME;
 ct.time.utime = 1404036000 + 60;
 calendar_record_set_caltime(alarm, _calendar_alarm.alarm_time, ct);

 // add alarm as child
 calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);

How to register package as reminder. In manifest.xml file, below code must be inserted.

 <app-control>
    <operation name="http://tizen.org/appcontrol/operation/view" />
    <mime name="application/x-tizen.calendar.reminder" />
 </app-control>

When alarm alerts, calendar-service sends data with key, value pairs.
With "ids" key, id array could be get.
Each detail value could be get with "each id" key.
The detail data is the combination of alarm values. id, time, tick, unit and type is connected with "&" character.
ex> id=64&time=1415106300&tick=0&unit=60&type=0

 // "ids" string is the key, to get id array
 char **ids = NULL;
 int len = 0;
 app_control_get_extra_data_array(b, "ids", &ids, &len);

 int i = 0;
 for (i = 0; i < len; i++) {
    // "id" is the key to get detail value
    char *value = NULL;
    app_control_get_extra_data(b, ids[i], &value);
    if (NULL == value) {
        continue;
    }
    // parse detail data

    // free
    free(ids[i]);
    ids[i] = NULL;
 }

 free(ids);

Database Change Notifications

Applications add/remove callback function to detect/ignore the calendar DB changes with calendar_db_add_changed_cb() / calendar_db_remove_changed_cb().
Clients await calendar change notification on client side. If calendar is changed by another module, server publishes inotify event. Inotify module broadcasts to subscribe modules. Internal inotify handler is called at client side. User callback function is called with user data.

 // add callback function
 void __event_changed_cb(const char *view_uri, void *user_data)
 {
 }
 // add changed noti callback
 calendar_db_add_changed_cb(_calendar_event._uri, __event_changed_cb, NULL);

Vcalendar

To exchange of personal calendaring and scheduling information, vcalendar is used. In order to avoid confusion with this referenced work, this is to be known as the vcalendar specification. Vcalendar ver2.0 is known as icalendar.

Table: Vcalendar example ( http://www.ietf.org/rfc/rfc2445.txt )
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR

Calendar service provides APIs to compose vcalendar stream. With stream, file could be made or data could be transmitted with json data.

 calendar_list_h list = NULL;
 // create or get list to make vcalendar stream

 char *stream = NULL;
 calendar_vcalendar_make_from_records(list, &stream);

 // jobs for stream

 // free
 free(stream);

Vcalendar could be parsed with calendar service APIs as well.

 // read  stream from file

 calendar_list_h list = NULL;
 calendar_vcalendar_parse_to_calendar(stream, &list);

 // jobs for list…
 calendar_list_destroy(list, true);