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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a9a656fd81bc80eb43cf61e14d95a3b582f66bae80417da16a7e5d621910581
4
- data.tar.gz: df88f0f0a1619dbdcba157316aba616019d146fe74e46aba6d7603dc30b40c26
3
+ metadata.gz: 278fc513495386d4f80933b71c9ebb97195f6d9d365a2da920dd60f6375553d8
4
+ data.tar.gz: c6b6c63d1c24c7726b6e816cd350e5d7260c11a6e4475b8b886bd4658bfca15a
5
5
  SHA512:
6
- metadata.gz: a0ad4eb07a4123fba9260916530046bd008a9f076a26a276f09a31cce789aa0801a38fff3d553b7b11fcb3de072c72aff758921ad601d7e643b808495425b0df
7
- data.tar.gz: 06bab135b068613fdf8c4d10554a327a35b2072eb5e37c5e7bc7509b8e62b05e596153cfa8985065b2739e99eb7bc90dd8282c173972929900aa763d58e218b1
6
+ metadata.gz: 3e9e9a916e27b8b6adad3f65beaa9658ebd7a786a2d78b10e83d405a5520228e5ad2bbc1ad07dbeeec2dbd86b726f2edf9b08834bf13dc09e1c82248dd389c5f
7
+ data.tar.gz: b993e6df56b6f1219675c40cb2219514951771c6bbeacdd371e237677133e458f4938dbeb144452c98e70ccc2a5e28145fc3c1b6dc54a66030dedac4f8cee070
@@ -18,8 +18,6 @@ jobs:
18
18
  fail-fast: false
19
19
  matrix:
20
20
  ruby:
21
- - 3.1
22
- - 3.2
23
21
  - 3.3
24
22
  - 3.4
25
23
  - "4.0"
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
 
@@ -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
 
@@ -63,7 +63,8 @@ module Icalendar
63
63
  end
64
64
 
65
65
  def rrule
66
- start = (respond_to?(:local_start_at) ? local_start_at : local_start).to_datetime
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?(:local_start_at) ? local_start_at : local_start).to_datetime.strftime '%Y%m%dT%H%M%S'
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
 
@@ -10,6 +10,7 @@ module Icalendar
10
10
 
11
11
  def initialize(value, params = {}, *args)
12
12
  params.delete 'tzid'
13
+ params.delete :tzid
13
14
  if value.is_a? String
14
15
  begin
15
16
  parsed_date = ::Date.strptime(value, FORMAT)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Icalendar
4
4
 
5
- VERSION = '2.12.4'
5
+ VERSION = '2.12.5'
6
6
 
7
7
  end
@@ -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:19910915T010000
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
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.6
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