icalendar-recurrence 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/main.yml +32 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -0
- data/README.md +6 -2
- data/icalendar-recurrence.gemspec +6 -6
- data/lib/icalendar/recurrence/event_extensions.rb +4 -0
- data/lib/icalendar/recurrence/schedule.rb +15 -15
- data/lib/icalendar/recurrence/time_util.rb +28 -8
- data/lib/icalendar/recurrence/version.rb +1 -1
- data/spec/lib/recurrence_spec.rb +57 -2
- data/spec/lib/schedule_spec.rb +6 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/fixtures/dst_exdate_event.ics +44 -0
- data/spec/support/fixtures/dst_rdate_event.ics +43 -0
- data/spec/support/fixtures/first_saturday_of_month_event.ics +2 -2
- metadata +34 -35
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 63f250f25a8343efa18c4ef48d0df4cdd5c64788f18955be18cd4a8aeacc2e00
|
|
4
|
+
data.tar.gz: 3d756b812cbd392f91801496f63202c25aaf49d043c4c1547a87835a05ee4989
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bad755f7b955d119a1aa052d5c74a2d7598f1ddc5e162fe303da056fe602428d662cce3013a8965bff2fed3fccaef4cface131c54346af73da15d2e20b37f4f0
|
|
7
|
+
data.tar.gz: 51857364b18289b65c3ca89c486661f3e7cf2d0efdd8a5375774e5fcc7267fe503e38100e57731d248c5a2fdef934109f754c838f1736ce7ad2a8aacf6209810
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Ruby
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- master
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
ruby:
|
|
20
|
+
- 3.2
|
|
21
|
+
- 3.3
|
|
22
|
+
- 3.4
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- name: Set up Ruby
|
|
27
|
+
uses: ruby/setup-ruby@v1
|
|
28
|
+
with:
|
|
29
|
+
ruby-version: ${{ matrix.ruby }}
|
|
30
|
+
bundler-cache: true
|
|
31
|
+
- name: Run rspec tests
|
|
32
|
+
run: bundle exec rake spec
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# icalendar-recurrence CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.2.1 (November 1, 2025)
|
|
4
|
+
|
|
5
|
+
- Preserve the timezone of time_object with ActiveSupport >= 7.2 (PR #43)
|
|
6
|
+
|
|
7
|
+
## 1.2.0 (November 1, 2023)
|
|
8
|
+
|
|
9
|
+
- Fix exception date handling across DST boundaries (PR #25)
|
|
10
|
+
- Add `:parent` to the Occurrence class, to match constructor for Icalendar::Component (PR #23)
|
|
11
|
+
- Add `Event#all_occurrences` method (PR #28)
|
|
12
|
+
- Move TZInfo to a runtime dependency (PR #31)
|
|
13
|
+
- Handle `RDATE` recurrence rules (PR #33)
|
|
14
|
+
- Use built-in `.utc` method for occurrence time in all cases (PR #35 & #37)
|
|
15
|
+
|
|
3
16
|
## 1.1.2 (September 23, 2016)
|
|
4
17
|
|
|
5
18
|
- Loosen dependency on ice_cube gem to allow minor upgrades (issue #16)
|
data/README.md
CHANGED
|
@@ -20,11 +20,15 @@ and run `bundle install` from your shell.
|
|
|
20
20
|
require 'date' # for parse method
|
|
21
21
|
require 'icalendar/recurrence'
|
|
22
22
|
|
|
23
|
-
calendars = Icalendar.parse(File.read(path_to_ics)) # parse an ICS file
|
|
23
|
+
calendars = Icalendar::Calendar.parse(File.read(path_to_ics)) # parse an ICS file
|
|
24
24
|
event = Array(calendars).first.events.first # retrieve the first event
|
|
25
25
|
event.occurrences_between(Date.parse("2014-01-01"), Date.parse("2014-02-01")) # get all occurrence for one month
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Get all occurrences
|
|
29
|
+
|
|
30
|
+
To get all occurrences you can use `all_occurrences`. This only works when you have specified an ending using `until` or `count` in your RRULE.
|
|
31
|
+
|
|
28
32
|
### Working with occurrences
|
|
29
33
|
|
|
30
34
|
An event occurrence is a simple struct object with `start_time` and `end_time` methods.
|
|
@@ -67,7 +71,7 @@ EOF
|
|
|
67
71
|
|
|
68
72
|
# An event that occurs every day, starting January 1, 2014 with one excluded
|
|
69
73
|
# date. January 28, 2014 will not appear in the occurrences.
|
|
70
|
-
calendars = Icalendar.parse(ics_string)
|
|
74
|
+
calendars = Icalendar::Calendar.parse(ics_string)
|
|
71
75
|
every_day_except_jan_28 = Array(calendars).first.events.first
|
|
72
76
|
puts "Every day except January 28, 2014, occurrences from 2014-01-01 to 2014-02-01:"
|
|
73
77
|
puts every_day_except_jan_28.occurrences_between(Date.parse("2014-01-01"), Date.parse("2014-02-01"))
|
|
@@ -19,13 +19,13 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
|
|
20
20
|
spec.add_runtime_dependency 'icalendar', '~> 2.0'
|
|
21
21
|
spec.add_runtime_dependency 'ice_cube', '~> 0.16'
|
|
22
|
+
spec.add_runtime_dependency 'tzinfo', '~> 2.0'
|
|
22
23
|
|
|
23
|
-
spec.add_development_dependency 'activesupport', '~>
|
|
24
|
+
spec.add_development_dependency 'activesupport', '~> 7.1'
|
|
24
25
|
spec.add_development_dependency 'awesome_print'
|
|
25
26
|
spec.add_development_dependency 'pry'
|
|
26
|
-
spec.add_development_dependency 'bundler', '~>
|
|
27
|
-
spec.add_development_dependency 'rake', '
|
|
28
|
-
spec.add_development_dependency 'rspec', '~>
|
|
29
|
-
spec.add_development_dependency 'timecop', '~> 0.
|
|
30
|
-
spec.add_development_dependency 'tzinfo', '~> 0.3'
|
|
27
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
28
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
|
30
|
+
spec.add_development_dependency 'timecop', '~> 0.9'
|
|
31
31
|
end
|
|
@@ -2,7 +2,7 @@ require 'ice_cube'
|
|
|
2
2
|
|
|
3
3
|
module Icalendar
|
|
4
4
|
module Recurrence
|
|
5
|
-
class Occurrence < Struct.new(:start_time, :end_time)
|
|
5
|
+
class Occurrence < Struct.new(:start_time, :end_time, :parent)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
class Schedule
|
|
@@ -49,20 +49,10 @@ module Icalendar
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def convert_ice_cube_occurrence(ice_cube_occurrence)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
tz = TZInfo::Timezone.get(timezone)
|
|
55
|
-
start_time = tz.local_to_utc(ice_cube_occurrence.start_time)
|
|
56
|
-
end_time = tz.local_to_utc(ice_cube_occurrence.end_time)
|
|
57
|
-
rescue TZInfo::InvalidTimezoneIdentifier => e
|
|
58
|
-
warn "Unknown TZID specified in ical event (#{timezone.inspect}), ignoring (will likely cause event to be at wrong time!)"
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
start_time ||= ice_cube_occurrence.start_time
|
|
63
|
-
end_time ||= ice_cube_occurrence.end_time
|
|
52
|
+
start_time = ice_cube_occurrence.start_time.utc
|
|
53
|
+
end_time = ice_cube_occurrence.end_time.utc
|
|
64
54
|
|
|
65
|
-
Icalendar::Recurrence::Occurrence.new(start_time, end_time)
|
|
55
|
+
Icalendar::Recurrence::Occurrence.new(start_time, end_time, @event)
|
|
66
56
|
end
|
|
67
57
|
|
|
68
58
|
def ice_cube_schedule
|
|
@@ -77,7 +67,17 @@ module Icalendar
|
|
|
77
67
|
|
|
78
68
|
event.exdate.each do |exception_date_or_dates|
|
|
79
69
|
Array(exception_date_or_dates).each do |exception_date|
|
|
80
|
-
|
|
70
|
+
# exception times should have the same tz offset as the event start or they'll be ignored as different times
|
|
71
|
+
# ignored if ActiveSupport::TimeWithZone is available
|
|
72
|
+
schedule.add_exception_time(TimeUtil.to_time(exception_date, moment: start_time))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
event.rdate.each do |extra_date_or_dates|
|
|
77
|
+
Array(extra_date_or_dates).each do |extra_date|
|
|
78
|
+
# exception times should have the same tz offset as the event start or they'll be ignored as different times
|
|
79
|
+
# ignored if ActiveSupport::TimeWithZone is available
|
|
80
|
+
schedule.add_recurrence_time(TimeUtil.to_time(extra_date, moment: start_time))
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
require 'tzinfo'
|
|
2
|
+
begin
|
|
3
|
+
require 'active_support/time'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
# that's ok, will just fall back to Time
|
|
6
|
+
end
|
|
2
7
|
|
|
3
8
|
module Icalendar
|
|
4
9
|
module Recurrence
|
|
5
10
|
module TimeUtil
|
|
6
|
-
def datetime_to_time(datetime)
|
|
11
|
+
def datetime_to_time(datetime, options = {})
|
|
7
12
|
raise ArgumentError, "Unsupported DateTime object passed (must be Icalendar::Values::DateTime#{datetime.class} passed instead)" unless supported_datetime_object?(datetime)
|
|
8
|
-
|
|
13
|
+
options[:moment] ||= datetime.to_date
|
|
14
|
+
offset = timezone_offset(datetime.ical_params["tzid"], options)
|
|
9
15
|
offset ||= datetime.strftime("%:z")
|
|
10
16
|
|
|
11
17
|
Time.new(datetime.year, datetime.month, datetime.mday, datetime.hour, datetime.min, datetime.sec, offset)
|
|
@@ -16,11 +22,18 @@ module Icalendar
|
|
|
16
22
|
Time.new(date.year, date.month, date.mday)
|
|
17
23
|
end
|
|
18
24
|
|
|
19
|
-
def to_time(time_object)
|
|
25
|
+
def to_time(time_object, options = {})
|
|
20
26
|
if supported_time_object?(time_object)
|
|
21
27
|
time_object
|
|
28
|
+
elsif supported_icalendar_object?(time_object)
|
|
29
|
+
if !defined?(ActiveSupport) || Gem::Version.new(ActiveSupport::VERSION::STRING) < Gem::Version.new('7.2')
|
|
30
|
+
time_object.value
|
|
31
|
+
else
|
|
32
|
+
# Preserve the timezone of the time_object
|
|
33
|
+
time_object.value.to_time.in_time_zone(time_object.value.time_zone)
|
|
34
|
+
end
|
|
22
35
|
elsif supported_datetime_object?(time_object)
|
|
23
|
-
datetime_to_time(time_object)
|
|
36
|
+
datetime_to_time(time_object, options)
|
|
24
37
|
elsif supported_date_object?(time_object)
|
|
25
38
|
date_to_time(time_object)
|
|
26
39
|
elsif time_object.is_a?(String)
|
|
@@ -41,8 +54,7 @@ module Icalendar
|
|
|
41
54
|
#
|
|
42
55
|
def timezone_offset(tzid, options = {})
|
|
43
56
|
tzid = Array(tzid).first
|
|
44
|
-
|
|
45
|
-
moment = options.fetch(:moment)
|
|
57
|
+
moment = options.fetch(:moment, Time.now)
|
|
46
58
|
utc_moment = to_time(moment.clone).utc
|
|
47
59
|
tzid = tzid.to_s.gsub(/^(["'])|(["'])$/, "")
|
|
48
60
|
utc_offset = TZInfo::Timezone.get(tzid).period_for_utc(utc_moment).utc_total_offset # this seems to work, but I feel like there is a lurking bug
|
|
@@ -50,7 +62,7 @@ module Icalendar
|
|
|
50
62
|
hour_offset = "+#{hour_offset}" if hour_offset >= 0
|
|
51
63
|
match = hour_offset.to_s.match(/(\+|-)(\d+)/)
|
|
52
64
|
"#{match[1]}#{match[2].rjust(2, "0")}:00"
|
|
53
|
-
rescue TZInfo::InvalidTimezoneIdentifier
|
|
65
|
+
rescue TZInfo::InvalidTimezoneIdentifier
|
|
54
66
|
nil
|
|
55
67
|
end
|
|
56
68
|
|
|
@@ -67,8 +79,16 @@ module Icalendar
|
|
|
67
79
|
time_object.is_a?(Icalendar::Values::DateTime)
|
|
68
80
|
end
|
|
69
81
|
|
|
82
|
+
def supported_icalendar_object?(time_object)
|
|
83
|
+
time_object.is_a?(Icalendar::Values::DateTime) && supported_activesupport_object?(time_object.value)
|
|
84
|
+
end
|
|
85
|
+
|
|
70
86
|
def supported_time_object?(time_object)
|
|
71
|
-
time_object.is_a?(Time)
|
|
87
|
+
time_object.is_a?(Time) || supported_activesupport_object?(time_object)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def supported_activesupport_object?(time_object)
|
|
91
|
+
defined?(ActiveSupport::TimeWithZone) && time_object.is_a?(ActiveSupport::TimeWithZone)
|
|
72
92
|
end
|
|
73
93
|
|
|
74
94
|
# Replaces the existing offset with one associated with given TZID. Does
|
data/spec/lib/recurrence_spec.rb
CHANGED
|
@@ -86,6 +86,36 @@ describe "Event#occurrences_between" do
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
context "event repeating weekly with exdates" do
|
|
90
|
+
let(:event) { example_event :dst_exdate }
|
|
91
|
+
|
|
92
|
+
it "properly skips christmas and new years" do
|
|
93
|
+
occurrences = event.occurrences_between(start_time, start_time + 4.months)
|
|
94
|
+
|
|
95
|
+
expect(occurrences.length).to eq(14)
|
|
96
|
+
christmas_day = Date.new 2019, 12, 25
|
|
97
|
+
expect(occurrences.all? { |o| o.start_time.to_date != christmas_day }).to eq(true)
|
|
98
|
+
new_years_day = Date.new 2020, 1, 1
|
|
99
|
+
expect(occurrences.all? { |o| o.start_time.to_date != new_years_day }).to eq(true)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
context "event repeating weekly with rdates" do
|
|
104
|
+
let(:event) { example_event :dst_rdate }
|
|
105
|
+
|
|
106
|
+
it "properly includes specified rdates" do
|
|
107
|
+
occurrences = event.occurrences_between(start_time, start_time + 4.months)
|
|
108
|
+
extra_day_after_period = Date.new 2020, 02, 01
|
|
109
|
+
occurrence = occurrences.find { |o| o.start_time.to_date == extra_day_after_period }
|
|
110
|
+
|
|
111
|
+
expect(occurrences.length).to eq(17)
|
|
112
|
+
expect(occurrence).to_not be_nil
|
|
113
|
+
|
|
114
|
+
expect(occurrence.start_time).to eq(Time.new 2020, 02, 01, 19, 0, 0, "+00:00")
|
|
115
|
+
expect(occurrence.end_time).to eq(Time.new 2020, 02, 01, 21, 0, 0, "+00:00")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
89
119
|
context "event repeating yearly" do
|
|
90
120
|
let(:event) { example_event :first_of_every_year }
|
|
91
121
|
|
|
@@ -145,8 +175,8 @@ describe "Event#occurrences_between" do
|
|
|
145
175
|
occurrences = event.occurrences_between(start_time, start_time + 55.days)
|
|
146
176
|
|
|
147
177
|
expected_start_times = [
|
|
148
|
-
Time.parse("2014-01-04 at
|
|
149
|
-
Time.parse("2014-02-01 at
|
|
178
|
+
Time.parse("2014-01-04 at 10am").force_zone("America/Los_Angeles"),
|
|
179
|
+
Time.parse("2014-02-01 at 10am").force_zone("America/Los_Angeles"),
|
|
150
180
|
]
|
|
151
181
|
|
|
152
182
|
expect(occurrences.length).to eq(2)
|
|
@@ -174,3 +204,28 @@ describe "Event#occurrences_between" do
|
|
|
174
204
|
end
|
|
175
205
|
end
|
|
176
206
|
end
|
|
207
|
+
|
|
208
|
+
describe "Event#all_occurrences" do
|
|
209
|
+
let(:start_time) { event.start_time }
|
|
210
|
+
|
|
211
|
+
context "event repeating once a month for three months" do
|
|
212
|
+
let(:event) { example_event :one_day_a_month_for_three_months }
|
|
213
|
+
|
|
214
|
+
it "get all 3 occurences" do
|
|
215
|
+
occurrences = event.all_occurrences
|
|
216
|
+
|
|
217
|
+
expect(occurrences.length).to eq(3)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context "event repeating yearly" do
|
|
222
|
+
let(:event) { example_event :first_of_every_year }
|
|
223
|
+
|
|
224
|
+
it "can not get all occurences when not using count or until" do
|
|
225
|
+
expect {
|
|
226
|
+
event.all_occurrences
|
|
227
|
+
}.to raise_error(ArgumentError)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
data/spec/lib/schedule_spec.rb
CHANGED
|
@@ -21,6 +21,12 @@ describe Icalendar::Recurrence::Schedule do
|
|
|
21
21
|
expect(occurrences.count).to eq(7)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
it 'returns object whose event method matches the origin event' do
|
|
25
|
+
# Simple test to make sure the event carried over; different __id__
|
|
26
|
+
expect(example_occurrence.parent.custom_properties).to eq example_event(:daily).custom_properties
|
|
27
|
+
expect(example_occurrence.parent.name).to eq example_event(:daily).name
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
context "timezoned event" do
|
|
25
31
|
let(:example_occurrence) do
|
|
26
32
|
timezoned_event = example_event :first_saturday_of_month
|
data/spec/spec_helper.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
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:Minimal example
|
|
7
|
+
X-WR-TIMEZONE:Europe/Berlin
|
|
8
|
+
X-WR-CALDESC:Example for #221
|
|
9
|
+
BEGIN:VTIMEZONE
|
|
10
|
+
TZID:Europe/Berlin
|
|
11
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
12
|
+
BEGIN:DAYLIGHT
|
|
13
|
+
TZOFFSETFROM:+0100
|
|
14
|
+
TZOFFSETTO:+0200
|
|
15
|
+
TZNAME:CEST
|
|
16
|
+
DTSTART:19700329T020000
|
|
17
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
18
|
+
END:DAYLIGHT
|
|
19
|
+
BEGIN:STANDARD
|
|
20
|
+
TZOFFSETFROM:+0200
|
|
21
|
+
TZOFFSETTO:+0100
|
|
22
|
+
TZNAME:CET
|
|
23
|
+
DTSTART:19701025T030000
|
|
24
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
25
|
+
END:STANDARD
|
|
26
|
+
END:VTIMEZONE
|
|
27
|
+
BEGIN:VEVENT
|
|
28
|
+
DTSTART;TZID=Europe/Berlin:20191016T100000
|
|
29
|
+
DTEND;TZID=Europe/Berlin:20191016T120000
|
|
30
|
+
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20200129T225959Z;BYDAY=WE
|
|
31
|
+
EXDATE;TZID=Europe/Berlin:20200101T100000
|
|
32
|
+
EXDATE;TZID=Europe/Berlin:20191225T100000
|
|
33
|
+
DTSTAMP:20191121T083508Z
|
|
34
|
+
UID:01234567890123456789@google.com
|
|
35
|
+
CREATED:20191001T090000Z
|
|
36
|
+
DESCRIPTION:This repeating event should not occur on the specified EXDATEs
|
|
37
|
+
LAST-MODIFIED:20191001T090000Z
|
|
38
|
+
LOCATION:Room 1
|
|
39
|
+
SEQUENCE:1
|
|
40
|
+
STATUS:CONFIRMED
|
|
41
|
+
SUMMARY:A repeating event
|
|
42
|
+
TRANSP:OPAQUE
|
|
43
|
+
END:VEVENT
|
|
44
|
+
END:VCALENDAR
|
|
@@ -0,0 +1,43 @@
|
|
|
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:Minimal example
|
|
7
|
+
X-WR-TIMEZONE:Europe/Berlin
|
|
8
|
+
X-WR-CALDESC:Example for #221
|
|
9
|
+
BEGIN:VTIMEZONE
|
|
10
|
+
TZID:Europe/Berlin
|
|
11
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
12
|
+
BEGIN:DAYLIGHT
|
|
13
|
+
TZOFFSETFROM:+0100
|
|
14
|
+
TZOFFSETTO:+0200
|
|
15
|
+
TZNAME:CEST
|
|
16
|
+
DTSTART:19700329T020000
|
|
17
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
18
|
+
END:DAYLIGHT
|
|
19
|
+
BEGIN:STANDARD
|
|
20
|
+
TZOFFSETFROM:+0200
|
|
21
|
+
TZOFFSETTO:+0100
|
|
22
|
+
TZNAME:CET
|
|
23
|
+
DTSTART:19701025T030000
|
|
24
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
25
|
+
END:STANDARD
|
|
26
|
+
END:VTIMEZONE
|
|
27
|
+
BEGIN:VEVENT
|
|
28
|
+
DTSTART;TZID=Europe/Berlin:20191016T100000
|
|
29
|
+
DTEND;TZID=Europe/Berlin:20191016T120000
|
|
30
|
+
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20200129T225959Z;BYDAY=WE
|
|
31
|
+
RDATE;TZID=Europe/Berlin:20200201T200000
|
|
32
|
+
DTSTAMP:20191121T083508Z
|
|
33
|
+
UID:01234567890123456789@google.com
|
|
34
|
+
CREATED:20191001T090000Z
|
|
35
|
+
DESCRIPTION:This repeating event should additionally occur on the specified RDATESs
|
|
36
|
+
LAST-MODIFIED:20191001T090000Z
|
|
37
|
+
LOCATION:Room 1
|
|
38
|
+
SEQUENCE:1
|
|
39
|
+
STATUS:CONFIRMED
|
|
40
|
+
SUMMARY:A repeating event
|
|
41
|
+
TRANSP:OPAQUE
|
|
42
|
+
END:VEVENT
|
|
43
|
+
END:VCALENDAR
|
|
@@ -29,8 +29,8 @@ DESCRIPTION:\nScheduled maintenance \n
|
|
|
29
29
|
X-MS-OLK-SENDER:mailto:foo@example.ca
|
|
30
30
|
ORGANIZER;SENT-BY="mailto:foo@example.ca":mailto:itadmin@example.ca
|
|
31
31
|
X-MS-OLK-SENDER:mailto:foo@example.ca
|
|
32
|
-
DTSTART;TZID="America/Los_Angeles":
|
|
33
|
-
DTEND;TZID="America/Los_Angeles":
|
|
32
|
+
DTSTART;TZID="America/Los_Angeles":20140104T100000
|
|
33
|
+
DTEND;TZID="America/Los_Angeles":20140104T110000
|
|
34
34
|
STATUS:CONFIRMED
|
|
35
35
|
CLASS:PUBLIC
|
|
36
36
|
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: icalendar-recurrence
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1
|
|
4
|
+
version: 1.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jordan Raine
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: icalendar
|
|
@@ -38,20 +37,34 @@ dependencies:
|
|
|
38
37
|
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '0.16'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: tzinfo
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.0'
|
|
41
54
|
- !ruby/object:Gem::Dependency
|
|
42
55
|
name: activesupport
|
|
43
56
|
requirement: !ruby/object:Gem::Requirement
|
|
44
57
|
requirements:
|
|
45
58
|
- - "~>"
|
|
46
59
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
60
|
+
version: '7.1'
|
|
48
61
|
type: :development
|
|
49
62
|
prerelease: false
|
|
50
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
64
|
requirements:
|
|
52
65
|
- - "~>"
|
|
53
66
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
67
|
+
version: '7.1'
|
|
55
68
|
- !ruby/object:Gem::Dependency
|
|
56
69
|
name: awesome_print
|
|
57
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,80 +99,65 @@ dependencies:
|
|
|
86
99
|
requirements:
|
|
87
100
|
- - "~>"
|
|
88
101
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
102
|
+
version: '2.0'
|
|
90
103
|
type: :development
|
|
91
104
|
prerelease: false
|
|
92
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
106
|
requirements:
|
|
94
107
|
- - "~>"
|
|
95
108
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
109
|
+
version: '2.0'
|
|
97
110
|
- !ruby/object:Gem::Dependency
|
|
98
111
|
name: rake
|
|
99
112
|
requirement: !ruby/object:Gem::Requirement
|
|
100
113
|
requirements:
|
|
101
|
-
- - "
|
|
114
|
+
- - ">="
|
|
102
115
|
- !ruby/object:Gem::Version
|
|
103
|
-
version:
|
|
116
|
+
version: 12.3.3
|
|
104
117
|
type: :development
|
|
105
118
|
prerelease: false
|
|
106
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
120
|
requirements:
|
|
108
|
-
- - "
|
|
121
|
+
- - ">="
|
|
109
122
|
- !ruby/object:Gem::Version
|
|
110
|
-
version:
|
|
123
|
+
version: 12.3.3
|
|
111
124
|
- !ruby/object:Gem::Dependency
|
|
112
125
|
name: rspec
|
|
113
126
|
requirement: !ruby/object:Gem::Requirement
|
|
114
127
|
requirements:
|
|
115
128
|
- - "~>"
|
|
116
129
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '
|
|
130
|
+
version: '3.12'
|
|
118
131
|
type: :development
|
|
119
132
|
prerelease: false
|
|
120
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
134
|
requirements:
|
|
122
135
|
- - "~>"
|
|
123
136
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '
|
|
137
|
+
version: '3.12'
|
|
125
138
|
- !ruby/object:Gem::Dependency
|
|
126
139
|
name: timecop
|
|
127
140
|
requirement: !ruby/object:Gem::Requirement
|
|
128
141
|
requirements:
|
|
129
142
|
- - "~>"
|
|
130
143
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: 0.
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - "~>"
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: 0.6.3
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: tzinfo
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - "~>"
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0.3'
|
|
144
|
+
version: '0.9'
|
|
146
145
|
type: :development
|
|
147
146
|
prerelease: false
|
|
148
147
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
148
|
requirements:
|
|
150
149
|
- - "~>"
|
|
151
150
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0.
|
|
153
|
-
description:
|
|
151
|
+
version: '0.9'
|
|
154
152
|
email:
|
|
155
153
|
- jnraine@gmail.com
|
|
156
154
|
executables: []
|
|
157
155
|
extensions: []
|
|
158
156
|
extra_rdoc_files: []
|
|
159
157
|
files:
|
|
158
|
+
- ".github/workflows/main.yml"
|
|
160
159
|
- ".gitignore"
|
|
161
160
|
- ".rspec"
|
|
162
|
-
- ".travis.yml"
|
|
163
161
|
- CHANGELOG.md
|
|
164
162
|
- Gemfile
|
|
165
163
|
- Guardfile
|
|
@@ -179,6 +177,8 @@ files:
|
|
|
179
177
|
- spec/lib/time_util_spec.rb
|
|
180
178
|
- spec/spec_helper.rb
|
|
181
179
|
- spec/support/fixtures/daily_event.ics
|
|
180
|
+
- spec/support/fixtures/dst_exdate_event.ics
|
|
181
|
+
- spec/support/fixtures/dst_rdate_event.ics
|
|
182
182
|
- spec/support/fixtures/embedded_timezone_event.ics
|
|
183
183
|
- spec/support/fixtures/every_monday_event.ics
|
|
184
184
|
- spec/support/fixtures/every_other_day_event.ics
|
|
@@ -201,7 +201,6 @@ homepage: https://github.com/icalendar/icalendar-recurrence
|
|
|
201
201
|
licenses:
|
|
202
202
|
- MIT
|
|
203
203
|
metadata: {}
|
|
204
|
-
post_install_message:
|
|
205
204
|
rdoc_options: []
|
|
206
205
|
require_paths:
|
|
207
206
|
- lib
|
|
@@ -216,9 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
216
215
|
- !ruby/object:Gem::Version
|
|
217
216
|
version: '0'
|
|
218
217
|
requirements: []
|
|
219
|
-
|
|
220
|
-
rubygems_version: 2.5.2.3
|
|
221
|
-
signing_key:
|
|
218
|
+
rubygems_version: 3.6.9
|
|
222
219
|
specification_version: 4
|
|
223
220
|
summary: Provides recurrence to icalendar gem.
|
|
224
221
|
test_files:
|
|
@@ -227,6 +224,8 @@ test_files:
|
|
|
227
224
|
- spec/lib/time_util_spec.rb
|
|
228
225
|
- spec/spec_helper.rb
|
|
229
226
|
- spec/support/fixtures/daily_event.ics
|
|
227
|
+
- spec/support/fixtures/dst_exdate_event.ics
|
|
228
|
+
- spec/support/fixtures/dst_rdate_event.ics
|
|
230
229
|
- spec/support/fixtures/embedded_timezone_event.ics
|
|
231
230
|
- spec/support/fixtures/every_monday_event.ics
|
|
232
231
|
- spec/support/fixtures/every_other_day_event.ics
|