PHP code example of erenav / icalendar

1. Go to this page and download the library: Download erenav/icalendar library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

erenav / icalendar example snippets


use Erenav\ICalendar\Component\{Calendar, Event};
use Erenav\ICalendar\Serializer\IcsSerializer;
use Erenav\ICalendar\ValueType\Duration;

$calendar = Calendar::build()
    ->prodId('-//Acme//Booking 1.0//EN')
    ->add(
        Event::build()
            ->uid('[email protected]')
            ->summary('Sprint Planning')
            ->starts(new DateTimeImmutable('2026-07-01 10:00', new DateTimeZone('UTC')))
            ->lasting(Duration::hours(1))
            ->addAttendee('[email protected]')
    )
    ->get();

echo (new IcsSerializer)->serialize($calendar);



use Erenav\ICalendar\Component\{Calendar, Event};
use Erenav\ICalendar\Parser\Parser;
use Erenav\ICalendar\Serializer\IcsSerializer;
use Erenav\ICalendar\ValueType\Duration;

// Build
$calendar = Calendar::build()
    ->prodId('-//Acme//EN')
    ->add(
        Event::build()
            ->uid('[email protected]')
            ->summary('Lunch')
            ->starts(new DateTimeImmutable('2026-07-01 12:00', new DateTimeZone('UTC')))
            ->lasting(Duration::hours(1))
    )
    ->get();

// Serialize
$ics = (new IcsSerializer)->serialize($calendar);

// Parse back
$parsed = Parser::lenient()->parseCalendar($ics);
echo $parsed->events()[0]->summary(); // "Lunch"

use Erenav\ICalendar\Component\{Calendar, Event};
use Erenav\ICalendar\Parameter\{Role, PartStat};
use Erenav\ICalendar\Property\{EventStatus, Transparency, Classification};
use Erenav\ICalendar\ValueType\Duration;

$event = Event::build()
    ->uid('[email protected]')
    ->timestamp(new DateTimeImmutable('now', new DateTimeZone('UTC'))) // DTSTAMP
    ->summary('Sprint Planning, Q3')          // commas/semicolons escaped automatically
    ->description("Agenda:\n- demo\n- retro")
    ->location('Room 4')
    ->url('https://acme.test/meetings/42')
    ->starts(new DateTimeImmutable('2026-07-01 09:30', new DateTimeZone('UTC')))
    ->lasting(Duration::hours(1))             // or ->ends($dateTime)
    ->status(EventStatus::Confirmed)
    ->transparency(Transparency::Opaque)
    ->classification(Classification::Private)
    ->priority(5)
    ->categories('work', 'planning')
    ->color('cornflowerblue')                 // RFC 7986
    ->organizer('[email protected]', name: 'The Boss')
    ->addAttendee('[email protected]', role: Role::Chair, rsvp: true, name: 'Alice')
    ->addAttendee('[email protected]', partStat: PartStat::Accepted)
    ->get();

$calendar = Calendar::build()
    ->prodId('-//Acme//Booking 1.0//EN')      // VERSION defaults to 2.0
    ->name('Team Calendar')                   // RFC 7986
    ->add($event)                             // accepts components or builders
    ->get();

use Erenav\ICalendar\Serializer\IcsSerializer;

$ics = (new IcsSerializer)->serialize($calendar);

// Strict mode enforces the serializer's selected 

use Erenav\ICalendar\Parser\Parser;

$calendar = Parser::lenient()->parseCalendar($icsString); // returns Calendar
$component = Parser::lenient()->parse($icsString);         // returns the root Component

// Strict parsing throws on malformed structure and typed-value violations instead of recovering:
$calendar = Parser::strict()->parseCalendar($icsString);

$event = $calendar->events()[0];

$event->uid();            // ?string
$event->summary();        // ?string  (already unescaped)
$event->description();    // ?string
$event->location();       // ?string
$event->start();          // ?DateTimeValue
$event->end();            // ?DateTimeValue  (computed from DTSTART+DURATION if no DTEND)
$event->effectiveEnd();   // ?DateTimeValue  (also applies RFC implicit duration)
$event->duration();       // ?Duration
$event->status();         // ?EventStatus
$event->priority();       // ?int
$event->color();          // ?string
$event->categories();     // list<string>
$event->organizer();      // ?Organizer
$event->attendees();      // list<Attendee>
$event->alarms();         // list<Alarm>
$event->recurrenceId();   // ?DateTimeValue
$event->recurrenceRange(); // ?Range (a RECURRENCE-ID parameter)
$event->recurrenceDatePeriods(); // list<Period>

// Calendar level
$calendar->productId();   // ?string
$calendar->version();     // ?string
$calendar->name();        // ?string
$calendar->events();      // list<Event>
$calendar->components();  // list<Component>  (events, time zones, todos, …)

$event->property('X-APPLE-TRAVEL-ADVISORY-BEHAVIOR')?->value()->toString();
$event->hasProperty('RRULE');
foreach ($event->properties as $property) { /* … */ }

$updated = $event->toBuilder()
    ->summary('Sprint Planning (rescheduled)')
    ->starts(new DateTimeImmutable('2026-07-02 09:30', new DateTimeZone('UTC')))
    ->get();

$event->summary();   // unchanged — original is immutable
$updated->summary(); // "Sprint Planning (rescheduled)"

use Erenav\ICalendar\ValueType\DateTimeValue;

DateTimeValue::utc(new DateTimeImmutable('2026-07-01 10:00', new DateTimeZone('UTC')));
//   → 20260701T100000Z

DateTimeValue::zoned(new DateTimeImmutable('2026-07-01 09:30'), 'America/New_York');
//   → DTSTART;TZID=America/New_York:20260701T093000

DateTimeValue::floating(new DateTimeImmutable('2026-07-01 09:30'));
//   → 20260701T093000   (no zone, "local" time)

DateTimeValue::date(new DateTimeImmutable('2026-07-01'));
//   → DTSTART;VALUE=DATE:20260701   (all-day)

$event = Event::build()
    ->starts($carbonInstance)                                  // inferred form
    ->ends(DateTimeValue::zoned($dt, 'Europe/Paris'))          // explicit form
    ->get();

// All-day event:
$allDay = Event::build()
    ->starts(DateTimeValue::date(new DateTimeImmutable('2026-07-01')))
    ->ends(DateTimeValue::date(new DateTimeImmutable('2026-07-02')))
    ->get();

use Erenav\ICalendar\ValueType\Duration;

Duration::hours(1);                 // PT1H
Duration::minutes(-15);             // -PT15M  (negative — e.g. an alarm trigger)
Duration::weeks(2);                 // P2W
Duration::of(days: 1, hours: 6);    // P1DT6H
Duration::parse('PT90M');           // from a string

// Interop (CarbonInterval extends DateInterval, so it works too):
Duration::fromDateInterval(new DateInterval('PT1H'));
Duration::hours(1)->toDateInterval();

use Erenav\ICalendar\Parameter\{Role, PartStat, CuType};

$event = Event::build()
    ->organizer('[email protected]', name: 'The Boss', sentBy: 'mailto:[email protected]')
    ->addAttendee('[email protected]', role: Role::Chair, partStat: PartStat::Accepted, rsvp: true, name: 'Alice')
    ->addAttendee('[email protected]', cuType: CuType::Room)
    ->get();

$attendee = $event->attendees()[0];     // a typed Attendee
$attendee->address()->toString();      // "mailto:[email protected]"
$attendee->email();                    // "[email protected]"
$attendee->role();                     // Role::Chair       (typed)
$attendee->participationStatus();      // PartStat::Accepted
$attendee->commonName();               // "Alice"
$attendee->rsvp();                     // true
$attendee->delegatedTo();              // list<CalAddress>
$attendee->delegatedFrom();            // list<CalAddress>
$attendee->members();                  // list<CalAddress>
$attendee->sentBy();                   // ?CalAddress
$attendee->directory();                // ?string (DIR URI)
$attendee->directoryUri();             // ?UriValue
$attendee->language();                 // ?string
$attendee->property;                   // the complete underlying Property escape hatch

$organizer = $event->organizer();
$organizer?->sentBy();                  // ?string (backward-compatible raw value)
$organizer?->sentByAddress();           // ?CalAddress
$organizer?->directory();               // ?string (DIR URI)
$organizer?->directoryUri();            // ?UriValue
$organizer?->language();                // ?string

$copy = Event::build();

if (($property = $source->property('UID')) !== null) {
    $copy->uidProperty($property);
}
if (($property = $source->property('DTSTART')) !== null) {
    $copy->startProperty($property);
}
if (($property = $source->property('RECURRENCE-ID')) !== null) {
    $copy->recurrenceIdProperty($property);
}
if (($property = $source->property('SEQUENCE')) !== null) {
    $copy->sequenceProperty($property);
}
if (($property = $source->property('ORGANIZER')) !== null) {
    $copy->organizerProperty($property);
}
foreach ($source->properties->all('ATTENDEE') as $property) {
    $copy->attendeeProperty($property);
}

$copiedEvent = $copy->get();

use Erenav\ICalendar\Component\{Event, Alarm};
use Erenav\ICalendar\Property\AlarmAction;
use Erenav\ICalendar\ValueType\Duration;

$event = Event::build()
    ->uid('[email protected]')
    ->addAlarm(
        Alarm::build()
            ->action(AlarmAction::Display)
            ->description('Starts in 15 minutes')
            ->trigger(Duration::minutes(-15))   // relative; or pass a DateTimeInterface for absolute
    )
    ->get();

$event->alarms()[0]->action();   // AlarmAction::Display
$event->alarms()[0]->trigger();  // Duration (or DateTimeValue)

use Erenav\ICalendar\Recurrence\{Recurrence, Weekday, WeekdayRule};

Recurrence::daily()->times(10);                          // FREQ=DAILY;COUNT=10
Recurrence::weekly()->every(2)->on(Weekday::Monday, Weekday::Wednesday);
Recurrence::monthly()->on(new WeekdayRule(Weekday::Friday, -1)); // last Friday of the month
Recurrence::yearly()->until(new DateTimeImmutable('2030-01-01', new DateTimeZone('UTC')));
Recurrence::parse('FREQ=WEEKLY;BYDAY=MO,WE');           // from an RRULE string

use Erenav\ICalendar\ValueType\DateTimeValue;

$event = Event::build()
    ->uid('[email protected]')
    ->starts(DateTimeValue::zoned(new DateTimeImmutable('2026-07-01 09:30'), 'America/New_York'))
    ->recurrence(Recurrence::weekly()->on(Weekday::Monday, Weekday::Wednesday))
    ->addExceptionDate(new DateTimeImmutable('2026-12-25 09:30', new DateTimeZone('America/New_York')))
    ->get();

$event->isRecurring();     // true
$event->recurrenceRule();  // ?Recurrence

$from = new DateTimeImmutable('2026-07-01 00:00:00', new DateTimeZone('UTC'));
$to   = new DateTimeImmutable('2026-08-01 00:00:00', new DateTimeZone('UTC'));

foreach ($event->occurrencesBetween($from, $to) as $occurrence) {
    echo $occurrence->format('Y-m-d H:i'); // DateTimeImmutable
}

foreach ($calendar->occurrencesBetween($from, $to) as $occurrence) {
    $occurrence->start;        // DateTimeImmutable (may differ from the slot if moved)
    $occurrence->recurrenceId; // the original slot in the series
    $occurrence->event;        // master, detached override, or materialized range result
    $occurrence->isOverride;   // true if a RECURRENCE-ID override applied
    $occurrence->end;          // RFC-effective end (including PERIOD/implicit duration)
}

$calendar = Calendar::build()
    ->prodId('-//Acme//EN')
    ->add(
        Event::build()->uid('1@acme')
            ->starts(DateTimeValue::zoned(new DateTimeImmutable('2026-07-01 09:30'), 'America/New_York')),
    )
    ->get()
    ->withTimeZones(); // prepends generated STANDARD/DAYLIGHT observances

$calendar->timeZones();          // list<TimeZone>
$calendar->timeZones()[0]->tzid(); // "America/New_York"

$tz = $calendar->timeZones()[0];
foreach ($tz->observances() as $observance) {
    $observance->isDaylight();        // bool
    $observance->offsetTo();          // ?UtcOffset
    $observance->recurrenceRule();    // ?Recurrence
    $observance->recurrenceDates();   // list<DateTimeValue>
}

use Erenav\ICalendar\Scheduling\{ITip, ITipValidator};
use Erenav\ICalendar\Parameter\PartStat;

$request = ITip::request($event);                                      // organizer invites attendees
$reply   = ITip::reply($event, '[email protected]', PartStat::Accepted); // attendee responds
$cancel  = ITip::cancel($event);                                       // + STATUS:CANCELLED, SEQUENCE++
$publish = ITip::publish([$eventA, $eventB]);                          // a non-interactive feed

$request->schedulingMethod(); // Method::Request   (typed METHOD getter)

$validator = new ITipValidator();

$validator->isValid($request);     // bool
$validator->validate($request);    // list<string> of problems (empty = valid)
$validator->assertValid($request); // throws SchedulingException if invalid

$event = Event::build()
    ->uid('[email protected]')
    ->property('X-ACME-ROOM-ID', '4')
    ->get();

$event->property('X-ACME-ROOM-ID')?->value()->toString(); // "4"
// VTODO / VJOURNAL etc. survive as GenericComponent; VTIMEZONE is a typed TimeZone.

Parser::strict()->parseCalendar($ics);          // reject supported structural/typed violations
(new IcsSerializer(strict: true))->serialize($c); // validate output before sending

use Erenav\ICalendar\Exception\{ICalendarException, ParseException, InvalidValueException, MissingPropertyException};

try {
    $calendar = Parser::strict()->parseCalendar($ics);
} catch (ParseException $e) {
    // malformed input in strict mode
} catch (ICalendarException $e) {
    // any other library error
}