Task 298: .ICAL File Format
Task 298: .ICAL File Format
File Format Specifications for .ICAL
The .ICAL file format is an alternative extension for iCalendar files (commonly .ICS), defined in RFC 5545 (Internet Calendaring and Scheduling Core Object Specification). It is a text-based format for representing calendaring and scheduling information, such as events, to-dos, journal entries, free/busy time, and time zones. The format is MIME type "text/calendar" and typically uses UTF-8 encoding. Files consist of content lines in the form name[;parameters]:value
, with line folding for lines exceeding 75 octets. The structure is hierarchical, starting with BEGIN:VCALENDAR
and ending with END:VCALENDAR
, containing properties and nested components like VEVENT, VTODO, etc.
List of All Properties Intrinsic to the File Format
These are the standard property names defined in the iCalendar specification, grouped by category or component. Properties are key elements in the file's structure, representing data like dates, descriptions, and relationships. Custom properties can use "X-" prefix, but only standard ones are listed here.
- Calendar Properties (VCALENDAR): CALSCALE, METHOD, PRODID, VERSION
- Descriptive Component Properties: ATTACH, CATEGORIES, CLASS, COMMENT, DESCRIPTION, GEO, LOCATION, PERCENT-COMPLETE, PRIORITY, RESOURCES, STATUS, SUMMARY
- Date and Time Component Properties: COMPLETED, DTEND, DUE, DTSTART, DURATION, FREEBUSY, TRANSP
- Time Zone Component Properties: TZID, TZNAME, TZOFFSETFROM, TZOFFSETTO, TZURL
- Relationship Component Properties: ATTENDEE, CONTACT, ORGANIZER, RECURRENCE-ID, RELATED-TO, URL, UID
- Recurrence Component Properties: EXDATE, EXRULE (deprecated), RDATE, RRULE
- Alarm Component Properties: ACTION, REPEAT, TRIGGER
- Change Management Component Properties: CREATED, DTSTAMP, LAST-MODIFIED, SEQUENCE, REQUEST-STATUS
- Miscellaneous Component Properties: IANA-PROPERTY, X-PROPERTY, REQUEST-STATUS
- Event Component (VEVENT): DTSTAMP, UID, DTSTART, CLASS, CREATED, GEO, LAST-MODIFIED, LOCATION, ORGANIZER, PRIORITY, SEQUENCE, STATUS, SUMMARY, TRANSP, URL, RECURRENCE-ID, RRULE, DTEND, DURATION, ATTACH, ATTENDEE, CATEGORIES, COMMENT, CONTACT, EXDATE, REQUEST-STATUS, RELATED-TO, RESOURCES, RDATE
- To-Do Component (VTODO): DTSTAMP, UID, CLASS, COMPLETED, CREATED, DESCRIPTION, DTSTART, GEO, LAST-MODIFIED, LOCATION, ORGANIZER, PERCENT-COMPLETE, PRIORITY, RECURRENCE-ID, SEQUENCE, STATUS, SUMMARY, URL, RRULE, DUE, DURATION, ATTACH, ATTENDEE, CATEGORIES, COMMENT, CONTACT, EXDATE, REQUEST-STATUS, RELATED-TO, RESOURCES, RDATE
- Journal Component (VJOURNAL): DTSTAMP, UID, CLASS, CREATED, DTSTART, LAST-MODIFIED, ORGANIZER, RECURRENCE-ID, SEQUENCE, STATUS, SUMMARY, URL, RRULE, ATTACH, ATTENDEE, CATEGORIES, COMMENT, CONTACT, DESCRIPTION, EXDATE, RELATED-TO, RDATE, REQUEST-STATUS
- Free/Busy Component (VFREEBUSY): DTSTAMP, UID, CONTACT, DTSTART, DTEND, ORGANIZER, URL, ATTENDEE, COMMENT, FREEBUSY, REQUEST-STATUS
- Time Zone Component (VTIMEZONE): TZID, LAST-MODIFIED, TZURL, DTSTART, TZOFFSETTO, TZOFFSETFROM, RRULE, COMMENT, RDATE, TZNAME
- Standard and Daylight Sub-Components (within VTIMEZONE): DTSTART, TZOFFSETTO, TZOFFSETFROM, RRULE, COMMENT, RDATE, TZNAME
- Alarm Component (VALARM): ACTION, TRIGGER, DURATION, REPEAT, ATTACH, DESCRIPTION, SUMMARY, ATTENDEE
Two Direct Download Links for .ICAL Files
Note: .ICAL is interchangeable with .ICS; these are valid iCalendar files.
- https://gist.githubusercontent.com/DeMarko/6142417/raw/1e7a9525fba42d35de36931ce42fa095ca345deb/sample.ics
- https://raw.githubusercontent.com/gadael/icsdb/master/data/France.ics
HTML/JavaScript for Drag-and-Drop .ICAL File Dump (Embeddable in a Ghost Blog or Similar)
This is a self-contained HTML snippet with JavaScript that can be embedded in a blog post. It creates a drop zone for .ICAL/.ICS files, parses the content (handling line folding and parameters), extracts all properties from the list above, and dumps them to the screen with values (hierarchically indented for components).
Python Class for .ICAL File Handling
This class opens, parses (decodes), reads, writes, and prints all properties from the list to console. It handles basic line unfolding and parameter parsing.
import os
class ICalHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self.components = {}
self._parse()
def _parse(self):
with open(self.filepath, 'r', encoding='utf-8') as f:
content = f.read()
lines = content.replace('\r\n ', '').replace('\n ', '').splitlines() # Unfold
current_component = None
for line in lines:
if line.startswith('BEGIN:'):
current_component = line[6:]
self.components[current_component] = {}
elif line.startswith('END:'):
current_component = None
else:
if ':' in line:
key_part, value = line.split(':', 1)
key, *params = key_part.split(';')
if current_component:
self.components[current_component][key] = (params, value)
else:
self.properties[key] = (params, value)
def print_properties(self):
print("Global Properties:")
for key, (params, value) in self.properties.items():
print(f"{key}{';' + ';'.join(params) if params else ''}: {value}")
for comp, props in self.components.items():
print(f"\nComponent {comp}:")
for key, (params, value) in props.items():
print(f" {key}{';' + ';'.join(params) if params else ''}: {value}")
def write(self, new_filepath=None):
filepath = new_filepath or self.filepath
with open(filepath, 'w', encoding='utf-8') as f:
f.write('BEGIN:VCALENDAR\n')
for key, (params, value) in self.properties.items():
f.write(f"{key}{';' + ';'.join(params) if params else ''}:{value}\n")
for comp, props in self.components.items():
f.write(f"BEGIN:{comp}\n")
for key, (params, value) in props.items():
f.write(f"{key}{';' + ';'.join(params) if params else ''}:{value}\n")
f.write(f"END:{comp}\n")
f.write('END:VCALENDAR\n')
# Example usage:
# handler = ICalHandler('sample.ical')
# handler.print_properties()
# handler.write('output.ical')
Java Class for .ICAL File Handling
This class opens, parses, reads, writes, and prints all properties to console. Uses basic string processing for unfolding.
import java.io.*;
import java.util.*;
public class ICalHandler {
private String filepath;
private Map<String, String> properties = new HashMap<>();
private Map<String, Map<String, String>> components = new HashMap<>();
public ICalHandler(String filepath) {
this.filepath = filepath;
parse();
}
private void parse() {
try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
StringBuilder content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
content.append(line.startsWith(" ") ? line.substring(1) : "\n" + line);
}
String[] lines = content.toString().trim().split("\n");
String currentComponent = null;
for (String l : lines) {
if (l.startsWith("BEGIN:")) {
currentComponent = l.substring(6);
components.put(currentComponent, new HashMap<>());
} else if (l.startsWith("END:")) {
currentComponent = null;
} else if (l.contains(":")) {
String[] parts = l.split(":", 2);
String keyPart = parts[0];
String value = parts[1];
String[] keyParams = keyPart.split(";");
String key = keyParams[0];
String params = keyParams.length > 1 ? ";" + String.join(";", Arrays.copyOfRange(keyParams, 1, keyParams.length)) : "";
String full = key + params + ":" + value;
if (currentComponent != null) {
components.get(currentComponent).put(key, full);
} else {
properties.put(key, full);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void printProperties() {
System.out.println("Global Properties:");
properties.forEach((key, value) -> System.out.println(value));
components.forEach((comp, props) -> {
System.out.println("\nComponent " + comp + ":");
props.forEach((key, value) -> System.out.println(" " + value));
});
}
public void write(String newFilepath) {
try (PrintWriter writer = new PrintWriter(new File(newFilepath != null ? newFilepath : filepath))) {
writer.println("BEGIN:VCALENDAR");
properties.forEach((key, value) -> writer.println(value));
components.forEach((comp, props) -> {
writer.println("BEGIN:" + comp);
props.forEach((key, value) -> writer.println(value));
writer.println("END:" + comp);
});
writer.println("END:VCALENDAR");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// Example usage:
// public static void main(String[] args) {
// ICalHandler handler = new ICalHandler("sample.ical");
// handler.printProperties();
// handler.write("output.ical");
// }
}
JavaScript Class for .ICAL File Handling
This class (for Node.js) opens, parses, reads, writes, and prints all properties to console. Uses fs module.
const fs = require('fs');
class ICalHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.components = {};
this.parse();
}
parse() {
let content = fs.readFileSync(this.filepath, 'utf-8');
content = content.replace(/\r\n\s/g, '').replace(/\n\s/g, ''); // Unfold
const lines = content.split(/\r?\n/);
let currentComponent = null;
lines.forEach(line => {
if (line.startsWith('BEGIN:')) {
currentComponent = line.substring(6);
this.components[currentComponent] = {};
} else if (line.startsWith('END:')) {
currentComponent = null;
} else if (line.includes(':')) {
const [keyPart, value] = line.split(':');
const [key, ...params] = keyPart.split(';');
const full = `${key}${params.length ? ';' + params.join(';') : ''}: ${value}`;
if (currentComponent) {
this.components[currentComponent][key] = full;
} else {
this.properties[key] = full;
}
}
});
}
printProperties() {
console.log('Global Properties:');
Object.values(this.properties).forEach(v => console.log(v));
Object.entries(this.components).forEach(([comp, props]) => {
console.log(`\nComponent ${comp}:`);
Object.values(props).forEach(v => console.log(` ${v}`));
});
}
write(newFilepath = this.filepath) {
let output = 'BEGIN:VCALENDAR\n';
Object.values(this.properties).forEach(v => output += v + '\n');
Object.entries(this.components).forEach(([comp, props]) => {
output += `BEGIN:${comp}\n`;
Object.values(props).forEach(v => output += v + '\n');
output += `END:${comp}\n`;
});
output += 'END:VCALENDAR\n';
fs.writeFileSync(newFilepath, output, 'utf-8');
}
}
// Example usage:
// const handler = new ICalHandler('sample.ical');
// handler.printProperties();
// handler.write('output.ical');
C++ Class for .ICAL File Handling
This class opens, parses, reads, writes, and prints all properties to console. Uses std::ifstream/std::ofstream for file I/O.
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <sstream>
class ICalHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties;
std::map<std::string, std::map<std::string, std::string>> components;
void parse() {
std::ifstream file(filepath);
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
// Unfold lines
size_t pos;
while ((pos = content.find("\r\n ")) != std::string::npos) content.erase(pos + 2, 1);
while ((pos = content.find("\n ")) != std::string::npos) content.erase(pos + 1, 1);
std::istringstream iss(content);
std::string line;
std::string currentComponent;
while (std::getline(iss, line)) {
if (line.rfind("BEGIN:", 0) == 0) {
currentComponent = line.substr(6);
components[currentComponent] = {};
} else if (line.rfind("END:", 0) == 0) {
currentComponent = "";
} else {
size_t colon = line.find(':');
if (colon != std::string::npos) {
std::string keyPart = line.substr(0, colon);
std::string value = line.substr(colon + 1);
size_t semi = keyPart.find(';');
std::string key = (semi == std::string::npos) ? keyPart : keyPart.substr(0, semi);
std::string params = (semi == std::string::npos) ? "" : keyPart.substr(semi);
std::string full = key + params + ":" + value;
if (!currentComponent.empty()) {
components[currentComponent][key] = full;
} else {
properties[key] = full;
}
}
}
}
}
public:
ICalHandler(const std::string& fp) : filepath(fp) {
parse();
}
void printProperties() {
std::cout << "Global Properties:" << std::endl;
for (const auto& p : properties) {
std::cout << p.second << std::endl;
}
for (const auto& c : components) {
std::cout << "\nComponent " << c.first << ":" << std::endl;
for (const auto& p : c.second) {
std::cout << " " << p.second << std::endl;
}
}
}
void write(const std::string& newFilepath = "") {
std::string outPath = newFilepath.empty() ? filepath : newFilepath;
std::ofstream file(outPath);
file << "BEGIN:VCALENDAR\n";
for (const auto& p : properties) {
file << p.second << "\n";
}
for (const auto& c : components) {
file << "BEGIN:" << c.first << "\n";
for (const auto& p : c.second) {
file << p.second << "\n";
}
file << "END:" << c.first << "\n";
}
file << "END:VCALENDAR\n";
file.close();
}
};
// Example usage:
// int main() {
// ICalHandler handler("sample.ical");
// handler.printProperties();
// handler.write("output.ical");
// return 0;
// }