├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.yml
├── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
├── dependabot.yml
├── release_template.md
└── workflows
│ └── coding-standards.yml
├── .gitignore
├── CONTRIBUTING.md
├── FUNDING.yml
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── ecs.php
├── examples
├── ICal.ics
└── index.php
├── phpstan.neon.dist
├── phpunit.xml
├── rector.php
├── src
└── ICal
│ ├── Event.php
│ └── ICal.php
└── tests
├── CleanCharacterTest.php
├── DynamicPropertiesTest.php
├── KeyValueTest.php
├── RecurrencesTest.php
├── Rfc5545RecurrenceTest.php
├── SingleEventsTest.php
└── ical
├── ical-monthly.ics
└── issue-196.ics
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://editorconfig.org/
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_size = 4
9 | indent_style = space
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: "Report something that's broken."
3 | labels: ["bug-normal"]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: "Before raising an issue, please check the issue has not already been fixed in `dev-master`. You can also search through our [closed issues](../issues?q=is%3Aissue+is%3Aclosed+)."
8 | - type: input
9 | id: php-version
10 | attributes:
11 | label: PHP Version
12 | description: Provide the PHP version that you are using.
13 | placeholder: 8.1.4
14 | validations:
15 | required: true
16 | - type: input
17 | id: php-date-timezone
18 | attributes:
19 | label: PHP date.timezone
20 | description: Provide the PHP date.timezone that you are using.
21 | placeholder: "[Country] / [City]"
22 | validations:
23 | required: true
24 | - type: input
25 | id: ics-parser-version
26 | attributes:
27 | label: ICS Parser Version
28 | description: Provide the `ics-parser` library version that you are using.
29 | placeholder: 3.2.1
30 | validations:
31 | required: true
32 | - type: input
33 | id: operating-system
34 | attributes:
35 | label: Operating System
36 | description: Provide the operating system that you are using.
37 | placeholder: "Windows / Mac / Linux"
38 | validations:
39 | required: true
40 | - type: textarea
41 | id: description
42 | attributes:
43 | label: Description
44 | description: Provide a detailed description of the issue that you are facing.
45 | validations:
46 | required: true
47 | - type: textarea
48 | id: steps-to-reproduce
49 | attributes:
50 | label: Steps to Reproduce
51 | description: Provide detailed steps to reproduce your issue. It is **essential** that you supply a copy of the iCal file that is causing the parser to behave incorrectly to allow us to investigate. Prior to uploading the iCal file, please remove any personal or identifying information.
52 | validations:
53 | required: true
54 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | > :information_source:
2 | > - File a bug on our [issue tracker](https://github.com/u01jmg3/ics-parser/issues) (if there isn't one already).
3 | > - If your patch is going to be large it might be a good idea to get the discussion started early. We are happy to discuss it in a new issue beforehand.
4 | > - Please follow the coding standards already adhered to in the file you're editing before committing
5 | > - This includes the use of *4 spaces* over tabs for indentation
6 | > - Trim all trailing whitespace
7 | > - Using single quotes (`'`) where possible
8 | > - Use `PHP_EOL` where possible or default to `\n`
9 | > - Using the [1TBS](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS_.28OTBS.29) indent style
10 | > - If a function is added or changed, please remember to update the [API documentation in the README](https://github.com/u01jmg3/ics-parser/blob/master/README.md#api)
11 | > - Please include unit tests to verify any new functionality
12 | > - Also check that existing tests still pass: `composer test`
13 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: composer
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | reviewers:
9 | - u01jmg3
10 | - package-ecosystem: "github-actions"
11 | directory: "/"
12 | schedule:
13 | interval: weekly
14 |
--------------------------------------------------------------------------------
/.github/release_template.md:
--------------------------------------------------------------------------------
1 | # Release Checklist
2 |
3 | - [ ] Update docblock in `src/ICal/ICal.php`
4 | - [ ] Ensure the documentation is up to date
5 | - [ ] Push the code changes to GitHub (`git push`)
6 | - [ ] Tag the release (`git tag v1.2.3`)
7 | - [ ] Push the tag (`git push --tag`)
8 | - [ ] Check [Packagist](https://packagist.org/packages/johngrogg/ics-parser) is updated
9 | - [ ] Notify anyone who opened [an issue or PR](https://github.com/u01jmg3/ics-parser/issues?q=is%3Aopen) of the fix
10 |
--------------------------------------------------------------------------------
/.github/workflows/coding-standards.yml:
--------------------------------------------------------------------------------
1 | name: Coding Standards
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | Scan:
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | php: [5.6, 7.4, '8.0', 8.1, 8.2, 8.3, 8.4]
20 |
21 | name: PHP ${{ matrix.php }}
22 |
23 | steps:
24 | - name: Checkout code
25 | uses: actions/checkout@v4
26 |
27 | - name: Setup PHP
28 | uses: shivammathur/setup-php@v2
29 | with:
30 | php-version: ${{ matrix.php }}
31 | tools: composer:2.2
32 | coverage: none
33 |
34 | - name: PHP 5.6 syntax check
35 | run: |
36 | find . -type f -iname "*.php" ! -name 'ecs.php' ! -name 'rector.php' ! -path 'tests/*' -print0 | xargs -0 -L 1 php -l | (! grep -v "No syntax errors detected")
37 | echo $?
38 | if: matrix.php == 5.6
39 |
40 | - name: Install dependencies for PHP 5.6
41 | run: composer update --quiet --no-scripts
42 | if: matrix.php == 5.6
43 |
44 | - name: PHP 7.4+ syntax check
45 | run: |
46 | find . -type f -iname "*.php" -print0 | xargs -0 -L 1 php -l | (! grep -v "No syntax errors detected")
47 | echo $?
48 | if: matrix.php >= 7.4
49 |
50 | - name: Install dependencies for PHP 7.4+
51 | run: composer install --quiet --no-scripts
52 | if: matrix.php >= 7.4
53 |
54 | - name: Execute tests
55 | run: vendor/bin/phpunit --verbose
56 |
57 | - name: Install additional dependencies
58 | run: |
59 | composer config allow-plugins.bamarni/composer-bin-plugin true --no-plugins
60 | composer require bamarni/composer-bin-plugin rector/rector squizlabs/php_codesniffer --dev --quiet --no-scripts
61 | composer bin easy-coding-standard config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
62 | composer bin easy-coding-standard require symplify/easy-coding-standard slevomat/coding-standard --dev --quiet --no-scripts
63 | if: matrix.php == 8.3
64 |
65 | - name: Execute PHPCodeSniffer
66 | run: vendor/bin/phpcs -n -s --standard=PSR12 src
67 | if: matrix.php == 8.3
68 |
69 | - name: Execute Rector
70 | run: vendor/bin/rector process src --dry-run
71 | if: matrix.php == 8.3
72 |
73 | - name: Execute ECS
74 | run: vendor/bin/ecs check src
75 | if: matrix.php == 8.3
76 |
77 | - name: Execute PHPStan
78 | run: vendor/bin/phpstan -v analyse src
79 | if: matrix.php == 8.3
80 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ###################
2 | # Compiled Source #
3 | ###################
4 | *.com
5 | *.class
6 | *.dll
7 | *.exe
8 | *.o
9 | *.so
10 |
11 | ############
12 | # Packages #
13 | ############
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | ######################
24 | # Logs and Databases #
25 | ######################
26 | *.log
27 | *.sqlite
28 |
29 | ######################
30 | # OS Generated Files #
31 | ######################
32 | .DS_Store
33 | .DS_Store?
34 | ._*
35 | .Spotlight-V100
36 | .Trashes
37 | .phpunit.result.cache
38 | ehthumbs.db
39 | Thumbs.db
40 | workbench
41 |
42 | ####################
43 | # Package Managers #
44 | ####################
45 | auth.json
46 | node_modules
47 | vendor
48 |
49 | ##########
50 | # Custom #
51 | ##########
52 | *.git
53 | *-report.*
54 |
55 | ########
56 | # IDEs #
57 | ########
58 | .idea
59 | *.iml
60 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | ICS Parser is an open source project. It is licensed under the [MIT license](https://opensource.org/licenses/MIT).
4 | We appreciate pull requests, here are our guidelines:
5 |
6 | 1. Firstly, check if your issue is present within the latest version (`dev-master`) as the problem may already have been fixed.
7 | 1. Log a bug in our [issue tracker](https://github.com/u01jmg3/ics-parser/issues) (if there isn't one already).
8 | - If your patch is going to be large it might be a good idea to get the discussion started early.
9 | - We are happy to discuss it in an issue beforehand.
10 | - If you could provide an iCal snippet causing the parser to behave incorrectly it is extremely useful for debugging
11 | - Please remove all irrelevant events
12 | 1. Please follow the coding standard already present in the file you are editing _before_ committing
13 | - Adhere to the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard
14 | - Use *4 spaces* instead of tabs for indentation
15 | - Trim all trailing whitespace and blank lines
16 | - Use single quotes (`'`) where possible instead of double
17 | - Use `PHP_EOL` where possible or default to `\n`
18 | - Abide by the [1TBS](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS_.28OTBS.29) indentation style
19 |
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: u01jmg3
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2018
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
6 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
7 | Software is furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10 | Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
15 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP ICS Parser
2 |
3 | [](https://packagist.org/packages/johngrogg/ics-parser)
4 | [](https://packagist.org/packages/johngrogg/ics-parser)
5 |
6 | ---
7 |
8 | ## Installation
9 |
10 | ### Requirements
11 | - PHP 5 (≥ 5.6.40)
12 | - [Valid ICS](https://icalendar.org/validator.html) (`.ics`, `.ical`, `.ifb`) file
13 | - [IANA](https://www.iana.org/time-zones), [Unicode CLDR](https://cldr.unicode.org) or [Windows](https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms912391(v=winembedded.11)) Time Zones
14 |
15 | ### Setup
16 |
17 | - Install [Composer](https://getcomposer.org/)
18 | - Add the following dependency to `composer.json`
19 | - :warning: **Note with Composer the owner is `johngrogg` and not `u01jmg3`**
20 | - To access the latest stable branch (`v3`) use the following
21 | - To access new features you can require [`dev-master`](https://getcomposer.org/doc/articles/aliases.md#branch-alias)
22 |
23 | ```yaml
24 | {
25 | "require": {
26 | "johngrogg/ics-parser": "^3"
27 | }
28 | }
29 | ```
30 |
31 | ## Running tests
32 |
33 | ```sh
34 | composer test
35 | ```
36 |
37 | ## How to use
38 |
39 | ### How to instantiate the Parser
40 |
41 | - Using the example script as a guide, [refer to this code](https://github.com/u01jmg3/ics-parser/blob/master/examples/index.php#L1-L22)
42 |
43 | #### What will the parser return?
44 |
45 | - Each key/value pair from the iCal file will be parsed creating an associative array for both the calendar and every event it contains.
46 | - Also injected will be content under `dtstart_tz` and `dtend_tz` for accessing start and end dates with time zone data applied.
47 | - Where possible [`DateTime`](https://secure.php.net/manual/en/class.datetime.php) objects are used and returned.
48 | - :information_source: **Note the parser is limited to [relative date formats](https://www.php.net/manual/en/datetime.formats.relative.php) which can inhibit how complex recurrence rule parts are processed (e.g. `BYDAY` combined with `BYSETPOS`)**
49 |
50 | ```php
51 | // Dump the whole calendar
52 | var_dump($ical->cal);
53 |
54 | // Dump every event
55 | var_dump($ical->events());
56 | ```
57 |
58 | - Also included are special `{property}_array` arrays which further resolve the contents of a key/value pair.
59 |
60 | ```php
61 | // Dump a parsed event's start date
62 | var_dump($event->dtstart_array);
63 |
64 | // array (size=4)
65 | // 0 =>
66 | // array (size=1)
67 | // 'TZID' => string 'America/Detroit' (length=15)
68 | // 1 => string '20160409T090000' (length=15)
69 | // 2 => int 1460192400
70 | // 3 => string 'TZID=America/Detroit:20160409T090000' (length=36)
71 | ```
72 |
73 | ### Are you using Outlook?
74 |
75 | Outlook has a quirk where it requires the User Agent string to be set in your request headers.
76 |
77 | We have done this for you by injecting a default User Agent string, if one has not been specified.
78 |
79 | If you wish to provide your own User agent string you can do so by using the `httpUserAgent` argument when creating your ICal object.
80 |
81 | ```php
82 | $ical = new ICal($url, array('httpUserAgent' => 'A Different User Agent'));
83 | ```
84 |
85 | ---
86 |
87 | ## When Parsing an iCal Feed
88 |
89 | Parsing [iCal/iCalendar/ICS](https://en.wikipedia.org/wiki/ICalendar) resources can pose several challenges. One challenge is that
90 | the specification is a moving target; the original RFC has only been updated four times in ten years. The other challenge is that vendors
91 | were both liberal (read: creative) in interpreting the specification and productive implementing proprietary extensions.
92 |
93 | However, what impedes efficient parsing most directly are recurrence rules for events. This library parses the original
94 | calendar into an easy to work with memory model. This requires that each recurring event is expanded or exploded. Hence,
95 | a single event that occurs daily will generate a new event instance for each day as this parser processes the
96 | calendar ([`$defaultSpan`](#variables) limits this). To get an idea how this is done take a look at the
97 | [call graph](https://user-images.githubusercontent.com/624195/45904641-f3cd0a80-bded-11e8-925f-7bcee04b8575.png).
98 |
99 | As a consequence the _entire_ calendar is parsed line-by-line, and thus loaded into memory, first. As you can imagine
100 | large calendars tend to get huge when exploded i.e. with all their recurrence rules evaluated. This is exacerbated when
101 | old calendars do not remove past events as they get fatter and fatter every year.
102 |
103 | This limitation is particularly painful if you only need a window into the original calendar. It seems wasteful to parse
104 | the entire fully exploded calendar into memory if you later are going to call the
105 | [`eventsFromInterval()` or `eventsFromRange()`](#methods) on it.
106 |
107 | In late 2018 [#190](https://github.com/u01jmg3/ics-parser/pull/190) added the option to drop all events outside a given
108 | range very early in the parsing process at the cost of some precision (time zone calculations are not calculated at that point). This
109 | massively reduces the total time for parsing a calendar. The same goes for memory consumption. The precondition is that
110 | you know upfront that you don't care about events outside a given range.
111 |
112 | Let's say you are only interested in events from yesterday, today and tomorrow. To compensate for the fact that the
113 | tricky time zone transformations and calculations have not been executed yet by the time the parser has to decide whether
114 | to keep or drop an event you can set it to filter for **+-2d** instead of +-1d. Once it is done you would then call
115 | `eventsFromRange()` with +-1d to get precisely the events in the window you are interested in. That is what the variables
116 | [`$filterDaysBefore` and `$filterDaysAfter`](#variables) are for.
117 |
118 | In Q1 2019 [#213](https://github.com/u01jmg3/ics-parser/pull/213) further improved the performance by immediately
119 | dropping _non-recurring_ events once parsed if they are outside that fuzzy window. This greatly reduces the maximum
120 | memory consumption for large calendars. PHP by default does not allocate more than 128MB heap and would otherwise crash
121 | with `Fatal error: Allowed memory size of 134217728 bytes exhausted`. It goes without saying that recurring events first
122 | need to be evaluated before non-fitting events can be dropped.
123 |
124 | ---
125 |
126 | ## API
127 |
128 | ### `ICal` API
129 |
130 | #### Variables
131 |
132 | | Name | Configurable | Default Value | Description |
133 | |--------------------------------|:------------------------:|-------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
134 | | `$alarmCount` | :heavy_multiplication_x: | N/A | Tracks the number of alarms in the current iCal feed |
135 | | `$cal` | :heavy_multiplication_x: | N/A | The parsed calendar |
136 | | `$defaultSpan` | :ballot_box_with_check: | `2` | The value in years to use for indefinite, recurring events |
137 | | `$defaultTimeZone` | :ballot_box_with_check: | [System default](https://secure.php.net/manual/en/function.date-default-timezone-get.php) | Enables customisation of the default time zone |
138 | | `$defaultWeekStart` | :ballot_box_with_check: | `MO` | The two letter representation of the first day of the week |
139 | | `$disableCharacterReplacement` | :ballot_box_with_check: | `false` | Toggles whether to disable all character replacement. Will replace curly quotes and other special characters with their standard equivalents if `false`. Can be a costly operation! |
140 | | `$eventCount` | :heavy_multiplication_x: | N/A | Tracks the number of events in the current iCal feed |
141 | | `$filterDaysAfter` | :ballot_box_with_check: | `null` | When set the parser will ignore all events more than roughly this many days _after_ now. To be on the safe side it is advised that you make the filter window `+/- 1` day larger than necessary. For performance reasons this filter is applied before any date and time zone calculations are done. Hence, depending the time zone settings of the parser and the calendar the cut-off date is not "calibrated". You can then use `$ical->eventsFromRange()` to precisely shrink the window. |
142 | | `$filterDaysBefore` | :ballot_box_with_check: | `null` | When set the parser will ignore all events more than roughly this many days _before_ now. See `$filterDaysAfter` above for more details. |
143 | | `$freeBusyCount` | :heavy_multiplication_x: | N/A | Tracks the free/busy count in the current iCal feed |
144 | | `$httpBasicAuth` | :heavy_multiplication_x: | `array()` | Holds the username and password for HTTP basic authentication |
145 | | `$httpUserAgent` | :ballot_box_with_check: | `null` | Holds the custom User Agent string header |
146 | | `$httpAcceptLanguage` | :heavy_multiplication_x: | `null` | Holds the custom Accept Language request header, e.g. "en" or "de" |
147 | | `$httpProtocolVersion` | :heavy_multiplication_x: | `null` | Holds the custom HTTP Protocol version, e.g. "1.0" or "1.1" |
148 | | `$shouldFilterByWindow` | :heavy_multiplication_x: | `false` | `true` if either `$filterDaysBefore` or `$filterDaysAfter` are set |
149 | | `$skipRecurrence` | :ballot_box_with_check: | `false` | Toggles whether to skip the parsing of recurrence rules |
150 | | `$todoCount` | :heavy_multiplication_x: | N/A | Tracks the number of todos in the current iCal feed |
151 | | `$windowMaxTimestamp` | :heavy_multiplication_x: | `null` | If `$filterDaysBefore` or `$filterDaysAfter` are set then the events are filtered according to the window defined by this field and `$windowMinTimestamp` |
152 | | `$windowMinTimestamp` | :heavy_multiplication_x: | `null` | If `$filterDaysBefore` or `$filterDaysAfter` are set then the events are filtered according to the window defined by this field and `$windowMaxTimestamp` |
153 |
154 | #### Methods
155 |
156 | | Method | Parameter(s) | Visibility | Description |
157 | |-------------------------------------------------|-----------------------------------------------------------------------------------------------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
158 | | `__construct` | `$files = false`, `$options = array()` | `public` | Creates the ICal object |
159 | | `initFile` | `$file` | `protected` | Initialises lines from a file |
160 | | `initLines` | `$lines` | `protected` | Initialises the parser using an array containing each line of iCal content |
161 | | `initString` | `$string` | `protected` | Initialises lines from a string |
162 | | `initUrl` | `$url`, `$username = null`, `$password = null`, `$userAgent = null`, `$acceptLanguage = null` | `protected` | Initialises lines from a URL. Accepts a username/password combination for HTTP basic authentication, a custom User Agent string and the accepted client language |
163 | | `addCalendarComponentWithKeyAndValue` | `$component`, `$keyword`, `$value` | `protected` | Add one key and value pair to the `$this->cal` array |
164 | | `calendarDescription` | - | `public` | Returns the calendar description |
165 | | `calendarName` | - | `public` | Returns the calendar name |
166 | | `calendarTimeZone` | `$ignoreUtc` | `public` | Returns the calendar time zone |
167 | | `cleanCharacters` | `$data` | `protected` | Replaces curly quotes and other special characters with their standard equivalents |
168 | | `eventsFromInterval` | `$interval` | `public` | Returns a sorted array of events following a given string |
169 | | `eventsFromRange` | `$rangeStart = false`, `$rangeEnd = false` | `public` | Returns a sorted array of events in a given range, or an empty array if no events exist in the range |
170 | | `events` | - | `public` | Returns an array of Events |
171 | | `fileOrUrl` | `$filename` | `protected` | Reads an entire file or URL into an array |
172 | | `filterValuesUsingBySetPosRRule` | `$bysetpos`, `$valueslist` | `protected` | Filters a provided values-list by applying a BYSETPOS RRule |
173 | | `freeBusyEvents` | - | `public` | Returns an array of arrays with all free/busy events |
174 | | `getDaysOfMonthMatchingByDayRRule` | `$bydays`, `$initialDateTime` | `protected` | Find all days of a month that match the BYDAY stanza of an RRULE |
175 | | `getDaysOfMonthMatchingByMonthDayRRule` | `$byMonthDays`, `$initialDateTime` | `protected` | Find all days of a month that match the BYMONTHDAY stanza of an RRULE |
176 | | `getDaysOfYearMatchingByDayRRule` | `$byDays`, `$initialDateTime` | `protected` | Find all days of a year that match the BYDAY stanza of an RRULE |
177 | | `getDaysOfYearMatchingByMonthDayRRule` | `$byMonthDays`, `$initialDateTime` | `protected` | Find all days of a year that match the BYMONTHDAY stanza of an RRULE |
178 | | `getDaysOfYearMatchingByWeekNoRRule` | `$byWeekNums`, `$initialDateTime` | `protected` | Find all days of a year that match the BYWEEKNO stanza of an RRULE |
179 | | `getDaysOfYearMatchingByYearDayRRule` | `$byYearDays`, `$initialDateTime` | `protected` | Find all days of a year that match the BYYEARDAY stanza of an RRULE |
180 | | `getDefaultTimeZone` | `$forceReturnSystemDefault` | `private` | Returns the default time zone if set or falls back to the system default if not set |
181 | | `hasEvents` | - | `public` | Returns a boolean value whether the current calendar has events or not |
182 | | `iCalDateToDateTime` | `$icalDate` | `public` | Returns a `DateTime` object from an iCal date time format |
183 | | `iCalDateToUnixTimestamp` | `$icalDate` | `public` | Returns a Unix timestamp from an iCal date time format |
184 | | `iCalDateWithTimeZone` | `$event`, `$key`, `$format = DATE_TIME_FORMAT` | `public` | Returns a date adapted to the calendar time zone depending on the event `TZID` |
185 | | `doesEventStartOutsideWindow` | `$event` | `protected` | Determines whether the event start date is outside `$windowMinTimestamp` / `$windowMaxTimestamp` |
186 | | `isFileOrUrl` | `$filename` | `protected` | Checks if a filename exists as a file or URL |
187 | | `isOutOfRange` | `$calendarDate`, `$minTimestamp`, `$maxTimestamp` | `protected` | Determines whether a valid iCalendar date is within a given range |
188 | | `isValidCldrTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is a valid CLDR time zone |
189 | | `isValidDate` | `$value` | `public` | Checks if a date string is a valid date |
190 | | `isValidIanaTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is a valid IANA time zone |
191 | | `isValidWindowsTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is a recognised Windows (non-CLDR) time zone |
192 | | `isValidTimeZoneId` | `$timeZone` | `protected` | Checks if a time zone is valid (IANA, CLDR, or Windows) |
193 | | `keyValueFromString` | `$text` | `public` | Gets the key value pair from an iCal string |
194 | | `parseLine` | `$line` | `protected` | Parses a line from an iCal file into an array of tokens |
195 | | `mb_chr` | `$code` | `protected` | Provides a polyfill for PHP 7.2's `mb_chr()`, which is a multibyte safe version of `chr()` |
196 | | `escapeParamText` | `$candidateText` | `protected` | Places double-quotes around texts that have characters not permitted in parameter-texts, but are permitted in quoted-texts. |
197 | | `parseDuration` | `$date`, `$duration` | `protected` | Parses a duration and applies it to a date |
198 | | `parseExdates` | `$event` | `public` | Parses a list of excluded dates to be applied to an Event |
199 | | `processDateConversions` | - | `protected` | Processes date conversions using the time zone |
200 | | `processEvents` | - | `protected` | Performs admin tasks on all events as read from the iCal file |
201 | | `processRecurrences` | - | `protected` | Processes recurrence rules |
202 | | `reduceEventsToMinMaxRange` | | `protected` | Reduces the number of events to the defined minimum and maximum range |
203 | | `removeLastEventIfOutsideWindowAndNonRecurring` | | `protected` | Removes the last event (i.e. most recently parsed) if its start date is outside the window spanned by `$windowMinTimestamp` / `$windowMaxTimestamp` |
204 | | `removeUnprintableChars` | `$data` | `protected` | Removes unprintable ASCII and UTF-8 characters |
205 | | `resolveIndicesOfRange` | `$indexes`, `$limit` | `protected` | Resolves values from indices of the range 1 -> `$limit` |
206 | | `sortEventsWithOrder` | `$events`, `$sortOrder = SORT_ASC` | `public` | Sorts events based on a given sort order |
207 | | `timeZoneStringToDateTimeZone` | `$timeZoneString` | `public` | Returns a `DateTimeZone` object based on a string containing a time zone name. |
208 | | `unfold` | `$lines` | `protected` | Unfolds an iCal file in preparation for parsing |
209 |
210 | #### Constants
211 |
212 | | Name | Description |
213 | |---------------------------|-----------------------------------------------|
214 | | `DATE_TIME_FORMAT_PRETTY` | Default pretty date time format to use |
215 | | `DATE_TIME_FORMAT` | Default date time format to use |
216 | | `ICAL_DATE_TIME_TEMPLATE` | String template to generate an iCal date time |
217 | | `ISO_8601_WEEK_START` | First day of the week, as defined by ISO-8601 |
218 | | `RECURRENCE_EVENT` | Used to isolate generated recurrence events |
219 | | `SECONDS_IN_A_WEEK` | The number of seconds in a week |
220 | | `TIME_FORMAT` | Default time format to use |
221 | | `TIME_ZONE_UTC` | UTC time zone string |
222 | | `UNIX_FORMAT` | Unix timestamp date format |
223 | | `UNIX_MIN_YEAR` | The year Unix time began |
224 |
225 | ---
226 |
227 | ### `Event` API (extends `ICal` API)
228 |
229 | #### Methods
230 |
231 | | Method | Parameter(s) | Visibility | Description |
232 | |---------------|---------------------------------------------|-------------|---------------------------------------------------------------------|
233 | | `__construct` | `$data = array()` | `public` | Creates the Event object |
234 | | `prepareData` | `$value` | `protected` | Prepares the data for output |
235 | | `printData` | `$html = HTML_TEMPLATE` | `public` | Returns Event data excluding anything blank within an HTML template |
236 | | `snakeCase` | `$input`, `$glue = '_'`, `$separator = '-'` | `protected` | Converts the given input to snake_case |
237 |
238 | #### Constants
239 |
240 | | Name | Description |
241 | |-----------------|-----------------------------------------------------|
242 | | `HTML_TEMPLATE` | String template to use when pretty printing content |
243 |
244 | ---
245 |
246 | ## Credits
247 | - [Jonathan Goode](https://github.com/u01jmg3) (programming, bug fixing, codebase enhancement, coding standard adoption)
248 | - [s0600204](https://github.com/s0600204) (major enhancements to RRULE support, many bug fixes and other contributions)
249 |
250 | ---
251 |
252 | ## Tools for Testing
253 |
254 | - [iCal Validator](https://icalendar.org/validator.html)
255 | - [Recurrence Rule Tester](https://jkbrzt.github.io/rrule/)
256 | - [Unix Timestamp Converter](https://www.unixtimestamp.com)
257 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "johngrogg/ics-parser",
3 | "description": "ICS Parser",
4 | "homepage": "https://github.com/u01jmg3/ics-parser",
5 | "keywords": [
6 | "ical",
7 | "ical-parser",
8 | "icalendar",
9 | "ics",
10 | "ics-parser",
11 | "ifb"
12 | ],
13 | "type": "library",
14 | "license": "MIT",
15 | "authors": [
16 | {
17 | "name": "Jonathan Goode",
18 | "role": "Developer/Owner"
19 | },
20 | {
21 | "name": "John Grogg",
22 | "email": "john.grogg@gmail.com",
23 | "role": "Developer/Prior Owner"
24 | }
25 | ],
26 | "funding": [
27 | {
28 | "type": "github",
29 | "url": "https://github.com/sponsors/u01jmg3"
30 | }
31 | ],
32 | "require": {
33 | "php": ">=5.6.40",
34 | "ext-mbstring": "*"
35 | },
36 | "require-dev": {
37 | "phpunit/phpunit": "^5|^9|^10"
38 | },
39 | "autoload": {
40 | "psr-0": {
41 | "ICal": "src/"
42 | }
43 | },
44 | "scripts": {
45 | "test": [
46 | "phpunit --colors=always"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ecs.php:
--------------------------------------------------------------------------------
1 | disableParallel();
85 |
86 | // https://github.com/easy-coding-standard/easy-coding-standard/blob/main/config/set/psr12.php
87 | $ecsConfig->import(SetList::PSR_12);
88 |
89 | $ecsConfig->lineEnding("\n");
90 |
91 | $ecsConfig->skip(array(
92 | // Fixers
93 | 'PhpCsFixer\Fixer\Whitespace\StatementIndentationFixer' => array('examples/index.php'),
94 | 'PhpCsFixer\Fixer\Basic\BracesFixer' => null,
95 | 'PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer' => null,
96 | 'PhpCsFixer\Fixer\Operator\NotOperatorWithSuccessorSpaceFixer' => null,
97 | 'PhpCsFixer\Fixer\Phpdoc\PhpdocScalarFixer' => null,
98 | 'PhpCsFixer\Fixer\Phpdoc\PhpdocSummaryFixer' => null,
99 | 'PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer' => null,
100 | 'PhpCsFixer\Fixer\ReturnNotation\SimplifiedNullReturnFixer' => null,
101 | // Requires PHP 7.1 and above
102 | 'PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer' => null,
103 | ));
104 |
105 | $ecsConfig->ruleWithConfiguration(SpaceAfterNotSniff::class, array('spacing' => 0));
106 |
107 | $ecsConfig->ruleWithConfiguration(ArraySyntaxFixer::class, array('syntax' => 'long'));
108 |
109 | $ecsConfig->ruleWithConfiguration(
110 | YodaStyleFixer::class,
111 | array(
112 | 'equal' => false,
113 | 'identical' => false,
114 | 'less_and_greater' => false,
115 | )
116 | );
117 |
118 | $ecsConfig->ruleWithConfiguration(ListSyntaxFixer::class, array('syntax' => 'long')); // PHP 5.6
119 |
120 | $ecsConfig->ruleWithConfiguration(
121 | BlankLineBeforeStatementFixer::class,
122 | array(
123 | 'statements' => array(
124 | 'continue',
125 | 'declare',
126 | 'return',
127 | 'throw',
128 | 'try',
129 | ),
130 | )
131 | );
132 |
133 | $ecsConfig->rules(
134 | array(
135 | AlphabeticallySortedUsesSniff::class,
136 | UnusedVariableSniff::class,
137 | SelfMemberReferenceSniff::class,
138 | BlankLinesBeforeNamespaceFixer::class,
139 | CastSpacesFixer::class,
140 | ClassDefinitionFixer::class,
141 | CompactNullableTypehintFixer::class,
142 | ConstantCaseFixer::class,
143 | ElseifFixer::class,
144 | EncodingFixer::class,
145 | FullOpeningTagFixer::class,
146 | FunctionDeclarationFixer::class,
147 | HeredocToNowdocFixer::class,
148 | IncludeFixer::class,
149 | LambdaNotUsedImportFixer::class,
150 | LineEndingFixer::class,
151 | LowercaseKeywordsFixer::class,
152 | LowercaseStaticReferenceFixer::class,
153 | MagicConstantCasingFixer::class,
154 | MagicMethodCasingFixer::class,
155 | MethodArgumentSpaceFixer::class,
156 | MultilineWhitespaceBeforeSemicolonsFixer::class,
157 | NativeFunctionCasingFixer::class,
158 | NativeFunctionTypeDeclarationCasingFixer::class,
159 | NoAliasFunctionsFixer::class,
160 | NoClosingTagFixer::class,
161 | NoEmptyPhpdocFixer::class,
162 | NoEmptyStatementFixer::class,
163 | NoExtraBlankLinesFixer::class,
164 | NoLeadingNamespaceWhitespaceFixer::class,
165 | NoMixedEchoPrintFixer::class,
166 | NoMultilineWhitespaceAroundDoubleArrowFixer::class,
167 | NoShortBoolCastFixer::class,
168 | NoSpacesAfterFunctionNameFixer::class,
169 | NoSpacesInsideParenthesisFixer::class,
170 | NoTrailingCommaInSinglelineFixer::class,
171 | NoTrailingWhitespaceInCommentFixer::class,
172 | NoUnneededControlParenthesesFixer::class,
173 | NoUnneededCurlyBracesFixer::class,
174 | NoUnreachableDefaultArgumentValueFixer::class,
175 | NoUnusedImportsFixer::class,
176 | NoUselessReturnFixer::class,
177 | NoWhitespaceInBlankLineFixer::class,
178 | NormalizeIndexBraceFixer::class,
179 | ObjectOperatorWithoutWhitespaceFixer::class,
180 | PhpdocIndentFixer::class,
181 | PhpdocInlineTagNormalizerFixer::class,
182 | PhpdocNoAccessFixer::class,
183 | PhpdocNoPackageFixer::class,
184 | PhpdocNoUselessInheritdocFixer::class,
185 | PhpdocParamOrderFixer::class,
186 | PhpdocSingleLineVarSpacingFixer::class,
187 | PhpdocToCommentFixer::class,
188 | PhpdocTrimFixer::class,
189 | PhpdocTypesFixer::class,
190 | SingleBlankLineAtEofFixer::class,
191 | SingleClassElementPerStatementFixer::class,
192 | SingleImportPerStatementFixer::class,
193 | SingleLineAfterImportsFixer::class,
194 | SingleLineCommentStyleFixer::class,
195 | SingleQuoteFixer::class,
196 | SpaceAfterSemicolonFixer::class,
197 | StandardizeNotEqualsFixer::class,
198 | SwitchCaseSemicolonToColonFixer::class,
199 | SwitchCaseSpaceFixer::class,
200 | TrailingCommaInMultilineFixer::class,
201 | TrimArraySpacesFixer::class,
202 | TypeDeclarationSpacesFixer::class,
203 | )
204 | );
205 | };
206 |
--------------------------------------------------------------------------------
/examples/ICal.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN
3 | VERSION:2.0
4 | CALSCALE:GREGORIAN
5 | METHOD:PUBLISH
6 | X-WR-CALNAME:Testkalender
7 | X-WR-TIMEZONE:UTC
8 | X-WR-CALDESC:Nur zum testen vom Google Kalender
9 | BEGIN:VFREEBUSY
10 | UID:f06ff6b3564b2f696bf42d393f8dea59
11 | ORGANIZER:MAILTO:jane_smith@host1.com
12 | DTSTAMP:20170316T204607Z
13 | DTSTART:20170213T204607Z
14 | DTEND:20180517T204607Z
15 | URL:https://www.host.com/calendar/busytime/jsmith.ifb
16 | FREEBUSY;FBTYPE=BUSY:20170623T070000Z/20170223T110000Z
17 | FREEBUSY;FBTYPE=BUSY:20170624T131500Z/20170316T151500Z
18 | FREEBUSY;FBTYPE=BUSY:20170715T131500Z/20170416T150000Z
19 | FREEBUSY;FBTYPE=BUSY:20170716T131500Z/20170516T100500Z
20 | END:VFREEBUSY
21 | BEGIN:VEVENT
22 | DTSTART:20171032T000000
23 | DTEND:20171101T2300
24 | DESCRIPTION:Invalid date - parser will skip the event
25 | SUMMARY:Invalid date - parser will skip the event
26 | DTSTAMP:20170406T063924
27 | LOCATION:
28 | UID:f81b0b41a2e138ae0903daee0a966e1e
29 | SEQUENCE:0
30 | END:VEVENT
31 | BEGIN:VEVENT
32 | DTSTART;VALUE=DATE;TZID=America/Los_Angeles:19410512
33 | DTEND;VALUE=DATE;TZID=America/Los_Angeles:19410512
34 | DTSTAMP;TZID=America/Los_Angeles:19410512T195741Z
35 | UID:dh3fki5du0opa7cs5n5s87ca02@google.com
36 | CREATED:20380101T141901Z
37 | DESCRIPTION;LANGUAGE=en-gb:
38 | LAST-MODIFIED:20380101T141901Z
39 | LOCATION:
40 | SEQUENCE:0
41 | STATUS:CONFIRMED
42 | SUMMARY;LANGUAGE=en-gb:Before 1970-Test: Konrad Zuse invents the Z3, the "first
43 | digital Computer"
44 | TRANSP:TRANSPARENT
45 | END:VEVENT
46 | BEGIN:VEVENT
47 | DTSTART;VALUE=DATE:20380201
48 | DTEND;VALUE=DATE:20380202
49 | DTSTAMP;TZID="GMT Standard Time":20380101T195741Z
50 | UID:dh3fki5du0opa7cs5n5s87ca01@google.com
51 | CREATED:20380101T141901Z
52 | DESCRIPTION;LANGUAGE=en-gb:
53 | LAST-MODIFIED:20380101T141901Z
54 | LOCATION:
55 | SEQUENCE:0
56 | STATUS:CONFIRMED
57 | SUMMARY;LANGUAGE=en-gb:Year 2038 problem test
58 | TRANSP:TRANSPARENT
59 | END:VEVENT
60 | BEGIN:VEVENT
61 | DTSTART:20160105T090000Z
62 | DTEND:20160107T173000Z
63 | DTSTAMP;TZID="Greenwich Mean Time:Dublin; Edinburgh; Lisbon; London":20110121T195741Z
64 | UID:15lc1nvupht8dtfiptenljoiv4@google.com
65 | CREATED:20110121T195616Z
66 | DESCRIPTION;LANGUAGE=en-gb:This is a short description\nwith a new line. Some "special" 's
67 | igns' may be interesting\, too.
68 | And a non-breaking space.
69 | LAST-MODIFIED:20150409T150000Z
70 | LOCATION:Kansas
71 | SEQUENCE:2
72 | STATUS:CONFIRMED
73 | SUMMARY;LANGUAGE=en-gb:My Holidays
74 | TRANSP:TRANSPARENT
75 | ORGANIZER;CN="My Name":mailto:my.name@mydomain.com
76 | END:VEVENT
77 | BEGIN:VEVENT
78 | ATTENDEE;CN="Page, Larry (l.page@google.com)";ROLE=REQ-PARTICIPANT;RSVP=FALSE:mailto:l.page@google.com
79 | ATTENDEE;CN="Brin, Sergey (s.brin@google.com)";ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:s.brin@google.com
80 | DTSTART;VALUE=DATE:20160112
81 | DTEND;VALUE=DATE:20160116
82 | DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
83 | UID:1koigufm110c5hnq6ln57murd4@google.com
84 | CREATED:20110119T142901Z
85 | DESCRIPTION;LANGUAGE=en-gb:Project xyz Review Meeting Minutes\n
86 | Agenda\n1. Review of project version 1.0 requirements.\n2.
87 | Definition
88 | of project processes.\n3. Review of project schedule.\n
89 | Participants: John Smith, Jane Doe, Jim Dandy\n-It was
90 | decided that the requirements need to be signed off by
91 | product marketing.\n-Project processes were accepted.\n
92 | -Project schedule needs to account for scheduled holidays
93 | and employee vacation time. Check with HR for specific
94 | dates.\n-New schedule will be distributed by Friday.\n-
95 | Next weeks meeting is cancelled. No meeting until 3/23.
96 | LAST-MODIFIED:20150409T150000Z
97 | LOCATION:
98 | SEQUENCE:2
99 | STATUS:CONFIRMED
100 | SUMMARY;LANGUAGE=en-gb:Test 2
101 | TRANSP:TRANSPARENT
102 | END:VEVENT
103 | BEGIN:VEVENT
104 | DTSTART;VALUE=DATE:20160119
105 | DTEND;VALUE=DATE:20160120
106 | DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
107 | UID:rq8jng4jgq0m1lvpj8486fttu0@google.com
108 | CREATED:20110119T141904Z
109 | DESCRIPTION;LANGUAGE=en-gb:
110 | LAST-MODIFIED:20150409T150000Z
111 | LOCATION:
112 | RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
113 | SEQUENCE:0
114 | STATUS:CONFIRMED
115 | SUMMARY;LANGUAGE=en-gb:DST Change
116 | TRANSP:TRANSPARENT
117 | END:VEVENT
118 | BEGIN:VEVENT
119 | DTSTART;VALUE=DATE:20160119
120 | DTEND;VALUE=DATE:20160120
121 | DTSTAMP;TZID="GMT Standard Time":20110121T195741Z
122 | UID:dh3fki5du0opa7cs5n5s87ca00@google.com
123 | CREATED:20110119T141901Z
124 | DESCRIPTION;LANGUAGE=en-gb:
125 | LAST-MODIFIED:20150409T150000Z
126 | LOCATION:
127 | RRULE:FREQ=WEEKLY;COUNT=5;INTERVAL=2;BYDAY=TU
128 | SEQUENCE:0
129 | STATUS:CONFIRMED
130 | SUMMARY;LANGUAGE=en-gb:Test 1
131 | TRANSP:TRANSPARENT
132 | END:VEVENT
133 | BEGIN:VEVENT
134 | SUMMARY:Duration Test
135 | DTSTART:20160425T150000Z
136 | DTSTAMP:20160424T150000Z
137 | DURATION:PT1H15M5S
138 | RRULE:FREQ=DAILY;COUNT=2
139 | UID:calendar-62-e7c39bf02382917349672271dd781c89
140 | END:VEVENT
141 | BEGIN:VEVENT
142 | SUMMARY:BYMONTHDAY Test
143 | DTSTART:20160922T130000Z
144 | DTEND:20160922T150000Z
145 | DTSTAMP:20160921T130000Z
146 | RRULE:FREQ=MONTHLY;UNTIL=20170923T000000Z;INTERVAL=1;BYMONTHDAY=23
147 | UID:33844fe8df15fbfc13c97fc41c0c4b00392c6870@google.com
148 | END:VEVENT
149 | BEGIN:VEVENT
150 | DTSTART;TZID=Europe/Paris:20160921T080000
151 | DTEND;TZID=Europe/Paris:20160921T090000
152 | RRULE:FREQ=WEEKLY;BYDAY=WE
153 | DTSTAMP:20161117T165045Z
154 | UID:884bc8350185031337d9ec49d2e7e101dd5ae5fb@google.com
155 | CREATED:20160920T133918Z
156 | DESCRIPTION:
157 | LAST-MODIFIED:20160920T133923Z
158 | LOCATION:
159 | SEQUENCE:1
160 | STATUS:CONFIRMED
161 | SUMMARY:Paris TimeZone Test
162 | TRANSP:OPAQUE
163 | END:VEVENT
164 | BEGIN:VEVENT
165 | DTSTART:20160215T080000Z
166 | DTEND:20160515T090000Z
167 | DTSTAMP:20161121T113027Z
168 | CREATED:20161121T113027Z
169 | UID:65323c541a30dd1f180e2bbfa2724995
170 | DESCRIPTION:
171 | LAST-MODIFIED:20161121T113027Z
172 | LOCATION:
173 | SEQUENCE:1
174 | STATUS:CONFIRMED
175 | SUMMARY:Long event covering the range from example with special chars:
176 | ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÕÖÒÓÔØÙÚÛÜÝÞß
177 | àáâãäåæçèéêėëìíîïðñòóôõöøùúûüūýþÿž
178 | ‘ ’ ‚ ‛ “ ” „ ‟ – — …
179 | TRANSP:OPAQUE
180 | END:VEVENT
181 | BEGIN:VEVENT
182 | CLASS:PUBLIC
183 | CREATED:20160706T161104Z
184 | DTEND;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160409T110000
185 | DTSTAMP:20160706T150005Z
186 | DTSTART;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160409T090000
187 | EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":
188 | 20160528T090000,
189 | 20160625T090000
190 | LAST-MODIFIED:20160707T182011Z
191 | EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160709T090000
192 | EXDATE;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160723T090000
193 | LOCATION:Sanctuary
194 | PRIORITY:5
195 | RRULE:FREQ=WEEKLY;COUNT=15;BYDAY=SA
196 | SEQUENCE:0
197 | SUMMARY:Microsoft Unicode CLDR EXDATE Test
198 | TRANSP:OPAQUE
199 | UID:040000008200E00074C5B7101A82E0080000000020F6512D0B48CF0100000000000000001000000058BFB8CBB85D504CB99FBA637BCFD6BF
200 | X-MICROSOFT-CDO-BUSYSTATUS:BUSY
201 | X-MICROSOFT-CDO-IMPORTANCE:1
202 | X-MICROSOFT-DISALLOW-COUNTER:FALSE
203 | END:VEVENT
204 | BEGIN:VEVENT
205 | DTSTART;VALUE=DATE:20170118
206 | DTEND;VALUE=DATE:20170118
207 | DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
208 | RRULE:FREQ=MONTHLY;BYSETPOS=3;BYDAY=WE;COUNT=5
209 | UID:4dnsuc3nknin15kv25cn7ridss@google.com
210 | CREATED:20170119T142059Z
211 | DESCRIPTION;LANGUAGE=en-gb:BYDAY Test 1
212 | LAST-MODIFIED:20170409T150000Z
213 | SEQUENCE:0
214 | STATUS:CONFIRMED
215 | SUMMARY;LANGUAGE=en-gb:BYDAY Test 1
216 | TRANSP:TRANSPARENT
217 | END:VEVENT
218 | BEGIN:VEVENT
219 | DTSTART;VALUE=DATE:20190101
220 | DTEND;VALUE=DATE:20190101
221 | DTSTAMP;TZID="GMT Standard Time":20190101T195741Z
222 | RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1
223 | UID:4dnsuc3nknin15kv25cn7ridssy@google.com
224 | CREATED:20190101T142059Z
225 | DESCRIPTION;LANGUAGE=en-gb:BYSETPOS First weekday of every month
226 | LAST-MODIFIED:20190101T150000Z
227 | SEQUENCE:0
228 | STATUS:CONFIRMED
229 | SUMMARY;LANGUAGE=en-gb:BYSETPOS First weekday of every month
230 | TRANSP:TRANSPARENT
231 | END:VEVENT
232 | BEGIN:VEVENT
233 | DTSTART;VALUE=DATE:20190131
234 | DTEND;VALUE=DATE:20190131
235 | DTSTAMP;TZID="GMT Standard Time":20190121T195741Z
236 | RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=SU,MO,TU,WE,TH,FR,SA;BYSETPOS=-1
237 | UID:4dnsuc3nknin15kv25cn7ridssx@google.com
238 | CREATED:20190119T142059Z
239 | DESCRIPTION;LANGUAGE=en-gb:BYSETPOS Last day of every month
240 | LAST-MODIFIED:20190409T150000Z
241 | SEQUENCE:0
242 | STATUS:CONFIRMED
243 | SUMMARY;LANGUAGE=en-gb:BYSETPOS Last day of every month
244 | TRANSP:TRANSPARENT
245 | END:VEVENT
246 | BEGIN:VEVENT
247 | DTSTART;VALUE=DATE:20170301
248 | DTEND;VALUE=DATE:20170301
249 | DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
250 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=WE
251 | UID:h6f7sdjbpt47v3dkral8lnsgcc@google.com
252 | CREATED:20170119T142040Z
253 | DESCRIPTION;LANGUAGE=en-gb:BYDAY Test 2
254 | LAST-MODIFIED:20170409T150000Z
255 | SEQUENCE:0
256 | STATUS:CONFIRMED
257 | SUMMARY;LANGUAGE=en-gb:BYDAY Test 2
258 | TRANSP:TRANSPARENT
259 | END:VEVENT
260 | BEGIN:VEVENT
261 | DTSTART;VALUE=DATE:20170111
262 | DTEND;VALUE=DATE:20170111
263 | DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
264 | RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=5;BYMONTH=1,2,3
265 | UID:f50e8b89a4a3b0070e0b687d03@google.com
266 | CREATED:20170119T142040Z
267 | DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 1
268 | LAST-MODIFIED:20170409T150000Z
269 | SEQUENCE:0
270 | STATUS:CONFIRMED
271 | SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 1
272 | TRANSP:TRANSPARENT
273 | END:VEVENT
274 | BEGIN:VEVENT
275 | DTSTART;VALUE=DATE:20170405
276 | DTEND;VALUE=DATE:20170405
277 | DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
278 | RRULE:FREQ=YEARLY;BYMONTH=4,5,6;BYDAY=WE;COUNT=5
279 | UID:675f06aa795665ae50904ebf0e@google.com
280 | CREATED:20170119T142040Z
281 | DESCRIPTION;LANGUAGE=en-gb:BYMONTH Multiple Test 2
282 | LAST-MODIFIED:20170409T150000Z
283 | SEQUENCE:0
284 | STATUS:CONFIRMED
285 | SUMMARY;LANGUAGE=en-gb:BYMONTH Multiple Test 2
286 | TRANSP:TRANSPARENT
287 | END:VEVENT
288 | BEGIN:VEVENT
289 | BEGIN:VALARM
290 | TRIGGER;VALUE=DURATION:-PT30M
291 | ACTION:DISPLAY
292 | DESCRIPTION:Buzz buzz
293 | END:VALARM
294 | DTSTART;VALUE=DATE;TZID=Germany/Berlin:20170123
295 | DTEND;VALUE=DATE;TZID=Germany/Berlin:20170123
296 | DTSTAMP;TZID="GMT Standard Time":20170121T195741Z
297 | RRULE:FREQ=MONTHLY;BYDAY=-2MO;COUNT=5
298 | EXDATE;VALUE=DATE:20171020
299 | UID:d287b7ec808fcf084983f10837@google.com
300 | CREATED:20170119T142040Z
301 | DESCRIPTION;LANGUAGE=en-gb:Negative BYDAY
302 | LAST-MODIFIED:20170409T150000Z
303 | SEQUENCE:0
304 | STATUS:CONFIRMED
305 | SUMMARY;LANGUAGE=en-gb:Negative BYDAY
306 | TRANSP:TRANSPARENT
307 | END:VEVENT
308 | BEGIN:VEVENT
309 | DTSTART;TZID=Australia/Sydney:20170813T190000
310 | DTEND;TZID=Australia/Sydney:20170813T213000
311 | RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=2SU;COUNT=2
312 | DTSTAMP:20170809T114431Z
313 | UID:testuid@google.com
314 | CREATED:20170802T135539Z
315 | DESCRIPTION:
316 | LAST-MODIFIED:20170802T135935Z
317 | LOCATION:
318 | SEQUENCE:1
319 | STATUS:CONFIRMED
320 | SUMMARY:Parent Recurrence Event
321 | TRANSP:OPAQUE
322 | END:VEVENT
323 | BEGIN:VEVENT
324 | DTSTART;TZID=Australia/Sydney:20170813T190000
325 | DTEND;TZID=Australia/Sydney:20170813T213000
326 | DTSTAMP:20170809T114431Z
327 | UID:testuid@google.com
328 | RECURRENCE-ID;TZID=Australia/Sydney:20170813T190000
329 | CREATED:20170802T135539Z
330 | DESCRIPTION:
331 | LAST-MODIFIED:20170809T105604Z
332 | LOCATION:Melbourne VIC\, Australia
333 | SEQUENCE:1
334 | STATUS:CONFIRMED
335 | SUMMARY:Override Parent Recurrence Event
336 | TRANSP:OPAQUE
337 | END:VEVENT
338 | END:VCALENDAR
339 |
--------------------------------------------------------------------------------
/examples/index.php:
--------------------------------------------------------------------------------
1 | 2, // Default value
11 | 'defaultTimeZone' => 'UTC',
12 | 'defaultWeekStart' => 'MO', // Default value
13 | 'disableCharacterReplacement' => false, // Default value
14 | 'filterDaysAfter' => null, // Default value
15 | 'filterDaysBefore' => null, // Default value
16 | 'httpUserAgent' => null, // Default value
17 | 'skipRecurrence' => false, // Default value
18 | ));
19 | // $ical->initFile('ICal.ics');
20 | // $ical->initUrl('https://raw.githubusercontent.com/u01jmg3/ics-parser/master/examples/ICal.ics', $username = null, $password = null, $userAgent = null);
21 | } catch (\Exception $e) {
22 | die($e);
23 | }
24 | ?>
25 |
26 |
27 |
28 |
29 |
30 |
31 | PHP ICS Parser example
32 |
33 |
34 |
35 |
36 |
PHP ICS Parser example
37 |
38 |
39 | The number of events
40 | eventCount ?>
41 |
42 |
43 | The number of free/busy time slots
44 | freeBusyCount ?>
45 |