icalendar 2.10.3 → 2.11.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 +4 -4
- data/.github/workflows/main.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/icalendar.gemspec +2 -0
- data/lib/icalendar/offset/active_support_exact.rb +23 -0
- data/lib/icalendar/offset/active_support_partial.rb +27 -0
- data/lib/icalendar/offset/null.rb +17 -0
- data/lib/icalendar/offset/time_zone_store.rb +29 -0
- data/lib/icalendar/offset.rb +42 -0
- data/lib/icalendar/parser.rb +1 -1
- data/lib/icalendar/values/helpers/time_with_zone.rb +12 -34
- data/lib/icalendar/version.rb +1 -1
- data/spec/fixtures/two_time_events.ics +1 -1
- metadata +35 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d203d559d0522f82ba0036892eb054a5be722cd6e19af063a85563c9b8357d3b
|
4
|
+
data.tar.gz: 711e78355b4f08581ce8fc6d69d81ac0851e55c50f2931e56bf38344f594c3c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30825c56f9cabbc358966a315f1678b785013cb3316eaec30c5f91f512746ca7bbdc6384c322d404b67f1d8bcf60b1e9a85ad2b3b9d5b0399c64cc2c4695b718
|
7
|
+
data.tar.gz: 2776f4a9dfb99205c392f1f13d12d281dea120d9626b94341d520c297417f32ca1f5b9a1378ec4edea8e63d2ccfaaf16945cc4cc52bb0901bd869da0f2121e5a
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## Unreleased
|
2
2
|
|
3
|
+
## 2.11.1 - 2025-06-06
|
4
|
+
- Gracefully deal with malformed ics files that use spaces in the value parameter instead of hyphens
|
5
|
+
|
6
|
+
## 2.11.0 - 2025-04-12
|
7
|
+
- Require gems for ruby 3.4 that used to be in stdlib - Go Sueyoshi
|
8
|
+
- Refactor how timezone offsets are calculated - Pat Allan
|
9
|
+
- 'tzid' param is always returned as an array when accessing `ical_params` directly, rather than matching whatever the input parsed. ICS output remains unchanged
|
10
|
+
|
3
11
|
## 2.10.3 - 2024-09-21
|
4
12
|
- Override Icalendar::Value.== so that value objects can be compared to each other.
|
5
13
|
- Correctly load activesupport before activesupport/time
|
data/icalendar.gemspec
CHANGED
@@ -31,7 +31,9 @@ ActiveSupport is required for TimeWithZone support, but not required for general
|
|
31
31
|
|
32
32
|
s.required_ruby_version = '>= 2.4.0'
|
33
33
|
|
34
|
+
s.add_dependency 'base64'
|
34
35
|
s.add_dependency 'ice_cube', '~> 0.16'
|
36
|
+
s.add_dependency 'logger'
|
35
37
|
s.add_dependency 'ostruct'
|
36
38
|
|
37
39
|
s.add_development_dependency 'rake', '~> 13.0'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class ActiveSupportExact < Icalendar::Offset
|
6
|
+
def valid?
|
7
|
+
support_classes_defined? && tz
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
Icalendar.logger.debug("Plan a - parsing #{value}/#{tzid} as ActiveSupport::TimeWithZone")
|
12
|
+
# plan a - use ActiveSupport::TimeWithZone
|
13
|
+
Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def tz
|
19
|
+
@tz ||= ActiveSupport::TimeZone[tzid]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class ActiveSupportPartial < Offset
|
6
|
+
def valid?
|
7
|
+
support_classes_defined? && tz
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan c - try to find an ActiveSupport::TimeWithZone based on the first word of the tzid
|
12
|
+
Icalendar.logger.debug("Plan c - parsing #{value}/#{tz.tzinfo.name} as ActiveSupport::TimeWithZone")
|
13
|
+
Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def normalized_tzid
|
17
|
+
[tz.tzinfo.name]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def tz
|
23
|
+
@tz ||= ActiveSupport::TimeZone[tzid.split.first]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class Null < Offset
|
6
|
+
def valid?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan d - just ignore the tzid
|
12
|
+
Icalendar.logger.info("Ignoring timezone #{tzid} for time #{value}")
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
class TimeZoneStore < Offset
|
6
|
+
def valid?
|
7
|
+
timezone_store && tz_info
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalized_value
|
11
|
+
# plan b - use definition from provided `VTIMEZONE`
|
12
|
+
offset = tz_info.offset_for_local(value).to_s
|
13
|
+
|
14
|
+
Icalendar.logger.debug("Plan b - parsing #{value} with offset: #{offset}")
|
15
|
+
if value.respond_to?(:change)
|
16
|
+
value.change offset: offset
|
17
|
+
else
|
18
|
+
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def tz_info
|
25
|
+
@tz_info ||= timezone_store.retrieve(tzid)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
class Offset
|
5
|
+
def self.build(value, params, timezone_store)
|
6
|
+
return nil if params.nil? || params['tzid'].nil?
|
7
|
+
|
8
|
+
tzid = Array(params['tzid']).first
|
9
|
+
|
10
|
+
[
|
11
|
+
Icalendar::Offset::ActiveSupportExact,
|
12
|
+
Icalendar::Offset::TimeZoneStore,
|
13
|
+
Icalendar::Offset::ActiveSupportPartial,
|
14
|
+
Icalendar::Offset::Null
|
15
|
+
].lazy.map { |klass| klass.new(tzid, value, timezone_store) }.detect(&:valid?)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(tzid, value, timezone_store)
|
19
|
+
@tzid = tzid
|
20
|
+
@value = value
|
21
|
+
@timezone_store = timezone_store
|
22
|
+
end
|
23
|
+
|
24
|
+
def normalized_tzid
|
25
|
+
Array(tzid)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :tzid, :value, :timezone_store
|
31
|
+
|
32
|
+
def support_classes_defined?
|
33
|
+
defined?(ActiveSupport::TimeZone) &&
|
34
|
+
defined?(Icalendar::Values::Helpers::ActiveSupportTimeWithZoneAdapter)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require_relative "offset/active_support_exact"
|
40
|
+
require_relative "offset/active_support_partial"
|
41
|
+
require_relative "offset/null"
|
42
|
+
require_relative "offset/time_zone_store"
|
data/lib/icalendar/parser.rb
CHANGED
@@ -105,7 +105,7 @@ module Icalendar
|
|
105
105
|
((multi_property && value =~ WRAP_IN_ARRAY_REGEX_1) || value =~ WRAP_IN_ARRAY_REGEX_2)
|
106
106
|
end
|
107
107
|
|
108
|
-
GET_WRAPPER_CLASS_GSUB_REGEX = /(?:\A
|
108
|
+
GET_WRAPPER_CLASS_GSUB_REGEX = /(?:\A|-|\s+)(.)/.freeze
|
109
109
|
|
110
110
|
def get_wrapper_class(component, fields)
|
111
111
|
klass = component.class.default_property_types[fields[:name]]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "icalendar/offset"
|
4
|
+
|
3
5
|
begin
|
4
6
|
require 'active_support'
|
5
7
|
require 'active_support/time'
|
@@ -21,7 +23,13 @@ module Icalendar
|
|
21
23
|
params = Icalendar::DowncasedHash(params)
|
22
24
|
@tz_utc = params['tzid'] == 'UTC'
|
23
25
|
@timezone_store = params.delete 'x-tz-store'
|
24
|
-
|
26
|
+
|
27
|
+
offset = Icalendar::Offset.build(value, params, timezone_store)
|
28
|
+
|
29
|
+
@offset_value = offset&.normalized_value
|
30
|
+
params['tzid'] = offset.normalized_tzid if offset
|
31
|
+
|
32
|
+
super (@offset_value || value), params
|
25
33
|
end
|
26
34
|
|
27
35
|
def __getobj__
|
@@ -29,9 +37,9 @@ module Icalendar
|
|
29
37
|
if set_offset?
|
30
38
|
orig_value
|
31
39
|
else
|
32
|
-
|
33
|
-
__setobj__(
|
34
|
-
|
40
|
+
new_value = Icalendar::Offset.build(orig_value, ical_params, timezone_store)&.normalized_value
|
41
|
+
__setobj__(new_value) unless new_value.nil?
|
42
|
+
new_value || orig_value
|
35
43
|
end
|
36
44
|
end
|
37
45
|
|
@@ -42,36 +50,6 @@ module Icalendar
|
|
42
50
|
|
43
51
|
private
|
44
52
|
|
45
|
-
def offset_value(value, params)
|
46
|
-
@offset_value = unless params.nil? || params['tzid'].nil?
|
47
|
-
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
48
|
-
support_classes_defined = defined?(ActiveSupport::TimeZone) && defined?(ActiveSupportTimeWithZoneAdapter)
|
49
|
-
if support_classes_defined && (tz = ActiveSupport::TimeZone[tzid])
|
50
|
-
Icalendar.logger.debug("Plan a - parsing #{value}/#{tzid} as ActiveSupport::TimeWithZone")
|
51
|
-
# plan a - use ActiveSupport::TimeWithZone
|
52
|
-
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
53
|
-
elsif !timezone_store.nil? && !(x_tz_info = timezone_store.retrieve(tzid)).nil?
|
54
|
-
# plan b - use definition from provided `VTIMEZONE`
|
55
|
-
offset = x_tz_info.offset_for_local(value).to_s
|
56
|
-
Icalendar.logger.debug("Plan b - parsing #{value} with offset: #{offset}")
|
57
|
-
if value.respond_to?(:change)
|
58
|
-
value.change offset: offset
|
59
|
-
else
|
60
|
-
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
61
|
-
end
|
62
|
-
elsif support_classes_defined && (tz = ActiveSupport::TimeZone[tzid.split.first])
|
63
|
-
# plan c - try to find an ActiveSupport::TimeWithZone based on the first word of the tzid
|
64
|
-
Icalendar.logger.debug("Plan c - parsing #{value}/#{tz.tzinfo.name} as ActiveSupport::TimeWithZone")
|
65
|
-
params['tzid'] = [tz.tzinfo.name]
|
66
|
-
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
67
|
-
else
|
68
|
-
# plan d - just ignore the tzid
|
69
|
-
Icalendar.logger.info("Ignoring timezone #{tzid} for time #{value}")
|
70
|
-
nil
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
53
|
def set_offset?
|
76
54
|
!!@offset_value
|
77
55
|
end
|
data/lib/icalendar/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icalendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Ahearn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: base64
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: ice_cube
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +38,20 @@ dependencies:
|
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: logger
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: ostruct
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -195,6 +223,11 @@ files:
|
|
195
223
|
- lib/icalendar/journal.rb
|
196
224
|
- lib/icalendar/logger.rb
|
197
225
|
- lib/icalendar/marshable.rb
|
226
|
+
- lib/icalendar/offset.rb
|
227
|
+
- lib/icalendar/offset/active_support_exact.rb
|
228
|
+
- lib/icalendar/offset/active_support_partial.rb
|
229
|
+
- lib/icalendar/offset/null.rb
|
230
|
+
- lib/icalendar/offset/time_zone_store.rb
|
198
231
|
- lib/icalendar/parser.rb
|
199
232
|
- lib/icalendar/timezone.rb
|
200
233
|
- lib/icalendar/timezone_store.rb
|