calendar/icsfile

This simple class generate a .ics file.

Maintainers

Package info

bitbucket.org/colonelMoutarde/icalendar-generator

Homepage

Issues

pkg:composer/calendar/icsfile

Statistics

Installs: 125 083

Dependents: 1

Suggesters: 0

10.1.0 2026-05-07 09:20 UTC

README

This library allows you to easily generate iCalendar (.ics) files in PHP, following the iCalendar (RFC 5545) specification.

Installation

Make sure you have installed the dependencies via Composer:

composer install

Include the Composer autoloader in your script:

require_once 'vendor/autoload.php';

Usage

Below is a basic example of creating an iCalendar event:

<?php

require_once 'vendor/autoload.php';

use Ical\Ical;
use Ical\Enum\CalendarTypeEnum;
use Ical\Enum\StatusEnum;
use Ical\Enum\TransparencyEnum;
use Ical\Exceptions\IcalendarException;

try {
    $ical = (new Ical())
        ->setName('test')
        ->setAddress('Paris')
        ->setDateStart(new \DateTimeImmutable('2014-11-21 15:00:00', new \DateTimeZone('UTC')))
        ->setDateEnd(new \DateTimeImmutable('2014-11-21 16:00:00', new \DateTimeZone('UTC')))
        ->setDateStamp(new \DateTimeImmutable('2014-11-21 15:00:00', new \DateTimeZone('UTC')))
        ->setCalendarType(CalendarTypeEnum::GREGORIAN)
        ->setTransparency(TransparencyEnum::OPAQUE) // Optional
        ->setDescription('wonder description')
        ->setSummary('Running')
        ->setOrganizer('foo@bar.fr', 'Foo Bar') // Optional — second parameter sets CN (display name)
        ->setFilename('myFileName')
        ->setStatus(StatusEnum::CONFIRMED) // Optional
        ->setSequence(2); // Number of updates (default is 1, optional)

    $ical->addHeader();

    echo $ical->getICAL();

} catch (IcalendarException $exc) {
    echo $exc->getMessage();
}

Methods (overview)

  • setName(string $name)
  • setProdId(string $organisation, string $product, string $language = 'EN')
  • setAddress(string $address)
  • setDateStart(DateTimeInterface $date, bool $normalizeToUTC = true)
  • setDateEnd(DateTimeInterface $date, bool $normalizeToUTC = true)
  • setDateStamp(DateTimeInterface $date, bool $normalizeToUTC = true)
  • setCalendarType(?CalendarTypeEnum $type)
  • setTransparency(?TransparencyEnum $transparency)
  • setDescription(string $description)
  • setSummary(string $summary)
  • setOrganizer(string $email, ?string $name = null)
  • setFilename(string $filename)
  • setStatus(StatusEnum $status)
  • setSequence(int $sequence)
  • setTimezoneICal(string $timezone)
  • setAlarm(bool $enabled)
  • setRepeat(bool $enabled)
  • setAlarmMinutesBefore(int $minutes)
  • setAlarmRepeat(int $repeatCount, ?int $intervalMinutes = null)
  • setIncludeVtimezone(bool $include)
  • setVtimezoneTzid(string $tzid)
  • setVtimezoneStandardTzname(string $value)
  • setVtimezoneStandardTzoffsetfrom(string $value)
  • setVtimezoneStandardTzoffsetto(string $value)
  • setVtimezoneStandardRrule(string $value)
  • setVtimezoneStandardDtstart(string $value)
  • setVtimezoneDaylightTzname(string $value)
  • setVtimezoneDaylightTzoffsetfrom(string $value)
  • setVtimezoneDaylightTzoffsetto(string $value)
  • setVtimezoneDaylightRrule(string $value)
  • setVtimezoneDaylightDtstart(string $value)
  • addHeader()
  • getICAL(?string $uid = null): string

PRODID (calendar identifier)

The PRODID field identifies the application that generated the file. The iCalendar standard (RFC 5545) requires the format:

-//Organisation//Product//LANG

Use setProdId() to generate a compliant PRODID. If you do not call it, the name field is used as a fallback (backward-compatible behavior).

$ical->setProdId('Bob Morane', 'Conference Calendar', 'FR');
// → PRODID:-//Bob Morane//Conference Calendar//FR

$ical->setProdId('My Company', 'Booking App');
// → PRODID:-//My Company//Booking App//EN  (defaults to EN)

ORGANIZER (event organizer)

The ORGANIZER field accepts an optional CN (Common Name) parameter to display a human-readable name. Some clients such as Google Calendar require it to accept event updates.

// With display name (recommended)
$ical->setOrganizer('ariane@example.com', 'Ariane');
// → ORGANIZER;CN=Ariane:mailto:ariane@example.com

// Without name (backward-compatible)
$ical->setOrganizer('ariane@example.com');
// → ORGANIZER:mailto:ariane@example.com

Timezone handling (strict by default)

By default, this library normalizes all provided timestamps to UTC (strict mode) and emits them with a trailing Z. This ensures consistent behavior regardless of the host/server timezone.

  • Strict default (real UTC normalization):
    • setDateStart($date) — the provided $date is converted to UTC using its own timezone before formatting.
  • Opt-out: preserve the literal clock time (no UTC conversion):
    • Pass normalizeToUTC: false to emit the time as-is without conversion.

Examples:

$paris = new \DateTimeImmutable('2025-10-02 10:00:00', new \DateTimeZone('Europe/Paris'));

// 1) Strict (default): convert to real UTC (10:00 Paris -> 08:00Z depending on DST)
$ical->setDateStart($paris); // outputs DTSTART:20251002T080000Z

// 2) Opt-out: keep the provided clock time as-is (no timezone conversion)
$ical->setDateStart($paris, normalizeToUTC: false); // outputs DTSTART:20251002T100000Z

Tip: For fully deterministic tests, build your DateTime with an explicit timezone (e.g., new DateTimeZone('UTC')).

Alarms (VALARM)

Two usage styles are supported and backward compatible:

  • Backward-compatible flags:
    • setAlarm(true) enables a default reminder of 15 minutes before the event.
    • setRepeat(true) adds a simple REPEAT:1.
  • Fine-grained configuration:
    • setAlarmMinutesBefore(int $minutes) sets the trigger offset before the event start, and auto-enables the alarm.
    • setAlarmRepeat(int $repeatCount, ?int $intervalMinutes = null) sets REPEAT and, if an interval is provided, adds a DURATION line accordingly.

Examples:

// Simple: 15 minutes before, one repeat
$ical->setAlarm(true)->setRepeat(true);

// Custom: 30 minutes before, repeat 3 times every 10 minutes
$ical->setAlarmMinutesBefore(30)->setAlarmRepeat(3, 10);

VTIMEZONE

A VTIMEZONE block is included in the generated ICS only when all of the following conditions are met:

  1. setIncludeVtimezone(true) (default) — VTIMEZONE is not disabled.
  2. setVtimezoneTzid() is called with a non-empty value that matches the value set via setTimezoneICal().
  3. Both setVtimezoneStandardDtstart() and setVtimezoneDaylightDtstart() are provided.

If any condition is not met, no VTIMEZONE block is emitted. You can also explicitly disable it regardless of the other settings:

$ical->setIncludeVtimezone(false);

Full example with VTIMEZONE for Europe/Paris:

$ical = (new Ical())
    ->setTimezoneICal('Europe/Paris')
    ->setVtimezoneTzid('Europe/Paris')
    ->setVtimezoneStandardTzname('CET')
    ->setVtimezoneStandardTzoffsetfrom('+0200')
    ->setVtimezoneStandardTzoffsetto('+0100')
    ->setVtimezoneStandardDtstart('19701025T030000')
    ->setVtimezoneStandardRrule('FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10')
    ->setVtimezoneDaylightTzname('CEST')
    ->setVtimezoneDaylightTzoffsetfrom('+0100')
    ->setVtimezoneDaylightTzoffsetto('+0200')
    ->setVtimezoneDaylightDtstart('19700329T020000')
    ->setVtimezoneDaylightRrule('FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3');

Example Output

BEGIN:VCALENDAR
PRODID:-//Bob Morane//Conference Calendar//FR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:myUid@foo.bar.fr
DTSTAMP:20141121T150000Z
DTSTART:20141121T150000Z
DTEND:20141121T160000Z
SUMMARY:Running
ORGANIZER;CN=Foo Bar:mailto:foo@bar.fr
LOCATION:Paris
DESCRIPTION:wonder description
STATUS:CONFIRMED
TRANSP:OPAQUE
SEQUENCE:2
END:VEVENT
END:VCALENDAR

Status values (StatusEnum)

The setStatus() method accepts a StatusEnum case:

  • StatusEnum::CONFIRMED
  • StatusEnum::TENTATIVE
  • StatusEnum::CANCELLED
use Ical\Enum\StatusEnum;

$ical->setStatus(StatusEnum::CONFIRMED);

Error Handling

All exceptions are instances of IcalendarException. Use try/catch to handle errors when generating the calendar.