PackageTrack
Sign in Get early access

spatie/calendar-links

Generate add to calendar links for Google, iCal and other calendar systems

2.1.0 8.1M downloads/mo #1604 most downloaded on Packagist spatie/calendar-links

What this package is like to depend on

Last release 3 days ago

20 Aug 2026

Release timing varies

gaps range from 9 days to 13 months

Most releases are documented

notes for 35 of 41 stable releases

Nothing withdrawn

no release was ever pulled

9 years old

41 releases · first in 2017

4 releases in the last 12 months

see the full history below

Release timeline

41 releases · Sep 2017 to Aug 2026
2018 2019 2020 2021 2022 2023 2024 2025 2026
Release Pre-release

Releases

latest 41
  1. 2.1.0 20 Aug 2026
    Release notes

    TL;DR: no upgrade steps, the public API is unchanged. Headline additions are guests and separate start/end timezones. ICS files carrying a timezone now name it with a TZID and ship a VTIMEZONE. Subclassing a generator? Read the last section.

    New Features

    • Guests (attendees): Link::guest() / Link::guests(), required and optional roles, rendered as add (Google), inv_list (Yahoo), to/cc (Outlook), ATTENDEE (ICS) (#229, #237)
    • Separate start and end timezones, for flights (#233, #246, #249)
    • ICS TRANSP, CLASS, RRULE, X-MICROSOFT-CDO-BUSYSTATUS, DTSTAMP via the options array (#230) (#244)
    • ICS extension points: additionalCalendarProperties(), additionalEventProperties() (#242, #247)

    Changed

    • ICS files carry a VTIMEZONE for every TZID they name, without which older Outlook desktop shifts the event (#249)
    • A recurring event in a zone that moves its clocks is written as a local time with a TZID, not a UTC instant, which used to put every occurrence past a DST change an hour off (#254)
    • Zones naming no place (+02:00, CEST, POSIX rule sets, all of Etc/) are written in UTC (#248)
    • ICS REMINDER and format are validated instead of silently falling back, so ['format' => 'FILE'] no longer returns a link where a file was asked for (#254)
    • Generators are easier to extend: four escaping helpers are now protected, Google/Yahoo read BASE_URL late statically (#242)
    • A false URL parameter renders as &key, not &key=

    Bug Fixes

    • ICS values could inject calendar properties through UID, PRODID, URL, RRULE, the enumerated options, and ATTENDEE addresses assigned straight to Link::$guests (#245, #254)
    • RFC 5545 gaps: long lines are folded without splitting a multibyte character, the file ends with CRLF, the data URI declares charset=utf-8, invalid control characters are dropped (#250)
    • ICS event properties landed after VALARM (#247)
    • VTIMEZONE stopped at the year it described. The last change of each kind now carries a yearly rule where the zone repeats predictably (#254)
    • All-day events across timezones gained or lost a day (#237)
    • Yahoo deep link parameters: no more stale view=d&type=20, wall clock ST/ET, DUR=allday for single days, exclusive ET for multi-day (#232)
    • Attendee escaping: a CAL-ADDRESS is a URI, not TEXT. Unusable guest addresses are rejected (#229)
    • A description or address of '0' is no longer dropped as empty

    Notes for subclasses

    The public API is unchanged, but three changes reach a subclass. The first two are fatal at load time:

    • Ics::$presentationOptions is now typed array. Redeclaring it untyped raises Type of Sub::$presentationOptions must be array. Drop the redeclaration or type it to match.
    • Ics::generateAlertComponent(), Yahoo::sanitizeText(), Yahoo::sanitizeAddressList() and BaseOutlook::sanitizeString() went private to protected. A same-named private method raises Access level to Sub::sanitizeText() must be protected or weaker. Widen it.
    • Yahoo::BASE_URL dropped view=d&type=20, so a subclass building its own URL from the constant gets the shorter value.

    Made by @alies-dev and the community.

    Full Changelog: 2.0.1...2.1.0

    Open source →
    Release notes

    Added

    • Add first-class guests (attendees) via Link::guest() and Link::guests(), with required and optional roles. Guests are rendered as add for Google, inv_list for Yahoo, to and cc for Outlook, and ATTENDEE properties in ICS (#229)
    • Support separate start and end timezones (useful for flights): new Link::$fromTimezone, Link::$toTimezone and Link::hasDistinctTimezones(). Google links use stz and etz, ICS files write each endpoint with its own TZID alongside a matching VTIMEZONE component, as RFC 5545 requires. Two spellings of one zone (UTC and Etc/UTC, or the bare Z that PHP leaves on a Z suffixed ISO string) count as a single zone, so only an event that genuinely crosses zones takes this path (#233, #246, #249)
    • ICS: support TRANSP, CLASS, RRULE and X-MICROSOFT-CDO-BUSYSTATUS via the options array (#230)
    • ICS: support a custom DTSTAMP via the options array. It defaults to the event start, which keeps the same Link producing the same bytes, so pass your own value when you need it read as a revision signal (#244)
    • Support repeated URL parameters in URL generators: an array value in $urlParameters is rendered as a repeated query parameter (#228)
    • ICS: subclasses can append extra properties through the new additionalCalendarProperties() and additionalEventProperties() extension points, emitted before the nested VALARM component so the output stays valid (#242, #247)

    Changed

    • Generators are easier to extend: Ics::generateAlertComponent(), Yahoo::sanitizeText(), Yahoo::sanitizeAddressList() and BaseOutlook::sanitizeString() are now protected, and Google/Yahoo read BASE_URL late statically so a subclass can redefine it
    • ICS: a recurring event in a timezone that moves its clocks is now written as a local time named by a TZID, with a matching VTIMEZONE, where it used to be written as a UTC instant. An RRULE repeats the local time of its DTSTART, so a start pinned to UTC put every occurrence past a daylight saving change an hour away from the time the event was booked for. A recurring event in UTC, in a zone that keeps one offset the year round, or in a zone that names no place is unaffected, and so is any event without an RRULE
    • ICS: REMINDER now has to be an array, its TIME a DateTimeInterface and its DESCRIPTION a string. A value of another type used to be dropped for the default alarm without a word, which handed back a reminder nobody asked for
    • ICS: the presentation format now has to be html or file. Anything else used to fall through to the data URI, so ['format' => 'FILE'] silently returned a link rather than the file it asked for
    • URL generators render a false parameter value as a bare flag (&key) rather than as an empty assignment (&key=)

    Fixed

    • Link::guest() and Link::guests() now ignore an address that is already on the guest list, compared without regard to case, so nobody is invited twice
    • ICS: an ATTENDEE address can no longer inject calendar properties. guest() rejects a control character, but Link::$guests is a public property that can be assigned around it, and a CR or an LF in an address ended the ATTENDEE property and started another. Control characters are now percent encoded into the mailto URI
    • ICS: URL and RRULE now reject every control character rather than only CR and LF. Neither a URI nor a recurrence rule admits one, and writing it out produced a file no parser accepts
    • ICS: the VTIMEZONE components now carry a yearly recurrence rule on the last change of each kind, whenever the zone repeats that change on a fixed weekday of a fixed month. Without one the component stopped at the year it described, and a recurring event past that resolved against the wrong offset for part of every later year on a client that does not resolve bare IANA names
    • A description or address of '0' is no longer dropped from the output as if it were empty
    • The exception for a negative date range no longer claims TO must be greater than FROM, since equal instants are accepted, and it now prints each time with its timezone
    • All-day events whose start and end dates carry different timezones no longer gain or lose a day. The end date is now read as the calendar date it was written as, instead of being converted as an instant
    • Timezones that name no place (an offset such as +02:00, an abbreviation such as CEST, the POSIX rule sets, and the whole Etc/ tree) are written in UTC instead of being named. Google silently ignores a ctz it cannot resolve, which left the event at the wrong instant for every viewer outside that offset, and an offset in an ICS TZID cut the property value in half at its colon. Parsing an ISO 8601 string with an offset is the everyday way to reach this (#248)
    • ICS: values passed through the options array can no longer inject calendar properties. UID and PRODID are escaped as the TEXT values they are, URL and RRULE reject a carriage return or line feed, the enumerated properties are checked against their allowed tokens, and a custom REMINDER.DESCRIPTION is escaped like the default one (#245)
    • ICS: close several RFC 5545 gaps. Content lines longer than 75 octets are folded (without splitting a multibyte character), the file ends with a CRLF, the data URI declares charset=utf-8 rather than the unregistered utf8, and control characters invalid in a TEXT value are dropped (#250)
    • Yahoo: align deep link parameters with the service parser. The base URL drops the stale view=d&type=20 pair, timed events send wall clock ST and ET values instead of UTC, single day all-day events send DUR=allday, and multi day all-day events send an exclusive ET end date (#232)
    • ICS: correct attendee escaping (a CAL-ADDRESS is a URI, not TEXT) and reject guest email addresses that no calendar service can carry (#229)

    Docs

    • Rework the README examples and fix the stale Google output (#231)
    • Document that Yahoo composes a multi day all-day event as a timed one running from midnight to midnight, since DUR=allday and an ET end date cannot be sent together (#243)

    Notes for subclasses

    The public API is unchanged, but two of the changes above are visible to a class extending a generator, and either can stop it loading:

    • Ics::$presentationOptions is now declared array, where 2.0.x left it untyped. A subclass that redeclares the property without a type raises Type of Sub::$presentationOptions must be array. Drop the redeclaration, or type it array to match
    • Ics::generateAlertComponent(), Yahoo::sanitizeText(), Yahoo::sanitizeAddressList() and BaseOutlook::sanitizeString() went from private to protected. A subclass that happens to declare a private method of the same name raises Access level to Sub::sanitizeText() must be protected or weaker. Widen it to protected, which is what it now overrides
    • Yahoo::BASE_URL no longer carries the stale view=d&type=20 pair. A subclass that built its own URL from the constant gets the shorter value
    Open source →
  2. 2.0.1 29 May 2026
    Release notes

    Fixed

    • Update the webOffice() link domain from outlook.office.com to outlook.cloud.microsoft. The old domain returned AADSTS90015: Requested query string is too long (#216, fixes #215)
    Open source →
  3. 1.11.1 16 Jan 2025
    Release notes

    What's changed:

    Full Changelog: 1.11.0...1.11.1

    Open source →
  4. 1.11.0 19 Feb 2024
    Release notes

    What's Changed

    • Allow URL customisations for Google, Outlook and Yahoo by @alies-dev in #191
    • Fix attempt for Google and "All day". by @vever001 in #192

    New Contributors

    Full Changelog: 1.10.0...1.11.0

    Open source →
  5. 1.10.0 26 Jan 2024
    Release notes

    What's Changed

    New Contributors

    Full Changelog: 1.9.0...1.10.0

    Open source →
  6. 1.9.0 26 Jan 2024
    Release notes

    What's Changed

    • ICS: support different presentation (file, html) by @alies-dev in #185
    • Update dependencies; bump min PHP version to 8.1 by @alies-dev in #187

    Full Changelog: 1.8.6...1.9.0

    Open source →
  7. 1.8.6 26 Jan 2024
    Release notes

    What's Changed

    Fixes:

    New Contributors

    Full Changelog: 1.8.5...1.8.6

    Open source →
  8. 1.8.5 02 Mar 2023

    Nothing published for this version

  9. 1.8.4 01 Mar 2023

    Nothing published for this version

  10. 1.8.3 01 Mar 2023

    Nothing published for this version

  11. 1.8.2 11 Dec 2022
    Release notes

    Changed

    • ICS: Use DESCRIPTION instead of X-ALT-DESC (as it has better support) by @cdubz in #158
    • Chore: fix tests, fix and improve CI
    Open source →
  12. 1.8.1 01 Dec 2022
    Release notes

    Changed

    • Remove PHP 7.4 support
    • Update dependencies
    Open source →
  13. 1.8.0 20 Aug 2022
    Release notes

    Changed

    • ICS: Add PRODID and DTSTAMP required parameters to make ICS valid by @makbeta
    • ICS: Fix HTML description for Outlook 2016 by @karthikbodu
    • Outlook: extract common logic for WebOffice and WebOutlook into a parent class by @lptn

    Fixed

    • Simplify format of test snapshots: do not use base64 by @lptn
    • Fix typo in README by @fabpot
    Open source →
  14. 1.7.2 09 Jun 2022
    Release notes

    Fixed

    • Outlook: Fixed #148 Support HTML-formatted description by @dravenk in #150
    Open source →
  15. 1.7.1 13 Feb 2022
    Release notes

    Changed

    • Outlook: Fixed location field characters (by @dravenk in #144)
    • Add missing dependency of php-cs-fixer and update it
    Open source →
  16. 1.7.0 13 Feb 2022
    Release notes

    Changed

    • New: Add support for outlook.office.com $link->webOffice(); (@dravenk and @gulios)
    • Google: Add timezone name if it is specified in both from and to dates and is the same for both (@bradyemerson)
    Open source →
  17. 1.6.0 22 Apr 2021
    Release notes

    Changed

    • Drop support for PHP 7.2 and PHP 7.3
    Open source →
  18. 1.5.0 22 Apr 2021
    Release notes

    Changed

    • ICS: support URLs as option (@gulios)
    • ICS: support all day events spanning multiple days (@mrshowerman)
    Open source →
  19. 1.4.4 13 Apr 2021
    Release notes

    Fixed

    • Yahoo link doesn’t work (yahoo changed param names) (@mukeshsah08).
    • Exception message on invalid dates range (idea by @jason-nabooki)
    Open source →
  20. 1.4.3 05 Mar 2021
    Release notes

    Changed

    • Google: use UTC timezone to bypass problems with some timezone names unsupported by Google calendar (⚠️ backwards-incompatible if you extended Google Generator)

    Fixed

    • Spaces replaced by "+" on Outlook.com #109
    Open source →
  21. 1.4.2 01 Sep 2020
    Release notes

    Changed

    • Simplify extending of ICS Generator
    Open source →
  22. 1.4.1 27 Aug 2020
    Release notes

    Changed

    • Simplify extending of WebOutlook (e.g. for Office365)
    • Yahoo: use allday parameter only for a single-day events
    • Improve exception hierarchy: InvalidLink now extends \InvalidArgumentException

    Added

    • Add more tests, reorganize existing
    Open source →
  23. 1.4.0 02 May 2020
    Release notes

    Added

    • Allow specifying custom UID ICS links (https://github.com/spatie/calendar-links/pull/85)
    • Support PHP 8.0
    • Support immutable dates (\DateTimeImmutable::class)

    Changed

    • Require PHP 7.2+
    Open source →
  24. 1.3.0 29 Apr 2020
    Release notes
    • Support custom generators ($link->formatWith(new Your\Generator())
    • Fix iCal links that contains special chars (use base64 for encoding)
    • Fix Outlook links: use new base URI and datetime formats
    • Fix Yahoo links: events had invalid end datetime (due to a bug on Yahoo side)
    Open source →
  25. 1.2.4 17 Jul 2019
    Release notes
    • Fix Google links for all-day events (use next day as end-date for single-day events)
    • Fix Outlook links for all-day events (omit enddt for single-day events)
    • Add a new Link::createAllDay static constructor to simplify creating of all-day events
    Open source →
  26. 1.2.3 14 Feb 2019
    Release notes
    • Fix iCal all day links (use DURATION according RFC 5545)
    Open source →
  27. 1.2.2 15 Jan 2019
    Release notes
    • Fix Yahoo links for multiple days events
    Open source →
  28. 1.2.1 13 Jan 2019
    Release notes
    • Fix iCal: Use CRLF instead of LF (according RFC 5545)
    • Fix iCal: Specify UID property (according RFC 5545)
    • Fix iCal: Escape ; character (according RFC 5545)
    • Fix iCal: Remove empty new line from .ics files
    Open source →
  29. 1.2.0 10 Jan 2019
    Release notes
    • Support timezones
    • Add outlook.com link generator
    Open source →
  30. 1.1.1 08 Oct 2018
    Release notes
    • Fix Yahoo links
    Open source →
  31. 1.1.0 13 Aug 2018
    Release notes
    • Add all day support
    Open source →
  32. 1.0.3 23 Jul 2018
    Release notes
    • Fix newlines in description
    Open source →
  33. 1.0.2 15 May 2018
    Release notes
    • Fix for iCal links in Safari
    Open source →
  34. 1.0.1 30 Apr 2018
    Release notes
    • Use \n instead of %0A when generating an ics file
    Open source →
  35. 1.0.0 28 Sep 2017
    Release notes
    • initial release
    Open source →
  36. 0.1.1 28 Sep 2017

    Nothing published for this version

  37. 0.1.0 28 Sep 2017

    Nothing published for this version

  38. 0.0.1 17 Sep 2017

    Nothing published for this version

  39. 2.0-beta.1 10 Feb 2026
    Release notes

    What's Changed

    Internal Updates

    New Contributors

    Full Changelog: 2.0-alpha.1...2.0-beta.1

    Open source →
  40. 2.0-alpha.1 19 Feb 2024
    Release notes

    What's Changed

    BC breaks

    API changes:

    • Link::create(...) doesn't have a 4th parameter to declare event as a full-day. Instead, use Link::createAllDay(...)

    If you extended classes from the package or used non-private members directly, these BC changes may be breaking for you:

    • REMOVED: Method Spatie\CalendarLinks\Link#__get() was removed
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$title changed from having no type to string
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$from changed from having no type to DateTimeImmutable
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$to changed from having no type to DateTimeImmutable
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$description changed from having no type to string
    • CHANGED: Property Spatie\CalendarLinks\Link#$description changed default value from NULL to ''
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$allDay changed from having no type to bool
    • CHANGED: Type of property Spatie\CalendarLinks\Link#$address changed from having no type to string
    • CHANGED: Property Spatie\CalendarLinks\Link#$address changed default value from NULL to ''
    • CHANGED: Method __construct() of class Spatie\CalendarLinks\Link became final
    • CHANGED: The return type of Spatie\CalendarLinks\Link::create() changed from no type to static
    • CHANGED: The return type of Spatie\CalendarLinks\Link::createAllDay() changed from self to the non-covariant static
    • CHANGED: The return type of Spatie\CalendarLinks\Link::createAllDay() changed from self to static
    • CHANGED: Parameter 1 of Spatie\CalendarLinks\Link::createAllDay() changed name from fromDate to from
    • CHANGED: The return type of Spatie\CalendarLinks\Link#description() changed from no type to static
    • CHANGED: The return type of Spatie\CalendarLinks\Link#address() changed from no type to static
    • REMOVED: Method Spatie\CalendarLinks\Exceptions\InvalidLink::invalidDateRange() was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\Yahoo#$dateFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\Yahoo#$dateTimeFormat was removed
    • CHANGED: Class Spatie\CalendarLinks\Generators\WebOffice became final
    • REMOVED: Constant Spatie\CalendarLinks\Generators\WebOffice::BASE_URL was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateTimeFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$urlParameters was removed
    • CHANGED: Constant Spatie\CalendarLinks\Generators\WebOffice::BASE_URL visibility reduced from protected to private
    • CHANGED: Method baseUrl() of class Spatie\CalendarLinks\Generators\WebOffice visibility reduced from public to protected
    • CHANGED: Type of property Spatie\CalendarLinks\Generators\Ics#$dateFormat changed from having no type to string
    • CHANGED: Type of property Spatie\CalendarLinks\Generators\Ics#$dateTimeFormat changed from having no type to string
    • CHANGED: Type of property Spatie\CalendarLinks\Generators\Ics#$options changed from having no type to array
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateTimeFormat was removed
    • CHANGED: Method baseUrl() of class Spatie\CalendarLinks\Generators\BaseOutlook visibility reduced from public to protected
    • CHANGED: Class Spatie\CalendarLinks\Generators\WebOutlook became final
    • REMOVED: Constant Spatie\CalendarLinks\Generators\WebOutlook::BASE_URL was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$dateTimeFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\BaseOutlook#$urlParameters was removed
    • CHANGED: Constant Spatie\CalendarLinks\Generators\WebOutlook::BASE_URL visibility reduced from protected to private
    • CHANGED: Method baseUrl() of class Spatie\CalendarLinks\Generators\WebOutlook visibility reduced from public to protected
    • REMOVED: Property Spatie\CalendarLinks\Generators\Google#$dateFormat was removed
    • REMOVED: Property Spatie\CalendarLinks\Generators\Google#$dateTimeFormat was removed

    Full Changelog: 1.11.0...2.0-alpha.1

    Open source →
  41. 2.0 10 Feb 2026
    Release notes

    Release Notes: v2.0

    See upgrade guide.

    TL;DR: for 95% installations, no actions required. The biggest change: Link::create() is not longer has 3rd parameter allDay: you need to use Link::createAllDay() instead.

    Breaking Changes

    • PHP 8.3+ required — minimum PHP version bumped from 8.1 to 8.3
    • $from and $to are now DateTimeImmutable — previously DateTime
    • Link properties are now public readonly — $title, $from, $to, and $allDay are public readonly; $description and $address are public. The magic __get() method has been removed.
    • Link::create() no longer accepts $allDay parameter — use Link::createAllDay() for all-day events instead
    • Link::createAllDay() end date semantics changed — the $numberOfDays parameter now represents inclusive days (e.g., createAllDay('Event', $date, 1) creates a single-day event). The constructor now internally adds +1
      day to convert to the exclusive end date expected by calendar services.
    • Link constructor is now final — subclassing the constructor is no longer possible; use static factory methods
    • InvalidLink::invalidDateRange() removed — the deprecated method has been deleted; use InvalidLink::negativeDateRange() instead
    • BaseOutlook::baseUrl() visibility changed — from public to protected
    • WebOffice and WebOutlook classes are now final
    • Generator constants are now typed — e.g., const string FORMAT_HTML = 'html' (PHP 8.3 typed constants)
    • Instance properties replaced with class constants — $dateFormat, $dateTimeFormat in Google, Yahoo, and BaseOutlook are now private const values, preventing override via subclassing

    New Features

    • Custom ICS PRODID — the Ics generator now accepts a PRODID option to customize the product identifier in generated ICS content
    • Link::createAllDay() returns static — previously returned self, now supports late static binding for subclasses

    Bug Fixes

    • All-day events off by one day — fixed all-day events created via the constructor being 1 day short
    • ICS DTSTAMP RFC 5545 compliance — DTSTAMP now always uses UTC datetime for all-day events (previously used date-only format)
    • ICS DTSTART for all-day events — added VALUE=DATE parameter to DTSTART for all-day events per RFC 5545
    • ICS escape string RFC 5545 compliance — escapeString() now properly handles backslashes, semicolons, commas, and newlines per RFC 5545 section 3.3.11 (previously used addcslashes which was non-compliant)
    • ICS VALARM trigger fix — fixed reminder/alert not working when a specific date was set as the trigger time

    Internal / Code Quality

    • declare(strict_types=1) added to all source files
    • Modern PHP 8.3+ features: typed class constants, #[\Override] attributes
    • DateTimeImmutable used throughout instead of mutable DateTime
    • PHPUnit #[Test] attributes replace @test annotations
    • PSR-12 coding style applied
    • Psalm upgraded to v6.15 with improved type annotations (@api, non-empty-string, non-empty-list)
    • Removed unnecessary clone calls thanks to immutable date objects

    Made by @alies-dev and the community.

    Full Changelog: 1.11.1...2.0

    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive