spatie/icalendar-generator

以iCalendar格式构建日历

2.8.1 2024-06-17 11:23 UTC

README

Latest Version on Packagist Tests Check & fix styling Psalm Total Downloads

想要创建在线日历,以便在iPhone日历应用或Google日历中显示吗?这可以通过生成iCalendar格式(RFC 5545)的日历来完成,这是一种可以被不同应用程序加载的文本格式。

此类日历的格式在RFC 5545中定义,阅读体验并不愉快。此包实现了RFC 5545以及来自RFC 7986的一些扩展,为您提供创建日历的易于使用的API。我们的意图不是完全实现这些RFC,而是提供一个易于使用的API。

以下是如何使用它的示例

use Spatie\IcalendarGenerator\Components\Calendar;
use Spatie\IcalendarGenerator\Components\Event;

Calendar::create('Laracon online')
    ->event(Event::create('Creating calender feeds')
        ->startsAt(new DateTime('6 March 2019 15:00'))
        ->endsAt(new DateTime('6 March 2019 16:00'))
    )
    ->get();

上述代码将生成以下字符串

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:Laracon online
X-WR-CALNAME:Laracon online
BEGIN:VEVENT
UID:5ef5c3f64cb2c
DTSTAMP;TZID=UTC:20200626T094630
SUMMARY:Creating calendar feeds
DTSTART:20190306T150000Z
DTEND:20190306T160000Z
DTSTAMP:20190419T135034Z
END:VEVENT
END:VCALENDAR

支持我们

我们投入了大量资源来创建最优质的开放源代码包。您可以通过购买我们的付费产品之一来支持我们。

我们非常感谢您从您的家乡寄给我们明信片,注明您正在使用我们哪个包。您可以在我们的联系页面上找到我们的地址。我们将所有收到的明信片发布在我们的虚拟明信片墙上。

安装

您可以通过composer安装此包

composer require spatie/icalendar-generator

升级

包的v1和v2版本之间有一些重大变化。有关更多信息,请参阅升级指南

用法

以下是如何创建一个日历的示例

$calendar = Calendar::create();

您可以给日历命名

$calendar = Calendar::create('Laracon Online');

可以给日历添加一个描述

$calendar = Calendar::create()
    ->name('Laracon Online')
    ->description('Experience Laracon all around the world');

最后,您希望将日历转换为文本,以便它可以流式传输或下载给用户。以下是这样做的方法

Calendar::create('Laracon Online')->get(); // BEGIN:VCALENDAR ...

在将日历流式传输到应用程序时,您可以通过分钟数设置日历的刷新间隔。设置后,日历应用程序将在指定的时间间隔后检查您的服务器以查找日历的更改

Calendar::create('Laracon Online')
    ->refreshInterval(5)
    ...

事件

可以按以下方式创建事件。名称不是必需的,但必须始终提供开始日期

Event::create('Laracon Online')
    ->startsAt(new DateTime('6 march 2019'));

您可以在事件上设置以下属性

Event::create()
    ->name('Laracon Online')
    ->description('Experience Laracon all around the world')
    ->uniqueIdentifier('A unique identifier can be set here')
    ->createdAt(new DateTime('6 march 2019'))
    ->startsAt(new DateTime('6 march 2019 15:00'))
    ->endsAt(new DateTime('6 march 2019 16:00'));

想快速创建一个带有开始和结束日期的事件吗?

Event::create('Laracon Online')
    ->period(new DateTime('6 march 2019'), new DateTime('7 march 2019'));

您可以为事件添加一个位置,如下所示

Event::create()
    ->address('Kruikstraat 22, 2018 Antwerp, Belgium')
    ->addressName('Spatie HQ')
    ->coordinates(51.2343, 4.4287)
    ...

您可以为事件设置组织者,电子邮件地址是必需的,但名称可以省略

Event::create()
    ->organizer('ruben@spatie.be', 'Ruben')
    ...

可以按如下方式添加事件的参与者

Event::create()
    ->attendee('ruben@spatie.be') // only an email address is required
    ->attendee('brent@spatie.be', 'Brent')
    ...

您也可以设置参与者的参与状态

Event::create()
    ->attendee('ruben@spatie.be', 'Ruben', ParticipationStatus::accepted())
    ...

有五种参与状态

  • ParticipationStatus::accepted()
  • ParticipationStatus::declined()
  • ParticipationStatus::tentative()
  • ParticipationStatus::needs_action()
  • ParticipationStatus::delegated()

您可以指示参与者必须对事件进行RSVP

Event::create()
    ->attendee('ruben@spatie.be', 'Ruben', ParticipationStatus::needs_action(), requiresResponse: true)
    ...

事件可以设置为透明,这样它就不会与日历中其他事件在视觉上重叠

Event::create()
    ->transparent()
    ...

可以创建跨越整个一天的事件

Event::create()
    ->fullDay()
    ...

事件的状\u600e\u53ef\u4ee5\u8bbe\u7f6e

Event::create()
    ->status(EventStatus::cancelled())
    ...

存在三种事件状\u600e

  • EventStatus::confirmed()
  • EventStatus::cancelled()
  • EventStatus::tentative()

事件可以分\u7c7b(\\u516c\u5f00、\\u7a7b\u9650、\\u4e3b\u9762)

Event::create()
    ->classification(Classification::private())
    ...

您可以添加\u4e00\u4e2aurl\u4e0a\u4ef6\u5982\u6b64

Event::create()
    ->attachment('https://spatie.be/logo.svg')
    ->attachment('https://spatie.be/feed.xml', 'application/json')
    ...

您可以添加\u4e00\u4e2a嵌入式\u4e0a\u4ef6(\u57fa\u786c64)如\u6b64

Event::create()
    ->embeddedAttachment($file->toString())
    ->embeddedAttachment($fileString, 'application/json')
    ->embeddedAttachment($base64String, 'application/json', needsEncoding: false)
    ...

您可以添加\u4e00\u4e2a\u56fe\u7247如\u6b64

Event::create()
    ->image('https://spatie.be/logo.svg')
    ->image('https://spatie.be/logo.svg', 'text/svg+xml')
    ->image('https://spatie.be/logo.svg', 'text/svg+xml', Display::badge())
    ...

存在四种不同的图\u7247\u663e\u793a\u7c7b

  • Display::badge()
  • Display::graphic()
  • Display::fullsize()
  • Display::thumbnail()

在创建事件后,应将其添加到日历中。有多种方法可以做到这一点

// As a single event parameter
$event = Event::create('Creating calendar feeds');

Calendar::create('Laracon Online')
    ->event($event)
    ...

// As an array of events
Calendar::create('Laracon Online')
    ->event([
        Event::create('Creating calender feeds'),
        Event::create('Creating contact lists'),
    ])
    ...

// As a closure
Calendar::create('Laracon Online')
    ->event(function(Event $event){
        $event->name('Creating calender feeds');
    })
    ...

使用Carbon

您可以使用流行的Carbon库

use Carbon\Carbon;

Event::create('Laracon Online')
    ->startsAt(Carbon::now())
    ...

时区

事件将使用您提供的DateTime对象中定义的时区。PHP始终在DateTime对象中设置这些时区。默认情况下,这将是UTC时区,但您可以更改此设置。

提醒一下:不要在DateTime对象上使用PHP的setTimezone函数,它会根据时区更改时间!更好的做法是创建一个新的包含时区的DateTime对象,如\u6b64

new DateTime('6 march 2019 15:00', new DateTimeZone('Europe/Brussels'))

对于省略时区,可以提出一\u4e2a观点。例如,当您想在中午显示一个事件时。我们定义中午为12点,但这个时间是相对的。对于比利时、澳大利亚或世界上任何其他国家的所有人来说,这都不一样。

这就是为什么您可以在事件上禁用时区

$starts = new DateTime('6 march 2019 12:00')

Event::create()
    ->startsAt($starts)
    ->withoutTimezone()
    ...

您甚至可以禁用整个日历的时区

Calendar::create()
   ->withoutTimezone()
    ...

每个日历都应该有描述日历中使用时区的时区组件。虽然不是所有日历客户端都要求这样做,但建议添加这些组件。

创建这样的时区组件相当复杂。这就是为什么这个包将自动为您添加它们,而无需配置。

您可以如\u6b64禁用此行为

Calendar::create()
   ->withoutAutoTimezoneComponents()
    ...

如果您愿意,您可以手动将时区添加到日历中,如\u6b64

$timezoneEntry = TimezoneEntry::create(
    TimezoneEntryType::daylight(),
    new DateTime('23 march 2020'),
    '+00:00',
    '+02:00'
);

$timezone = Timezone::create('Europe/Brussels')
    ->entry($timezoneEntry)
    ...

Calendar::create()
    ->timezone($timezone)
    ...

稍后会更详细地介绍这些时区。

警报

警报允许日历客户端发送有关特定事件的提醒。例如,iPhone上的Apple Mail将向用户发送有关事件的推送通知。警报始终属于事件,具有描述和将在事件发生前多少分钟触发的时间数

Event::create('Laracon Online')
    ->alertMinutesBefore(5, 'Laracon online is going to start in five minutes');

您也可以在事件后触发警报

Event::create('Laracon Online')
    ->alertMinutesAfter(5, 'Laracon online has ended, see you next year!');

或者在一个特定的日期上触发警报

Event::create('Laracon Online')
    ->alertAt(
       new DateTime('05/16/2020 12:00:00'),
       'Laracon online has ended, see you next year!'
    );

在日历或事件上移除时区也会移除警报上的时区。

重复事件

事件可以重复,例如您的每月公司晚宴。可以这样做

Event::create('Laracon Online')
    ->repeatOn(new DateTime('05/16/2020 12:00:00'));

您也可以在一系列日期上重复事件

Event::create('Laracon Online')
    ->repeatOn([new DateTime('05/16/2020 12:00:00'), new DateTime('08/13/2020 15:00:00')]);

重复规则

重复规则或简称RRule,它使您能够在日历中通过描述在RRule中何时重复来添加重复事件。首先,我们必须创建一个RRule

$rrule = RRule::frequency(RecurrenceFrequency::daily());

此规则描述了一个将每天重复的事件。您还可以将频率设置为secondlyminutelyhourlyweeklymonthlyyearly

可以将RRule添加到事件中,如\u6b64

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::monthly()));

可以微调RRule以满足您的个人喜好;让我们看看!

RRule可以从一个特定的时间点开始

$rrule = RRule::frequency(RecurrenceFrequency::daily())->starting(new DateTime('now'));

并停止在另一个特定的时间点

$rrule = RRule::frequency(RecurrenceFrequency::daily())->until(new DateTime('now'));

它可以只重复几次,例如10次

$rrule = RRule::frequency(RecurrenceFrequency::daily())->times(10);

重复的间隔可以改变

$rrule = RRule::frequency(RecurrenceFrequency::daily())->interval(2);

例如,如果这个事件在星期一开始,那么这个事件的下一次重复将不会在星期二发生,而是在星期三。您可以为所有频率做同样的事情。

事件也可以在特定的星期几重复

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::friday()
);

或者在一个月中的特定星期几

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::friday(), 3
);

或者在一个月的最后工作日

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::sunday(), -1
);

您可以在一个月的特定一天重复

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonthDay(16);

甚至可以给出一个月中的日期数组

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonthDay(
   [5, 10, 15, 20]
);

重复可以在某些月份进行(例如仅在第二季度)

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonth(
   [RecurrenceMonth::april(), RecurrenceMonth::may(), RecurrenceMonth::june()]
);

或者仅在一个月中

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonth(
   RecurrenceMonth::october()
);

可以设置一周开始的那天

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->weekStartsOn(
   ReccurenceDay::monday()
);

您可以提供一个特定日期,事件将不会重复

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()))
    ->doNotRepeatOn(new DateTime('05/16/2020 12:00:00'));

也可以给出一个日期数组,事件将不会重复

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()))
    ->doNotRepeatOn([new DateTime('05/16/2020 12:00:00'), new DateTime('08/13/2020 15:00:00')]);

或者您可以以字符串的形式添加RRules

Event::create('SymfonyCon')
    ->rruleAsString('FREQ=DAILY;INTERVAL=1');

如果您以字符串形式添加RRules,则包中未知DTSTART和UNTIL中包含的时间区域,因为字符串永远不会被解析和评估。如果它们是已知的,您可以分别添加DTSTART和UNTIL以帮助包发现时间区域

Event::create('SymfonyCon')
    ->rruleAsString(
        'DTSTART=20231207T090000Z;FREQ=DAILY;INTERVAL=1;UNTIL=20231208T090000Z',
        new DateTime('7 december 2023 09:00:00', new DateTimeZone('UTC')),
        new DateTime('8 december 2023 09:00:00', new DateTimeZone('UTC'))
    );

与Laravel一起使用

您可以使用Laravel响应将数据流式传输到日历应用程序

$calendar = Calendar::create('Laracon Online');

return response($calendar->get())
    ->header('Content-Type', 'text/calendar; charset=utf-8');

如果您想为用户提供下载日历并将其导入日历应用程序的功能

$calendar = Calendar::create('Laracon Online');

return response($calendar->get(), 200, [
   'Content-Type' => 'text/calendar; charset=utf-8',
   'Content-Disposition' => 'attachment; filename="my-awesome-calendar.ics"',
]);

构建时区

如果您想自己构建时区组件,您就来到了正确的位置,尽管我们建议您首先阅读RFC中关于时区的部分。

您可以创建一个时区,如下所示

$timezone = Timezone::create('Europe/Brussels');

可以提供最后修改日期

$timezone = Timezone::create('Europe/Brussels')
    ->lastModified(new DateTime('16 may 2020 12:00:00'));

或者添加一个包含时区更多信息的数据的URL

$timezone = Timezone::create('Europe/Brussels')
    ->url('https://spatie.be');

一个时区由多个条目组成,其中时区相对于UTC的变化时间,此类条目可以用于标准或夏令时

$entry = TimezoneEntry::create(
    TimezoneEntryType::standard(),
    new DateTime('16 may 2020 12:00:00'),
    '+00:00',
    '+02:00'
);

首先提供条目类型(standarddaylight)。然后是一个DateTime,表示时间更改。最后,更改之前和更改之后相对于UTC的偏移量。

还可以给此条目一个名称和描述

$entry = TimezoneEntry::create(...)
    ->name('Europe - Brussels')
    ->description('Belgian timezones ftw!');

可以给此条目一个RRule,如下所示

$entry = TimezoneEntry::create(...)
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()));

最后,可以将条目添加到时区中

$timezone = Timezone::create('Europe/Brussels')
   ->entry($timezoneEntry);

甚至可以添加多个条目

$timezone = Timezone::create('Europe/Brussels')
   ->entry([$timezoneEntryOne, $timezoneEntryTwo]);

现在我们已经构建了我们的时区,是时候(👀)将其添加到我们的日历中了

$calendar = Calendar::create('Calendar with timezones')
   ->timezone($timezone);

还可以添加多个时区

$calendar = Calendar::create('Calendar with timezones')
   ->timezone([$timezoneOne, $timezoneTwo]);

扩展包

我们尽量使此包尽可能简单。这就是为什么包中不包括RFC的许多属性和子组件。我们已经使您能够向每个组件添加其他属性或子组件,如果您需要包中未包含的功能。但请注意!从现在起,您需要自己正确实现RFC。

附加属性

您可以这样向组件添加新属性

Calendar::create()
    ->appendProperty(
        TextProperty::create('ORGANIZER', 'ruben@spatie.be')
    )
    ...

这里我们添加了TextProperty,这是一个默认的键值属性类型,其值为文本。您也可以使用包中包含的默认属性之一,或者通过扩展Property类来创建自己的属性。

有时属性可以有一些额外的参数,这些是键值条目,可以添加到属性中,如下所示

$property = TextProperty::create('ORGANIZER', 'ruben@spatie.be')
    ->addParameter(Parameter::create('CN', 'RUBEN VAN ASSCHE'));

Calendar::create()
    ->appendProperty($property)
    ...

附加子组件

可以以这种方式附加子组件

Calendar::create()
    ->appendSubComponent(
        Event::create('Extending icalendar-generator')
    )
    ...

您可以通过扩展Component类来创建自己的子组件。

测试

composer test

替代方案

我们努力使API简单易用。还需要更多吗?那么请查看Markus Poerschke的此

变更日志

请参阅变更日志以获取有关最近更改的更多信息。

贡献

请参阅CONTRIBUTING以获取详细信息。

安全

如果您发现了关于安全的错误,请发送邮件至security@spatie.be而不是使用问题跟踪器。

Postcardware

您可以使用此软件包,但如果它进入您的生产环境,我们非常希望您能从您的家乡寄给我们一张明信片,注明您正在使用我们的哪个软件包。

我们的地址是:Spatie,Kruikstraat 22,box 12,2018 安特卫普,比利时。

我们将收到的所有明信片发布在我们的公司网站上

鸣谢

许可证

MIT许可证(MIT)。请参阅许可证文件以获取更多信息。