icalendar 2.12.4 → 2.12.5
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 +4 -4
- data/.github/workflows/main.yml +0 -2
- data/CHANGELOG.md +5 -0
- data/lib/icalendar/parser.rb +11 -1
- data/lib/icalendar/tzinfo.rb +3 -2
- data/lib/icalendar/values/date.rb +1 -0
- data/lib/icalendar/version.rb +1 -1
- data/spec/fixtures/invalid_nesting.ics +47 -0
- data/spec/parser_spec.rb +8 -0
- data/spec/tzinfo_spec.rb +44 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 278fc513495386d4f80933b71c9ebb97195f6d9d365a2da920dd60f6375553d8
|
|
4
|
+
data.tar.gz: c6b6c63d1c24c7726b6e816cd350e5d7260c11a6e4475b8b886bd4658bfca15a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e9e9a916e27b8b6adad3f65beaa9658ebd7a786a2d78b10e83d405a5520228e5ad2bbc1ad07dbeeec2dbd86b726f2edf9b08834bf13dc09e1c82248dd389c5f
|
|
7
|
+
data.tar.gz: b993e6df56b6f1219675c40cb2219514951771c6bbeacdd371e237677133e458f4938dbeb144452c98e70ccc2a5e28145fc3c1b6dc54a66030dedac4f8cee070
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## 2.12.5 - 2026-09-25
|
|
4
|
+
- Generate timezone transition onsets and recurrence dates using the previous UTC offset. - Ben Abulafia
|
|
5
|
+
- Limit parsing depth
|
|
6
|
+
- Remove TZID from DATE values - Henry Blyth
|
|
7
|
+
|
|
3
8
|
## 2.12.4 - 2026-07-31
|
|
4
9
|
- TEXT unescape fix - Vincent Gao
|
|
5
10
|
|
data/lib/icalendar/parser.rb
CHANGED
|
@@ -6,8 +6,9 @@ require 'stringio'
|
|
|
6
6
|
module Icalendar
|
|
7
7
|
|
|
8
8
|
class Parser
|
|
9
|
-
attr_writer :component_class
|
|
9
|
+
attr_writer :component_class, :parse_depth_limit
|
|
10
10
|
attr_reader :source, :strict, :timezone_store, :verbose
|
|
11
|
+
attr_accessor :parse_depth
|
|
11
12
|
|
|
12
13
|
CLEAN_BAD_WRAPPING_GSUB_REGEX = /\r?\n[ \t]/.freeze
|
|
13
14
|
|
|
@@ -39,6 +40,7 @@ module Icalendar
|
|
|
39
40
|
read_in_data
|
|
40
41
|
@strict = strict
|
|
41
42
|
@verbose = verbose
|
|
43
|
+
@parse_depth = 0
|
|
42
44
|
@timezone_store = TimezoneStore.new
|
|
43
45
|
end
|
|
44
46
|
|
|
@@ -139,9 +141,16 @@ module Icalendar
|
|
|
139
141
|
@component_class ||= Icalendar::Calendar
|
|
140
142
|
end
|
|
141
143
|
|
|
144
|
+
def parse_depth_limit
|
|
145
|
+
# a valid ics file should only have 3 levels. Leaving some room for non-compliant custom components
|
|
146
|
+
@parse_depth_limit ||= 5
|
|
147
|
+
end
|
|
148
|
+
|
|
142
149
|
PARSE_COMPONENT_KLASS_NAME_GSUB_REGEX = /\AV/.freeze
|
|
143
150
|
|
|
144
151
|
def parse_component(component)
|
|
152
|
+
self.parse_depth += 1
|
|
153
|
+
raise ParseError.new("ics file nested too deeply") if parse_depth > parse_depth_limit
|
|
145
154
|
while (fields = next_fields)
|
|
146
155
|
if fields[:name] == 'end'
|
|
147
156
|
klass_name = fields[:value].gsub(PARSE_COMPONENT_KLASS_NAME_GSUB_REGEX, '').downcase.capitalize
|
|
@@ -161,6 +170,7 @@ module Icalendar
|
|
|
161
170
|
parse_property component, fields
|
|
162
171
|
end
|
|
163
172
|
end
|
|
173
|
+
self.parse_depth -= 1
|
|
164
174
|
component
|
|
165
175
|
end
|
|
166
176
|
|
data/lib/icalendar/tzinfo.rb
CHANGED
|
@@ -63,7 +63,8 @@ module Icalendar
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def rrule
|
|
66
|
-
|
|
66
|
+
# RFC 5545 defines the onset in local time using TZOFFSETFROM.
|
|
67
|
+
start = (respond_to?(:local_end_at) ? local_end_at : local_end).to_datetime
|
|
67
68
|
# this is somewhat of a hack, but seems to work ok
|
|
68
69
|
# assumes that no timezone transition is in law as "4th X of the month"
|
|
69
70
|
# but only as 1st X, 2nd X, 3rd X, or Last X
|
|
@@ -78,7 +79,7 @@ module Icalendar
|
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def dtstart
|
|
81
|
-
(respond_to?(:
|
|
82
|
+
(respond_to?(:local_end_at) ? local_end_at : local_end).to_datetime.strftime '%Y%m%dT%H%M%S'
|
|
82
83
|
end
|
|
83
84
|
end
|
|
84
85
|
|
data/lib/icalendar/version.rb
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
BEGIN:VCALENDAR
|
|
2
|
+
VERSION:2.0
|
|
3
|
+
PRODID:bsprodidfortestabc123
|
|
4
|
+
BEGIN:VEVENT
|
|
5
|
+
UID:bsuidfortestabc123
|
|
6
|
+
ORGANIZER:mailto:joebob@random.net
|
|
7
|
+
ATTACH:http://bush.sucks.org/impeach/him.rhtml
|
|
8
|
+
ATTACH:http://corporations-dominate.existence.net/why.rhtml
|
|
9
|
+
SUMMARY:This is a really long summary
|
|
10
|
+
to test the method of unfolding lines\,
|
|
11
|
+
so I'm just going to ma
|
|
12
|
+
ke it
|
|
13
|
+
a whol
|
|
14
|
+
e
|
|
15
|
+
bunch of lines.
|
|
16
|
+
CLASS:PRIVATE
|
|
17
|
+
PRIORITY:2
|
|
18
|
+
CUSTOMFIELD:Not properly noted as custom with X- prefix.
|
|
19
|
+
GEO:37.386013;-122.0829322
|
|
20
|
+
DTSTART;TZID=US-Mountain:20050120T170000
|
|
21
|
+
DTEND:20050120T184500
|
|
22
|
+
DTSTAMP:20050118T211523Z
|
|
23
|
+
NAME:SomeName
|
|
24
|
+
BEGIN:X-NESTING
|
|
25
|
+
BEGIN:X-NESTING
|
|
26
|
+
BEGIN:X-NESTING
|
|
27
|
+
BEGIN:X-NESTING
|
|
28
|
+
BEGIN:X-NESTING
|
|
29
|
+
BEGIN:X-NESTING
|
|
30
|
+
BEGIN:X-NESTING
|
|
31
|
+
BEGIN:X-NESTING
|
|
32
|
+
BEGIN:X-NESTING
|
|
33
|
+
BEGIN:X-NESTING
|
|
34
|
+
BEGIN:X-NESTING
|
|
35
|
+
END:X-NESTING
|
|
36
|
+
END:X-NESTING
|
|
37
|
+
END:X-NESTING
|
|
38
|
+
END:X-NESTING
|
|
39
|
+
END:X-NESTING
|
|
40
|
+
END:X-NESTING
|
|
41
|
+
END:X-NESTING
|
|
42
|
+
END:X-NESTING
|
|
43
|
+
END:X-NESTING
|
|
44
|
+
END:X-NESTING
|
|
45
|
+
END:X-NESTING
|
|
46
|
+
END:VEVENT
|
|
47
|
+
END:VCALENDAR
|
data/spec/parser_spec.rb
CHANGED
|
@@ -87,6 +87,14 @@ describe Icalendar::Parser do
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
describe '#parse with invalid nesting' do
|
|
91
|
+
let(:fn) { 'invalid_nesting.ics' }
|
|
92
|
+
|
|
93
|
+
it 'raises a ParseError' do
|
|
94
|
+
expect { subject.parse }.to raise_error Icalendar::Parser::ParseError
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
90
98
|
describe '#parse with bad line' do
|
|
91
99
|
let(:fn) { 'single_event_bad_line.ics' }
|
|
92
100
|
|
data/spec/tzinfo_spec.rb
CHANGED
|
@@ -25,6 +25,49 @@ describe 'TZInfo::Timezone' do
|
|
|
25
25
|
specify { expect(subject.standards.first.rrule.first.value_ical).to eq "FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10" }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
describe 'transition onset' do
|
|
29
|
+
let(:date) { DateTime.new 2021, 7, 1 }
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
'Europe/Stockholm' => {
|
|
33
|
+
daylight: ['20210328T020000', '+0100', '20210328T010000Z'],
|
|
34
|
+
standard: ['20211031T030000', '+0200', '20211031T010000Z']
|
|
35
|
+
},
|
|
36
|
+
'Australia/Lord_Howe' => {
|
|
37
|
+
daylight: ['20211003T020000', '+1030', '20211002T153000Z'],
|
|
38
|
+
standard: ['20210404T020000', '+1100', '20210403T150000Z']
|
|
39
|
+
}
|
|
40
|
+
}.each do |identifier, transitions|
|
|
41
|
+
context identifier do
|
|
42
|
+
let(:tz) { TZInfo::Timezone.get identifier }
|
|
43
|
+
|
|
44
|
+
transitions.each do |kind, (local, offset, utc)|
|
|
45
|
+
it "serializes the #{kind} onset using the previous offset" do
|
|
46
|
+
calendar = Icalendar::Calendar.new
|
|
47
|
+
calendar.add_timezone subject
|
|
48
|
+
parsed = Icalendar::Calendar.parse(calendar.to_ical).first.timezones.first
|
|
49
|
+
component = parsed.public_send("#{kind}s").first
|
|
50
|
+
|
|
51
|
+
expect(component.dtstart.value_ical).to eq local
|
|
52
|
+
expect(component.tzoffsetfrom.value_ical).to eq offset
|
|
53
|
+
onset = DateTime.strptime("#{component.dtstart.value_ical}#{offset}", '%Y%m%dT%H%M%S%z')
|
|
54
|
+
expect(onset.new_offset(0).strftime('%Y%m%dT%H%M%SZ')).to eq utc
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'when the clock change crosses midnight' do
|
|
61
|
+
let(:tz) { TZInfo::Timezone.get 'America/Santiago' }
|
|
62
|
+
|
|
63
|
+
it 'uses the previous-offset date for both DTSTART and the recurrence rule' do
|
|
64
|
+
standard = subject.standards.first
|
|
65
|
+
expect(standard.dtstart.value_ical).to eq '20210404T000000'
|
|
66
|
+
expect(standard.rrule.first.value_ical).to eq 'FREQ=YEARLY;BYDAY=1SU;BYMONTH=4'
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
28
71
|
describe 'no end transition' do
|
|
29
72
|
let(:tz) { TZInfo::Timezone.get 'Asia/Shanghai' }
|
|
30
73
|
let(:date) { DateTime.now }
|
|
@@ -34,7 +77,7 @@ describe 'TZInfo::Timezone' do
|
|
|
34
77
|
BEGIN:VTIMEZONE
|
|
35
78
|
TZID:Asia/Shanghai
|
|
36
79
|
BEGIN:STANDARD
|
|
37
|
-
DTSTART:
|
|
80
|
+
DTSTART:19910915T020000
|
|
38
81
|
TZOFFSETFROM:+0900
|
|
39
82
|
TZOFFSETTO:+0800
|
|
40
83
|
TZNAME:CST
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: icalendar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.12.
|
|
4
|
+
version: 2.12.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Ahearn
|
|
@@ -261,6 +261,7 @@ files:
|
|
|
261
261
|
- spec/fixtures/bad_wrapping.ics
|
|
262
262
|
- spec/fixtures/custom_component.ics
|
|
263
263
|
- spec/fixtures/event.ics
|
|
264
|
+
- spec/fixtures/invalid_nesting.ics
|
|
264
265
|
- spec/fixtures/nondefault_values.ics
|
|
265
266
|
- spec/fixtures/nonstandard.ics
|
|
266
267
|
- spec/fixtures/recurrence.ics
|
|
@@ -320,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
320
321
|
- !ruby/object:Gem::Version
|
|
321
322
|
version: '0'
|
|
322
323
|
requirements: []
|
|
323
|
-
rubygems_version: 4.0.
|
|
324
|
+
rubygems_version: 4.0.16
|
|
324
325
|
specification_version: 4
|
|
325
326
|
summary: A ruby implementation of the iCalendar specification (RFC-5545).
|
|
326
327
|
test_files:
|
|
@@ -331,6 +332,7 @@ test_files:
|
|
|
331
332
|
- spec/fixtures/bad_wrapping.ics
|
|
332
333
|
- spec/fixtures/custom_component.ics
|
|
333
334
|
- spec/fixtures/event.ics
|
|
335
|
+
- spec/fixtures/invalid_nesting.ics
|
|
334
336
|
- spec/fixtures/nondefault_values.ics
|
|
335
337
|
- spec/fixtures/nonstandard.ics
|
|
336
338
|
- spec/fixtures/recurrence.ics
|