Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Oct 29, 2020
1 parent ca5a8f4 commit a65dc5c
Show file tree
Hide file tree
Showing 28 changed files with 891 additions and 19 deletions.
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ Calendar::create()
....
```

It is also possible to define your own timezone components here: **INSERT LINK**

#### Alerts

Alerts allow calendar clients to send reminders about specific events. For example, Apple Mail on an iPhone will send users a notification about the event. An alert always belongs to an event and has a description and the number of minutes before the event it will be triggered:
Expand Down Expand Up @@ -416,6 +418,92 @@ $rrule = RRule::frequeny(RecurrenceFrequency::monthly())->weekStartsOn(
);
```

### Timezones

When using datetimes with timezones the package will output a timezone identifier so the calendar client can calculate the correct time for an event. Such identifier will look like this `Europe\Brussels` for a Belgian timezone or `UTC` for the UTC timezone.

Most of the calendar clients know these identifiers and can find the correct timezones but some don't, that's why you can explicitly define the timezones within your calendar.

This is a quite complicated feature, that's why when you add a datetime with a timezone to the package we'll generate these blocks for you automatically and you shouldn't think about these issues with timezones again!

If you still want to create your own timezones then you're in the right place, although I advise you to read the [section](https://tools.ietf.org/html/rfc5545#section-3.6.5) on timzones from the RFC first.

You can create a timezone as such:

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

It is possible to provide the last modified date:

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

Or add an url with more information about the timezone:

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

A timezone has multiple entries in time where the time of the timezone changed relative to UTC, such entry can be constructed for standard or daylight time:

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

Firstly you provide the type of entry (`standard` or `daylight`), next the point where the time changes. And then an offset relative to UTC from before the change and an offset relative to UTC during the change.

It is also possible to give this entry a name and description:

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

An RRule for the entry can be given as such:

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

In the end you can add an entry to a timezone:

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

Or even add multiple entries:

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

Now we've constructed our timezone it is time(👀) to add this timezone to our calendar:

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

And also here it is possible to add multiple timezones:

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


### Use with Laravel

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"require-dev": {
"larapack/dd": "^1.0",
"phpunit/phpunit": "^8.2",
"spatie/phpunit-snapshot-assertions": "^4.2",
"vimeo/psalm": "^3.12"
},
"autoload": {
Expand Down
11 changes: 8 additions & 3 deletions src/Builders/ComponentBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ class ComponentBuilder
{
private ComponentPayload $componentPayload;

public static function create(ComponentPayload $componentPayload): self
{
return new self($componentPayload);
}

public function __construct(ComponentPayload $componentPayload)
{
$this->componentPayload = $componentPayload;
Expand All @@ -26,15 +31,15 @@ public function build(): string

public function buildComponent(): array
{
$lines[] = "BEGIN:V{$this->componentPayload->getType()}";
$lines[] = "BEGIN:{$this->componentPayload->getType()}";

$lines = array_merge(
$lines,
$this->buildProperties(),
$this->buildSubComponents()
);

$lines[] = "END:V{$this->componentPayload->getType()}";
$lines[] = "END:{$this->componentPayload->getType()}";

return $lines;
}
Expand Down Expand Up @@ -79,7 +84,7 @@ private function chipLine(string $line): array
while (strlen($line) > 0) {
if (strlen($line) > 75) {
$chippedLines[] = mb_strcut($line, 0, 75, 'utf-8');
$line = ' '.mb_strcut($line, 75, strlen($line), 'utf-8');
$line = ' ' . mb_strcut($line, 75, strlen($line), 'utf-8');
} else {
$chippedLines[] = $line;

Expand Down
2 changes: 1 addition & 1 deletion src/Components/Alert.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function __construct(string $description = null)

public function getComponentType(): string
{
return 'ALARM';
return 'VALARM';
}

public function getRequiredProperties(): array
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function __construct(string $name = null)

public function getComponentType(): string
{
return 'CALENDAR';
return 'VCALENDAR';
}

public function getRequiredProperties(): array
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function __construct(string $name = null)

public function getComponentType(): string
{
return 'EVENT';
return 'VEVENT';
}

public function getRequiredProperties(): array
Expand Down
94 changes: 94 additions & 0 deletions src/Components/Timezone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Spatie\IcalendarGenerator\Components;

use Closure;
use DateTimeInterface;
use DateTimeZone;
use Spatie\IcalendarGenerator\ComponentPayload;
use Spatie\IcalendarGenerator\Properties\DateTimeProperty;
use Spatie\IcalendarGenerator\Properties\TextProperty;
use Spatie\IcalendarGenerator\ValueObjects\DateTimeValue;

class Timezone extends Component
{
private string $identifier;

private ?DateTimeValue $lastModified = null;

private ?string $url = null;

/** @var \Spatie\IcalendarGenerator\Components\TimezoneEntry[] */
private array $entries = [];

public static function create(string $identifier): self
{
return new self($identifier);
}

public function __construct(string $identifier)
{
$this->identifier = $identifier;
}

public function lastModified(DateTimeInterface $lastModified): self
{
$this->lastModified = DateTimeValue::create($lastModified)
->convertToTimezone(new DateTimeZone('UTC'));

return $this;
}

public function url(string $url): self
{
$this->url = $url;

return $this;
}

/**
* @param $entry \Spatie\IcalendarGenerator\Components\TimezoneEntry|array
*
* @return \Spatie\IcalendarGenerator\Components\Timezone
*/
public function entry($entry): Timezone
{
if (is_null($entry)) {
return $this;
}

$this->entries = array_merge(
$this->entries,
is_array($entry) ? $entry : [$entry]
);

return $this;
}

public function getComponentType(): string
{
return 'VTIMEZONE';
}

public function getRequiredProperties(): array
{
return [
'TZID',
];
}

protected function payload(): ComponentPayload
{
return ComponentPayload::create($this->getComponentType())
->property(TextProperty::create('TZID', $this->identifier))
->optional(
$this->url,
fn() => TextProperty::create('TZURL', $this->url)->withoutEscaping()
)
->optional(
$this->lastModified,
fn() => DateTimeProperty::create('LAST-MODIFIED', $this->lastModified)
)
->subComponent(...$this->entries);
}
}
117 changes: 117 additions & 0 deletions src/Components/TimezoneEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace Spatie\IcalendarGenerator\Components;

use DateTimeInterface;
use DateTimeZone;
use Spatie\IcalendarGenerator\ComponentPayload;
use Spatie\IcalendarGenerator\Enums\TimezoneEntryType;
use Spatie\IcalendarGenerator\Properties\DateTimeProperty;
use Spatie\IcalendarGenerator\Properties\RRuleProperty;
use Spatie\IcalendarGenerator\Properties\TextProperty;
use Spatie\IcalendarGenerator\ValueObjects\DateTimeValue;
use Spatie\IcalendarGenerator\ValueObjects\RRule;
use Spatie\IcalendarGenerator\ValueObjects\TimezoneTransition;

class TimezoneEntry extends Component
{
private TimezoneEntryType $type;

private DateTimeValue $starts;

private string $offsetFrom;

private string $offsetTo;

private ?RRule $rrule = null;

private ?string $name = null;

private ?string $description = null;

public static function create(
TimezoneEntryType $type,
DateTimeInterface $starts,
string $offsetFrom,
string $offsetTo
): self {
return new self($type, $starts, $offsetFrom, $offsetTo);
}

public static function createFromTransition(TimezoneTransition $transition): self
{
return new self(
$transition->type,
$transition->start,
$transition->offsetFrom->format('%r%H%M'),
$transition->offsetTo->format('%r%H%M')
);
}

public function __construct(
TimezoneEntryType $type,
DateTimeInterface $starts,
string $offsetFrom,
string $offsetTo
) {
$this->type = $type;
$this->starts = DateTimeValue::create($starts);
$this->offsetFrom = $offsetFrom;
$this->offsetTo = $offsetTo;
}

public function name(string $name): self
{
$this->name = $name;

return $this;
}

public function description(string $description)
{
$this->description = $description;

return $this;
}

public function rrule(RRule $rrule): self
{
$this->rrule = $rrule;

return $this;
}

public function getComponentType(): string
{
return $this->type->value;
}

public function getRequiredProperties(): array
{
return [
'DTSTART',
'TZOFFSETFROM',
'TZOFFSETTO',
];
}

protected function payload(): ComponentPayload
{
return ComponentPayload::create($this->getComponentType())
->property(DateTimeProperty::create('DTSTART', $this->starts, true))
->property(TextProperty::create('TZOFFSETFROM', $this->offsetFrom))
->property(TextProperty::create('TZOFFSETTO', $this->offsetTo))
->optional(
$this->name,
fn() => TextProperty::create('TZNAME', $this->name)
)
->optional(
$this->description,
fn() => TextProperty::create('COMMENT', $this->description)
)
->optional(
$this->rrule,
fn() => RRuleProperty::create('RRULE', $this->rrule)
);
}
}
Loading

0 comments on commit a65dc5c

Please sign in to comment.