ezcal

Ergonomic iCalendar + vCard for Rust

The simplest way to read and write .ics and .vcf files. RFC 5545 + RFC 6350 in one crate.

RFC 5545 + RFC 6350 in one crate
Quick Start GitHub crates.io
cargo add ezcal

Features

📅

iCalendar (RFC 5545)

Create and parse .ics files with events, todos, alarms, and recurrence rules. Works with Google Calendar, Apple Calendar, and Outlook.

📇

vCard (RFC 6350)

Create and parse .vcf files with contacts, structured names, addresses, emails, and phone numbers. Multi-contact file support.

🔄

Round-Trip Safe

Unknown properties are preserved through parse-serialize cycles. Your X-CUSTOM fields won't get lost.

Builder Pattern

Ergonomic builder API lets you create events and contacts in just a few lines. No wrestling with raw strings.

🕒

chrono Integration

Optional chrono feature for seamless conversion between iCal date-times and Rust's chrono types.

🛡️

Great Error Messages

Parse errors include line numbers and descriptive messages. No more guessing what went wrong.

Quick Start

📅

Create an Event

use ezcal::ical::{Calendar, Event};

let cal = Calendar::new()
    .event(
        Event::new()
            .summary("Team Standup")
            .location("Room 42")
            .starts("2026-03-15T09:00:00")
            .ends("2026-03-15T09:30:00")
    )
    .build();

std::fs::write("meeting.ics", cal.to_string())?;
🔍

Parse an .ics File

use ezcal::ical::Calendar;

let ics = std::fs::read_to_string("meeting.ics")?;
let calendar = Calendar::parse(&ics)?;

for event in calendar.events() {
    println!("{}: {}",
        event.get_starts().unwrap(),
        event.get_summary()
            .unwrap_or("(untitled)")
    );
}
📇

Create a Contact

use ezcal::vcard::Contact;

let card = Contact::new()
    .full_name("Jane Doe")
    .email("jane@example.com")
    .phone("+1-555-0123")
    .organization("Acme Corp")
    .build();

std::fs::write("jane.vcf", card.to_string())?;
👥

Parse a .vcf File

use ezcal::vcard::Contact;

let vcf = std::fs::read_to_string("contacts.vcf")?;
let contacts = Contact::parse_all(&vcf)?;

for c in &contacts {
    println!("{}: {}",
        c.get_full_name().unwrap(),
        c.get_email()
            .unwrap_or("(no email)")
    );
}

API Reference

iCalendar (ezcal::ical)

Type Description
Calendar VCALENDAR container. Builder + parser for .ics files.
Event VEVENT component. Summary, location, start/end, categories, alarms, RRULE.
Todo VTODO component. Summary, due date, status, priority, percent-complete.
Alarm VALARM component. Display and audio alarms with triggers.
RecurrenceRule RRULE representation. Parse and serialize recurrence rules.

vCard (ezcal::vcard)

Type Description
Contact VCARD container. Builder + parser for .vcf files. Multi-contact support.
StructuredName N property. Family, given, additional, prefix, suffix.
Address ADR property. Street, city, region, postal code, country.

Common (ezcal::datetime)

Type Description
DateTimeValue Date/DateTime/DateTimeUtc/DateTimeTz. Parses iCal and ISO 8601 formats. Optional chrono conversion.
Property Generic content-line property with name, parameters, and value.

Why ezcal?

ical

Archived
Read-only, archived Aug 2024

icalendar

iCal only
No vCard, limited parsing

calcard

33K LOC
Enterprise-grade, complex API

ezcal

Just Works
iCal + vCard, simple API, round-trip safe